"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.BaseTooltipPluginView = exports.baseTooltipContentCn = exports.defaultPlacement = exports.BaseTooltipCn = void 0;
const jsx_runtime_1 = require("react/jsx-runtime");
const uikit_1 = require("@gravity-ui/uikit");
const prosemirror_state_1 = require("prosemirror-state");
// @ts-ignore // TODO: fix cjs build
const prosemirror_utils_1 = require("prosemirror-utils");
const classname_1 = require("../../classname.js");
const extensions_1 = require("../../extensions/index.js");
const ErrorBoundary_1 = require("../../react-utils/ErrorBoundary.js");
require("./index.css");
exports.BaseTooltipCn = (0, classname_1.cn)('base-tooltip');
exports.defaultPlacement = [
    'bottom-start',
    'top-start',
    'bottom-end',
    'top-end',
];
const b = exports.BaseTooltipCn;
exports.baseTooltipContentCn = (0, classname_1.cn)('base-tooltip-content');
class BaseTooltipPluginView {
    view;
    currentNode = null;
    content;
    nodeType;
    popupPlacement;
    manualHidden = false;
    destroyed = false;
    disableHideOnEscapeKeyDown;
    focusTrackingRafId = null;
    idPrefix;
    renderItem;
    constructor(view, options) {
        this.view = view;
        this.idPrefix = options.idPrefix;
        this.content = options.content;
        this.nodeType = options.nodeType;
        this.popupPlacement = options.popupPlacement;
        this.disableHideOnEscapeKeyDown = options.disableHideOnEscapeKeyDown;
    }
    update(view) {
        this.view = view;
        this.updateTooltipView();
    }
    destroy() {
        this.destroyed = true;
        this.renderItem?.remove();
        this.renderItem = undefined;
    }
    onEscapeDown = () => {
        if (this.disableHideOnEscapeKeyDown)
            return false;
        if (this.currentNode && !this.manualHidden) {
            this.hidePopupManual();
            return true;
        }
        return false;
    };
    setCurrentNode(node) {
        const view = this.view;
        const domAtPos = view.domAtPos.bind(view);
        const { selection } = view.state;
        const { $from, $to } = selection;
        const parent = (0, prosemirror_utils_1.findSelectedNodeOfType)(node)(selection) || (0, prosemirror_utils_1.findParentNodeOfType)(node)(selection);
        if (!parent || !$from.node(parent.depth).eq($to.node(parent.depth))) {
            this.currentNode = null;
            this.manualHidden = false;
            this.stopTrackingViewFocus();
            return;
        }
        if (!this.currentNode ||
            !this.currentNode.node.eq(parent.node) ||
            this.currentNode.pos !== parent.pos) {
            this.manualHidden = false;
            this.stopTrackingViewFocus();
        }
        const { pos, node: parentNode } = parent;
        const dom = (0, prosemirror_utils_1.findDomRefAtPos)(pos, domAtPos);
        this.currentNode = {
            pos,
            node: parentNode,
            dom,
        };
    }
    changeAttrsCb = (attrs, options) => {
        if (this.currentNode) {
            let tr = this.view.state.tr.setNodeMarkup(this.currentNode.pos, undefined, {
                ...this.currentNode.node.attrs,
                ...attrs,
            }, options?.marks || []);
            if (options?.setSelection) {
                tr = tr.setSelection(prosemirror_state_1.NodeSelection.create(tr.doc, this.currentNode.pos));
            }
            this.view.dispatch(tr);
            if (options?.setFocus) {
                this.view.focus();
            }
            if (options?.hideTooltip) {
                this.hidePopupManual();
            }
        }
    };
    updateTooltipView() {
        const oldNode = this.currentNode;
        this.setCurrentNode(this.view.state.schema.nodes[this.nodeType.name]);
        // Do not rerender if we have the same node in selection
        if (oldNode &&
            this.currentNode?.node.eq(oldNode.node) &&
            this.currentNode.pos === oldNode.pos &&
            this.currentNode.dom === oldNode.dom)
            return;
        this.render();
    }
    render() {
        if (this.destroyed)
            return;
        this.renderItem = this.renderItem ?? this.createRenderItem();
        this.renderItem.rerender();
    }
    onOutsideClick = () => {
        this.hidePopupManual();
    };
    popupOpenChangeHandler = (open, event, reason) => {
        if (open)
            return;
        if (reason === 'focus-out' && event.relatedTarget === this.view.dom)
            return;
        if (this.disableHideOnEscapeKeyDown && reason === 'escape-key')
            return;
        this.hidePopupManual();
        requestAnimationFrame(() => {
            if (this.destroyed || this.view.hasFocus())
                return;
            this.startTrackingViewFocus();
        });
    };
    startTrackingViewFocus() {
        this.focusTrackingRafId = requestAnimationFrame(() => {
            if (this.destroyed)
                return;
            if (this.view.hasFocus()) {
                this.focusTrackingRafId = null;
                this.manualHidden = false;
                this.render();
                return;
            }
            this.startTrackingViewFocus();
        });
    }
    stopTrackingViewFocus() {
        if (this.focusTrackingRafId !== null) {
            cancelAnimationFrame(this.focusTrackingRafId);
            this.focusTrackingRafId = null;
        }
    }
    hidePopupManual() {
        this.manualHidden = true;
        this.render();
    }
    renderContent(currentNode) {
        if (!this.content)
            return null;
        // hack for popup rerender
        window.dispatchEvent(new CustomEvent('scroll'));
        return ((0, jsx_runtime_1.jsx)(uikit_1.Popup, { open: true, hasArrow: false, anchorElement: currentNode.dom, placement: this.popupPlacement || exports.defaultPlacement, onOpenChange: this.popupOpenChangeHandler, children: (0, jsx_runtime_1.jsx)("div", { className: b(), children: this.content(this.view, currentNode, this.changeAttrsCb) }) }));
    }
    createRenderItem() {
        return (0, extensions_1.getReactRendererFromState)(this.view.state).createItem(this.idPrefix, () => this.currentNode && !this.manualHidden ? ((0, jsx_runtime_1.jsx)(ErrorBoundary_1.ErrorLoggerBoundary, { children: this.renderContent(this.currentNode) })) : null);
    }
}
exports.BaseTooltipPluginView = BaseTooltipPluginView;
//# sourceMappingURL=index.js.map
