@selfcommunity/react-ui
Version:
React UI Components to integrate a Community created with SelfCommunity Platform.
189 lines (188 loc) • 7.39 kB
JavaScript
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';
}