mirror of
https://github.com/BookStackApp/BookStack.git
synced 2025-05-03 07:49:57 +00:00
Lexical: Connected link selector to link form
This commit is contained in:
parent
accf2565a0
commit
1ef4044419
3 changed files with 69 additions and 14 deletions
resources/js/wysiwyg
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
## In progress
|
## In progress
|
||||||
|
|
||||||
//
|
- Link heading-based ID reference menu
|
||||||
|
|
||||||
## Main Todo
|
## Main Todo
|
||||||
|
|
||||||
|
@ -10,8 +10,6 @@
|
||||||
- Alignments: Handle inline block content (image, video)
|
- Alignments: Handle inline block content (image, video)
|
||||||
- Image paste upload
|
- Image paste upload
|
||||||
- Keyboard shortcuts support
|
- Keyboard shortcuts support
|
||||||
- Link popup menu for cross-content reference
|
|
||||||
- Link heading-based ID reference menu
|
|
||||||
- Drawing gallery integration
|
- Drawing gallery integration
|
||||||
- Support media src conversions (https://github.com/tinymce/tinymce/blob/release/6.6/modules/tinymce/src/plugins/media/main/ts/core/UrlPatterns.ts)
|
- Support media src conversions (https://github.com/tinymce/tinymce/blob/release/6.6/modules/tinymce/src/plugins/media/main/ts/core/UrlPatterns.ts)
|
||||||
- Media resize support (like images)
|
- Media resize support (like images)
|
||||||
|
|
|
@ -5,9 +5,9 @@ import {
|
||||||
EditorSelectFormFieldDefinition
|
EditorSelectFormFieldDefinition
|
||||||
} from "../../framework/forms";
|
} from "../../framework/forms";
|
||||||
import {EditorUiContext} from "../../framework/core";
|
import {EditorUiContext} from "../../framework/core";
|
||||||
import {$createTextNode, $getSelection} from "lexical";
|
import {$createTextNode, $getSelection, $insertNodes} from "lexical";
|
||||||
import {$isImageNode, ImageNode} from "../../../nodes/image";
|
import {$isImageNode, ImageNode} from "../../../nodes/image";
|
||||||
import {$createLinkNode} from "@lexical/link";
|
import {$createLinkNode, $isLinkNode} from "@lexical/link";
|
||||||
import {$createMediaNodeFromHtml, $createMediaNodeFromSrc, $isMediaNode, MediaNode} from "../../../nodes/media";
|
import {$createMediaNodeFromHtml, $createMediaNodeFromSrc, $isMediaNode, MediaNode} from "../../../nodes/media";
|
||||||
import {$insertNodeToNearestRoot} from "@lexical/utils";
|
import {$insertNodeToNearestRoot} from "@lexical/utils";
|
||||||
import {$getNodeFromSelection} from "../../../utils/selection";
|
import {$getNodeFromSelection} from "../../../utils/selection";
|
||||||
|
@ -16,6 +16,8 @@ import {EditorActionField} from "../../framework/blocks/action-field";
|
||||||
import {EditorButton} from "../../framework/buttons";
|
import {EditorButton} from "../../framework/buttons";
|
||||||
import {showImageManager} from "../../../utils/images";
|
import {showImageManager} from "../../../utils/images";
|
||||||
import searchImageIcon from "@icons/editor/image-search.svg";
|
import searchImageIcon from "@icons/editor/image-search.svg";
|
||||||
|
import searchIcon from "@icons/search.svg";
|
||||||
|
import {showLinkSelector} from "../../../utils/links";
|
||||||
|
|
||||||
export function $showImageForm(image: ImageNode, context: EditorUiContext) {
|
export function $showImageForm(image: ImageNode, context: EditorUiContext) {
|
||||||
const imageModal: EditorFormModal = context.manager.createModal('image');
|
const imageModal: EditorFormModal = context.manager.createModal('image');
|
||||||
|
@ -97,23 +99,62 @@ export const link: EditorFormDefinition = {
|
||||||
async action(formData, context: EditorUiContext) {
|
async action(formData, context: EditorUiContext) {
|
||||||
context.editor.update(() => {
|
context.editor.update(() => {
|
||||||
|
|
||||||
|
const url = formData.get('url')?.toString() || '';
|
||||||
|
const title = formData.get('title')?.toString() || ''
|
||||||
|
const target = formData.get('target')?.toString() || '';
|
||||||
|
const text = formData.get('text')?.toString() || '';
|
||||||
|
|
||||||
const selection = $getSelection();
|
const selection = $getSelection();
|
||||||
|
let link = $getNodeFromSelection(selection, $isLinkNode);
|
||||||
|
if ($isLinkNode(link)) {
|
||||||
|
link.setURL(url);
|
||||||
|
link.setTarget(target);
|
||||||
|
link.setTitle(title);
|
||||||
|
} else {
|
||||||
|
link = $createLinkNode(url, {
|
||||||
|
title: title,
|
||||||
|
target: target,
|
||||||
|
});
|
||||||
|
|
||||||
const linkNode = $createLinkNode(formData.get('url')?.toString() || '', {
|
$insertNodes([link]);
|
||||||
title: formData.get('title')?.toString() || '',
|
}
|
||||||
target: formData.get('target')?.toString() || '',
|
|
||||||
});
|
|
||||||
linkNode.append($createTextNode(formData.get('text')?.toString() || ''));
|
|
||||||
|
|
||||||
selection?.insertNodes([linkNode]);
|
if ($isLinkNode(link)) {
|
||||||
|
for (const child of link.getChildren()) {
|
||||||
|
child.remove(true);
|
||||||
|
}
|
||||||
|
link.append($createTextNode(text));
|
||||||
|
}
|
||||||
});
|
});
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
fields: [
|
fields: [
|
||||||
{
|
{
|
||||||
label: 'URL',
|
build() {
|
||||||
name: 'url',
|
return new EditorActionField(
|
||||||
type: 'text',
|
new EditorFormField({
|
||||||
|
label: 'URL',
|
||||||
|
name: 'url',
|
||||||
|
type: 'text',
|
||||||
|
}),
|
||||||
|
new EditorButton({
|
||||||
|
label: 'Browse links',
|
||||||
|
icon: searchIcon,
|
||||||
|
action(context: EditorUiContext) {
|
||||||
|
showLinkSelector(entity => {
|
||||||
|
const modal = context.manager.getActiveModal('link');
|
||||||
|
if (modal) {
|
||||||
|
modal.getForm().setValues({
|
||||||
|
url: entity.link,
|
||||||
|
text: entity.name,
|
||||||
|
title: entity.name,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Text to display',
|
label: 'Text to display',
|
||||||
|
|
16
resources/js/wysiwyg/utils/links.ts
Normal file
16
resources/js/wysiwyg/utils/links.ts
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
import {EntitySelectorPopup} from "../../components";
|
||||||
|
|
||||||
|
type EditorEntityData = {
|
||||||
|
link: string;
|
||||||
|
name: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function showLinkSelector(callback: (entity: EditorEntityData) => any, selectionText?: string) {
|
||||||
|
const selector: EntitySelectorPopup = window.$components.first('entity-selector-popup') as EntitySelectorPopup;
|
||||||
|
selector.show((entity: EditorEntityData) => callback(entity), {
|
||||||
|
initialValue: selectionText,
|
||||||
|
searchEndpoint: '/search/entity-selector',
|
||||||
|
entityTypes: 'page,book,chapter,bookshelf',
|
||||||
|
entityPermission: 'view',
|
||||||
|
});
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue