"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.INSERT_INLINE_IMAGE_COMMAND = void 0;
exports.InsertInlineImageModal = InsertInlineImageModal;
exports.default = InlineImagePlugin;
const tslib_1 = require("tslib");
const LexicalComposerContext_1 = require("@lexical/react/LexicalComposerContext");
const utils_1 = require("@lexical/utils");
const lexical_1 = require("lexical");
const react_1 = require("react");
const React = tslib_1.__importStar(require("react"));
const InlineImageNode_1 = require("../../nodes/InlineImageNode");
const canUseDOM_1 = require("../../shared/canUseDOM");
const Modal_1 = require("../../../Modal");
const UploadFile_1 = require("../../../UploadFile");
const Select_1 = require("../../../Select");
const Input_1 = require("../../../Input");
const Checkbox_1 = require("../../../Checkbox");
/**
 * Gets the DOM selection for a target window when DOM APIs are available.
 * @param targetWindow Window to read selection from.
 * @returns DOM selection, or null when unavailable.
 */
const getDOMSelection = (targetWindow) => canUseDOM_1.CAN_USE_DOM ? (targetWindow || window).getSelection() : null;
exports.INSERT_INLINE_IMAGE_COMMAND = (0, lexical_1.createCommand)('INSERT_INLINE_IMAGE_COMMAND');
/**
 * Renders the modal used to insert an inline image.
 * @param props Insert inline image modal props.
 * @returns Inline image insert modal element.
 */
function InsertInlineImageModal({ activeEditor, setShow, show, }) {
    const [cdnLink, setCdnLink] = (0, react_1.useState)('');
    let position = 'center';
    let altText = 'photo';
    let fileName = '';
    let showCaption = false;
    /**
     * Hides the modal and resets local upload state.
     */
    const onHide = () => {
        setShow(false);
        setCdnLink('');
        fileName = '';
    };
    /**
     * Stores uploaded file metadata returned from the CDN uploader.
     * @param fileUpload Uploaded file object.
     * @param linkCdn CDN URLs returned by the uploader.
     */
    const handleListenUpload = (fileUpload, linkCdn) => {
        fileName = fileUpload.name;
        setCdnLink(linkCdn[0]);
    };
    /**
     * Dispatches the inline image insert command with the current modal values.
     */
    const handleSubmit = () => {
        activeEditor.dispatchCommand(exports.INSERT_INLINE_IMAGE_COMMAND, {
            altText,
            position,
            showCaption,
            src: cdnLink,
        });
        onHide();
    };
    /**
     * Updates caption visibility from the checkbox field.
     * @param e Checkbox change event.
     */
    const handleShowCaptionChange = (e) => {
        showCaption = e.target.checked;
    };
    /**
     * Updates the selected inline image position.
     * @param e Select change event.
     */
    const handlePositionChange = (e) => {
        position = e.target.value;
    };
    return (React.createElement(Modal_1.PHXModal, { onHide: onHide, onPrimaryClick: handleSubmit, show: show, title: 'Insert Image' },
        React.createElement("div", { className: 'space-y-4' },
            React.createElement(UploadFile_1.PHXUploadFile, { apiCdnUpload: '/api/cdn-upload', basePath: '', defaultLink: [cdnLink], fileName: fileName, fileType: 'image', handleListenUpload: handleListenUpload, inCard: false, label: 'Image upload', moduleId: 'insert-editor-image', oneImg: true, projectId: 'phenikaa', titleHeaderAction: '' }),
            React.createElement(Input_1.PHXInput, { defaultValue: altText, label: 'Alt Text', onChange: (e) => (altText = e.target.value) }),
            React.createElement(Select_1.PHXSelect, { defaultValue: position, label: 'Position', onChange: handlePositionChange, style: {
                    border: '1px gray solid',
                } },
                React.createElement("option", { value: 'left' }, "Left"),
                React.createElement("option", { value: 'center' }, "Center"),
                React.createElement("option", { value: 'right' }, "Right"),
                React.createElement("option", { value: 'full' }, "Full Width")),
            React.createElement(Checkbox_1.PHXCheckbox, { key: 'show_cation', onChange: handleShowCaptionChange, title: 'Show Caption' }))));
}
/**
 * Registers inline image insertion and drag/drop behavior with Lexical.
 * @returns Null because the plugin renders no UI.
 */
function InlineImagePlugin() {
    const [editor] = (0, LexicalComposerContext_1.useLexicalComposerContext)();
    (0, react_1.useEffect)(() => {
        if (!editor.hasNodes([InlineImageNode_1.InlineImageNode])) {
            throw new Error('ImagesPlugin: ImageNode not registered on editor');
        }
        return (0, utils_1.mergeRegister)(editor.registerCommand(exports.INSERT_INLINE_IMAGE_COMMAND, (payload) => {
            const imageNode = (0, InlineImageNode_1.$createInlineImageNode)(payload);
            (0, lexical_1.$insertNodes)([imageNode]);
            if ((0, lexical_1.$isRootOrShadowRoot)(imageNode.getParentOrThrow())) {
                (0, utils_1.$wrapNodeInElement)(imageNode, lexical_1.$createParagraphNode).selectEnd();
            }
            return true;
        }, lexical_1.COMMAND_PRIORITY_EDITOR), editor.registerCommand(lexical_1.DRAGSTART_COMMAND, (event) => onDragStart(event), lexical_1.COMMAND_PRIORITY_HIGH), editor.registerCommand(lexical_1.DRAGOVER_COMMAND, (event) => onDragover(event), lexical_1.COMMAND_PRIORITY_LOW), editor.registerCommand(lexical_1.DROP_COMMAND, (event) => onDrop(event, editor), lexical_1.COMMAND_PRIORITY_HIGH));
    }, [editor]);
    return null;
}
const TRANSPARENT_IMAGE = 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7';
/**
 * Starts dragging the currently selected inline image.
 * @param event Drag start event from Lexical.
 * @returns True when drag data was written.
 */
function onDragStart(event) {
    const img = document.createElement('img');
    img.src = TRANSPARENT_IMAGE;
    const node = getImageNodeInSelection();
    if (!node) {
        return false;
    }
    const dataTransfer = event.dataTransfer;
    if (!dataTransfer) {
        return false;
    }
    dataTransfer.setData('text/plain', '_');
    dataTransfer.setDragImage(img, 0, 0);
    dataTransfer.setData('application/x-lexical-drag', JSON.stringify({
        data: {
            altText: node.__altText,
            caption: node.__caption,
            height: node.__height,
            key: node.getKey(),
            position: node.__position,
            showCaption: node.__showCaption,
            src: node.__src,
            width: node.__width,
        },
        type: 'image',
    }));
    return true;
}
/**
 * Handles drag over behavior for selected inline images.
 * @param event Drag over event from Lexical.
 * @returns True when an inline image is being dragged.
 */
function onDragover(event) {
    const node = getImageNodeInSelection();
    if (!node) {
        return false;
    }
    if (!canDropImage(event)) {
        event.preventDefault();
    }
    return true;
}
/**
 * Drops the dragged inline image into a valid editor location.
 * @param event Drop event from Lexical.
 * @param editor Active Lexical editor.
 * @returns True when a selected inline image handled the drop.
 */
function onDrop(event, editor) {
    const node = getImageNodeInSelection();
    if (!node) {
        return false;
    }
    const data = getDragImageData(event);
    if (!data) {
        return false;
    }
    event.preventDefault();
    if (canDropImage(event)) {
        const range = getDragSelection(event);
        node.remove();
        const rangeSelection = (0, lexical_1.$createRangeSelection)();
        if (range !== null && range !== undefined) {
            rangeSelection.applyDOMRange(range);
        }
        (0, lexical_1.$setSelection)(rangeSelection);
        editor.dispatchCommand(exports.INSERT_INLINE_IMAGE_COMMAND, data);
    }
    return true;
}
/**
 * Gets the selected inline image node.
 * @returns Selected inline image node, or null when selection is not an inline image.
 */
function getImageNodeInSelection() {
    const selection = (0, lexical_1.$getSelection)();
    if (!(0, lexical_1.$isNodeSelection)(selection)) {
        return null;
    }
    const nodes = selection.getNodes();
    const node = nodes[0];
    return (0, InlineImageNode_1.$isInlineImageNode)(node) ? node : null;
}
/**
 * Reads inline image payload from drag data.
 * @param event Drag event carrying data transfer payload.
 * @returns Inline image payload, or null when drag data is not an image payload.
 */
function getDragImageData(event) {
    var _a;
    const dragData = (_a = event.dataTransfer) === null || _a === void 0 ? void 0 : _a.getData('application/x-lexical-drag');
    if (!dragData) {
        return null;
    }
    const { data, type } = JSON.parse(dragData);
    if (type !== 'image') {
        return null;
    }
    return data;
}
/**
 * Checks whether an inline image can be dropped on the current event target.
 * @param event Drag event to inspect.
 * @returns True when the target is a valid editor drop location.
 */
function canDropImage(event) {
    const target = event.target;
    return !!(target &&
        target instanceof HTMLElement &&
        !target.closest('code, span.editor-image') &&
        target.parentElement &&
        target.parentElement.closest('div.ContentEditable__root'));
}
/**
 * Gets the DOM range at the drag event location.
 * @param event Drag event to inspect.
 * @returns DOM range for the drop location.
 */
function getDragSelection(event) {
    let range;
    const target = event.target;
    const targetWindow = target == null
        ? null
        : target.nodeType === 9
            ? target.defaultView
            : target.ownerDocument.defaultView;
    const domSelection = getDOMSelection(targetWindow);
    if (document.caretRangeFromPoint) {
        range = document.caretRangeFromPoint(event.clientX, event.clientY);
    }
    else if (event.rangeParent && domSelection !== null) {
        domSelection.collapse(event.rangeParent, event.rangeOffset || 0);
        range = domSelection.getRangeAt(0);
    }
    else {
        throw Error('Cannot get the selection when dragging');
    }
    return range;
}
//# sourceMappingURL=index.js.map