"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.EntangledPortal = void 0;
const tslib_1 = require("tslib");
const React = tslib_1.__importStar(require("react"));
const rxjs_1 = require("rxjs");
const rect_1 = require("../util/rect$");
const EditorPortal_1 = require("../web/react/util/EditorPortal");
class EntangledPortalState {
    constructor(opts) {
        this.opts = opts;
        this.baseEl = null;
        this.destEl = null;
        this.baseSub = void 0;
        this.destSub = void 0;
        this.baseRect$ = new rxjs_1.BehaviorSubject(void 0);
        this.entangle = () => {
            const { destEl, opts } = this;
            if (!destEl)
                return;
            const baseRect = this.baseRect$.getValue();
            if (!baseRect)
                return;
            let { x, y } = baseRect;
            const { position } = opts;
            if (position)
                [x, y] = position(baseRect, destEl.getBoundingClientRect());
            const style = destEl.style;
            style.left = x + 'px';
            style.top = y + 'px';
        };
        this.base = (el) => {
            this.baseEl = el;
            this.baseSub?.unsubscribe();
            this.baseSub = void 0;
            if (el) {
                const baseRect$ = this.baseRect$;
                baseRect$.next(el.getBoundingClientRect());
                this.baseSub = (0, rect_1.rerender$)(el)
                    .pipe((0, rxjs_1.throttleTime)(20, void 0, { trailing: true }))
                    .subscribe(() => {
                    const rect = el.getBoundingClientRect();
                    const oldRect = baseRect$.getValue();
                    if (oldRect &&
                        rect.x === oldRect.x &&
                        rect.y === oldRect.y &&
                        rect.width === oldRect.width &&
                        rect.height === oldRect.height)
                        return;
                    baseRect$.next(rect);
                });
            }
            else
                this.baseRect$.next(void 0);
            this.opts.onBase?.(el);
        };
        this.dest = (el) => {
            this.destEl = el;
            this.destSub?.unsubscribe();
            this.destSub = void 0;
            if (el) {
                el.style.position = 'fixed';
                this.destSub = (0, rect_1.resize$)(el)
                    .pipe((0, rxjs_1.throttleTime)(20, void 0, { trailing: true }))
                    .subscribe(this.entangle);
                this.entangle();
            }
            this.opts.onDest?.(el);
        };
        /** -------------------------------------------------- {@link UiLifeCycles} */
        this.start = () => {
            const subscription = this.baseRect$.subscribe(() => {
                this.entangle();
            });
            return () => {
                subscription.unsubscribe();
                this.baseRect$.next(void 0);
                this.baseSub?.unsubscribe();
                this.baseSub = void 0;
            };
        };
    }
}
/**
 * Renders a <span> with a <div> child rendered in a portal, such that the
 * <div> position is entangled with the <span> position, the <div> moves
 * with the <span> on resize and scroll.
 */
const EntangledPortal = (props) => {
    const { span, children } = props;
    // biome-ignore lint: props are set on every re-render in the render body
    const state = React.useMemo(() => new EntangledPortalState(props), []);
    state.opts = props;
    // biome-ignore lint: too many dependencies
    React.useEffect(state.start, [state]);
    return (React.createElement("span", { ...span, ref: state.base },
        React.createElement(EditorPortal_1.EditorPortal, null,
            React.createElement("div", { ref: state.dest }, children))));
};
exports.EntangledPortal = EntangledPortal;
//# sourceMappingURL=EntangledPortal.js.map