"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.default = InlineImageComponent;
const tslib_1 = require("tslib");
const LexicalAutoFocusPlugin_1 = require("@lexical/react/LexicalAutoFocusPlugin");
const LexicalComposerContext_1 = require("@lexical/react/LexicalComposerContext");
const LexicalNestedComposer_1 = require("@lexical/react/LexicalNestedComposer");
const LexicalRichTextPlugin_1 = require("@lexical/react/LexicalRichTextPlugin");
const useLexicalNodeSelection_1 = require("@lexical/react/useLexicalNodeSelection");
const utils_1 = require("@lexical/utils");
const lexical_1 = require("lexical");
const React = tslib_1.__importStar(require("react"));
const react_1 = require("react");
const FloatingLinkEditorPlugin_1 = tslib_1.__importDefault(require("../plugins/FloatingLinkEditorPlugin"));
const FloatingTextFormatToolbarPlugin_1 = tslib_1.__importDefault(require("../plugins/FloatingTextFormatToolbarPlugin"));
const LinkPlugin_1 = tslib_1.__importDefault(require("../plugins/LinkPlugin"));
const ContentEditable_1 = tslib_1.__importDefault(require("../ui/ContentEditable"));
const Placeholder_1 = tslib_1.__importDefault(require("../ui/Placeholder"));
const InlineImageNode_1 = require("./InlineImageNode");
const LexicalErrorBoundary_1 = require("../shared/LexicalErrorBoundary");
const imageCache = new Set();
/**
 * Suspends rendering until an image source has loaded.
 * @param src Image source URL to preload.
 */
function useSuspenseImage(src) {
    if (!imageCache.has(src)) {
        throw new Promise((resolve) => {
            const img = new Image();
            img.src = src;
            img.onload = () => {
                imageCache.add(src);
                resolve(null);
            };
        });
    }
}
/**
 * Renders an image after ensuring its source is loaded.
 * @param props Lazy image render props.
 * @returns Rendered image element.
 */
function LazyImage({ altText, className, height, imageRef, position, src, width, }) {
    useSuspenseImage(src);
    return (React.createElement("img", { ref: imageRef, alt: altText, className: className || undefined, "data-position": position, draggable: 'false', src: src, style: {
            display: 'block',
            height,
            width,
        } }));
}
/**
 * Renders an inline image node with selection, resize, and caption behavior.
 * @param props Inline image component props.
 * @returns Inline image decorator element.
 */
function InlineImageComponent({ altText, caption, height, nodeKey, position, showCaption, src, width, }) {
    const imageRef = (0, react_1.useRef)(null);
    const [isSelected, setSelected, clearSelection] = (0, useLexicalNodeSelection_1.useLexicalNodeSelection)(nodeKey);
    const [isResizing] = (0, react_1.useState)(false);
    const [editor] = (0, LexicalComposerContext_1.useLexicalComposerContext)();
    const [selection, setSelection] = (0, react_1.useState)(null);
    const activeEditorRef = (0, react_1.useRef)(null);
    /**
     * Removes the selected inline image when delete or backspace is pressed.
     * @param payload Keyboard event from the editor command.
     * @returns False to allow other command handlers to continue.
     */
    const onDelete = (0, react_1.useCallback)((payload) => {
        if (isSelected && (0, lexical_1.$isNodeSelection)((0, lexical_1.$getSelection)())) {
            const event = payload;
            event.preventDefault();
            const node = (0, lexical_1.$getNodeByKey)(nodeKey);
            if ((0, InlineImageNode_1.$isInlineImageNode)(node)) {
                node.remove();
            }
        }
        return false;
    }, [isSelected, nodeKey]);
    /**
     * Moves focus into the caption editor when enter is pressed on a selected image.
     * @param event Keyboard event from the editor command.
     * @returns True when focus moved into the caption editor.
     */
    const onEnter = (0, react_1.useCallback)((event) => {
        const latestSelection = (0, lexical_1.$getSelection)();
        if (isSelected && showCaption && (0, lexical_1.$isNodeSelection)(latestSelection) && latestSelection.getNodes().length === 1) {
            // Move focus into nested editor
            (0, lexical_1.$setSelection)(null);
            event.preventDefault();
            caption.focus();
            return true;
        }
        return false;
    }, [caption, isSelected, showCaption]);
    /**
     * Restores focus from the caption editor back to the inline image node.
     * @returns True when focus was restored to the parent editor.
     */
    const onEscape = (0, react_1.useCallback)(() => {
        if (activeEditorRef.current === caption) {
            (0, lexical_1.$setSelection)(null);
            editor.update(() => {
                setSelected(true);
                const parentRootElement = editor.getRootElement();
                if (parentRootElement !== null) {
                    parentRootElement.focus();
                }
            });
            return true;
        }
        return false;
    }, [caption, editor, setSelected]);
    (0, react_1.useEffect)(() => {
        let isMounted = true;
        const unregister = (0, utils_1.mergeRegister)(editor.registerUpdateListener(({ editorState }) => {
            if (isMounted) {
                setSelection(editorState.read(() => (0, lexical_1.$getSelection)()));
            }
        }), editor.registerCommand(lexical_1.SELECTION_CHANGE_COMMAND, (_, activeEditor) => {
            activeEditorRef.current = activeEditor;
            return false;
        }, lexical_1.COMMAND_PRIORITY_LOW), editor.registerCommand(lexical_1.CLICK_COMMAND, (payload) => {
            const event = payload;
            if (isResizing) {
                return true;
            }
            if (event.target === imageRef.current) {
                if (event.shiftKey) {
                    setSelected(!isSelected);
                }
                else {
                    clearSelection();
                    setSelected(true);
                }
                return true;
            }
            return false;
        }, lexical_1.COMMAND_PRIORITY_LOW), editor.registerCommand(lexical_1.DRAGSTART_COMMAND, (event) => {
            if (event.target === imageRef.current) {
                // TODO This is just a temporary workaround for FF to behave like other browsers.
                // Ideally, this handles drag & drop too (and all browsers).
                event.preventDefault();
                return true;
            }
            return false;
        }, lexical_1.COMMAND_PRIORITY_LOW), editor.registerCommand(lexical_1.KEY_DELETE_COMMAND, onDelete, lexical_1.COMMAND_PRIORITY_LOW), editor.registerCommand(lexical_1.KEY_BACKSPACE_COMMAND, onDelete, lexical_1.COMMAND_PRIORITY_LOW), editor.registerCommand(lexical_1.KEY_ENTER_COMMAND, onEnter, lexical_1.COMMAND_PRIORITY_LOW), editor.registerCommand(lexical_1.KEY_ESCAPE_COMMAND, onEscape, lexical_1.COMMAND_PRIORITY_LOW));
        return () => {
            isMounted = false;
            unregister();
        };
    }, [clearSelection, editor, isResizing, isSelected, nodeKey, onDelete, onEnter, onEscape, setSelected]);
    const draggable = isSelected && (0, lexical_1.$isNodeSelection)(selection) && !isResizing;
    const isFocused = isSelected || isResizing;
    return (React.createElement(react_1.Suspense, { fallback: null },
        React.createElement(React.Fragment, null,
            React.createElement("div", { draggable: draggable },
                React.createElement(LazyImage, { altText: altText, className: isFocused ? `focused ${(0, lexical_1.$isNodeSelection)(selection) ? 'draggable' : ''}` : null, height: height, imageRef: imageRef, position: position, src: src, width: width })),
            showCaption && (React.createElement("div", { className: 'image-caption-container' },
                React.createElement(LexicalNestedComposer_1.LexicalNestedComposer, { initialEditor: caption },
                    React.createElement(LexicalAutoFocusPlugin_1.AutoFocusPlugin, null),
                    React.createElement(LinkPlugin_1.default, null),
                    React.createElement(FloatingLinkEditorPlugin_1.default, { isLinkEditMode: false, setIsLinkEditMode: () => {
                            console.log('FloatingLinkEditorPlugin');
                        } }),
                    React.createElement(FloatingTextFormatToolbarPlugin_1.default, null),
                    React.createElement(LexicalRichTextPlugin_1.RichTextPlugin, { contentEditable: React.createElement(ContentEditable_1.default, { className: 'InlineImageNode__contentEditable' }), ErrorBoundary: LexicalErrorBoundary_1.LexicalErrorBoundary, placeholder: React.createElement(Placeholder_1.default, { className: 'InlineImageNode__placeholder' }, "Enter a caption...") })))))));
}
//# sourceMappingURL=InlineImageComponent.js.map