/*!
 * Copyright (c) 2026 Vertex Software LLC. All rights reserved.
 */
import { 
// eslint-disable-next-line @typescript-eslint/no-unused-vars
h, Host, } from "@stencil/core";
import { Point } from "@vertexvis/geometry";
import classNames from "classnames";
import { getMouseClientPosition } from "../../lib/dom";
import { PinController } from "../../lib/pins/controller";
import { getPinColors, isTextPin, PinModel, } from "../../lib/pins/model";
import { readDOM } from "../../lib/stencil";
import { translatePointToRelative, translatePointToScreen, } from "../viewer-pin-tool/utils";
import { getComputedStyle } from "./utils";
export class VertexPinLabel {
    constructor() {
        /**
         * The current text value of the component. Value is updated on user
         * interaction.
         */
        this.value = '';
        this.focused = false;
        this.textareaRows = 1;
        this.computeContentSize = () => {
            readDOM(() => {
                if (this.contentEl != null) {
                    this.contentElBounds = this.contentEl.getBoundingClientRect();
                    const computedStyles = getComputedStyle(this.contentEl);
                    this.textareaRows = Math.max(1, Math.ceil((parseFloat(computedStyles.getPropertyValue('height')) -
                        parseFloat(computedStyles.getPropertyValue('borderWidth')) * 2) /
                        parseFloat(computedStyles.getPropertyValue('lineHeight'))));
                }
            });
        };
        this.handleInputKeyDown = (event) => {
            event.stopPropagation();
            if (event.key === 'Enter' &&
                (event.ctrlKey || event.metaKey || event.shiftKey)) {
                this.textareaRows += 1;
                this.value += '\n';
            }
            else if (event.key === 'Enter') {
                event.preventDefault();
                this.submit();
            }
        };
        this.handlePointerDown = (event) => {
            var _a;
            if (!this.focused) {
                if (this.elementBounds != null) {
                    if (event.buttons !== 2) {
                        event.preventDefault();
                        event.stopPropagation();
                    }
                    this.relativePointerDownPosition = translatePointToRelative(getMouseClientPosition(event, this.elementBounds), this.elementBounds);
                    this.pinPointerDownPosition = (_a = this.pin) === null || _a === void 0 ? void 0 : _a.label.point;
                    window.addEventListener('pointermove', this.handlePointerMove);
                    window.addEventListener('pointerup', this.handlePointerUp);
                }
            }
        };
        this.handlePointerMove = (event) => {
            var _a;
            if (this.elementBounds != null &&
                this.relativePointerDownPosition &&
                this.pinPointerDownPosition != null) {
                // Prevent selection of any text while interacting with the label
                event.preventDefault();
                event.stopPropagation();
                const point = getMouseClientPosition(event, this.elementBounds);
                const relative = translatePointToRelative(point, this.elementBounds);
                const relativeDelta = Point.subtract(relative, this.relativePointerDownPosition);
                const myUpdatedPin = this.pin != null
                    ? {
                        ...this.pin,
                        label: {
                            ...this.pin.label,
                            point: Point.add(this.pinPointerDownPosition, relativeDelta),
                        },
                    }
                    : undefined;
                if (myUpdatedPin) {
                    (_a = this.pinController) === null || _a === void 0 ? void 0 : _a.updatePin(myUpdatedPin);
                    this.computeScreenPosition();
                }
            }
        };
        this.handlePointerUp = (event) => {
            var _a, _b;
            if (((_a = this.pinController) === null || _a === void 0 ? void 0 : _a.getToolMode()) === 'edit' &&
                this.relativePointerDownPosition != null &&
                this.elementBounds != null) {
                const pointerDownScreen = translatePointToScreen(this.relativePointerDownPosition, this.elementBounds);
                const distanceBetweenStartAndEndPoint = Point.distance(pointerDownScreen, getMouseClientPosition(event, this.elementBounds));
                if (distanceBetweenStartAndEndPoint <= 2) {
                    this.focused = true;
                    this.labelFocused.emit((_b = this.pin) === null || _b === void 0 ? void 0 : _b.id);
                }
            }
            this.relativePointerDownPosition = undefined;
            this.pinPointerDownPosition = undefined;
            window.removeEventListener('pointermove', this.handlePointerMove);
            window.removeEventListener('pointerup', this.handlePointerUp);
        };
        this.handleTextBlur = () => {
            this.submit();
        };
        this.handleTextInput = (event) => {
            const input = event.target;
            this.value = input.value;
        };
    }
    /**
     * Gives focus to the component's internal text input.
     */
    async setFocus() {
        var _a, _b, _c;
        // HTMLInputElement.focus() doesn't exist in tests.
        if (typeof ((_a = this.inputEl) === null || _a === void 0 ? void 0 : _a.focus) === 'function') {
            this.focused = true;
            this.labelFocused.emit((_b = this.pin) === null || _b === void 0 ? void 0 : _b.id);
            (_c = this.inputEl) === null || _c === void 0 ? void 0 : _c.focus();
        }
    }
    /**
     * @internal
     * submits the current text, unfocuses the input and emits the blur event to consumers
     */
    async submit() {
        var _a, _b;
        this.focused = false;
        this.labelBlurred.emit((_a = this.pin) === null || _a === void 0 ? void 0 : _a.id);
        if (this.pin != null) {
            (_b = this.pinController) === null || _b === void 0 ? void 0 : _b.updatePin({
                ...this.pin,
                label: {
                    point: this.pin.label.point,
                    text: this.value,
                },
            });
        }
    }
    watchFocusChange() {
        var _a, _b;
        this.labelChanged.emit();
        if (this.focused) {
            (_a = this.pinController) === null || _a === void 0 ? void 0 : _a.setSelectedPinId((_b = this.pin) === null || _b === void 0 ? void 0 : _b.id);
        }
    }
    watchPinChange() {
        var _a, _b;
        this.value = (_b = (_a = this.pin) === null || _a === void 0 ? void 0 : _a.label.text) !== null && _b !== void 0 ? _b : '';
        this.computeScreenPosition();
    }
    watchElementBoundsChange() {
        this.computeScreenPosition();
    }
    componentWillLoad() {
        var _a, _b;
        this.value = (_b = (_a = this.pin) === null || _a === void 0 ? void 0 : _a.label.text) !== null && _b !== void 0 ? _b : '';
        this.computeScreenPosition();
    }
    componentDidLoad() {
        this.resizeObserver = new ResizeObserver(() => {
            this.labelChanged.emit();
        });
        this.contentResizeObserver = new ResizeObserver(this.computeContentSize);
        if (this.hostEl != null) {
            this.resizeObserver.observe(this.hostEl);
        }
        if (this.contentEl != null) {
            this.contentResizeObserver.observe(this.contentEl);
        }
        if (this.pinController == null) {
            this.pinController = new PinController(new PinModel());
        }
    }
    disconnectedCallback() {
        var _a, _b;
        (_a = this.resizeObserver) === null || _a === void 0 ? void 0 : _a.disconnect();
        (_b = this.contentResizeObserver) === null || _b === void 0 ? void 0 : _b.disconnect();
    }
    componentDidRender() {
        if (this.focused && this.inputEl != null) {
            this.setFocus();
        }
    }
    render() {
        var _a, _b, _c;
        const { primaryColor, accentColor } = getPinColors(this.pin);
        return (h(Host, { key: 'a02e4135406398f7e49331ac65ec121c216c75c1' }, h("div", { key: '14373fe8ac2fff6988c38d8c73fdd484c0fd8079', class: classNames('pin-label-input-wrapper', {
                focused: this.focused,
            }), onPointerDown: this.handlePointerDown, style: {
                top: `${((_a = this.computedScreenPosition) === null || _a === void 0 ? void 0 : _a.y.toString()) || 0}px`,
                left: `${((_b = this.computedScreenPosition) === null || _b === void 0 ? void 0 : _b.x.toString()) || 0}px`,
                minWidth: this.computeMinWidth(),
                maxWidth: this.computeMaxWidth(),
                maxHeight: this.computeMaxHeight(),
                borderColor: primaryColor,
                background: accentColor,
            } }, h("textarea", { key: '10f15845d35c653f4d84c9d39694977e79ec329a', id: `pin-label-input-${(_c = this.pin) === null || _c === void 0 ? void 0 : _c.id}`, class: classNames('pin-label-input', 'pin-label-text', {
                ['readonly']: !this.focused,
            }), disabled: !this.focused, ref: (ref) => (this.inputEl = ref), value: this.value, rows: this.textareaRows, onKeyDown: this.handleInputKeyDown, onInput: this.handleTextInput, onBlur: this.handleTextBlur }), !this.focused && (h("div", { key: 'a82f6c8553da740e3ab8c9c4f0b92cd1c3647ec6', class: "pin-input-drag-target", onPointerDown: this.handlePointerDown }))), h("div", { key: '197aae8a4bcb977305f662b61308310eacb0b780', ref: (el) => (this.contentEl = el), class: classNames('pin-label-text', 'pin-label-hidden'), style: {
                maxWidth: this.computeMaxWidth(),
                maxHeight: this.computeMaxHeight(),
            } }, this.hiddenContent())));
    }
    hiddenContent() {
        // This corrects some inconsistencies in how a newline character
        // is represented in a div. Leveraging `<br>`s results in a more
        // consistent representation of the newlines in a textarea.
        return this.value.includes('\n')
            ? this.value
                .split('\n')
                .reduce((res, str) => [...res, str, h("br", null)], [])
            : [this.value];
    }
    computeMinWidth() {
        if (this.contentElBounds != null) {
            const expected = `${this.contentElBounds.width + 8}px`;
            return `min(${expected}, ${this.computeMaxWidth()})`;
        }
        else {
            return `var(--viewer-annotations-pin-label-min-width)`;
        }
    }
    computeMaxWidth() {
        const expected = `var(--viewer-annotations-pin-label-max-width)`;
        return `min(${expected}, ${this.computeRemainingWidth()})`;
    }
    computeMaxHeight() {
        const expected = `var(--viewer-annotations-pin-label-max-height)`;
        return `min(${expected}, ${this.computeRemainingHeight()})`;
    }
    computeRemainingWidth() {
        var _a, _b;
        return `calc(${((_a = this.elementBounds) === null || _a === void 0 ? void 0 : _a.width.toString()) || 0}px - ${((_b = this.computedScreenPosition) === null || _b === void 0 ? void 0 : _b.x.toString()) || 0}px)`;
    }
    computeRemainingHeight() {
        var _a, _b;
        return `calc(${((_a = this.elementBounds) === null || _a === void 0 ? void 0 : _a.height.toString()) || 0}px - ${((_b = this.computedScreenPosition) === null || _b === void 0 ? void 0 : _b.y.toString()) || 0}px)`;
    }
    computeScreenPosition() {
        this.computedScreenPosition =
            isTextPin(this.pin) &&
                this.elementBounds != null &&
                this.pin.label.point != null
                ? translatePointToScreen(this.pin.label.point, this.elementBounds)
                : undefined;
    }
    static get is() { return "vertex-viewer-pin-label"; }
    static get originalStyleUrls() {
        return {
            "$": ["viewer-pin-label.css"]
        };
    }
    static get styleUrls() {
        return {
            "$": ["viewer-pin-label.css"]
        };
    }
    static get properties() {
        return {
            "pin": {
                "type": "unknown",
                "mutable": false,
                "complexType": {
                    "original": "TextPin",
                    "resolved": "TextPin | undefined",
                    "references": {
                        "TextPin": {
                            "location": "import",
                            "path": "../../lib/pins/model",
                            "id": "src/lib/pins/model.ts::TextPin",
                            "referenceLocation": "TextPin"
                        }
                    }
                },
                "required": false,
                "optional": true,
                "docs": {
                    "tags": [],
                    "text": "The pin to draw for the group"
                },
                "getter": false,
                "setter": false
            },
            "elementBounds": {
                "type": "unknown",
                "mutable": false,
                "complexType": {
                    "original": "DOMRect",
                    "resolved": "DOMRect | undefined",
                    "references": {
                        "DOMRect": {
                            "location": "global",
                            "id": "global::DOMRect"
                        }
                    }
                },
                "required": false,
                "optional": true,
                "docs": {
                    "tags": [],
                    "text": "The dimensions of the canvas for the pins"
                },
                "getter": false,
                "setter": false
            },
            "value": {
                "type": "string",
                "mutable": true,
                "complexType": {
                    "original": "string",
                    "resolved": "string",
                    "references": {}
                },
                "required": false,
                "optional": false,
                "docs": {
                    "tags": [],
                    "text": "The current text value of the component. Value is updated on user\ninteraction."
                },
                "getter": false,
                "setter": false,
                "reflect": false,
                "attribute": "value",
                "defaultValue": "''"
            },
            "pinController": {
                "type": "unknown",
                "mutable": false,
                "complexType": {
                    "original": "PinController",
                    "resolved": "PinController | undefined",
                    "references": {
                        "PinController": {
                            "location": "import",
                            "path": "../../lib/pins/controller",
                            "id": "src/lib/pins/controller.ts::PinController",
                            "referenceLocation": "PinController"
                        }
                    }
                },
                "required": false,
                "optional": true,
                "docs": {
                    "tags": [],
                    "text": "The controller that drives behavior for pin operations"
                },
                "getter": false,
                "setter": false
            }
        };
    }
    static get states() {
        return {
            "focused": {},
            "computedScreenPosition": {},
            "textareaRows": {},
            "contentElBounds": {}
        };
    }
    static get events() {
        return [{
                "method": "labelChanged",
                "name": "labelChanged",
                "bubbles": true,
                "cancelable": true,
                "composed": true,
                "docs": {
                    "tags": [{
                            "name": "internal",
                            "text": undefined
                        }],
                    "text": ""
                },
                "complexType": {
                    "original": "void",
                    "resolved": "void",
                    "references": {}
                }
            }, {
                "method": "labelFocused",
                "name": "labelFocused",
                "bubbles": true,
                "cancelable": true,
                "composed": true,
                "docs": {
                    "tags": [],
                    "text": "Emitted whenever the label is focused, with the ID of the\nassociated pin (or undefined if no pin is provided)."
                },
                "complexType": {
                    "original": "string | undefined",
                    "resolved": "string | undefined",
                    "references": {}
                }
            }, {
                "method": "labelBlurred",
                "name": "labelBlurred",
                "bubbles": true,
                "cancelable": true,
                "composed": true,
                "docs": {
                    "tags": [],
                    "text": "Emitted whenever the label is blurred, with the ID of the\nassociated pin (or undefined if no pin is provided)."
                },
                "complexType": {
                    "original": "string | undefined",
                    "resolved": "string | undefined",
                    "references": {}
                }
            }];
    }
    static get methods() {
        return {
            "setFocus": {
                "complexType": {
                    "signature": "() => Promise<void>",
                    "parameters": [],
                    "references": {
                        "Promise": {
                            "location": "global",
                            "id": "global::Promise"
                        }
                    },
                    "return": "Promise<void>"
                },
                "docs": {
                    "text": "Gives focus to the component's internal text input.",
                    "tags": []
                }
            },
            "submit": {
                "complexType": {
                    "signature": "() => Promise<void>",
                    "parameters": [],
                    "references": {
                        "Promise": {
                            "location": "global",
                            "id": "global::Promise"
                        }
                    },
                    "return": "Promise<void>"
                },
                "docs": {
                    "text": "",
                    "tags": [{
                            "name": "internal",
                            "text": "submits the current text, unfocuses the input and emits the blur event to consumers"
                        }]
                }
            }
        };
    }
    static get elementRef() { return "hostEl"; }
    static get watchers() {
        return [{
                "propName": "focused",
                "methodName": "watchFocusChange"
            }, {
                "propName": "pin",
                "methodName": "watchPinChange"
            }, {
                "propName": "elementBounds",
                "methodName": "watchElementBoundsChange"
            }];
    }
}
//# sourceMappingURL=viewer-pin-label.js.map
