// #region Imports
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;
};
var __metadata = (this && this.__metadata) || function (k, v) {
    if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
import { HTMLElementEventEmitter } from '@breadstone/mosaik-elements';
import { Emit } from '../../../../../events/Decorators/EmitDecorator';
import { Listen } from '../../../../../events/Decorators/ListenDecorator';
import { Themeable } from '../../../../Behaviors/Themeable';
import { KeyboardController } from '../../../../Controllers/KeyboardController';
import { PanController } from '../../../../Controllers/PanController';
import { PinchController } from '../../../../Controllers/PinchController';
import { Attribute } from '../../../../Decorators/AttributeDecorator';
import { Component } from '../../../../Decorators/ComponentDecorator';
import { DesignerCanvasInteractionMode } from '../../../../Types/DesignerCanvasInteractionMode';
import { Key } from '../../../../Types/Key';
import { CustomElement } from '../../../Abstracts/CustomElement';
import { designerCanvasElementTemplate } from './DesignerCanvasElementTemplate';
import { designerCanvasElementCosmopolitanStyle } from './Themes/DesignerCanvasElement.Cosmopolitan';
import { designerCanvasElementJoyStyle } from './Themes/DesignerCanvasElement.Joy';
import { designerCanvasElementMemphisStyle } from './Themes/DesignerCanvasElement.Memphis';
// #endregion
/**
 * DesignerCanvas - The navigable spatial interaction surface around the frame.
 *
 * @description
 * Owns viewport interaction such as panning and zooming. Uses PanController,
 * PinchController, and KeyboardController from the foundation for gesture
 * and keyboard handling. Supports Ctrl+scroll zoom, pinch-to-zoom,
 * drag-to-pan, and keyboard shortcuts for common zoom levels.
 *
 * @name DesignerCanvas
 * @element mosaik-designer-canvas
 * @category Layouts
 *
 * @slot - Default slot for exactly one mosaik-designer-frame
 *
 * @csspart viewport - The viewport layer that clips and contains the transform surface
 * @csspart transform - The transform layer that applies pan and zoom transformations
 *
 * @cssprop {String} --designer-canvas-background-color - Background color of the canvas surface
 * @cssprop {String} --designer-canvas-dot-color - Color of the dotted grid background pattern
 * @cssprop {String} --designer-canvas-dot-size - Size of individual grid dots
 * @cssprop {String} --designer-canvas-dot-spacing - Distance between grid dots
 * @cssprop {String} --designer-canvas-transition-duration - Transition duration for animated state changes
 * @cssprop {String} --designer-canvas-transition-mode - Transition timing function
 * @cssprop {String} --designer-canvas-transition-property - Transition properties to animate
 *
 * @dependency {DesignerFrameElement} - The frame element placed inside the canvas
 *
 * @fires zoomChanged {DesignerZoomChangedEvent} - Fired when the zoom level changes
 *
 * @example
 * Pannable and zoomable canvas with a centered artboard:
 * ```html
 * <mosaik-designer-canvas>
 *   <mosaik-designer-frame></mosaik-designer-frame>
 * </mosaik-designer-canvas>
 * ```
 *
 * @example
 * Canvas with custom zoom bounds:
 * ```html
 * <mosaik-designer-canvas min-zoom="0.25" max-zoom="4">
 *   <mosaik-designer-frame></mosaik-designer-frame>
 * </mosaik-designer-canvas>
 * ```
 *
 * @public
 */
let DesignerCanvasElement = class DesignerCanvasElement extends Themeable(CustomElement) {
    // #region Fields
    _panController;
    _pinchController;
    _keyboardController;
    _zoomChanged;
    _zoomFactor;
    _panFactor;
    _zoom;
    _minZoom;
    _maxZoom;
    _panX;
    _panY;
    _panStartX;
    _panStartY;
    _isPanning;
    _interactionMode;
    // #endregion
    // #region Ctor
    /**
     * Constructs a new instance of the {@link DesignerCanvasElement} class.
     *
     * @public
     */
    constructor() {
        super();
        this._zoomFactor = 0.1;
        this._panFactor = 50;
        this._zoom = 1;
        this._minZoom = 0.1;
        this._maxZoom = 8;
        this._panX = 0;
        this._panY = 0;
        this._panStartX = 0;
        this._panStartY = 0;
        this._isPanning = false;
        this._interactionMode = DesignerCanvasInteractionMode.Pointer;
        this._zoomChanged = new HTMLElementEventEmitter(this, 'zoomChanged');
        this._panController = new PanController(this);
        this._pinchController = new PinchController(this);
        this._keyboardController = new KeyboardController(this);
        this._keyboardController
            .add(Key.Add, () => this.zoomIn(), (event) => (event?.metaKey || event?.ctrlKey) ?? false, { preventDefault: true })
            .add(Key.Equals, () => this.zoomIn(), (event) => (event?.metaKey || event?.ctrlKey) ?? false, { preventDefault: true })
            .add(Key.Subtract, () => this.zoomOut(), (event) => (event?.metaKey || event?.ctrlKey) ?? false, { preventDefault: true })
            .add(Key.Dash, () => this.zoomOut(), (event) => (event?.metaKey || event?.ctrlKey) ?? false, { preventDefault: true })
            .add(Key.Zero, () => this.resetZoom(), (event) => (event?.metaKey || event?.ctrlKey) ?? false, { preventDefault: true })
            .add(Key.One, () => this.setZoom(1), (event) => (event?.metaKey || event?.ctrlKey) ?? false, { preventDefault: true })
            .add(Key.Two, () => this.setZoom(2), (event) => (event?.metaKey || event?.ctrlKey) ?? false, { preventDefault: true });
    }
    // #endregion
    // #region Properties
    /**
     * Returns the `is` property.
     *
     * @public
     * @static
     * @readonly
     */
    static get is() {
        return 'mosaik-designer-canvas';
    }
    /**
     * Gets or sets the `zoom` property.
     * The current zoom level (1 = 100%).
     *
     * @public
     * @attr
     */
    get zoom() {
        return this._zoom;
    }
    set zoom(value) {
        const clamped = Math.min(this._maxZoom, Math.max(this._minZoom, value));
        if (this._zoom !== clamped) {
            this._zoom = clamped;
            this.requestUpdate('zoom');
        }
    }
    /**
     * Gets or sets the `minZoom` property.
     *
     * @public
     * @attr
     */
    get minZoom() {
        return this._minZoom;
    }
    set minZoom(value) {
        if (this._minZoom !== value) {
            this._minZoom = value;
            this.requestUpdate('minZoom');
        }
    }
    /**
     * Gets or sets the `maxZoom` property.
     *
     * @public
     * @attr
     */
    get maxZoom() {
        return this._maxZoom;
    }
    set maxZoom(value) {
        if (this._maxZoom !== value) {
            this._maxZoom = value;
            this.requestUpdate('maxZoom');
        }
    }
    /**
     * Returns the current pan X offset.
     *
     * @public
     * @readonly
     */
    get panX() {
        return this._panX;
    }
    /**
     * Returns the current pan Y offset.
     *
     * @public
     * @readonly
     */
    get panY() {
        return this._panY;
    }
    /**
     * Returns the current zoom level as percentage string.
     *
     * @public
     * @readonly
     */
    get zoomLevel() {
        return `${Math.round(this._zoom * 100)}%`;
    }
    /**
     * Returns whether the canvas is currently being panned.
     *
     * @public
     * @readonly
     */
    get isPanning() {
        return this._isPanning;
    }
    /**
     * Gets or sets the `interactionMode` property.
     * Controls whether drag-to-pan is enabled and which cursor is shown.
     *
     * - `pointer`: default cursor, no drag-to-pan.
     * - `pan`: grab cursor, drag-to-pan enabled.
     *
     * @public
     * @attr
     */
    get interactionMode() {
        return this._interactionMode;
    }
    set interactionMode(value) {
        if (this._interactionMode !== value) {
            this._interactionMode = value;
            this.requestUpdate('interactionMode');
        }
    }
    /**
     * Called when the zoom level changes.
     *
     * @public
     * @readonly
     * @eventProperty
     */
    get zoomChanged() {
        return this._zoomChanged;
    }
    // #endregion
    // #region Methods
    /**
     * Zooms in by the zoom factor.
     *
     * @public
     */
    zoomIn() {
        this.zoom = this._zoom + this._zoomFactor;
        this._zoomChanged.emit(this._zoom);
    }
    /**
     * Zooms out by the zoom factor.
     *
     * @public
     */
    zoomOut() {
        this.zoom = this._zoom - this._zoomFactor;
        this._zoomChanged.emit(this._zoom);
    }
    /**
     * Sets a specific zoom level.
     *
     * @public
     */
    setZoom(level) {
        this.zoom = level;
        this._zoomChanged.emit(this._zoom);
    }
    /**
     * Resets zoom and pan to defaults.
     *
     * @public
     */
    resetZoom() {
        this._zoom = 1;
        this._panX = 0;
        this._panY = 0;
        this.requestUpdate('zoom');
        this._zoomChanged.emit(this._zoom);
    }
    /**
     * Fits the frame within the visible canvas area.
     *
     * @public
     */
    fitToFrame() {
        this._panX = 0;
        this._panY = 0;
        this._zoom = 1;
        this.requestUpdate('zoom');
        this._zoomChanged.emit(this._zoom);
    }
    /**
     * Pans the canvas by a given delta.
     *
     * @public
     */
    panBy(deltaX, deltaY) {
        this._panX += deltaX;
        this._panY += deltaY;
        this.requestUpdate('panX');
    }
    /**
     * @protected
     */
    onPanStart() {
        if (this._interactionMode !== DesignerCanvasInteractionMode.Pan) {
            return;
        }
        this._panStartX = this._panX;
        this._panStartY = this._panY;
        this._isPanning = true;
        this.toggleAttribute('is-panning', true);
    }
    /**
     * @protected
     */
    onPanMove(event) {
        if (this._interactionMode !== DesignerCanvasInteractionMode.Pan) {
            return;
        }
        event.stopPropagation();
        const deltaX = event.detail.deltaX / this._zoom;
        const deltaY = event.detail.deltaY / this._zoom;
        this._panX = this._panStartX + deltaX;
        this._panY = this._panStartY + deltaY;
        this.requestUpdate('panX');
    }
    /**
     * @protected
     */
    onPanEnd() {
        if (!this._isPanning) {
            return;
        }
        this._panStartX = this._panX;
        this._panStartY = this._panY;
        this._isPanning = false;
        this.toggleAttribute('is-panning', false);
    }
    /**
     * @protected
     */
    onPinchMove(event) {
        event.stopPropagation();
        this.zoom = event.detail.scale;
        this._zoomChanged.emit(this._zoom);
    }
    /**
     * Handles wheel events for Ctrl+scroll zoom.
     *
     * @protected
     */
    onWheel(event) {
        if (!event.ctrlKey) {
            return;
        }
        event.preventDefault();
        event.stopPropagation();
        const delta = -event.deltaY * 0.01;
        const newZoom = Math.min(this._maxZoom, Math.max(this._minZoom, this._zoom + delta));
        if (newZoom === this._zoom) {
            return;
        }
        this._zoom = newZoom;
        this.requestUpdate('zoom');
        this._zoomChanged.emit(this._zoom);
    }
};
__decorate([
    Attribute({ type: Number }),
    __metadata("design:type", Number),
    __metadata("design:paramtypes", [Number])
], DesignerCanvasElement.prototype, "zoom", null);
__decorate([
    Attribute({ type: Number }),
    __metadata("design:type", Number),
    __metadata("design:paramtypes", [Number])
], DesignerCanvasElement.prototype, "minZoom", null);
__decorate([
    Attribute({ type: Number }),
    __metadata("design:type", Number),
    __metadata("design:paramtypes", [Number])
], DesignerCanvasElement.prototype, "maxZoom", null);
__decorate([
    Attribute({ type: String }),
    __metadata("design:type", String),
    __metadata("design:paramtypes", [String])
], DesignerCanvasElement.prototype, "interactionMode", null);
__decorate([
    Emit({ eventName: 'zoomChanged' }),
    __metadata("design:type", Object),
    __metadata("design:paramtypes", [])
], DesignerCanvasElement.prototype, "zoomChanged", null);
__decorate([
    Listen('panstart'),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", []),
    __metadata("design:returntype", void 0)
], DesignerCanvasElement.prototype, "onPanStart", null);
__decorate([
    Listen('panmove'),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [CustomEvent]),
    __metadata("design:returntype", void 0)
], DesignerCanvasElement.prototype, "onPanMove", null);
__decorate([
    Listen('panend'),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", []),
    __metadata("design:returntype", void 0)
], DesignerCanvasElement.prototype, "onPanEnd", null);
__decorate([
    Listen('pinchmove'),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [CustomEvent]),
    __metadata("design:returntype", void 0)
], DesignerCanvasElement.prototype, "onPinchMove", null);
__decorate([
    Listen('wheel'),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [WheelEvent]),
    __metadata("design:returntype", void 0)
], DesignerCanvasElement.prototype, "onWheel", null);
DesignerCanvasElement = __decorate([
    Component({
        selector: 'mosaik-designer-canvas',
        template: designerCanvasElementTemplate,
        themes: {
            joy: designerCanvasElementJoyStyle,
            memphis: designerCanvasElementMemphisStyle,
            cosmopolitan: designerCanvasElementCosmopolitanStyle
        },
        imports: []
    }),
    __metadata("design:paramtypes", [])
], DesignerCanvasElement);
export { DesignerCanvasElement };
//# sourceMappingURL=DesignerCanvasElement.js.map