// #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 DockPanelElement_1;
import { Attribute } from '../../../Decorators/AttributeDecorator';
import { Component } from '../../../Decorators/ComponentDecorator';
import { Themeable } from '../../../Behaviors/Themeable';
import { Fitable } from '../../../Behaviors/Fitable';
import { Orientable } from '../../../Behaviors/Orientable';
import { Reversible } from '../../../Behaviors/Reversible';
import { Gapable } from '../../../Behaviors/Gapable';
import { LayoutElement } from '../Abstracts/LayoutElement';
import { dockPanelElementTemplate } from './DockPanelElementTemplate';
import { CssLength } from '@breadstone/mosaik-themes';
// #endregion
/**
 * Dock Panel - A flexible layout container for docking child elements to specific edges with fill capability.
 *
 * @description
 * The Dock Panel component provides a powerful layout system where child elements can be docked to specific
 * edges (top, bottom, left, right) of the container, with one element optionally filling the remaining space.
 * This creates flexible, adaptive layouts perfect for application shells, dashboards, and structured interfaces.
 * The component handles automatic space distribution, supports gap spacing between docked elements, respects
 * orientation and reversal settings, and provides responsive behavior. Child elements use dock attributes
 * to specify their positioning preference, while the panel manages the overall layout flow and space allocation.
 * Essential for creating professional application layouts with headers, sidebars, content areas, and footers.
 *
 * @name Dock Panel
 * @element mosaik-dock-panel
 * @category Layouts
 *
 * @slot - The default slot for child elements with dock positioning attributes.
 *
 * @csspart surface - The main container surface for docked elements.
 *
 * @example
 * ```html
 * <mosaik-dock-panel>
 *   <header dock="top">Application Header</header>
 *   <nav dock="left">Navigation Sidebar</nav>
 *   <footer dock="bottom">Application Footer</footer>
 *   <main dock="fill">Main Content Area</main>
 * </mosaik-dock-panel>
 * ```
 *
 * @example
 * ```html
 * <mosaik-dock-panel gap="16px" orientation="vertical">
 *   <div dock="start">Start Panel</div>
 *   <div dock="end">End Panel</div>
 *   <div dock="fill">Center Content</div>
 * </mosaik-dock-panel>
 * ```
 *
 * @public
 */
let DockPanelElement = DockPanelElement_1 = class DockPanelElement extends Themeable(Reversible(Orientable(Fitable(Gapable(LayoutElement))))) {
    // #region Fields
    _childResize;
    _lastChildFill;
    _hostResize;
    _pending;
    // #endregion
    // #region Ctor
    /**
     * Constructs a new instance of the `{@link DockPanelElement}` class.
     *
     * @public
     */
    constructor() {
        super();
        this._childResize = new Map();
        this._lastChildFill = false;
        this._pending = false;
    }
    // #endregion
    /**
     * Returns the `is` property.
     *
     * @public
     * @static
     * @override
     * @readonly
    */
    static get is() {
        return 'mosaik-dock-panel';
    }
    /**
     * The `lastChildFill` property determines whether the last child element should fill the remaining space.
     *
     * @public
     * @attr
     */
    get lastChildFill() {
        return this._lastChildFill;
    }
    set lastChildFill(value) {
        if (this._lastChildFill !== value) {
            this._lastChildFill = value;
            this.requestUpdate('lastChildFill');
            this.scheduleLayout();
        }
    }
    // #region Methods
    static getDock(el) {
        const raw = el.getAttribute('dock') ??
            el.getAttribute('data-dock') ??
            el.getAttribute('dock-panel-dock') ??
            (el.dir === 'rtl' ? 'right' : 'left');
        const v = raw.toLowerCase();
        return v === 'left' || v === 'right' || v === 'top' || v === 'bottom' ? v : 'left';
    }
    static setDock(el, dock) {
        el.setAttribute('dock', dock);
    }
    /**
     * @public
     * @override
     */
    disconnectedCallback() {
        super.disconnectedCallback();
        this.cleanupObservers();
    }
    /**
     * @protected
     * @override
     */
    onApplyTemplate() {
        super.onApplyTemplate();
        this._hostResize = new ResizeObserver(() => this.scheduleLayout());
        this._hostResize.observe(this);
        this.reobserveChildren();
        this.scheduleLayout();
    }
    /**
     * @protected
     * @override
     */
    onItemsChanged() {
        super.onItemsChanged();
        this.reobserveChildren();
        this.scheduleLayout();
    }
    // #endregion
    /**
     * @private
     */
    reobserveChildren() {
        for (const ro of this._childResize.values())
            ro.disconnect();
        this._childResize.clear();
        const kids = this.items;
        for (const el of kids) {
            const ro = new ResizeObserver(() => this.scheduleLayout());
            ro.observe(el);
            this._childResize.set(el, ro);
        }
    }
    /**
     * @private
     */
    cleanupObservers() {
        this._hostResize?.disconnect();
        for (const ro of this._childResize.values())
            ro.disconnect();
        this._childResize.clear();
    }
    /**
     * @private
     */
    scheduleLayout() {
        if (this._pending) {
            return;
        }
        this._pending = true;
        requestAnimationFrame(() => {
            this._pending = false;
            this.layoutNow();
        });
    }
    /**
     * @private
     */
    layoutNow() {
        const surface = this.getTemplatePart('surface');
        // if (!surface) {
        //     return;
        // }
        // CssLength → px
        const gapPx = this.resolveCssLengthToPx(this.gap, surface);
        const W = surface.clientWidth;
        const H = surface.clientHeight;
        if (W <= 0 || H <= 0) {
            return;
        }
        let x = 0, y = 0, w = W, h = H;
        const kids = this.items.filter((el) => this.isVisible(el));
        const n = kids.length;
        for (const el of kids)
            this.prepForMeasure(el);
        for (let i = 0; i < n; i++) {
            const el = kids[i];
            const isLast = i === n - 1;
            const dock = DockPanelElement_1.getDock(el);
            if (isLast && this._lastChildFill) {
                this.place(el, x, y, w, h);
                continue;
            }
            switch (dock) {
                case 'top': {
                    this.place(el, x, y, w, undefined);
                    const usedH = Math.ceil(el.getBoundingClientRect().height);
                    this.place(el, x, y, w, usedH);
                    y += usedH + gapPx;
                    h -= usedH + gapPx;
                    break;
                }
                case 'bottom': {
                    this.place(el, x, y + Math.max(0, h - 1), w, undefined);
                    const usedH = Math.ceil(el.getBoundingClientRect().height);
                    const top = y + h - usedH;
                    this.place(el, x, top, w, usedH);
                    h -= usedH + gapPx;
                    break;
                }
                case 'left': {
                    this.place(el, x, y, undefined, h);
                    const usedW = Math.ceil(el.getBoundingClientRect().width);
                    this.place(el, x, y, usedW, h);
                    x += usedW + gapPx;
                    w -= usedW + gapPx;
                    break;
                }
                case 'right': {
                    this.place(el, x + Math.max(0, w - 1), y, undefined, h);
                    const usedW = Math.ceil(el.getBoundingClientRect().width);
                    const left = x + w - usedW;
                    this.place(el, left, y, usedW, h);
                    w -= usedW + gapPx;
                    break;
                }
            }
        }
    }
    /**
     * @private
     */
    resolveCssLengthToPx(value, surface) {
        if (value === undefined) {
            return 0;
        }
        if (value === 'auto') {
            return 0;
        }
        if (typeof value === 'number') {
            return value; // treat bare numbers as px
        }
        if (!CssLength.isCssLength(value)) {
            return 0;
        }
        // Use a hidden probe element to let the browser compute absolute pixels for us.
        const probe = document.createElement('div');
        probe.style.position = 'absolute';
        probe.style.visibility = 'hidden';
        probe.style.pointerEvents = 'none';
        probe.style.width = value;
        probe.style.height = '0';
        // Use the layout surface as the containing block for % and font-relative units.
        surface.appendChild(probe);
        const px = probe.getBoundingClientRect().width;
        surface.removeChild(probe);
        // Normalize to non-negative integer pixels to avoid subpixel accumulation jitter
        return Math.max(0, Math.round(px));
    }
    /**
     * @private
     */
    isVisible(el) {
        const s = getComputedStyle(el);
        return s.display !== 'none' && s.visibility !== 'hidden';
    }
    /**
     * @private
     */
    prepForMeasure(el) {
        el.style.position = 'absolute';
        el.style.left = '0px';
        el.style.top = '0px';
        el.style.right = '';
        el.style.bottom = '';
        el.style.width = '';
        el.style.height = '';
    }
    /**
     * @private
     */
    place(el, left, top, width, height) {
        el.style.position = 'absolute';
        el.style.left = `${left}px`;
        el.style.top = `${top}px`;
        el.style.width = width === undefined ? '' : `${width}px`;
        el.style.height = height === undefined ? '' : `${height}px`;
    }
};
__decorate([
    Attribute({
        type: Boolean,
        useDefault: true
    }),
    __metadata("design:type", Boolean),
    __metadata("design:paramtypes", [Boolean])
], DockPanelElement.prototype, "lastChildFill", null);
DockPanelElement = DockPanelElement_1 = __decorate([
    Component({
        selector: 'mosaik-dock-panel',
        template: dockPanelElementTemplate,
        imports: []
    }),
    __metadata("design:paramtypes", [])
], DockPanelElement);
export { DockPanelElement };
//# sourceMappingURL=DockPanelElement.js.map