import { jsx as _jsx } from "react/jsx-runtime";
import { Popup } from '@gravity-ui/uikit';
import { NodeSelection } from 'prosemirror-state';
// @ts-ignore // TODO: fix cjs build
import { findDomRefAtPos, findParentNodeOfType, findSelectedNodeOfType } from 'prosemirror-utils';
import { cn } from "../../classname.js";
import { getReactRendererFromState } from "../../extensions/index.js";
import { ErrorLoggerBoundary } from "../../react-utils/ErrorBoundary.js";
import "./index.css";
export const BaseTooltipCn = cn('base-tooltip');
export const defaultPlacement = [
    'bottom-start',
    'top-start',
    'bottom-end',
    'top-end',
];
const b = BaseTooltipCn;
export const baseTooltipContentCn = cn('base-tooltip-content');
export 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 = findSelectedNodeOfType(node)(selection) || 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 = 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(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 (_jsx(Popup, { open: true, hasArrow: false, anchorElement: currentNode.dom, placement: this.popupPlacement || defaultPlacement, onOpenChange: this.popupOpenChangeHandler, children: _jsx("div", { className: b(), children: this.content(this.view, currentNode, this.changeAttrsCb) }) }));
    }
    createRenderItem() {
        return getReactRendererFromState(this.view.state).createItem(this.idPrefix, () => this.currentNode && !this.manualHidden ? (_jsx(ErrorLoggerBoundary, { children: this.renderContent(this.currentNode) })) : null);
    }
}
//# sourceMappingURL=index.js.map
