// #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);
};
var TileManagerTileElement_1;
import { emit, HTMLElementEventEmitter } from '@breadstone/mosaik-elements';
import { createRef } from 'lit/directives/ref.js';
import { Themeable } from '../../../Behaviors/Themeable';
import { addDragController } from '../../../Controllers/DragController';
import { addDragResizeController } from '../../../Controllers/DragResizeController';
import { InlineStyleController } from '../../../Controllers/InlineStyleController';
import { Attribute } from '../../../Decorators/AttributeDecorator';
import { Component } from '../../../Decorators/ComponentDecorator';
import { CustomElement } from '../../Abstracts/CustomElement';
import { ButtonElement } from '../../Buttons/Button/ButtonElement';
import { tileManagerTileElementCosmopolitanStyle } from './Themes/TileManagerTileElement.Cosmopolitan';
import { tileManagerTileElementJoyStyle } from './Themes/TileManagerTileElement.Joy';
import { tileManagerTileElementMemphisStyle } from './Themes/TileManagerTileElement.Memphis';
import { createTileDragGhost, createTileGhost } from './TileManagerGhostUtil';
import { createTileManagerDragStack, swapTiles } from './TileManagerPosition';
import { createTileManagerResizeState } from './TileManagerResizeState';
import { tileManagerTileElementTemplate } from './TileManagerTileElementTemplate';
// #endregion
let nextTileId = 1;
/**
 * Finds an element matching a predicate in the event's composed path.
 *
 * @param predicate - The predicate function.
 * @param event - The event.
 * @returns The matching element or undefined.
 *
 * @internal
 */
function findElementFromEventPath(predicate, event) {
    const path = event.composedPath();
    for (const element of path) {
        if (element instanceof Element && predicate(element)) {
            return element;
        }
    }
    return undefined;
}
/**
 * Checks if the element's direction is LTR.
 *
 * @param element - The element to check.
 * @returns True if the element's direction is LTR.
 *
 * @internal
 */
function isLTR(element) {
    return getComputedStyle(element).direction !== 'rtl';
}
/**
 * Starts a view transition if supported, otherwise executes callback immediately.
 *
 * @param callback - The callback to execute.
 * @returns The view transition or a promise.
 *
 * @internal
 */
function startViewTransition(callback) {
    if ('startViewTransition' in document) {
        const transition = document.startViewTransition(callback);
        return { transition };
    }
    callback();
    return {};
}
/**
 * Dispatches a cancelable custom event.
 *
 * @param target - The event target.
 * @param eventName - The event name.
 * @param detail - The event detail.
 * @returns The dispatched event.
 *
 * @internal
 */
function dispatchCancelableEvent(target, eventName, detail) {
    const event = new CustomEvent(eventName, {
        bubbles: true,
        cancelable: true,
        composed: true,
        detail: detail
    });
    target.dispatchEvent(event);
    return event;
}
/**
 * The tile component is used within the `mosaik-tile-manager` as a container
 * for displaying various types of information.
 *
 * @element mosaik-tile-manager-tile
 * @category Layout
 *
 * @slot - Default slot for the tile's content.
 * @slot actions - Renders items after the default actions in the tile header.
 * @slot bottom-adorner - Renders the bottom resize handle of the tile.
 * @slot corner-adorner - Renders the corner resize handle of the tile.
 * @slot fullscreen-action - Renders the fullscreen action element of the tile header.
 * @slot maximize-action - Renders the maximize action element of the tile header.
 * @slot side-adorner - Renders the side resize handle of the tile.
 * @slot title - Renders the title of the tile header.
 *
 * @csspart actions - The actions part.
 * @csspart base - The wrapper for the entire tile content, header and content.
 * @csspart content-container - The container wrapping the tile's main content.
 * @csspart fullscreen-button - The fullscreen-button part.
 * @csspart actions - The actions container of the tile header.
 * @csspart maximize-button - The maximize-button part.
 * @csspart header - The container for the tile header, including title and actions.
 * @csspart trigger - The corner resize handle.
 * @csspart trigger-bottom - The bottom resize handle.
 * @csspart trigger-side - The side resize handle.
 *
 * @cssprop {String} --tile-manager-tile-action-color - The manager tile action color CSS custom property.
 * @cssprop {String} --tile-manager-tile-action-hover-background - The manager tile action hover background CSS custom property.
 * @cssprop {String} --tile-manager-tile-action-hover-color - The manager tile action hover color CSS custom property.
 * @cssprop {String} --tile-manager-tile-action-radius - The manager tile action radius CSS custom property.
 * @cssprop {String} --tile-manager-tile-action-size - The manager tile action size CSS custom property.
 * @cssprop {String} --tile-manager-tile-background - The manager tile background CSS custom property.
 * @cssprop {String} --tile-manager-tile-border-color - The manager tile border color CSS custom property.
 * @cssprop {String} --tile-manager-tile-border-radius - The manager tile border radius CSS custom property.
 * @cssprop {String} --tile-manager-tile-border-width - The manager tile border width CSS custom property.
 * @cssprop {String} --tile-manager-tile-content-padding-bottom - The manager tile content padding bottom CSS custom property.
 * @cssprop {String} --tile-manager-tile-content-padding-left - The manager tile content padding left CSS custom property.
 * @cssprop {String} --tile-manager-tile-content-padding-right - The manager tile content padding right CSS custom property.
 * @cssprop {String} --tile-manager-tile-content-padding-top - The manager tile content padding top CSS custom property.
 * @cssprop {String} --tile-manager-tile-dragging-opacity - The manager tile dragging opacity CSS custom property.
 * @cssprop {String} --tile-manager-tile-drop-target-color - The manager tile drop target color CSS custom property.
 * @cssprop {String} --tile-manager-tile-focus-ring-active-width - The manager tile focus ring active width CSS custom property.
 * @cssprop {String} --tile-manager-tile-focus-ring-color - The manager tile focus ring color CSS custom property.
 * @cssprop {String} --tile-manager-tile-focus-ring-inward-offset - The manager tile focus ring inward offset CSS custom property.
 * @cssprop {String} --tile-manager-tile-focus-ring-outward-offset - The manager tile focus ring outward offset CSS custom property.
 * @cssprop {String} --tile-manager-tile-font-family - The manager tile font family CSS custom property.
 * @cssprop {String} --tile-manager-tile-font-letter-spacing - The manager tile font letter spacing CSS custom property.
 * @cssprop {String} --tile-manager-tile-font-line-height - The manager tile font line height CSS custom property.
 * @cssprop {String} --tile-manager-tile-font-size - The manager tile font size CSS custom property.
 * @cssprop {String} --tile-manager-tile-font-text-decoration - The manager tile font text decoration CSS custom property.
 * @cssprop {String} --tile-manager-tile-font-text-transform - The manager tile font text transform CSS custom property.
 * @cssprop {String} --tile-manager-tile-font-weight - The manager tile font weight CSS custom property.
 * @cssprop {String} --tile-manager-tile-gap - The manager tile gap CSS custom property.
 * @cssprop {String} --tile-manager-tile-header-background - The manager tile header background CSS custom property.
 * @cssprop {String} --tile-manager-tile-header-border-color - The manager tile header border color CSS custom property.
 * @cssprop {String} --tile-manager-tile-header-padding-bottom - The manager tile header padding bottom CSS custom property.
 * @cssprop {String} --tile-manager-tile-header-padding-left - The manager tile header padding left CSS custom property.
 * @cssprop {String} --tile-manager-tile-header-padding-right - The manager tile header padding right CSS custom property.
 * @cssprop {String} --tile-manager-tile-header-padding-top - The manager tile header padding top CSS custom property.
 * @cssprop {String} --tile-manager-tile-padding-bottom - The manager tile padding bottom CSS custom property.
 * @cssprop {String} --tile-manager-tile-padding-left - The manager tile padding left CSS custom property.
 * @cssprop {String} --tile-manager-tile-padding-right - The manager tile padding right CSS custom property.
 * @cssprop {String} --tile-manager-tile-padding-top - The manager tile padding top CSS custom property.
 * @cssprop {String} --tile-manager-tile-resize-handle-color - The manager tile resize handle color CSS custom property.
 * @cssprop {String} --tile-manager-tile-resize-indicator-color - The manager tile resize indicator color CSS custom property.
 * @cssprop {String} --tile-manager-tile-shadow - The manager tile shadow CSS custom property.
 * @cssprop {String} --tile-manager-tile-shadow-blur - The manager tile shadow blur CSS custom property.
 * @cssprop {String} --tile-manager-tile-shadow-color - The manager tile shadow color CSS custom property.
 * @cssprop {String} --tile-manager-tile-shadow-offset-x - The manager tile shadow offset x CSS custom property.
 * @cssprop {String} --tile-manager-tile-shadow-offset-y - The manager tile shadow offset y CSS custom property.
 * @cssprop {String} --tile-manager-tile-shadow-spread - The manager tile shadow spread CSS custom property.
 * @cssprop {String} --tile-manager-tile-title-color - The manager tile title color CSS custom property.
 * @cssprop {String} --tile-manager-tile-title-font-size - The manager tile title font size CSS custom property.
 * @cssprop {String} --tile-manager-tile-title-font-weight - The manager tile title font weight CSS custom property.
 * @cssprop {String} --tile-manager-tile-transition-duration - The manager tile transition duration CSS custom property.
 * @cssprop {String} --tile-manager-tile-transition-mode - The manager tile transition mode CSS custom property.
 * @cssprop {String} --tile-manager-tile-transition-property - The manager tile transition property CSS custom property.
 * @cssprop {String} --tile-manager-tile-translate - The manager tile translate CSS custom property.
 *
 * @fires tileFullscreen {TileFullscreenEvent} - Fired when tile the fullscreen state changes.
 * @fires tileMaximize {TileMaximizeEvent} - Fired when tile the maximize state changes.
 * @fires tileDragStart {TileDragStartEvent} - Fired when a drag operation on a tile is about to begin.
 * @fires tileDragEnd {TileDragEndEvent} - Fired when a drag operation with a tile is successfully completed.
 * @fires tileDragCancel {TileDragCancelEvent} - Fired when a tile drag operation is canceled by the user.
 * @fires tileResizeStart {TileResizeStartEvent} - Fired when a resize operation on a tile is about to begin.
 * @fires tileResizeEnd {TileResizeEndEvent} - Fired when a resize operation on a tile is successfully completed.
 * @fires tileResizeCancel {TileResizeCancelEvent} - Fired when a resize operation on a tile is canceled by the user.
 *
 * @dependency mosaik-button - The Button element.
 *
 * @example
 * Basic tile spanning two columns:
 * ```html
 * <mosaik-tile-manager-tile col-span="2" row-span="1">
 *   <p>Tile content goes here</p>
 * </mosaik-tile-manager-tile>
 * ```
 *
 * @example
 * Maximized tile with resize and fullscreen disabled:
 * ```html
 * <mosaik-tile-manager-tile maximized disable-resize disable-fullscreen col-span="3" row-span="2">
 *   <p>Full-width content</p>
 * </mosaik-tile-manager-tile>
 * ```
 *
 * @public
 */
let TileManagerTileElement = TileManagerTileElement_1 = class TileManagerTileElement extends Themeable(CustomElement) {
    // #region Fields
    _colSpan;
    _rowSpan;
    _colStart;
    _rowStart;
    _position;
    _maximized;
    _fullscreen;
    _disableResize;
    _disableFullscreen;
    _disableMaximize;
    _isDragging;
    _resizeStartRect;
    _headerRef;
    _tileContentRef;
    _resizeState;
    _dragStack;
    _dragController;
    _resizeController;
    _inlineStyleController;
    _tileFullscreen;
    _tileMaximize;
    _tileDragStart;
    _tileDragEnd;
    _tileDragCancel;
    _tileResizeStart;
    _tileResizeEnd;
    _tileResizeCancel;
    // #endregion
    // #region Ctor
    /**
     * Constructs a new instance of the `TileManagerTileElement` class.
     *
     * @public
     */
    constructor() {
        super();
        this._colSpan = 1;
        this._rowSpan = 1;
        this._colStart = null;
        this._rowStart = null;
        this._position = -1;
        this._maximized = false;
        this._fullscreen = false;
        this._disableResize = false;
        this._disableFullscreen = false;
        this._disableMaximize = false;
        this._isDragging = false;
        this._resizeStartRect = null;
        this._headerRef = createRef();
        this._tileContentRef = createRef();
        this._resizeState = createTileManagerResizeState();
        this._dragStack = createTileManagerDragStack();
        this._inlineStyleController = new InlineStyleController(this);
        // Initialize drag controller with callbacks bound to this instance
        this._dragController = addDragController(this, {
            skip: this._skipDrag.bind(this),
            matchTarget: this._matchDragTarget.bind(this),
            ghost: this._createDragGhost.bind(this),
            start: this._handleDragStart.bind(this),
            over: this._handleDragOver.bind(this),
            end: this._handleDragEnd.bind(this),
            cancel: this._handleDragCancel.bind(this)
        });
        // Initialize resize controller with deferred mode and grid snapping
        this._resizeController = addDragResizeController(this, {
            enabled: false,
            mode: 'deferred',
            minWidth: 50,
            minHeight: 50,
            matchHandle: this._matchResizeHandle.bind(this),
            ghost: this._createResizeGhost.bind(this),
            snap: this._calculateSnappedDimensions.bind(this),
            start: this.handleResizeStart.bind(this),
            resize: this.handleResize.bind(this),
            end: this.handleResizeEnd.bind(this),
            cancel: this.handleResizeCancelled.bind(this)
        });
        this._tileFullscreen = new HTMLElementEventEmitter(this, 'tileFullscreen');
        this._tileMaximize = new HTMLElementEventEmitter(this, 'tileMaximize');
        this._tileDragStart = new HTMLElementEventEmitter(this, 'tileDragStart');
        this._tileDragEnd = new HTMLElementEventEmitter(this, 'tileDragEnd');
        this._tileDragCancel = new HTMLElementEventEmitter(this, 'tileDragCancel');
        this._tileResizeStart = new HTMLElementEventEmitter(this, 'tileResizeStart');
        this._tileResizeEnd = new HTMLElementEventEmitter(this, 'tileResizeEnd');
        this._tileResizeCancel = new HTMLElementEventEmitter(this, 'tileResizeCancel');
    }
    // #endregion
    // #region Properties
    /**
     * Returns the `is` property.
     *
     * @public
     * @static
     * @readonly
     */
    static get is() {
        return 'mosaik-tile-manager-tile';
    }
    /**
     * Gets or sets the `colSpan` property.
     *
     * @public
     * @attr
     */
    get colSpan() {
        return this._colSpan;
    }
    set colSpan(value) {
        const newValue = Math.max(1, value);
        if (this._colSpan !== newValue) {
            this._colSpan = newValue;
            this._inlineStyleController.set('tile-col-span', this._colSpan.toString());
            this.requestUpdate('colSpan');
        }
    }
    /**
     * Gets or sets the `rowSpan` property.
     *
     * @public
     * @attr
     */
    get rowSpan() {
        return this._rowSpan;
    }
    set rowSpan(value) {
        const newValue = Math.max(1, value);
        if (this._rowSpan !== newValue) {
            this._rowSpan = newValue;
            this._inlineStyleController.set('tile-row-span', this._rowSpan.toString());
            this.requestUpdate('rowSpan');
        }
    }
    /**
     * Gets or sets the `colStart` property.
     *
     * @public
     * @attr
     */
    get colStart() {
        return this._colStart;
    }
    set colStart(value) {
        const newValue = value !== null ? Math.max(0, value) || null : null;
        if (this._colStart !== newValue) {
            this._colStart = newValue;
            this._inlineStyleController.set('tile-col-start', this._colStart ? this._colStart.toString() : 'auto');
            this.requestUpdate('colStart');
        }
    }
    /**
     * Gets or sets the `rowStart` property.
     *
     * @public
     * @attr
     */
    get rowStart() {
        return this._rowStart;
    }
    set rowStart(value) {
        const newValue = value !== null ? Math.max(0, value) || null : null;
        if (this._rowStart !== newValue) {
            this._rowStart = newValue;
            this._inlineStyleController.set('tile-row-start', this._rowStart ? this._rowStart.toString() : 'auto');
            this.requestUpdate('rowStart');
        }
    }
    /**
     * Gets or sets the `position` property.
     *
     * @public
     * @attr
     */
    get position() {
        return this._position;
    }
    set position(value) {
        if (this._position !== value) {
            this._position = value;
            this._inlineStyleController.setStyle('order', this._position.toString());
            this.requestUpdate('position');
        }
    }
    /**
     * Gets or sets the `maximized` property.
     *
     * @public
     * @attr
     */
    get maximized() {
        return this._maximized;
    }
    set maximized(value) {
        if (this._maximized !== value) {
            this._maximized = value;
            this.requestUpdate('maximized');
            this.tileManager?.requestUpdate();
        }
    }
    /**
     * Indicates whether the tile occupies the whole screen.
     *
     * @public
     * @readonly
     */
    get fullscreen() {
        return this._fullscreen;
    }
    /**
     * Gets or sets the `disableResize` property.
     *
     * @public
     * @attr
     */
    get disableResize() {
        return this._disableResize;
    }
    set disableResize(value) {
        if (this._disableResize !== value) {
            this._disableResize = value;
            this.requestUpdate('disableResize');
        }
    }
    /**
     * Gets or sets the `disableFullscreen` property.
     *
     * @public
     * @attr
     */
    get disableFullscreen() {
        return this._disableFullscreen;
    }
    set disableFullscreen(value) {
        if (this._disableFullscreen !== value) {
            this._disableFullscreen = value;
            this.requestUpdate('disableFullscreen');
        }
    }
    /**
     * Gets or sets the `disableMaximize` property.
     *
     * @public
     * @attr
     */
    get disableMaximize() {
        return this._disableMaximize;
    }
    set disableMaximize(value) {
        if (this._disableMaximize !== value) {
            this._disableMaximize = value;
            this.requestUpdate('disableMaximize');
        }
    }
    /**
     * Gets the parent tile manager element.
     *
     * @public
     * @readonly
     */
    get tileManager() {
        return this.closest('mosaik-tile-manager');
    }
    /**
     * Gets the current resize mode from the parent tile manager.
     *
     * @public
     * @readonly
     */
    get resizeMode() {
        return this.tileManager?.resizeMode ?? 'none';
    }
    /**
     * Gets whether resize is disabled for this tile.
     *
     * @public
     * @readonly
     */
    get resizeDisabled() {
        return (this.disableResize ||
            this.maximized ||
            this.fullscreen ||
            this.resizeMode === 'none');
    }
    /**
     * Gets whether the tile is currently resizing.
     *
     * @public
     * @readonly
     */
    get isResizing() {
        return this._resizeController.isResizing;
    }
    /**
     * Gets whether the tile is currently being dragged.
     *
     * @public
     * @readonly
     */
    get isDragging() {
        return this._isDragging;
    }
    /**
     * Gets whether the tile is draggable.
     *
     * @public
     * @readonly
     */
    get isDraggable() {
        return this.tileManager?.dragMode !== 'none';
    }
    /**
     * Gets the header reference for tile-header drag mode.
     *
     * @public
     * @readonly
     */
    get headerRef() {
        return this._headerRef;
    }
    /**
     * Gets the tile content reference.
     *
     * @public
     * @readonly
     */
    get tileContentRef() {
        return this._tileContentRef;
    }
    /**
     * Gets whether the tile is resizable.
     *
     * @public
     * @readonly
     */
    get isResizable() {
        return !this.disableResize && this.tileManager?.resizeMode !== 'none';
    }
    /**
     * Gets the CSS grid container from the tile manager.
     *
     * @public
     * @readonly
     */
    get cssContainer() {
        return this.tileManager?.renderRoot.querySelector('[part~="base"]');
    }
    /**
     * Gets the tileFullscreen event emitter.
     *
     * @public
     * @readonly
     */
    get tileFullscreen() {
        return this._tileFullscreen;
    }
    /**
     * Gets the tileMaximize event emitter.
     *
     * @public
     * @readonly
     */
    get tileMaximize() {
        return this._tileMaximize;
    }
    /**
     * Gets the tileDragStart event emitter.
     *
     * @public
     * @readonly
     */
    get tileDragStart() {
        return this._tileDragStart;
    }
    /**
     * Gets the tileDragEnd event emitter.
     *
     * @public
     * @readonly
     */
    get tileDragEnd() {
        return this._tileDragEnd;
    }
    /**
     * Gets the tileDragCancel event emitter.
     *
     * @public
     * @readonly
     */
    get tileDragCancel() {
        return this._tileDragCancel;
    }
    /**
     * Gets the tileResizeStart event emitter.
     *
     * @public
     * @readonly
     */
    get tileResizeStart() {
        return this._tileResizeStart;
    }
    /**
     * Gets the tileResizeEnd event emitter.
     *
     * @public
     * @readonly
     */
    get tileResizeEnd() {
        return this._tileResizeEnd;
    }
    /**
     * Gets the tileResizeCancel event emitter.
     *
     * @public
     * @readonly
     */
    get tileResizeCancel() {
        return this._tileResizeCancel;
    }
    // #endregion
    // #region Methods
    /**
     * @public
     * @override
     */
    connectedCallback() {
        super.connectedCallback();
        this.id = this.id || 'tile-' + (nextTileId++).toString();
        this.style.viewTransitionName = this.style.viewTransitionName || 'tile-transition-' + this.id;
        // Set up ARIA attributes
        this.setAttribute('role', 'gridcell');
        this.setAttribute('tabindex', '0');
        this.updateAriaAttributes();
        // Listen for fullscreen changes to sync state
        document.addEventListener('fullscreenchange', this._handleFullscreenChange);
        // Listen for keyboard events
        this.addEventListener('keydown', this.handleKeyDown);
    }
    /**
     * @public
     * @override
     */
    disconnectedCallback() {
        super.disconnectedCallback();
        document.removeEventListener('fullscreenchange', this._handleFullscreenChange);
        this.removeEventListener('keydown', this.handleKeyDown);
    }
    /**
     * Sets the drag state (visual appearance while dragging).
     *
     * @param state - Whether dragging is active.
     *
     * @public
     */
    setDragState(state = true) {
        this._isDragging = state;
        // Set the tile content opacity
        const tileContent = this._tileContentRef.value;
        if (tileContent) {
            tileContent.style.opacity = state ? '0' : '';
        }
        this.style.pointerEvents = state ? 'none' : '';
        this.part.toggle('dragging', state);
        this.requestUpdate();
    }
    /**
     * Handles the maximize button click.
     * When maximized, toggles to restored state and vice versa.
     *
     * @public
     */
    async handleMaximize() {
        const newState = !this.maximized;
        const event = dispatchCancelableEvent(this, 'tileMaximize', {
            tile: this,
            state: newState
        });
        if (event.defaultPrevented) {
            return;
        }
        this.style.zIndex = '1';
        await startViewTransition(() => {
            this.maximized = newState;
        }).transition?.finished;
        this.style.zIndex = '';
    }
    /**
     * Handles the fullscreen button click.
     *
     * @public
     */
    handleFullscreen() {
        const newState = !this._fullscreen;
        const event = dispatchCancelableEvent(this, 'tileFullscreen', {
            tile: this,
            state: newState
        });
        if (event.defaultPrevented) {
            return;
        }
        this._fullscreen = newState;
        if (this._fullscreen) {
            void this.requestFullscreen();
        }
        else if (document.fullscreenElement === this) {
            void document.exitFullscreen();
        }
        this.requestUpdate('fullscreen');
    }
    /**
     * Handles resize pointer down - delegates to DragResizeController.
     * This method is called from the template's pointer event handlers.
     *
     * @param _event - The pointer event (handled by controller).
     *
     * @public
     */
    handleResizePointerDown(_event) {
        // Resize is now handled entirely by the DragResizeController
        // This method is kept for backward compatibility with the template
    }
    /**
     * Handles resize pointer move - delegates to DragResizeController.
     *
     * @param _event - The pointer event (handled by controller).
     *
     * @public
     */
    handleResizePointerMove(_event) {
        // Resize is now handled entirely by the DragResizeController
    }
    /**
     * Handles resize pointer up - delegates to DragResizeController.
     *
     * @public
     */
    handleResizePointerUp() {
        // Resize is now handled entirely by the DragResizeController
    }
    /**
     * Handles resize cancel (e.g., Escape key).
     * This can be called externally to cancel an in-progress resize.
     *
     * @public
     */
    handleResizeCancel() {
        if (!this._resizeController.isResizing) {
            return;
        }
        this._resizeController.dispose();
    }
    /**
     * Called after the component's first update cycle has completed.
     * At this point the template has rendered and refs are available.
     *
     * @protected
     * @override
     */
    firstUpdated() {
        // Configure drag controller after template has rendered so refs are available
        this._setDragConfiguration();
        // Configure resize controller
        this._setResizeConfiguration();
    }
    /**
     * @protected
     * @override
     */
    updated(_changedProperties) {
        super.updated(_changedProperties);
        this._inlineStyleController.setStyle('grid-column', 'var(--tile-col-start, auto) / span var(--tile-col-span, 1)');
        this._inlineStyleController.setStyle('grid-row', 'var(--tile-row-start, auto) / span var(--tile-row-span, 1)');
        // Update ARIA attributes when relevant properties change
        this.updateAriaAttributes();
        // Update resize controller when relevant properties change
        if (_changedProperties.has('disableResize') ||
            _changedProperties.has('maximized') ||
            _changedProperties.has('fullscreen')) {
            this._setResizeConfiguration();
        }
    }
    /**
     * Configures the drag controller based on the current drag mode.
     *
     * @private
     */
    _setDragConfiguration() {
        const dragMode = this.tileManager?.dragMode ?? 'none';
        const enabled = dragMode !== 'none' && !this.maximized && !this.fullscreen;
        this._dragController.set({
            enabled,
            trigger: dragMode === 'tile-header'
                ? () => this._headerRef.value ?? null
                : undefined
        });
    }
    /**
     * Configures the resize controller based on the current resize mode.
     *
     * @private
     */
    _setResizeConfiguration() {
        const resizeMode = this.resizeMode;
        const enabled = !this.resizeDisabled;
        this._resizeController.set({
            enabled,
            mode: 'deferred'
        });
    }
    /**
     * Handles the fullscreenchange event to sync internal state.
     *
     * @private
     */
    _handleFullscreenChange = () => {
        // If this tile was in fullscreen but now fullscreen element is null or different
        if (this._fullscreen && document.fullscreenElement !== this) {
            this._fullscreen = false;
            this.requestUpdate('fullscreen');
        }
    };
    /**
     * Determines whether a drag operation should be skipped.
     *
     * @param event - The pointer event.
     * @returns True if the drag should be skipped.
     *
     * @private
     */
    _skipDrag(event) {
        // Skip if maximized or fullscreen
        if (this.maximized || this.fullscreen) {
            return true;
        }
        // Skip if resizing
        if (this.isResizing) {
            return true;
        }
        // Skip if click is on an interactive element (trigger, actions, buttons)
        return Boolean(findElementFromEventPath((element) => element.matches('[part*=trigger]') ||
            element.matches('#tile-actions') ||
            element.matches('[part*=button]') ||
            element.matches('mosaik-button') ||
            element.matches('button'), event));
    }
    /**
     * Determines if an element is a valid drop target.
     *
     * @param element - The element to check.
     * @returns True if the element is a valid drop target.
     *
     * @private
     */
    _matchDragTarget(element) {
        return element !== this && TileManagerTileElement_1.is === element.localName;
    }
    /**
     * Creates a drag ghost element.
     *
     * @returns The ghost element.
     *
     * @private
     */
    _createDragGhost() {
        return createTileDragGhost(this);
    }
    /**
     * Handles drag start.
     *
     * @param _params - The drag callback parameters.
     * @returns False to cancel the drag, otherwise undefined/true.
     *
     * @private
     */
    _handleDragStart(_params) {
        const event = dispatchCancelableEvent(this, 'tileDragStart', {
            tile: this,
            position: this.position
        });
        if (event.defaultPrevented) {
            return false;
        }
        this.setDragState(true);
        this._dragStack.push(this);
        return undefined;
    }
    /**
     * Handles dragging over a potential drop target.
     *
     * @param params - The drag callback parameters.
     *
     * @private
     */
    _handleDragOver(params) {
        const match = params.state.element;
        if (!match) {
            return;
        }
        // Check if this is the same tile as the top of the stack
        if (this._dragStack.peek() === match) {
            // Check if we should swap back
            if (this._shouldSwap(params.event, params.state.pointerState.direction, match)) {
                this._dragStack.pop();
                this._dragStack.push(match);
                this._performSwap(match);
            }
            return;
        }
        // New tile encountered, push and swap
        this._dragStack.push(match);
        this._performSwap(match);
    }
    /**
     * Determines whether tiles should be swapped based on pointer position and direction.
     *
     * @param event - The pointer event.
     * @param direction - The drag direction.
     * @param match - The matched tile.
     * @returns True if tiles should be swapped.
     *
     * @private
     */
    _shouldSwap(event, direction, match) {
        const ltr = isLTR(this);
        const { left, top, width, height } = match.getBoundingClientRect();
        const relativeX = (event.clientX - left) / width;
        const relativeY = (event.clientY - top) / height;
        switch (direction) {
            case 'start':
                return (this.position > match.position &&
                    (ltr ? relativeX <= 0.25 : relativeX >= 0.75));
            case 'end':
                return (this.position < match.position &&
                    (ltr ? relativeX >= 0.75 : relativeX <= 0.25));
            case 'top':
                return this.position > match.position && relativeY <= 0.25;
            case 'bottom':
                return this.position < match.position && relativeY >= 0.75;
            default:
                return false;
        }
    }
    /**
     * Performs the swap animation with the matched tile.
     *
     * @param match - The tile to swap with.
     *
     * @private
     */
    _performSwap(match) {
        startViewTransition(() => swapTiles(this, match));
    }
    /**
     * Handles drag end.
     *
     * @param _params - The drag callback parameters.
     *
     * @private
     */
    _handleDragEnd(_params) {
        this.setDragState(false);
        this._dragStack.reset();
        emit(this, 'tileDragEnd', {
            tile: this,
            position: this.position
        });
    }
    /**
     * Handles drag cancel.
     *
     * @private
     */
    _handleDragCancel() {
        startViewTransition(() => {
            this._dragStack.restore();
            this._dragStack.reset();
        });
        this._dragController.dispose();
        this.setDragState(false);
        emit(this, 'tileDragCancel', {
            tile: this,
            position: this.position
        });
    }
    /**
     * Determines the resize handle type from the target element.
     *
     * @param target - The target element.
     * @returns The resize handle type.
     *
     * @private
     */
    _determineResizeHandle(target) {
        if (target.classList.contains('side')) {
            return 'side';
        }
        if (target.classList.contains('corner')) {
            return 'corner';
        }
        return 'bottom';
    }
    /**
     * Matches the resize handle from an element.
     * Used by DragResizeController.
     *
     * @param element - The element to check.
     * @returns The resize handle type or null.
     *
     * @private
     */
    _matchResizeHandle(element) {
        if (!element.classList.contains('resize-handle')) {
            return null;
        }
        if (element.classList.contains('side')) {
            return 'side';
        }
        if (element.classList.contains('corner')) {
            return 'corner';
        }
        if (element.classList.contains('bottom')) {
            return 'bottom';
        }
        return 'corner';
    }
    /**
     * Creates a resize ghost element for the deferred resize mode.
     * Used by DragResizeController.
     *
     * @returns The ghost element.
     *
     * @private
     */
    _createResizeGhost() {
        return createTileGhost(this);
    }
    /**
     * Calculates snapped dimensions during resize operations.
     * Uses the TileManagerResizeState for grid-aware snapping.
     *
     * @param dimensions - The current dimensions.
     * @param delta - The delta from start position.
     * @param handle - The resize handle being used.
     * @returns The snapped dimensions.
     *
     * @private
     */
    _calculateSnappedDimensions(dimensions, delta, handle) {
        if (!this._resizeStartRect) {
            return dimensions;
        }
        const isWidthResize = handle === 'side' || handle === 'corner';
        const isHeightResize = handle === 'bottom' || handle === 'corner';
        let snappedWidth = dimensions.width;
        let snappedHeight = dimensions.height;
        if (isWidthResize) {
            snappedWidth = this._resizeState.calculateSnappedWidth({
                deltaX: delta.x,
                current: { width: dimensions.width }
            });
        }
        if (isHeightResize) {
            snappedHeight = this._resizeState.calculateSnappedHeight({
                deltaY: delta.y,
                current: { height: dimensions.height }
            });
        }
        return {
            width: snappedWidth,
            height: snappedHeight
        };
    }
    /**
     * Handles resize start from the DragResizeController.
     *
     * @param params - The resize callback parameters.
     * @returns False to cancel the resize, otherwise undefined.
     *
     * @private
     */
    handleResizeStart(params) {
        const event = dispatchCancelableEvent(this, 'tileResizeStart', {
            tile: this,
            colSpan: this.colSpan,
            rowSpan: this.rowSpan
        });
        if (event.defaultPrevented) {
            return false;
        }
        // Store initial rect for snap calculations
        this._resizeStartRect = this.getBoundingClientRect();
        // Initialize resize state for grid-aware snapping
        const cssContainer = this.cssContainer;
        if (cssContainer) {
            this._resizeState.updateState(this._resizeStartRect, this, cssContainer);
        }
        this.style.zIndex = '1';
        this.part.add('resizing');
        this.requestUpdate();
        return undefined;
    }
    /**
     * Handles resize movement from the DragResizeController.
     *
     * @param _params - The resize callback parameters.
     *
     * @private
     */
    handleResize(_params) {
        // The controller handles ghost updates via the snap callback
        // Additional UI updates can be done here if needed
    }
    /**
     * Handles resize end from the DragResizeController.
     *
     * @param params - The resize callback parameters.
     *
     * @private
     */
    handleResizeEnd(params) {
        const { state } = params;
        const { dimensions, ghost } = state;
        // Calculate new grid spans from final dimensions
        let newColSpan = this.colSpan;
        let newRowSpan = this.rowSpan;
        if (ghost) {
            const ghostRect = new DOMRect(parseFloat(ghost.style.left), parseFloat(ghost.style.top), dimensions.width, dimensions.height);
            const { colSpan, rowSpan } = this._resizeState.calculateResizedGridPosition(ghostRect);
            newColSpan = colSpan;
            newRowSpan = rowSpan;
            // Apply new spans with view transition
            void startViewTransition(() => {
                this.colSpan = newColSpan;
                this.rowSpan = newRowSpan;
            }).transition?.updateCallbackDone;
        }
        // Reset state
        this._resizeStartRect = null;
        this.style.zIndex = '';
        this.part.remove('resizing');
        emit(this, 'tileResizeEnd', {
            tile: this,
            colSpan: newColSpan,
            rowSpan: newRowSpan
        });
        this.requestUpdate();
    }
    /**
     * Handles resize cancel from the DragResizeController.
     *
     * @param _state - The resize state at cancel.
     *
     * @private
     */
    handleResizeCancelled(_state) {
        this._resizeStartRect = null;
        this.style.zIndex = '';
        this.part.remove('resizing');
        emit(this, 'tileResizeCancel', {
            tile: this,
            colSpan: this.colSpan,
            rowSpan: this.rowSpan
        });
        this.requestUpdate();
    }
    /**
     * Updates ARIA attributes based on current state.
     *
     * @private
     */
    updateAriaAttributes() {
        // Set aria-label with tile info
        const titleSlot = this.querySelector('[slot="title"]');
        const titleText = titleSlot?.textContent?.trim() ?? `Tile ${(this.position + 1).toString()}`;
        this.setAttribute('aria-label', titleText);
        // Set aria-expanded for maximized/fullscreen states
        if (this.maximized || this.fullscreen) {
            this.setAttribute('aria-expanded', 'true');
        }
        else {
            this.removeAttribute('aria-expanded');
        }
        // Set aria-grabbed during drag operations
        if (this._isDragging) {
            this.setAttribute('aria-grabbed', 'true');
        }
        else {
            this.removeAttribute('aria-grabbed');
        }
        // Set column/row position for grid cell
        if (this.colStart !== null) {
            this.setAttribute('aria-colindex', this.colStart.toString());
        }
        if (this.rowStart !== null) {
            this.setAttribute('aria-rowindex', this.rowStart.toString());
        }
        // Set span info
        this.setAttribute('aria-colspan', this.colSpan.toString());
        this.setAttribute('aria-rowspan', this.rowSpan.toString());
    }
    /**
     * Handles keyboard events for accessibility.
     *
     * @param event - The keyboard event.
     *
     * @private
     */
    handleKeyDown = (event) => {
        // Don't handle if target is an interactive element within the tile
        if (event.target !== this) {
            return;
        }
        const tileManager = this.tileManager;
        if (!tileManager) {
            return;
        }
        switch (event.key) {
            case 'Enter':
            case ' ':
                // Toggle maximize on Enter/Space
                if (!this.disableMaximize) {
                    event.preventDefault();
                    void this.handleMaximize();
                }
                break;
            case 'F11':
            case 'f':
                // Toggle fullscreen on F11 or 'f'
                if (!this.disableFullscreen && (event.key === 'F11' || event.ctrlKey)) {
                    event.preventDefault();
                    this.handleFullscreen();
                }
                break;
            case 'Escape':
                // Exit maximized/fullscreen state
                if (this.maximized) {
                    event.preventDefault();
                    void this.handleMaximize();
                }
                else if (this.fullscreen) {
                    event.preventDefault();
                    this.handleFullscreen();
                }
                else if (this.isResizing) {
                    event.preventDefault();
                    this.handleResizeCancel();
                }
                break;
            case 'ArrowUp':
            case 'ArrowDown':
            case 'ArrowLeft':
            case 'ArrowRight':
                this.handleArrowKeyNavigation(event);
                break;
            case 'Home':
                // Focus first tile
                event.preventDefault();
                this.focusTile(0);
                break;
            case 'End':
                // Focus last tile
                event.preventDefault();
                this.focusTile(tileManager.tiles.length - 1);
                break;
        }
    };
    /**
     * Handles arrow key navigation between tiles.
     *
     * @param event - The keyboard event.
     *
     * @private
     */
    handleArrowKeyNavigation(event) {
        const tileManager = this.tileManager;
        if (!tileManager) {
            return;
        }
        event.preventDefault();
        const tiles = tileManager.tiles;
        const currentIndex = this.position;
        let targetIndex = currentIndex;
        // Calculate target based on arrow key
        // This is a simplified grid navigation - assumes tiles are in order
        switch (event.key) {
            case 'ArrowUp':
                targetIndex = Math.max(0, currentIndex - 1);
                break;
            case 'ArrowDown':
                targetIndex = Math.min(tiles.length - 1, currentIndex + 1);
                break;
            case 'ArrowLeft':
                if (isLTR(this)) {
                    targetIndex = Math.max(0, currentIndex - 1);
                }
                else {
                    targetIndex = Math.min(tiles.length - 1, currentIndex + 1);
                }
                break;
            case 'ArrowRight':
                if (isLTR(this)) {
                    targetIndex = Math.min(tiles.length - 1, currentIndex + 1);
                }
                else {
                    targetIndex = Math.max(0, currentIndex - 1);
                }
                break;
        }
        // If Shift is held and drag is enabled, swap tiles
        if (event.shiftKey && tileManager.dragMode !== 'none') {
            const targetTile = tiles.find((t) => t.position === targetIndex);
            if (targetTile && targetTile !== this) {
                startViewTransition(() => swapTiles(this, targetTile));
            }
        }
        else {
            // Just focus the target tile
            this.focusTile(targetIndex);
        }
    }
    /**
     * Focuses a tile by its position index.
     *
     * @param position - The position index of the tile to focus.
     *
     * @private
     */
    focusTile(position) {
        const tileManager = this.tileManager;
        if (!tileManager) {
            return;
        }
        const tiles = tileManager.tiles;
        const targetTile = tiles.find((t) => t.position === position);
        if (targetTile) {
            targetTile.focus();
        }
    }
};
__decorate([
    Attribute({ type: Number }),
    __metadata("design:type", Number),
    __metadata("design:paramtypes", [Number])
], TileManagerTileElement.prototype, "colSpan", null);
__decorate([
    Attribute({ type: Number }),
    __metadata("design:type", Number),
    __metadata("design:paramtypes", [Number])
], TileManagerTileElement.prototype, "rowSpan", null);
__decorate([
    Attribute({ type: Number }),
    __metadata("design:type", Object),
    __metadata("design:paramtypes", [Object])
], TileManagerTileElement.prototype, "colStart", null);
__decorate([
    Attribute({ type: Number }),
    __metadata("design:type", Object),
    __metadata("design:paramtypes", [Object])
], TileManagerTileElement.prototype, "rowStart", null);
__decorate([
    Attribute({ type: Number }),
    __metadata("design:type", Number),
    __metadata("design:paramtypes", [Number])
], TileManagerTileElement.prototype, "position", null);
__decorate([
    Attribute({
        type: Boolean,
        useDefault: true
    }),
    __metadata("design:type", Boolean),
    __metadata("design:paramtypes", [Boolean])
], TileManagerTileElement.prototype, "maximized", null);
__decorate([
    Attribute({
        type: Boolean,
        useDefault: true
    }),
    __metadata("design:type", Boolean),
    __metadata("design:paramtypes", [Boolean])
], TileManagerTileElement.prototype, "disableResize", null);
__decorate([
    Attribute({
        type: Boolean,
        useDefault: true
    }),
    __metadata("design:type", Boolean),
    __metadata("design:paramtypes", [Boolean])
], TileManagerTileElement.prototype, "disableFullscreen", null);
__decorate([
    Attribute({
        type: Boolean,
        useDefault: true
    }),
    __metadata("design:type", Boolean),
    __metadata("design:paramtypes", [Boolean])
], TileManagerTileElement.prototype, "disableMaximize", null);
TileManagerTileElement = TileManagerTileElement_1 = __decorate([
    Component({
        selector: 'mosaik-tile-manager-tile',
        template: tileManagerTileElementTemplate,
        themes: {
            joy: tileManagerTileElementJoyStyle,
            memphis: tileManagerTileElementMemphisStyle,
            cosmopolitan: tileManagerTileElementCosmopolitanStyle
        },
        imports: [
            ButtonElement
        ]
    }),
    __metadata("design:paramtypes", [])
], TileManagerTileElement);
export { TileManagerTileElement };
//# sourceMappingURL=TileManagerTileElement.js.map