"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.joinBackwardToOpenTab = exports.removeTabWhenCursorAtTheStartOfTab = exports.removeTab = exports.createTab = exports.liftEmptyBlockFromTabPanel = exports.tabEnter = exports.tabPanelArrowDown = exports.dragAutoSwitch = void 0;
const utils_js_1 = require("@diplodoc/transform/lib/plugins/utils.js");
const prosemirror_state_1 = require("prosemirror-state");
const prosemirror_utils_1 = require("prosemirror-utils");
const __1 = require("../../index.js");
const lodash_1 = require("../../../lodash.js");
const helpers_1 = require("../../../table-utils/helpers.js");
const utils_1 = require("../../../utils/index.js");
const selection_1 = require("../../../utils/selection.js");
const const_1 = require("./YfmTabsSpecs/const.js");
const const_2 = require("./const.js");
const utils_2 = require("./utils.js");
const dragAutoSwitch = () => new prosemirror_state_1.Plugin({
    view: TabsAutoSwitchOnDragOver.view,
});
exports.dragAutoSwitch = dragAutoSwitch;
class TabsAutoSwitchOnDragOver {
    static TAB_SELECTOR = '.yfm-tab:not([data-diplodoc-is-active=true])';
    static OPEN_TIMEOUT = 500; //ms
    static THROTTLE_WAIT = 50; //ms
    static view = (view) => new this(view);
    _tabElem = null;
    _editorView;
    _timeout = null;
    _docListener;
    constructor(view) {
        this._editorView = view;
        this._docListener = (0, lodash_1.throttle)(this._onDocEvent.bind(this), TabsAutoSwitchOnDragOver.THROTTLE_WAIT);
        document.addEventListener('mousemove', this._docListener);
        document.addEventListener('dragover', this._docListener);
    }
    destroy() {
        this._clear();
        this._docListener.cancel();
        document.removeEventListener('mousemove', this._docListener);
        document.removeEventListener('dragover', this._docListener);
    }
    _onDocEvent(event) {
        const view = this._editorView;
        if (!view.dragging)
            return;
        const pos = view.posAtCoords({ left: event.clientX, top: event.clientY });
        if (pos) {
            const elem = (0, prosemirror_utils_1.findDomRefAtPos)(pos.pos, view.domAtPos.bind(view));
            const tabElem = elem.closest(TabsAutoSwitchOnDragOver.TAB_SELECTOR);
            if (tabElem === this._tabElem)
                return;
            this._clear();
            if (tabElem)
                this._setTabElem(tabElem);
        }
    }
    _clear() {
        if (this._timeout !== null)
            clearTimeout(this._timeout);
        this._timeout = null;
        this._tabElem = null;
    }
    _setTabElem(elem) {
        this._tabElem = elem;
        this._timeout = setTimeout(this._switchTab.bind(this), TabsAutoSwitchOnDragOver.OPEN_TIMEOUT);
    }
    _switchTab() {
        if (this._editorView.dragging && this._tabElem) {
            (0, utils_2.switchTabByElem)(this._tabElem);
        }
        this._clear();
    }
}
const tabPanelArrowDown = (state, dispatch, view) => {
    const { selection: sel } = state;
    const tabsParentNode = (0, prosemirror_utils_1.findParentNodeOfType)((0, const_2.tabsType)(state.schema))(state.selection);
    if ((0, utils_2.atEndOfPanel)(view) && view?.endOfTextblock('down')) {
        const [direction, $cursor] = ['after', sel.$to];
        let $pos;
        if ((0, selection_1.isTextSelection)(sel)) {
            $pos =
                (0, __1.findFakeParaPosForTextSelection)(sel, direction) ??
                    (0, __1.findFakeParaPosClosestToPos)($cursor, $cursor.depth - 2, direction);
        }
        if ((0, __1.isGapCursorSelection)(sel) && tabsParentNode) {
            $pos = state.doc.resolve(tabsParentNode.pos + tabsParentNode.node.nodeSize);
        }
        if ($pos) {
            dispatch?.((0, __1.createFakeParagraph)(state.tr, $pos, direction).scrollIntoView());
            return true;
        }
    }
    return false;
};
exports.tabPanelArrowDown = tabPanelArrowDown;
const tabEnter = (state) => {
    const $cursor = (0, selection_1.get$Cursor)(state.selection);
    return $cursor?.node($cursor.depth).type === (0, const_2.tabType)(state.schema);
};
exports.tabEnter = tabEnter;
const liftEmptyBlockFromTabPanel = (state, dispatch) => {
    const { selection, schema } = state;
    if (!(0, selection_1.isTextSelection)(selection))
        return false;
    const { $cursor } = selection;
    // cursor should be inside an empty textblock
    if (!$cursor || $cursor.parent.content.size)
        return false;
    if ($cursor.depth > 2 && // depth must be at least 3
        (0, utils_1.isSameNodeType)($cursor.node(-1), (0, const_2.tabPanelType)(schema)) &&
        (0, utils_1.isSameNodeType)($cursor.node(-2), (0, const_2.tabsType)(schema))) {
        // current texblock is last child
        if ($cursor.after() === $cursor.end(-1)) {
            if (dispatch) {
                const copeidNode = $cursor.parent.copy();
                const tabsAfter = $cursor.after(-2);
                const { tr } = state;
                tr.insert(tabsAfter, copeidNode)
                    .delete($cursor.before(), $cursor.after())
                    .setSelection(prosemirror_state_1.TextSelection.create(tr.doc, tr.mapping.map(tabsAfter)));
                dispatch(tr.scrollIntoView());
            }
            return true;
        }
    }
    return false;
};
exports.liftEmptyBlockFromTabPanel = liftEmptyBlockFromTabPanel;
const createTab = (afterTab, tabsParentNode) => (state, dispatch, view) => {
    const tabNodes = (0, prosemirror_utils_1.findChildren)(tabsParentNode.node, (node) => node.type.name === (0, const_2.tabType)(state.schema).name);
    const tabPanels = (0, prosemirror_utils_1.findChildren)(tabsParentNode.node, (tabNode) => {
        return tabNode.type.name === (0, const_2.tabPanelType)(state.schema).name;
    });
    const afterPanelNode = tabPanels.filter((tabPanelNode) => tabPanelNode.node.attrs[const_1.TabPanelAttrs.ariaLabelledby] ===
        afterTab.node.attrs[const_1.TabAttrs.dataDiplodocid])[0];
    const tabId = (0, utils_js_1.generateID)();
    const panelId = (0, utils_js_1.generateID)();
    const newPanel = (0, const_2.tabPanelType)(state.schema).create({
        [const_1.TabPanelAttrs.ariaLabelledby]: tabId,
        [const_1.TabPanelAttrs.id]: panelId,
        [const_1.TabPanelAttrs.class]: const_2.tabPanelInactiveClassname,
    }, (0, __1.pType)(state.schema).createAndFill());
    const newTab = (0, const_2.tabType)(state.schema).create({
        [const_1.TabAttrs.id]: tabId,
        [const_1.TabAttrs.dataDiplodocid]: tabId,
        [const_1.TabAttrs.dataDiplodocKey]: tabId,
        [const_1.TabAttrs.dataDiplodocIsActive]: 'false',
        [const_1.TabAttrs.class]: const_2.tabInactiveClassname,
        [const_1.TabAttrs.role]: 'tab',
        [const_1.TabAttrs.ariaControls]: panelId,
    });
    const { tr } = state;
    // Change relative pos to absolute
    tabNodes.forEach((v) => {
        v.pos = v.pos + tabsParentNode.pos + 1;
    });
    tabPanels.forEach((v) => {
        v.pos = v.pos + tabsParentNode.pos + 1;
    });
    dispatch?.(tr
        .insert(afterPanelNode.pos + afterPanelNode.node.nodeSize, newPanel)
        .insert(afterTab.pos + afterTab.node.nodeSize, newTab)
        .setSelection(prosemirror_state_1.TextSelection.create(tr.doc, afterTab.pos + afterTab.node.nodeSize + 1)));
    view?.focus();
    if (view) {
        (0, utils_2.execAfterPaint)(() => (0, utils_2.switchTabById)(view.dom, tabId));
    }
    return true;
};
exports.createTab = createTab;
const removeTab = (tabToRemove, tabsParentNode) => (state, dispatch, view) => {
    const tabList = (0, prosemirror_utils_1.findChildren)(tabsParentNode.node, (tabNode) => {
        return tabNode.type.name === (0, const_2.tabsListType)(state.schema).name;
    })[0];
    const tabToRemoveIdx = (0, helpers_1.findChildIndex)(tabList.node, tabToRemove.node);
    const tabNodes = (0, prosemirror_utils_1.findChildren)(tabList.node, (node) => node.type.name === (0, const_2.tabType)(state.schema).name);
    const tabPanels = (0, prosemirror_utils_1.findChildren)(tabsParentNode.node, (tabNode) => {
        return tabNode.type.name === (0, const_2.tabPanelType)(state.schema).name;
    });
    const panelToRemove = tabPanels.filter((tabPanelNode) => tabPanelNode.node.attrs[const_1.TabPanelAttrs.ariaLabelledby] ===
        tabToRemove.node.attrs[const_1.TabAttrs.dataDiplodocid])[0];
    if (panelToRemove && dispatch) {
        const { tr } = state;
        if (tabNodes.length <= 1) {
            tr.delete(tabsParentNode.pos, tabsParentNode.pos + tabsParentNode.node.nodeSize);
        }
        else {
            const newTabIdx = tabToRemoveIdx - 1 < 0 ? 1 : tabToRemoveIdx - 1;
            // Change relative pos to absolute
            tabNodes.forEach((v) => {
                v.pos = v.pos + tabsParentNode.pos + 2;
            });
            tabPanels.forEach((v) => {
                v.pos = v.pos + tabsParentNode.pos + 1;
            });
            const newTabNode = tabNodes[newTabIdx];
            const newActiveTabId = newTabNode.node.attrs[const_1.TabAttrs.dataDiplodocid];
            tr
                // Delete panel
                .delete(panelToRemove.pos, panelToRemove.pos + panelToRemove.node.nodeSize)
                // Delete tab
                .delete(tabToRemove.pos, tabToRemove.pos + tabToRemove.node.nodeSize)
                .setSelection(prosemirror_state_1.TextSelection.create(tr.doc, tr.mapping.map(newTabNode.pos + newTabNode.node.nodeSize - 1)));
            // Set new active tab
            if (view)
                (0, utils_2.execAfterPaint)(() => (0, utils_2.switchTabById)(view.dom, newActiveTabId));
        }
        dispatch(tr);
        view?.focus();
        return true;
    }
    return false;
};
exports.removeTab = removeTab;
const removeTabWhenCursorAtTheStartOfTab = (state, dispatch) => {
    const tabToRemove = (0, prosemirror_utils_1.findParentNodeOfType)((0, const_2.tabType)(state.schema))(state.selection);
    const tabsParentNode = (0, prosemirror_utils_1.findParentNodeOfType)((0, const_2.tabsType)(state.schema))(state.selection);
    if (tabsParentNode &&
        tabToRemove &&
        state.selection.from === tabToRemove.pos + 1 &&
        state.selection.from === state.selection.to) {
        return (0, exports.removeTab)(tabToRemove, tabsParentNode)(state, dispatch);
    }
    return false;
};
exports.removeTabWhenCursorAtTheStartOfTab = removeTabWhenCursorAtTheStartOfTab;
const joinBackwardToOpenTab = (state, dispatch) => {
    const $cursor = (0, selection_1.get$Cursor)(state.selection);
    if (!$cursor || $cursor.parentOffset !== 0)
        return false;
    // --> cursor at start of textblock
    const textBlockIndex = $cursor.index($cursor.depth - 1);
    if (textBlockIndex <= 0)
        return false;
    const nodeBefore = $cursor.node($cursor.depth - 1).child(textBlockIndex - 1);
    if (nodeBefore.type !== (0, const_2.tabsType)(state.schema)) {
        return false;
    }
    const tabsParent = (0, prosemirror_utils_1.findParentNodeOfTypeClosestToPos)(state.doc.resolve($cursor.pos - 2), (0, const_2.tabsType)(state.schema));
    if (!tabsParent) {
        return false;
    }
    const activePanel = (0, prosemirror_utils_1.findChildren)(tabsParent.node, (n) => n.attrs.class === const_2.tabPanelActiveClassname)[0];
    if (dispatch) {
        const posEndOfLastLayoutCell = activePanel.pos + tabsParent.pos + activePanel.node.nodeSize;
        const tr = state.tr;
        tr.delete($cursor.before(), $cursor.after());
        tr.insert(posEndOfLastLayoutCell, $cursor.parent);
        tr.setSelection(prosemirror_state_1.TextSelection.create(tr.doc, posEndOfLastLayoutCell + 1));
        dispatch(tr.scrollIntoView());
    }
    return true;
};
exports.joinBackwardToOpenTab = joinBackwardToOpenTab;
//# sourceMappingURL=plugins.js.map
