UNPKG

@selfcommunity/react-ui

Version:

React UI Components to integrate a Community created with SelfCommunity Platform.

189 lines (188 loc) • 7.39 kB
import { jsx as _jsx } from "react/jsx-runtime"; import { Suspense, useCallback, useEffect, useRef } from 'react'; import { $getNodeByKey, $getSelection, $isNodeSelection, $setSelection, CLICK_COMMAND, COMMAND_PRIORITY_LOW, DecoratorNode, KEY_BACKSPACE_COMMAND, KEY_DELETE_COMMAND, KEY_ENTER_COMMAND, KEY_ESCAPE_COMMAND, SELECTION_CHANGE_COMMAND, TextNode } from 'lexical'; import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext'; import { useLexicalNodeSelection } from '@lexical/react/useLexicalNodeSelection'; import { mergeRegister } from '@lexical/utils'; const imageCache = new Set(); function useSuspenseImage(src) { if (!imageCache.has(src)) { throw new Promise((resolve) => { const img = new Image(); img.src = src; img.onload = () => { imageCache.add(src); resolve(); }; }); } } function LazyImage({ altText, className, imageRef, src, width, height, maxWidth }) { useSuspenseImage(src); return (_jsx("img", { className: className, src: src, alt: altText, ref: imageRef, style: { height: `${height}${height === 'inherit' ? '' : 'px'}`, maxWidth, width: `${width}${width === 'inherit' ? '' : 'px'}` } })); } function ImageComponent({ src, altText, nodeKey, maxWidth }) { const imageRef = useRef(null); const buttonRef = useRef(null); const [isSelected, setSelected, clearSelection] = useLexicalNodeSelection(nodeKey); const [editor] = useLexicalComposerContext(); const activeEditorRef = useRef(null); const onDelete = useCallback((payload) => { if (isSelected && $isNodeSelection($getSelection())) { const event = payload; event.preventDefault(); const node = $getNodeByKey(nodeKey); if ($isImageNode(node)) { node.remove(); } setSelected(false); } return false; }, [isSelected, nodeKey, setSelected]); const onEnter = useCallback((event) => { const latestSelection = $getSelection(); const buttonElem = buttonRef.current; if (isSelected && $isNodeSelection(latestSelection) && latestSelection.getNodes().length === 1) { if (buttonElem !== null && buttonElem !== document.activeElement) { event.preventDefault(); buttonElem.focus(); return true; } } return false; }, [isSelected]); const onEscape = useCallback((event) => { if (buttonRef.current === event.target) { $setSelection(null); editor.update(() => { setSelected(true); const parentRootElement = editor.getRootElement(); if (parentRootElement !== null) { parentRootElement.focus(); } }); return true; } return false; }, [editor, setSelected]); useEffect(() => { const unregister = mergeRegister(editor.registerCommand(SELECTION_CHANGE_COMMAND, (_, activeEditor) => { activeEditorRef.current = activeEditor; return false; }, COMMAND_PRIORITY_LOW), editor.registerCommand(CLICK_COMMAND, (payload) => { const event = payload; if (event.target === imageRef.current) { if (event.shiftKey) { setSelected(!isSelected); } else { clearSelection(); setSelected(true); } return true; } return false; }, COMMAND_PRIORITY_LOW), editor.registerCommand(KEY_DELETE_COMMAND, onDelete, COMMAND_PRIORITY_LOW), editor.registerCommand(KEY_BACKSPACE_COMMAND, onDelete, COMMAND_PRIORITY_LOW), editor.registerCommand(KEY_ENTER_COMMAND, onEnter, COMMAND_PRIORITY_LOW), editor.registerCommand(KEY_ESCAPE_COMMAND, onEscape, COMMAND_PRIORITY_LOW)); return () => { unregister(); }; }, [clearSelection, editor, isSelected, nodeKey, onDelete, onEnter, onEscape, setSelected]); return (_jsx(Suspense, Object.assign({ fallback: null }, { children: _jsx(LazyImage, { className: isSelected ? `selected` : null, src: src, altText: altText, imageRef: imageRef, maxWidth: maxWidth }) }))); } function convertImageElement(domNode) { if (domNode instanceof HTMLImageElement) { const { alt: altText, src } = domNode; const node = $createImageNode({ altText, src, maxWidth: '100%' }); return { node }; } return null; } // eslint-disable-next-line @typescript-eslint/ban-ts-ignore // @ts-ignore export class ImageNode extends DecoratorNode { static getType() { return 'image'; } static clone(node) { return new ImageNode(node.__src, node.__altText, node.__maxWidth, node.__key); } constructor(src, altText, maxWidth, key) { super(key); this.__src = src; this.__altText = altText; this.__maxWidth = maxWidth; } setWidthAndHeight(width, height) { const writable = this.getWritable(); // eslint-disable-next-line @typescript-eslint/ban-ts-ignore // @ts-ignore writable.__width = width; // eslint-disable-next-line @typescript-eslint/ban-ts-ignore // @ts-ignore writable.__height = height; } // View createDOM(config) { const div = document.createElement('div'); const theme = config.theme; const className = theme.image; if (className !== undefined) { div.className = className; } return div; } updateDOM() { return false; } static importDOM() { // eslint-disable-next-line @typescript-eslint/ban-ts-ignore // @ts-ignore const dom = TextNode.importDOM(); return Object.assign({ img: (node) => ({ conversion: convertImageElement, priority: 0 }) }, dom); } exportDOM() { const element = document.createElement('img'); element.setAttribute('src', this.__src); element.setAttribute('alt', this.__altText); element.setAttribute('style', `max-width: ${this.__maxWidth}px;`); return { element }; } decorate() { return _jsx(ImageComponent, { src: this.__src, altText: this.__altText, maxWidth: this.__maxWidth, nodeKey: this.getKey() }); } static importJSON(serializedNode) { const { altText, maxWidth, src } = serializedNode; const node = $createImageNode({ altText, src, maxWidth }); return node; } exportJSON() { return { // eslint-disable-next-line @typescript-eslint/ban-ts-ignore // @ts-ignore altText: this.getAltText(), maxWidth: this.__maxWidth, // eslint-disable-next-line @typescript-eslint/ban-ts-ignore // @ts-ignore src: this.getSrc(), type: 'image', version: 1 }; } } export function $createImageNode({ src, altText, maxWidth }) { return new ImageNode(src, altText, maxWidth); } export function $isImageNode(node) { return node.getType() === 'image'; }