import { StateEffect, StateField } from '@codemirror/state';
import { Decoration, EditorView, WidgetType, } from '@codemirror/view';
export function createInlinePanelController() {
    const showEffect = StateEffect.define();
    let callbacksRef = null;
    class InlinePanelWidget extends WidgetType {
        options;
        resizeObserver = null;
        constructor(options) {
            super();
            this.options = options;
        }
        toDOM(view) {
            const container = document.createElement('div');
            container.className = 'cm-inline-panel';
            this.resizeObserver = new ResizeObserver(() => view.requestMeasure());
            this.resizeObserver.observe(container);
            callbacksRef?.onMount(container);
            return container;
        }
        destroy() {
            this.resizeObserver?.disconnect();
            this.resizeObserver = null;
            callbacksRef?.onUnmount();
        }
        eq(other) {
            return (other.options.pos === this.options.pos &&
                other.options.placement === this.options.placement);
        }
        ignoreEvent() {
            return true;
        }
    }
    const buildDecoration = (options) => Decoration.set([
        Decoration.widget({
            widget: new InlinePanelWidget(options),
            block: true,
            side: options.placement === 'below' ? 1 : -1,
        }).range(options.pos),
    ]);
    const readOptions = (decorations) => {
        const widget = decorations.iter().value?.spec?.widget;
        return widget instanceof InlinePanelWidget ? widget.options : null;
    };
    const field = StateField.define({
        create() {
            return Decoration.none;
        },
        update(decorations, transaction) {
            for (const effect of transaction.effects) {
                if (effect.is(showEffect)) {
                    return effect.value === null
                        ? Decoration.none
                        : buildDecoration(effect.value);
                }
            }
            // Nothing to do when the panel is closed, or when the document is unchanged
            if (decorations.size === 0 || !transaction.docChanged) {
                return decorations.map(transaction.changes);
            }
            // Re-anchor manually because CodeMirror's default mapping silently drops
            // block-widget decorations when their anchor line is deleted
            const options = readOptions(decorations);
            if (options === null) {
                return decorations.map(transaction.changes);
            }
            const side = options.placement === 'below' ? 1 : -1;
            const lineNumber = Math.min(transaction.startState.doc.lineAt(options.pos).number, transaction.newDoc.lines);
            const line = transaction.newDoc.line(lineNumber);
            return buildDecoration({
                ...options,
                pos: side === 1 ? line.to : line.from,
            });
        },
        provide: (f) => EditorView.decorations.from(f),
    });
    return {
        extension: field,
        show: (options) => showEffect.of(options),
        hide: () => showEffect.of(null),
        updateCallbacks: (callbacks) => {
            callbacksRef = callbacks;
        },
    };
}
//# sourceMappingURL=inlinePanel.js.map