var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
    return c > 3 && r && Object.defineProperty(target, key, r), r;
};
import { assertExists } from '@blockeditor/global/utils';
import { createInlineKeyDownHandler, InlineEditor, } from '@blockeditor/inline';
import { ShadowlessElement, WithDisposable } from '@blockeditor/lit';
import { Text, Workspace } from '@blockeditor/store';
import { css, html } from 'lit';
import { customElement, property, query } from 'lit/decorators.js';
import { classMap } from 'lit/directives/class-map.js';
import { z } from 'zod';
import { onVBeforeinput, onVCompositionEnd } from './hooks.js';
let RichText = class RichText extends WithDisposable(ShadowlessElement) {
    constructor() {
        super(...arguments);
        this.embedChecker = () => false;
        this.readonly = false;
        // If it is true rich-text will prevent events related to clipboard bubbling up and handle them by itself.
        this.enableClipboard = true;
        // If it is true rich-text will handle undo/redo by itself. (including v-range restore)
        // It will listen ctrl+z/ctrl+shift+z and call undoManager.undo/redo, keydown event will not
        // bubble up if pressed ctrl+z/ctrl+shift+z.
        this.enableUndoRedo = true;
        this.enableAutoScrollVertically = true;
        this.enableAutoScrollHorizontally = true;
        this.wrapText = true;
        // `attributesSchema` will be overwritten to `z.object({})` if `enableFormat` is false.
        this.enableFormat = true;
        this._inlineEditor = null;
        this._onStackItemAdded = (event) => {
            const inlineRange = this.inlineEditor?.getInlineRange();
            if (inlineRange) {
                event.stackItem.meta.set('richtext-v-range', inlineRange);
            }
        };
        this._onStackItemPopped = (event) => {
            const inlineRange = event.stackItem.meta.get('richtext-v-range');
            if (inlineRange && this.inlineEditor?.isValidInlineRange(inlineRange)) {
                this.inlineEditor?.setInlineRange(inlineRange);
            }
        };
        this._onCopy = (e) => {
            const inlineEditor = this.inlineEditor;
            assertExists(inlineEditor);
            const inlineRange = inlineEditor.getInlineRange();
            if (!inlineRange)
                return;
            const text = inlineEditor.yTextString.slice(inlineRange.index, inlineRange.index + inlineRange.length);
            e.clipboardData?.setData('text/plain', text);
            e.preventDefault();
            e.stopPropagation();
        };
        this._onCut = (e) => {
            const inlineEditor = this.inlineEditor;
            assertExists(inlineEditor);
            const inlineRange = inlineEditor.getInlineRange();
            if (!inlineRange)
                return;
            const text = inlineEditor.yTextString.slice(inlineRange.index, inlineRange.index + inlineRange.length);
            inlineEditor.deleteText(inlineRange);
            inlineEditor.setInlineRange({
                index: inlineRange.index,
                length: 0,
            });
            e.clipboardData?.setData('text/plain', text);
            e.preventDefault();
            e.stopPropagation();
        };
        this._onPaste = (e) => {
            const inlineEditor = this.inlineEditor;
            assertExists(inlineEditor);
            const inlineRange = inlineEditor.getInlineRange();
            if (!inlineRange)
                return;
            const text = e.clipboardData?.getData('text/plain');
            if (!text)
                return;
            inlineEditor.insertText(inlineRange, text);
            inlineEditor.setInlineRange({
                index: inlineRange.index + text.length,
                length: 0,
            });
            e.preventDefault();
            e.stopPropagation();
        };
    }
    get inlineEditorContainer() {
        assertExists(this._inlineEditorContainer);
        return this._inlineEditorContainer;
    }
    get inlineEditor() {
        return this._inlineEditor;
    }
    _init() {
        if (this._inlineEditor) {
            throw new Error('Inline editor already exists.');
        }
        if (!this.enableFormat) {
            this.attributesSchema = z.object({});
        }
        // init inline editor
        this._inlineEditor = new InlineEditor(this._yText, {
            isEmbed: delta => this.embedChecker(delta),
            hooks: {
                beforeinput: onVBeforeinput,
                compositionEnd: onVCompositionEnd,
            },
            inlineRangeProvider: this.inlineRangeProvider,
        });
        if (this.attributesSchema) {
            this._inlineEditor.setAttributeSchema(this.attributesSchema);
        }
        if (this.attributeRenderer) {
            this._inlineEditor.setAttributeRenderer(this.attributeRenderer);
        }
        const inlineEditor = this._inlineEditor;
        const markdownShortcutHandler = this.markdownShortcutHandler;
        if (markdownShortcutHandler) {
            const keyDownHandler = createInlineKeyDownHandler(inlineEditor, {
                inputRule: {
                    key: [' ', 'Enter'],
                    handler: context => markdownShortcutHandler(context, this.undoManager),
                },
            });
            inlineEditor.disposables.addFromEvent(this.inlineEditorContainer, 'keydown', keyDownHandler);
        }
        // init auto scroll
        inlineEditor.disposables.add(inlineEditor.slots.inlineRangeUpdate.on(([inlineRange, sync]) => {
            if (!inlineRange || !sync)
                return;
            inlineEditor
                .waitForUpdate()
                .then(() => {
                if (!inlineEditor.mounted)
                    return;
                const range = inlineEditor.toDomRange(inlineRange);
                if (!range)
                    return;
                // scroll container is window
                if (this.enableAutoScrollVertically) {
                    const rangeRect = range.getBoundingClientRect();
                    if (rangeRect.top < 0) {
                        this.scrollIntoView({ block: 'start' });
                    }
                    else if (rangeRect.bottom > window.innerHeight) {
                        this.scrollIntoView({ block: 'end' });
                    }
                }
                // scroll container is `inlineEditorContainer`
                if (this.enableAutoScrollHorizontally) {
                    const containerRect = this.inlineEditorContainer.getBoundingClientRect();
                    const rangeRect = range.getBoundingClientRect();
                    let scrollLeft = this.inlineEditorContainer.scrollLeft;
                    if (rangeRect.left + rangeRect.width >
                        containerRect.left + containerRect.width) {
                        scrollLeft +=
                            rangeRect.left +
                                rangeRect.width -
                                (containerRect.left + containerRect.width) +
                                2;
                    }
                    this.inlineEditorContainer.scrollLeft = scrollLeft;
                }
            })
                .catch(console.error);
        }));
        inlineEditor.mount(this.inlineEditorContainer);
        inlineEditor.setReadonly(this.readonly);
    }
    get _yText() {
        return this.yText instanceof Text ? this.yText.yText : this.yText;
    }
    _unmount() {
        if (this.inlineEditor?.mounted) {
            this.inlineEditor.unmount();
        }
        this._inlineEditor = null;
    }
    async getUpdateComplete() {
        const result = await super.getUpdateComplete();
        await this.inlineEditor?.waitForUpdate();
        return result;
    }
    connectedCallback() {
        super.connectedCallback();
        assertExists(this._yText, 'rich-text need yText to init.');
        assertExists(this._yText.doc, 'yText should be bind to yDoc.');
        if (!this.undoManager) {
            this.undoManager = new Workspace.Y.UndoManager(this._yText, {
                trackedOrigins: new Set([this._yText.doc.clientID]),
            });
        }
        if (this.enableUndoRedo) {
            this.disposables.addFromEvent(this, 'keydown', (e) => {
                if (e.ctrlKey || e.metaKey) {
                    if (e.key === 'z') {
                        if (e.shiftKey) {
                            this.undoManager.redo();
                        }
                        else {
                            this.undoManager.undo();
                        }
                        e.stopPropagation();
                    }
                }
            });
            this.undoManager.on('stack-item-added', this._onStackItemAdded);
            this.undoManager.on('stack-item-popped', this._onStackItemPopped);
            this.disposables.add({
                dispose: () => {
                    this.undoManager.off('stack-item-added', this._onStackItemAdded);
                    this.undoManager.off('stack-item-popped', this._onStackItemPopped);
                },
            });
        }
        if (this.enableClipboard) {
            this.disposables.addFromEvent(this, 'copy', this._onCopy);
            this.disposables.addFromEvent(this, 'cut', this._onCut);
            this.disposables.addFromEvent(this, 'paste', this._onPaste);
        }
        this.updateComplete
            .then(() => {
            this._unmount();
            this._init();
            this.disposables.add({
                dispose: () => {
                    this._unmount();
                },
            });
        })
            .catch(console.error);
    }
    updated() {
        if (this._inlineEditor) {
            this._inlineEditor.setReadonly(this.readonly);
        }
    }
    render() {
        const classes = classMap({
            'inline-editor': true,
            'nowrap-lines': !this.wrapText,
        });
        return html `<div class=${classes}></div>`;
    }
};
RichText.styles = css `
    rich-text {
      display: block;
      height: 100%;
      width: 100%;
    }

    .inline-editor {
      height: 100%;
      width: 100%;
      outline: none;
      cursor: text;
    }

    rich-text .nowrap-lines {
      overflow-x: auto;
    }

    rich-text v-line {
      scroll-margin-top: 50px;
      scroll-margin-bottom: 30px;
    }

    rich-text .nowrap-lines v-text span,
    rich-text .nowrap-lines v-element span {
      white-space: pre !important;
    }
  `;
__decorate([
    query('.inline-editor')
], RichText.prototype, "_inlineEditorContainer", void 0);
__decorate([
    property({ attribute: false })
], RichText.prototype, "yText", void 0);
__decorate([
    property({ attribute: false })
], RichText.prototype, "attributesSchema", void 0);
__decorate([
    property({ attribute: false })
], RichText.prototype, "attributeRenderer", void 0);
__decorate([
    property({ attribute: false })
], RichText.prototype, "markdownShortcutHandler", void 0);
__decorate([
    property({ attribute: false })
], RichText.prototype, "embedChecker", void 0);
__decorate([
    property({ attribute: false })
], RichText.prototype, "readonly", void 0);
__decorate([
    property({ attribute: false })
], RichText.prototype, "inlineRangeProvider", void 0);
__decorate([
    property({ attribute: false })
], RichText.prototype, "undoManager", void 0);
__decorate([
    property({ attribute: false })
], RichText.prototype, "enableClipboard", void 0);
__decorate([
    property({ attribute: false })
], RichText.prototype, "enableUndoRedo", void 0);
__decorate([
    property({ attribute: false })
], RichText.prototype, "enableAutoScrollVertically", void 0);
__decorate([
    property({ attribute: false })
], RichText.prototype, "enableAutoScrollHorizontally", void 0);
__decorate([
    property({ attribute: false })
], RichText.prototype, "wrapText", void 0);
__decorate([
    property({ attribute: false })
], RichText.prototype, "enableFormat", void 0);
RichText = __decorate([
    customElement('rich-text')
], RichText);
export { RichText };
//# sourceMappingURL=rich-text.js.map