import * as React from 'react';
import { BehaviorSubject, throttleTime } from 'rxjs';
import { resize$, rerender$ } from '../util/rect$';
import { EditorPortal } from '../web/react/util/EditorPortal';
class EntangledPortalState {
    opts;
    baseEl = null;
    destEl = null;
    baseSub = void 0;
    destSub = void 0;
    baseRect$ = new BehaviorSubject(void 0);
    constructor(opts) {
        this.opts = opts;
    }
    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';
    };
    base = (el) => {
        this.baseEl = el;
        this.baseSub?.unsubscribe();
        this.baseSub = void 0;
        if (el) {
            const baseRect$ = this.baseRect$;
            baseRect$.next(el.getBoundingClientRect());
            this.baseSub = rerender$(el)
                .pipe(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);
    };
    dest = (el) => {
        this.destEl = el;
        this.destSub?.unsubscribe();
        this.destSub = void 0;
        if (el) {
            el.style.position = 'fixed';
            this.destSub = resize$(el)
                .pipe(throttleTime(20, void 0, { trailing: true }))
                .subscribe(this.entangle);
            this.entangle();
        }
        this.opts.onDest?.(el);
    };
    /** -------------------------------------------------- {@link UiLifeCycles} */
    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.
 */
export 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, null,
            React.createElement("div", { ref: state.dest }, children))));
};
//# sourceMappingURL=EntangledPortal.js.map