Lexical: Added selection to state for aligned reading
Connected up to work with image form
This commit is contained in:
		
							parent
							
								
									e959c468f6
								
							
						
					
					
						commit
						0722960260
					
				| 
						 | 
				
			
			@ -57,6 +57,16 @@ export class ImageNode extends DecoratorNode<EditorDecoratorAdapter> {
 | 
			
		|||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    setSrc(src: string): void {
 | 
			
		||||
        const self = this.getWritable();
 | 
			
		||||
        self.__src = src;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    getSrc(): string {
 | 
			
		||||
        const self = this.getLatest();
 | 
			
		||||
        return self.__src;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    setAltText(altText: string): void {
 | 
			
		||||
        const self = this.getWritable();
 | 
			
		||||
        self.__alt = altText;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -88,6 +88,7 @@ export class ImageDecorator extends EditorDecorator {
 | 
			
		|||
        let startingHeight = element.clientHeight;
 | 
			
		||||
        let startingRatio = startingWidth / startingHeight;
 | 
			
		||||
        let hasHeight = false;
 | 
			
		||||
        let firstChange = true;
 | 
			
		||||
        context.editor.getEditorState().read(() => {
 | 
			
		||||
            startingWidth = node.getWidth() || startingWidth;
 | 
			
		||||
            startingHeight = node.getHeight() || startingHeight;
 | 
			
		||||
| 
						 | 
				
			
			@ -109,7 +110,7 @@ export class ImageDecorator extends EditorDecorator {
 | 
			
		|||
            if (flipYChange) {
 | 
			
		||||
                yChange = 0 - yChange;
 | 
			
		||||
            }
 | 
			
		||||
            const balancedChange = Math.sqrt(Math.pow(xChange, 2) + Math.pow(yChange, 2));
 | 
			
		||||
            const balancedChange = Math.sqrt(Math.pow(Math.abs(xChange), 2) + Math.pow(Math.abs(yChange), 2));
 | 
			
		||||
            const increase = xChange + yChange > 0;
 | 
			
		||||
            const directedChange = increase ? balancedChange : 0-balancedChange;
 | 
			
		||||
            const newWidth = Math.max(5, Math.round(startingWidth + directedChange));
 | 
			
		||||
| 
						 | 
				
			
			@ -118,11 +119,13 @@ export class ImageDecorator extends EditorDecorator {
 | 
			
		|||
                newHeight = newWidth * startingRatio;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            const updateOptions = firstChange ? {} : {tag: 'history-merge'};
 | 
			
		||||
            context.editor.update(() => {
 | 
			
		||||
                const node = this.getNode() as ImageNode;
 | 
			
		||||
                node.setWidth(newWidth);
 | 
			
		||||
                node.setHeight(newHeight);
 | 
			
		||||
            });
 | 
			
		||||
            }, updateOptions);
 | 
			
		||||
            firstChange = false;
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        const mouseUpListener = (event: MouseEvent) => {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -25,6 +25,7 @@ import {
 | 
			
		|||
} from "@lexical/rich-text";
 | 
			
		||||
import {$isLinkNode, $toggleLink, LinkNode} from "@lexical/link";
 | 
			
		||||
import {EditorUiContext} from "../framework/core";
 | 
			
		||||
import {$isImageNode, ImageNode} from "../../nodes/image";
 | 
			
		||||
 | 
			
		||||
export const undo: EditorButtonDefinition = {
 | 
			
		||||
    label: 'Undo',
 | 
			
		||||
| 
						 | 
				
			
			@ -168,3 +169,35 @@ export const link: EditorButtonDefinition = {
 | 
			
		|||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const image: EditorButtonDefinition = {
 | 
			
		||||
    label: 'Insert/Edit Image',
 | 
			
		||||
    action(context: EditorUiContext) {
 | 
			
		||||
        const imageModal = context.manager.createModal('image');
 | 
			
		||||
        const selection = context.lastSelection;
 | 
			
		||||
        const selectedImage = getNodeFromSelection(selection, $isImageNode) as ImageNode|null;
 | 
			
		||||
 | 
			
		||||
        context.editor.getEditorState().read(() => {
 | 
			
		||||
            let formDefaults = {};
 | 
			
		||||
            if (selectedImage) {
 | 
			
		||||
                formDefaults = {
 | 
			
		||||
                    src: selectedImage.getSrc(),
 | 
			
		||||
                    alt: selectedImage.getAltText(),
 | 
			
		||||
                    height: selectedImage.getHeight(),
 | 
			
		||||
                    width: selectedImage.getWidth(),
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                context.editor.update(() => {
 | 
			
		||||
                    const selection = $createNodeSelection();
 | 
			
		||||
                    selection.add(selectedImage.getKey());
 | 
			
		||||
                    $setSelection(selection);
 | 
			
		||||
                });
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            imageModal.show(formDefaults);
 | 
			
		||||
        });
 | 
			
		||||
    },
 | 
			
		||||
    isActive(selection: BaseSelection|null): boolean {
 | 
			
		||||
        return selectionContainsNodeType(selection, $isImageNode);
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -2,6 +2,7 @@ import {EditorFormDefinition, EditorSelectFormFieldDefinition} from "../framewor
 | 
			
		|||
import {EditorUiContext} from "../framework/core";
 | 
			
		||||
import {$createLinkNode} from "@lexical/link";
 | 
			
		||||
import {$createTextNode, $getSelection} from "lexical";
 | 
			
		||||
import {$createImageNode} from "../../nodes/image";
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
export const link: EditorFormDefinition = {
 | 
			
		||||
| 
						 | 
				
			
			@ -47,4 +48,42 @@ export const link: EditorFormDefinition = {
 | 
			
		|||
            }
 | 
			
		||||
        } as EditorSelectFormFieldDefinition,
 | 
			
		||||
    ],
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const image: EditorFormDefinition = {
 | 
			
		||||
    submitText: 'Apply',
 | 
			
		||||
    action(formData, context: EditorUiContext) {
 | 
			
		||||
        context.editor.update(() => {
 | 
			
		||||
            const selection = $getSelection();
 | 
			
		||||
            const imageNode = $createImageNode(formData.get('src')?.toString() || '', {
 | 
			
		||||
                alt: formData.get('alt')?.toString() || '',
 | 
			
		||||
                height: Number(formData.get('height')?.toString() || '0'),
 | 
			
		||||
                width: Number(formData.get('width')?.toString() || '0'),
 | 
			
		||||
            });
 | 
			
		||||
            selection?.insertNodes([imageNode]);
 | 
			
		||||
        });
 | 
			
		||||
        return true;
 | 
			
		||||
    },
 | 
			
		||||
    fields: [
 | 
			
		||||
        {
 | 
			
		||||
            label: 'Source',
 | 
			
		||||
            name: 'src',
 | 
			
		||||
            type: 'text',
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            label: 'Alternative description',
 | 
			
		||||
            name: 'alt',
 | 
			
		||||
            type: 'text',
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            label: 'Width',
 | 
			
		||||
            name: 'width',
 | 
			
		||||
            type: 'text',
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            label: 'Height',
 | 
			
		||||
            name: 'height',
 | 
			
		||||
            type: 'text',
 | 
			
		||||
        },
 | 
			
		||||
    ],
 | 
			
		||||
};
 | 
			
		||||
| 
						 | 
				
			
			@ -10,6 +10,7 @@ export type EditorUiContext = {
 | 
			
		|||
    editor: LexicalEditor,
 | 
			
		||||
    translate: (text: string) => string,
 | 
			
		||||
    manager: EditorUIManager,
 | 
			
		||||
    lastSelection: BaseSelection|null,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export abstract class EditorUiElement {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -6,18 +6,20 @@ import {
 | 
			
		|||
} from "lexical";
 | 
			
		||||
import {getMainEditorFullToolbar} from "./toolbars";
 | 
			
		||||
import {EditorUIManager} from "./framework/manager";
 | 
			
		||||
import {link as linkFormDefinition} from "./defaults/form-definitions";
 | 
			
		||||
import {image as imageFormDefinition, link as linkFormDefinition} from "./defaults/form-definitions";
 | 
			
		||||
import {DecoratorListener} from "lexical/LexicalEditor";
 | 
			
		||||
import type {NodeKey} from "lexical/LexicalNode";
 | 
			
		||||
import {EditorDecoratorAdapter} from "./framework/decorator";
 | 
			
		||||
import {ImageDecorator} from "./decorators/image";
 | 
			
		||||
import {EditorUiContext} from "./framework/core";
 | 
			
		||||
 | 
			
		||||
export function buildEditorUI(element: HTMLElement, editor: LexicalEditor) {
 | 
			
		||||
    const manager = new EditorUIManager();
 | 
			
		||||
    const context = {
 | 
			
		||||
    const context: EditorUiContext = {
 | 
			
		||||
        editor,
 | 
			
		||||
        manager,
 | 
			
		||||
        translate: (text: string): string => text,
 | 
			
		||||
        lastSelection: null,
 | 
			
		||||
    };
 | 
			
		||||
    manager.setContext(context);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -31,6 +33,10 @@ export function buildEditorUI(element: HTMLElement, editor: LexicalEditor) {
 | 
			
		|||
        title: 'Insert/Edit link',
 | 
			
		||||
        form: linkFormDefinition,
 | 
			
		||||
    });
 | 
			
		||||
    manager.registerModal('image', {
 | 
			
		||||
        title: 'Insert/Edit Image',
 | 
			
		||||
        form: imageFormDefinition
 | 
			
		||||
    })
 | 
			
		||||
 | 
			
		||||
    // Register decorator listener
 | 
			
		||||
    // Maybe move to manager?
 | 
			
		||||
| 
						 | 
				
			
			@ -54,6 +60,7 @@ export function buildEditorUI(element: HTMLElement, editor: LexicalEditor) {
 | 
			
		|||
    editor.registerCommand(SELECTION_CHANGE_COMMAND, () => {
 | 
			
		||||
        const selection = $getSelection();
 | 
			
		||||
        toolbar.updateState({editor, selection});
 | 
			
		||||
        context.lastSelection = selection;
 | 
			
		||||
        return false;
 | 
			
		||||
    }, COMMAND_PRIORITY_LOW);
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -2,7 +2,7 @@ import {EditorButton, FormatPreviewButton} from "./framework/buttons";
 | 
			
		|||
import {
 | 
			
		||||
    blockquote, bold, code,
 | 
			
		||||
    dangerCallout,
 | 
			
		||||
    h2, h3, h4, h5,
 | 
			
		||||
    h2, h3, h4, h5, image,
 | 
			
		||||
    infoCallout, italic, link, paragraph,
 | 
			
		||||
    redo, strikethrough, subscript,
 | 
			
		||||
    successCallout, superscript, underline,
 | 
			
		||||
| 
						 | 
				
			
			@ -40,5 +40,6 @@ export function getMainEditorFullToolbar(): EditorContainerUiElement {
 | 
			
		|||
        new EditorButton(code),
 | 
			
		||||
 | 
			
		||||
        new EditorButton(link),
 | 
			
		||||
        new EditorButton(image),
 | 
			
		||||
    ]);
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
		Reference in New Issue