Lexical: Connected link selector to link form

This commit is contained in:
Dan Brown 2024-08-16 11:22:12 +01:00
parent accf2565a0
commit 1ef4044419
No known key found for this signature in database
GPG Key ID: 46D9F943C24A2EF9
3 changed files with 69 additions and 14 deletions

View File

@ -2,7 +2,7 @@
## In progress
//
- Link heading-based ID reference menu
## Main Todo
@ -10,8 +10,6 @@
- Alignments: Handle inline block content (image, video)
- Image paste upload
- Keyboard shortcuts support
- Link popup menu for cross-content reference
- Link heading-based ID reference menu
- 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)
- Media resize support (like images)

View File

@ -5,9 +5,9 @@ import {
EditorSelectFormFieldDefinition
} from "../../framework/forms";
import {EditorUiContext} from "../../framework/core";
import {$createTextNode, $getSelection} from "lexical";
import {$createTextNode, $getSelection, $insertNodes} from "lexical";
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 {$insertNodeToNearestRoot} from "@lexical/utils";
import {$getNodeFromSelection} from "../../../utils/selection";
@ -16,6 +16,8 @@ import {EditorActionField} from "../../framework/blocks/action-field";
import {EditorButton} from "../../framework/buttons";
import {showImageManager} from "../../../utils/images";
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) {
const imageModal: EditorFormModal = context.manager.createModal('image');
@ -97,23 +99,62 @@ export const link: EditorFormDefinition = {
async action(formData, context: EditorUiContext) {
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();
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() || '', {
title: formData.get('title')?.toString() || '',
target: formData.get('target')?.toString() || '',
});
linkNode.append($createTextNode(formData.get('text')?.toString() || ''));
$insertNodes([link]);
}
selection?.insertNodes([linkNode]);
if ($isLinkNode(link)) {
for (const child of link.getChildren()) {
child.remove(true);
}
link.append($createTextNode(text));
}
});
return true;
},
fields: [
{
label: 'URL',
name: 'url',
type: 'text',
build() {
return new EditorActionField(
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',

View 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',
});
}