import * as i0 from '@angular/core';
import { InjectionToken, inject, signal, contentChildren, input, numberAttribute, output, effect, ChangeDetectionStrategy, Component, PLATFORM_ID, Injectable, viewChild, computed, Directive } from '@angular/core';
import { BreakpointObserver } from '@angular/cdk/layout';
import { injectElement, arrayShallowEquals, ResizeObserverService, MutationObserverService, zonefreeScheduler, px, typedFromEvent, getActualTarget, isElement } from '@ngstarter-ui/components/core';
import { isPlatformBrowser, DOCUMENT } from '@angular/common';
import { toObservable, takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { Subscription, BehaviorSubject, combineLatest, distinctUntilChanged, startWith, debounceTime, map, merge, filter } from 'rxjs';

const tilesSwap = (order, currentVisualIndex, newVisualIndex) => {
    const reverseOrder = new Map();
    order.forEach((visual, key) => {
        reverseOrder.set(visual, key);
    });
    const keyOfDragged = reverseOrder.get(currentVisualIndex);
    const keyOfTarget = reverseOrder.get(newVisualIndex);
    if (keyOfDragged === undefined || keyOfTarget === undefined) {
        return order;
    }
    const newOrder = new Map(order);
    newOrder.set(keyOfDragged, newVisualIndex);
    newOrder.set(keyOfTarget, currentVisualIndex);
    return newOrder;
};
const tilesShift = (order, currentVisualIndex, newVisualIndex) => {
    const reverseOrder = new Map();
    order.forEach((visual, key) => {
        reverseOrder.set(visual, key);
    });
    const keyOfDragged = reverseOrder.get(currentVisualIndex);
    if (keyOfDragged === undefined || currentVisualIndex === newVisualIndex) {
        return order;
    }
    const newOrder = new Map();
    const from = currentVisualIndex;
    const to = newVisualIndex;
    if (from < to) { // Dragging down/right
        order.forEach((visual, key) => {
            if (visual > from && visual <= to) {
                newOrder.set(key, visual - 1); // Shift up/left
            }
            else if (key !== keyOfDragged) {
                newOrder.set(key, visual);
            }
        });
    }
    else { // Dragging up/left
        order.forEach((visual, key) => {
            if (visual >= to && visual < from) {
                newOrder.set(key, visual + 1); // Shift down/right
            }
            else if (key !== keyOfDragged) {
                newOrder.set(key, visual);
            }
        });
    }
    // Place the dragged item at the new position
    newOrder.set(keyOfDragged, to);
    return newOrder;
};
const TILES_REORDER = new InjectionToken('TILES_REORDER', { factory: () => tilesShift });

class Tiles {
    reorder = inject(TILES_REORDER);
    el = injectElement();
    element = signal(null, ...(ngDevMode ? [{ debugName: "element" }] : /* istanbul ignore next */ []));
    order = signal(new Map(), ...(ngDevMode ? [{ debugName: "order" }] : /* istanbul ignore next */ []));
    tiles = contentChildren(Tile, ...(ngDevMode ? [{ debugName: "tiles" }] : /* istanbul ignore next */ []));
    items = input([], ...(ngDevMode ? [{ debugName: "items" }] : /* istanbul ignore next */ []));
    gap = input(24, ...(ngDevMode ? [{ debugName: "gap" }] : /* istanbul ignore next */ []));
    columns = input(12, { ...(ngDevMode ? { debugName: "columns" } : /* istanbul ignore next */ {}), transform: numberAttribute });
    orderChange = output();
    orderChanged = output();
    layoutChanged = output();
    rearrangeTimer;
    lastSourceVisualIndex = null;
    lastTargetVisualIndex = null;
    lastSwapTime = 0;
    lastSwapX = 0;
    lastSwapY = 0;
    initialItems = null;
    isInitialized = false;
    isDragging = false;
    constructor() {
        effect(() => {
            const tiles = this.tiles();
            const currentOrder = this.order();
            const newOrder = new Map();
            tiles.forEach((tile, index) => {
                if (currentOrder.has(tile)) {
                    newOrder.set(tile, currentOrder.get(tile));
                }
                else {
                    newOrder.set(tile, index);
                }
            });
            // Cleanup old entries and check if changed
            let changed = newOrder.size !== currentOrder.size;
            if (!changed) {
                for (const [tile, visualIndex] of newOrder) {
                    if (currentOrder.get(tile) !== visualIndex) {
                        changed = true;
                        break;
                    }
                }
            }
            if (changed) {
                this.order.set(newOrder);
                const currentItems = this.items();
                if (currentItems.length > 0 && this.isInitialized && !this.isDragging) {
                    const layout = this.calculateLayout(newOrder);
                    this.layoutChanged.emit(layout);
                }
            }
            this.isInitialized = true;
        });
    }
    calculateLayout(currentOrder) {
        const tiles = this.tiles();
        const currentItems = this.items();
        const layout = new Array(currentItems.length);
        tiles.forEach((tile, index) => {
            const visualIndex = currentOrder.get(tile) ?? index;
            if (visualIndex < layout.length) {
                layout[visualIndex] = currentItems[index];
            }
        });
        return layout.filter(item => item !== undefined);
    }
    onDragEnd(tile) {
        if (this.rearrangeTimer) {
            clearTimeout(this.rearrangeTimer);
            this.rearrangeTimer = null;
        }
        const currentItems = this.items();
        const currentOrder = this.order();
        const tiles = this.tiles();
        if (currentItems.length > 0 && tiles.length > 0) {
            const newItems = new Array(currentItems.length);
            tiles.forEach((tile, index) => {
                const visualIndex = currentOrder.get(tile) ?? index;
                if (visualIndex < newItems.length) {
                    newItems[visualIndex] = currentItems[index];
                }
            });
            // Filter out empty slots if any (though they shouldn't exist if data is consistent)
            const filteredNewItems = newItems.filter(item => item !== undefined);
            if (filteredNewItems.length === currentItems.length) {
                if (this.initialItems && !arrayShallowEquals(this.initialItems, filteredNewItems)) {
                    this.orderChanged.emit(filteredNewItems);
                    this.layoutChanged.emit(filteredNewItems);
                }
            }
            else {
                console.warn('Tiles: New items length mismatch, skip emission', filteredNewItems.length, currentItems.length);
            }
        }
        this.lastSourceVisualIndex = null;
        this.lastTargetVisualIndex = null;
        this.lastSwapTime = 0;
        this.initialItems = null;
        this.isDragging = false;
    }
    onDragStart() {
        this.isDragging = true;
        if (!this.initialItems) {
            this.initialItems = [...this.items()];
        }
    }
    rearrange(element, event) {
        const draggedElement = this.element();
        const tiles = this.tiles();
        if (!draggedElement || draggedElement === element) {
            return;
        }
        const draggedTile = tiles.find((tile) => tile.element === draggedElement);
        let targetTile = tiles.find((tile) => tile.element === element);
        if (!draggedTile || !targetTile) {
            return;
        }
        const order = this.order();
        const draggedVisualIndex = order.get(draggedTile);
        let targetVisualIndex = order.get(targetTile);
        if (draggedVisualIndex === undefined || targetVisualIndex === undefined) {
            return;
        }
        const isMovingForward = draggedVisualIndex < targetVisualIndex;
        const areSameSize = draggedTile.width() === targetTile.width() && draggedTile.height() === targetTile.height();
        const columns = this.columns();
        const isFullWidth = draggedTile.width() === columns || (draggedTile.width() === 1 && columns === 1);
        // Get the actual floating element's position
        const draggedWrapper = draggedTile.wrapper()?.nativeElement;
        if (!draggedWrapper) {
            return;
        }
        const floatingRect = draggedWrapper.getBoundingClientRect();
        // Check if the edge of the floating block has crossed the midpoint of the target tile
        const targetRect = element.getBoundingClientRect();
        const midX = targetRect.left + targetRect.width / 2;
        const midY = targetRect.top + targetRect.height / 2;
        const deltaX = Math.abs(targetRect.left - floatingRect.left);
        const deltaY = Math.abs(targetRect.top - floatingRect.top);
        let shouldRearrange = false;
        if (isFullWidth && !isMovingForward) {
            // Special logic for full-width tiles when moving up:
            // if the top edge of the dragged tile has passed the bottom third of the target tile
            if (floatingRect.top < targetRect.bottom - targetRect.height * 0.3) {
                shouldRearrange = true;
            }
        }
        else if (isFullWidth && isMovingForward) {
            // Special logic for full-width tiles when moving down:
            // if the bottom edge of the dragged tile has passed the top third of the target tile
            if (floatingRect.bottom > targetRect.top + targetRect.height * 0.3) {
                shouldRearrange = true;
            }
        }
        else if (deltaX > deltaY) {
            // Primarily horizontal movement
            const edgeX = isMovingForward ? floatingRect.right : floatingRect.left;
            const threshold = areSameSize ? (isMovingForward ? targetRect.left + targetRect.width * 0.2 : targetRect.right - targetRect.width * 0.2) : midX;
            if (isMovingForward ? edgeX >= threshold : edgeX <= threshold) {
                shouldRearrange = true;
            }
        }
        else {
            // Primarily vertical movement
            const edgeY = isMovingForward ? floatingRect.bottom : floatingRect.top;
            const threshold = areSameSize ? (isMovingForward ? targetRect.top + targetRect.height * 0.2 : targetRect.bottom - targetRect.height * 0.2) : midY;
            if (isMovingForward ? edgeY >= threshold : edgeY <= threshold) {
                shouldRearrange = true;
            }
        }
        if (!shouldRearrange) {
            return;
        }
        // Adjustment for full-width tiles to displace the entire row
        if (isFullWidth) {
            const targetTop = targetRect.top;
            let finalTargetVisualIndex = targetVisualIndex;
            tiles.forEach((t) => {
                const tIndex = order.get(t);
                if (tIndex !== undefined) {
                    if (isMovingForward ? tIndex > finalTargetVisualIndex : tIndex < finalTargetVisualIndex) {
                        const r = t.element.getBoundingClientRect();
                        if (Math.abs(r.top - targetTop) < 5) {
                            finalTargetVisualIndex = tIndex;
                        }
                    }
                }
            });
            if (finalTargetVisualIndex !== targetVisualIndex) {
                const newTargetTile = tiles.find((t) => order.get(t) === finalTargetVisualIndex);
                if (newTargetTile) {
                    targetTile = newTargetTile;
                    targetVisualIndex = finalTargetVisualIndex;
                }
            }
        }
        // Recalculate size equality after potential adjustment
        const areSameSizeNow = draggedTile.width() === targetTile.width() && draggedTile.height() === targetTile.height();
        // Prevent bouncing: if we just swapped with this tile, don't swap back
        // unless the pointer has moved significantly in the opposite direction.
        const now = Date.now();
        if (now - this.lastSwapTime < 500 && this.lastTargetVisualIndex === draggedVisualIndex && this.lastSourceVisualIndex === targetVisualIndex) {
            const movementX = event.clientX - this.lastSwapX;
            const movementY = event.clientY - this.lastSwapY;
            // If we are moving in the same direction as the original swap, don't swap back
            const wasMovingForward = (this.lastSourceVisualIndex ?? 0) < (this.lastTargetVisualIndex ?? 0);
            if (deltaX > deltaY) {
                if (wasMovingForward ? movementX > -10 : movementX < 10) {
                    return;
                }
            }
            else {
                if (wasMovingForward ? movementY > -10 : movementY < 10) {
                    return;
                }
            }
        }
        // Skip if this is the exact same target we've already started to rearrange to
        if (this.rearrangeTimer && this.lastTargetVisualIndex === targetVisualIndex) {
            return;
        }
        if (this.rearrangeTimer) {
            clearTimeout(this.rearrangeTimer);
        }
        this.lastSourceVisualIndex = draggedVisualIndex;
        this.lastTargetVisualIndex = targetVisualIndex;
        this.rearrangeTimer = setTimeout(() => {
            const orderAtTimeOfExecution = this.order();
            let newOrder;
            this.lastSwapTime = Date.now();
            this.lastSwapX = event.clientX;
            this.lastSwapY = event.clientY;
            if (areSameSizeNow || (isFullWidth && targetTile.width() === 12)) {
                // Use swap for tiles of the same size or full-width tiles swapping with other full-width tiles
                newOrder = new Map(orderAtTimeOfExecution);
                newOrder.set(draggedTile, targetVisualIndex);
                newOrder.set(targetTile, draggedVisualIndex);
            }
            else {
                newOrder = this.reorder(orderAtTimeOfExecution, draggedVisualIndex, targetVisualIndex);
            }
            this.order.set(new Map(newOrder));
            const result = new Map();
            tiles.forEach((tile, index) => {
                result.set(index, newOrder.get(tile) ?? index);
            });
            this.orderChange.emit(result);
            this.rearrangeTimer = null;
        }, 50);
    }
    static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: Tiles, deps: [], target: i0.ɵɵFactoryTarget.Component });
    static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "21.2.4", type: Tiles, isStandalone: true, selector: "ngs-tiles", inputs: { items: { classPropertyName: "items", publicName: "items", isSignal: true, isRequired: false, transformFunction: null }, gap: { classPropertyName: "gap", publicName: "gap", isSignal: true, isRequired: false, transformFunction: null }, columns: { classPropertyName: "columns", publicName: "columns", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { orderChange: "orderChange", orderChanged: "orderChanged", layoutChanged: "layoutChanged" }, host: { properties: { "style.--ngs-tiles-columns": "columns()", "style.grid-template-columns": "\"repeat(\" + columns() + \", 1fr)\"" }, classAttribute: "ngs-tiles" }, queries: [{ propertyName: "tiles", predicate: Tile, isSignal: true }], exportAs: ["ngsTiles"], ngImport: i0, template: '<ng-content/>', isInline: true, styles: [":host{--ngs-tiles-columns: 12;--ngs-tiles-row-height: 20px;--ngs-tiles-gap: calc(var(--spacing, .25rem) * 5);position:relative;-webkit-user-select:none;user-select:none;z-index:0;display:grid;grid-template-columns:repeat(var(--ngs-tiles-columns, 12),1fr);grid-auto-flow:dense;justify-items:stretch;gap:var(--ngs-tiles-gap);grid-auto-rows:minmax(var(--ngs-tiles-row-height),auto)}@media(max-width:639px){:host{--ngs-tiles-columns: 1 !important;grid-template-columns:1fr!important}}:host._dragged ngs-tile{pointer-events:auto}:host._dragged ngs-tile._dragged{pointer-events:none}:host._dragged ngs-tile>.t-wrapper{pointer-events:none;will-change:transform}:host._dragged ngs-tile>*{pointer-events:none}\n/*! tailwindcss v4.2.2 | MIT License | https://tailwindcss.com */\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: Tiles, decorators: [{
            type: Component,
            args: [{ selector: 'ngs-tiles', exportAs: 'ngsTiles', template: '<ng-content/>', changeDetection: ChangeDetectionStrategy.OnPush, host: {
                        'class': 'ngs-tiles',
                        '[style.--ngs-tiles-columns]': 'columns()',
                        '[style.grid-template-columns]': '"repeat(" + columns() + ", 1fr)"',
                    }, styles: [":host{--ngs-tiles-columns: 12;--ngs-tiles-row-height: 20px;--ngs-tiles-gap: calc(var(--spacing, .25rem) * 5);position:relative;-webkit-user-select:none;user-select:none;z-index:0;display:grid;grid-template-columns:repeat(var(--ngs-tiles-columns, 12),1fr);grid-auto-flow:dense;justify-items:stretch;gap:var(--ngs-tiles-gap);grid-auto-rows:minmax(var(--ngs-tiles-row-height),auto)}@media(max-width:639px){:host{--ngs-tiles-columns: 1 !important;grid-template-columns:1fr!important}}:host._dragged ngs-tile{pointer-events:auto}:host._dragged ngs-tile._dragged{pointer-events:none}:host._dragged ngs-tile>.t-wrapper{pointer-events:none;will-change:transform}:host._dragged ngs-tile>*{pointer-events:none}\n/*! tailwindcss v4.2.2 | MIT License | https://tailwindcss.com */\n"] }]
        }], ctorParameters: () => [], propDecorators: { tiles: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => Tile), { isSignal: true }] }], items: [{ type: i0.Input, args: [{ isSignal: true, alias: "items", required: false }] }], gap: [{ type: i0.Input, args: [{ isSignal: true, alias: "gap", required: false }] }], columns: [{ type: i0.Input, args: [{ isSignal: true, alias: "columns", required: false }] }], orderChange: [{ type: i0.Output, args: ["orderChange"] }], orderChanged: [{ type: i0.Output, args: ["orderChanged"] }], layoutChanged: [{ type: i0.Output, args: ["layoutChanged"] }] } });

class TileService {
    isBrowser = isPlatformBrowser(inject(PLATFORM_ID));
    el = injectElement();
    tiles = inject(Tiles);
    sub = new Subscription();
    offset$ = new BehaviorSubject([NaN, NaN]);
    resize$ = inject(ResizeObserverService).observe(this.el);
    mutations$ = inject(MutationObserverService).observe(this.el, { childList: true });
    lastRect = null;
    position$ = combineLatest([
        this.offset$.pipe(distinctUntilChanged(arrayShallowEquals)),
        this.resize$.pipe(startWith(null)),
        this.mutations$.pipe(startWith(null)),
        toObservable(this.tiles.order).pipe(debounceTime(0, zonefreeScheduler())),
    ]).pipe(debounceTime(0, zonefreeScheduler()), map(([offset, _, __, order]) => ({ offset, order })));
    init(element) {
        if (this.isBrowser && element) {
            this.sub.add(this.position$.subscribe(({ offset, order }) => {
                this.animate(element);
                this.setPosition(element, offset);
                this.setRect(element, offset);
            }));
        }
        else {
            this.el.style.setProperty('position', 'relative');
        }
    }
    animate(element) {
        const isDragging = !Number.isNaN(this.offset$.value[0]);
        const currentRect = this.el.getBoundingClientRect();
        if (isDragging || (element.style.position === 'fixed' && !element.style.transition.includes('top'))) {
            this.lastRect = currentRect;
            return;
        }
        if (this.lastRect) {
            const dx = this.lastRect.left - currentRect.left;
            const dy = this.lastRect.top - currentRect.top;
            if (dx !== 0 || dy !== 0) {
                element.style.setProperty('transition', 'none');
                element.style.setProperty('transform', `translate(${px(dx)}, ${px(dy)})`);
                // Trigger reflow
                element.offsetHeight;
                element.style.removeProperty('transition');
                element.style.removeProperty('transform');
            }
        }
        this.lastRect = currentRect;
    }
    setOffset(offset) {
        this.offset$.next(offset);
    }
    ngOnDestroy() {
        this.sub.unsubscribe();
    }
    getRect([left, top]) {
        const elTop = Number.isNaN(top) ? this.el.offsetTop : top;
        const elLeft = Number.isNaN(left) ? this.el.offsetLeft : left;
        const rect = {
            top: elTop,
            left: elLeft,
            width: this.el.clientWidth,
            height: this.el.clientHeight,
            right: NaN,
            bottom: NaN,
            y: elTop,
            x: elLeft,
        };
        return {
            ...rect,
            toJSON: () => JSON.stringify(rect),
        };
    }
    setRect({ style }, offset) {
        const isDragging = !Number.isNaN(offset[0]);
        if (!isDragging) {
            return;
        }
        const { top, left, width, height } = this.getRect(offset);
        style.width = px(width);
        style.height = px(height);
        style.top = px(top);
        style.left = px(left);
    }
    setPosition(element, [left]) {
        const isDragging = !Number.isNaN(left);
        if (isDragging) {
            element.style.setProperty('transition', 'none');
            element.style.setProperty('position', 'fixed');
            element.style.setProperty('z-index', '100');
            // This inline style will override the CSS class, making the floating tile visible
            element.style.setProperty('opacity', '1');
            element.style.setProperty('pointer-events', 'none');
        }
        else if (element.style.position === 'fixed') {
            const { top, left, width, height } = this.el.getBoundingClientRect();
            const currentTop = parseFloat(element.style.top);
            const currentLeft = parseFloat(element.style.left);
            // If already at target, clean up immediately
            if (Math.abs(currentTop - top) < 1 && Math.abs(currentLeft - left) < 1) {
                this.cleanup(element);
                return;
            }
            // Transition for smooth return
            element.style.setProperty('transition', 'top 300ms cubic-bezier(0.2, 0, 0, 1), left 300ms cubic-bezier(0.2, 0, 0, 1), width 300ms cubic-bezier(0.2, 0, 0, 1), height 300ms cubic-bezier(0.2, 0, 0, 1), transform 300ms cubic-bezier(0.2, 0, 0, 1)');
            element.style.top = px(top);
            element.style.left = px(left);
            element.style.width = px(width);
            element.style.height = px(height);
            const onTransitionEnd = (event) => {
                if (event.propertyName === 'top' || event.propertyName === 'left') {
                    element.removeEventListener('transitionend', onTransitionEnd);
                    this.cleanup(element);
                }
            };
            element.addEventListener('transitionend', onTransitionEnd);
            // Fallback cleanup if transitionend doesn't fire
            setTimeout(() => {
                element.removeEventListener('transitionend', onTransitionEnd);
                this.cleanup(element);
            }, 400);
        }
    }
    cleanup(element) {
        element.style.removeProperty('transition');
        element.style.removeProperty('position');
        element.style.removeProperty('z-index');
        element.style.removeProperty('opacity');
        element.style.removeProperty('pointer-events');
        element.style.removeProperty('top');
        element.style.removeProperty('left');
        element.style.removeProperty('width');
        element.style.removeProperty('height');
    }
    static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: TileService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
    static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: TileService });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: TileService, decorators: [{
            type: Injectable
        }] });

const BREAKPOINTS = {
    sm: '(min-width: 640px)',
    md: '(min-width: 768px)',
    lg: '(min-width: 1024px)',
    xl: '(min-width: 1280px)',
};
class Tile {
    element = injectElement();
    wrapper = viewChild.required('wrapper');
    service = inject(TileService);
    breakpointObserver = inject(BreakpointObserver);
    breakpointState = signal(this.breakpointObserver.isMatched(Object.values(BREAKPOINTS)), ...(ngDevMode ? [{ debugName: "breakpointState" }] : /* istanbul ignore next */ []));
    tiles = inject(Tiles);
    dragged = signal(false, ...(ngDevMode ? [{ debugName: "dragged" }] : /* istanbul ignore next */ []));
    initialized = signal(false, ...(ngDevMode ? [{ debugName: "initialized" }] : /* istanbul ignore next */ []));
    heightPx = signal(null, ...(ngDevMode ? [{ debugName: "heightPx" }] : /* istanbul ignore next */ []));
    width = input.required(...(ngDevMode ? [{ debugName: "width" }] : /* istanbul ignore next */ []));
    widthSm = input(undefined, { ...(ngDevMode ? { debugName: "widthSm" } : /* istanbul ignore next */ {}), alias: 'width.sm' });
    widthMd = input(undefined, { ...(ngDevMode ? { debugName: "widthMd" } : /* istanbul ignore next */ {}), alias: 'width.md' });
    widthLg = input(undefined, { ...(ngDevMode ? { debugName: "widthLg" } : /* istanbul ignore next */ {}), alias: 'width.lg' });
    widthXl = input(undefined, { ...(ngDevMode ? { debugName: "widthXl" } : /* istanbul ignore next */ {}), alias: 'width.xl' });
    height = input.required(...(ngDevMode ? [{ debugName: "height" }] : /* istanbul ignore next */ []));
    heightSm = input(undefined, { ...(ngDevMode ? { debugName: "heightSm" } : /* istanbul ignore next */ {}), alias: 'height.sm' });
    heightMd = input(undefined, { ...(ngDevMode ? { debugName: "heightMd" } : /* istanbul ignore next */ {}), alias: 'height.md' });
    heightLg = input(undefined, { ...(ngDevMode ? { debugName: "heightLg" } : /* istanbul ignore next */ {}), alias: 'height.lg' });
    heightXl = input(undefined, { ...(ngDevMode ? { debugName: "heightXl" } : /* istanbul ignore next */ {}), alias: 'height.xl' });
    gridColumn = computed(() => {
        const cols = this.tiles.columns();
        return `span min(var(--ngs-tile-w, ${cols}), ${cols})`;
    }, ...(ngDevMode ? [{ debugName: "gridColumn" }] : /* istanbul ignore next */ []));
    gridRow = computed(() => `span var(--ngs-tile-h, ${this.height()})`, ...(ngDevMode ? [{ debugName: "gridRow" }] : /* istanbul ignore next */ []));
    constructor() {
        this.breakpointObserver.observe(Object.values(BREAKPOINTS)).subscribe(() => {
            this.breakpointState.set(!this.breakpointState());
        });
    }
    onPointerMove(event) {
        if (this.dragged()) {
            return;
        }
        this.tiles.rearrange(this.element, event);
    }
    onDrag(offset, end = false) {
        const isDragging = !Number.isNaN(offset[0]);
        this.tiles.element.set(isDragging ? this.element : null);
        this.service.setOffset(offset);
        if (isDragging) {
            if (!this.dragged()) {
                const { height } = this.element.getBoundingClientRect();
                this.heightPx.set(height + 'px');
            }
            this.dragged.set(true);
            this.tiles.onDragStart();
            this.tiles.el.classList.add('_dragged');
        }
        else {
            this.dragged.set(false);
            this.heightPx.set(null);
            this.tiles.el.classList.remove('_dragged');
            if (end) {
                this.tiles.onDragEnd(this);
            }
        }
    }
    ngAfterViewInit() {
        this.service.init(this.wrapper()?.nativeElement);
        setTimeout(() => this.initialized.set(true));
    }
    ngOnDestroy() {
        if (this.tiles.element() === this.element) {
            this.tiles.element.set(null);
        }
    }
    static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: Tile, deps: [], target: i0.ɵɵFactoryTarget.Component });
    static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "21.2.4", type: Tile, isStandalone: true, selector: "ngs-tile", inputs: { width: { classPropertyName: "width", publicName: "width", isSignal: true, isRequired: true, transformFunction: null }, widthSm: { classPropertyName: "widthSm", publicName: "width.sm", isSignal: true, isRequired: false, transformFunction: null }, widthMd: { classPropertyName: "widthMd", publicName: "width.md", isSignal: true, isRequired: false, transformFunction: null }, widthLg: { classPropertyName: "widthLg", publicName: "width.lg", isSignal: true, isRequired: false, transformFunction: null }, widthXl: { classPropertyName: "widthXl", publicName: "width.xl", isSignal: true, isRequired: false, transformFunction: null }, height: { classPropertyName: "height", publicName: "height", isSignal: true, isRequired: true, transformFunction: null }, heightSm: { classPropertyName: "heightSm", publicName: "height.sm", isSignal: true, isRequired: false, transformFunction: null }, heightMd: { classPropertyName: "heightMd", publicName: "height.md", isSignal: true, isRequired: false, transformFunction: null }, heightLg: { classPropertyName: "heightLg", publicName: "height.lg", isSignal: true, isRequired: false, transformFunction: null }, heightXl: { classPropertyName: "heightXl", publicName: "height.xl", isSignal: true, isRequired: false, transformFunction: null } }, host: { listeners: { "pointermove": "onPointerMove($event)" }, properties: { "class._dragged": "dragged()", "class._initialized": "initialized()", "style.--ngs-tile-w": "width()", "style.--ngs-tile-w-sm": "widthSm() ?? null", "style.--ngs-tile-w-md": "widthMd() ?? null", "style.--ngs-tile-w-lg": "widthLg() ?? null", "style.--ngs-tile-w-xl": "widthXl() ?? null", "style.--ngs-tile-h": "height()", "style.--ngs-tile-h-sm": "heightSm() ?? null", "style.--ngs-tile-h-md": "heightMd() ?? null", "style.--ngs-tile-h-lg": "heightLg() ?? null", "style.--ngs-tile-h-xl": "heightXl() ?? null", "style.grid-column": "gridColumn()", "style.grid-row": "gridRow()", "style.min-height": "dragged() ? heightPx() : null", "style.min-width": "\"0\"", "style.order": "tiles.order().get(this) ?? 0" } }, providers: [
            TileService
        ], viewQueries: [{ propertyName: "wrapper", first: true, predicate: ["wrapper"], descendants: true, isSignal: true }], exportAs: ["ngsTile"], ngImport: i0, template: "<div class=\"t-placeholder\"></div>\n<div #wrapper class=\"t-wrapper\">\n  <ng-content/>\n</div>\n", styles: [":host{display:flex;flex-direction:column;position:relative;overflow:visible;-webkit-user-select:none;user-select:none;min-width:0}@media(min-width:640px){:host{--ngs-tile-w: var(--ngs-tile-w-sm, var(--ngs-tile-w));--ngs-tile-h: var(--ngs-tile-h-sm, var(--ngs-tile-h))}}@media(min-width:768px){:host{--ngs-tile-w: var(--ngs-tile-w-md, var(--ngs-tile-w-sm, var(--ngs-tile-w)));--ngs-tile-h: var(--ngs-tile-h-md, var(--ngs-tile-h-sm, var(--ngs-tile-h)))}}@media(min-width:1024px){:host{--ngs-tile-w: var(--ngs-tile-w-lg, var(--ngs-tile-w-md, var(--ngs-tile-w-sm, var(--ngs-tile-w))));--ngs-tile-h: var(--ngs-tile-h-lg, var(--ngs-tile-h-md, var(--ngs-tile-h-sm, var(--ngs-tile-h))))}}@media(min-width:1280px){:host{--ngs-tile-w: var(--ngs-tile-w-xl, var(--ngs-tile-w-lg, var(--ngs-tile-w-md, var(--ngs-tile-w-sm, var(--ngs-tile-w)))));--ngs-tile-h: var(--ngs-tile-h-xl, var(--ngs-tile-h-lg, var(--ngs-tile-h-md, var(--ngs-tile-h-sm, var(--ngs-tile-h)))))}}:host .t-placeholder{position:absolute;inset:0;box-sizing:border-box;border-radius:var(--ngs-radius-xl);border:2px dashed var(--ngs-color-border);background-color:var(--ngs-color-surface-container);z-index:1;opacity:0;transition:opacity .15s ease-in-out;pointer-events:none}:host._dragged .t-placeholder{opacity:1}:host>.t-wrapper{flex:1;z-index:2;border-radius:inherit}:host._initialized>.t-wrapper{transition:opacity .15s ease-in-out,transform .3s cubic-bezier(.2,0,0,1),top .3s ease-in-out,left .3s ease-in-out,width .3s ease-in-out,height .3s ease-in-out}:host._dragged>.t-wrapper{z-index:100}:host [ngsTileHandle]{touch-action:none;-webkit-user-select:none;user-select:none}\n/*! tailwindcss v4.2.2 | MIT License | https://tailwindcss.com */\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: Tile, decorators: [{
            type: Component,
            args: [{ selector: 'ngs-tile', exportAs: 'ngsTile', changeDetection: ChangeDetectionStrategy.OnPush, providers: [
                        TileService
                    ], host: {
                        '[class._dragged]': 'dragged()',
                        '[class._initialized]': 'initialized()',
                        '[style.--ngs-tile-w]': 'width()',
                        '[style.--ngs-tile-w-sm]': 'widthSm() ?? null',
                        '[style.--ngs-tile-w-md]': 'widthMd() ?? null',
                        '[style.--ngs-tile-w-lg]': 'widthLg() ?? null',
                        '[style.--ngs-tile-w-xl]': 'widthXl() ?? null',
                        '[style.--ngs-tile-h]': 'height()',
                        '[style.--ngs-tile-h-sm]': 'heightSm() ?? null',
                        '[style.--ngs-tile-h-md]': 'heightMd() ?? null',
                        '[style.--ngs-tile-h-lg]': 'heightLg() ?? null',
                        '[style.--ngs-tile-h-xl]': 'heightXl() ?? null',
                        '[style.grid-column]': 'gridColumn()',
                        '[style.grid-row]': 'gridRow()',
                        '[style.min-height]': 'dragged() ? heightPx() : null',
                        '[style.min-width]': '"0"',
                        '[style.order]': 'tiles.order().get(this) ?? 0',
                        '(pointermove)': 'onPointerMove($event)',
                    }, template: "<div class=\"t-placeholder\"></div>\n<div #wrapper class=\"t-wrapper\">\n  <ng-content/>\n</div>\n", styles: [":host{display:flex;flex-direction:column;position:relative;overflow:visible;-webkit-user-select:none;user-select:none;min-width:0}@media(min-width:640px){:host{--ngs-tile-w: var(--ngs-tile-w-sm, var(--ngs-tile-w));--ngs-tile-h: var(--ngs-tile-h-sm, var(--ngs-tile-h))}}@media(min-width:768px){:host{--ngs-tile-w: var(--ngs-tile-w-md, var(--ngs-tile-w-sm, var(--ngs-tile-w)));--ngs-tile-h: var(--ngs-tile-h-md, var(--ngs-tile-h-sm, var(--ngs-tile-h)))}}@media(min-width:1024px){:host{--ngs-tile-w: var(--ngs-tile-w-lg, var(--ngs-tile-w-md, var(--ngs-tile-w-sm, var(--ngs-tile-w))));--ngs-tile-h: var(--ngs-tile-h-lg, var(--ngs-tile-h-md, var(--ngs-tile-h-sm, var(--ngs-tile-h))))}}@media(min-width:1280px){:host{--ngs-tile-w: var(--ngs-tile-w-xl, var(--ngs-tile-w-lg, var(--ngs-tile-w-md, var(--ngs-tile-w-sm, var(--ngs-tile-w)))));--ngs-tile-h: var(--ngs-tile-h-xl, var(--ngs-tile-h-lg, var(--ngs-tile-h-md, var(--ngs-tile-h-sm, var(--ngs-tile-h)))))}}:host .t-placeholder{position:absolute;inset:0;box-sizing:border-box;border-radius:var(--ngs-radius-xl);border:2px dashed var(--ngs-color-border);background-color:var(--ngs-color-surface-container);z-index:1;opacity:0;transition:opacity .15s ease-in-out;pointer-events:none}:host._dragged .t-placeholder{opacity:1}:host>.t-wrapper{flex:1;z-index:2;border-radius:inherit}:host._initialized>.t-wrapper{transition:opacity .15s ease-in-out,transform .3s cubic-bezier(.2,0,0,1),top .3s ease-in-out,left .3s ease-in-out,width .3s ease-in-out,height .3s ease-in-out}:host._dragged>.t-wrapper{z-index:100}:host [ngsTileHandle]{touch-action:none;-webkit-user-select:none;user-select:none}\n/*! tailwindcss v4.2.2 | MIT License | https://tailwindcss.com */\n"] }]
        }], ctorParameters: () => [], propDecorators: { wrapper: [{ type: i0.ViewChild, args: ['wrapper', { isSignal: true }] }], width: [{ type: i0.Input, args: [{ isSignal: true, alias: "width", required: true }] }], widthSm: [{ type: i0.Input, args: [{ isSignal: true, alias: "width.sm", required: false }] }], widthMd: [{ type: i0.Input, args: [{ isSignal: true, alias: "width.md", required: false }] }], widthLg: [{ type: i0.Input, args: [{ isSignal: true, alias: "width.lg", required: false }] }], widthXl: [{ type: i0.Input, args: [{ isSignal: true, alias: "width.xl", required: false }] }], height: [{ type: i0.Input, args: [{ isSignal: true, alias: "height", required: true }] }], heightSm: [{ type: i0.Input, args: [{ isSignal: true, alias: "height.sm", required: false }] }], heightMd: [{ type: i0.Input, args: [{ isSignal: true, alias: "height.md", required: false }] }], heightLg: [{ type: i0.Input, args: [{ isSignal: true, alias: "height.lg", required: false }] }], heightXl: [{ type: i0.Input, args: [{ isSignal: true, alias: "height.xl", required: false }] }] } });

class TileHandleDirective {
    doc = inject(DOCUMENT);
    tile = inject(Tile);
    x = NaN;
    y = NaN;
    startX = NaN;
    startY = NaN;
    isActuallyDragging = false;
    pointerSub = merge(typedFromEvent(this.doc, 'pointerup'), typedFromEvent(this.doc, 'pointermove'))
        .pipe(filter(() => !Number.isNaN(this.startX)), takeUntilDestroyed())
        .subscribe((event) => {
        if (event.type === 'pointerup') {
            this.onPointer(NaN, NaN, true);
        }
        else {
            const dx = event.x - this.startX;
            const dy = event.y - this.startY;
            if (!this.isActuallyDragging && Math.sqrt(dx * dx + dy * dy) > 2) {
                this.isActuallyDragging = true;
            }
            if (this.isActuallyDragging) {
                this.tile.onDrag([event.x - this.x, event.y - this.y]);
            }
        }
    });
    onPointer(x = NaN, y = NaN, end = false) {
        const { left, top } = this.tile.element.getBoundingClientRect();
        this.x = x - left;
        this.y = y - top;
        if (end) {
            this.startX = NaN;
            this.startY = NaN;
            this.isActuallyDragging = false;
            this.tile.onDrag([NaN, NaN], end);
        }
    }
    onStart(event) {
        const pointerEvent = event;
        const target = getActualTarget(pointerEvent);
        const { x, y, pointerId } = pointerEvent;
        if (isElement(target)) {
            target.releasePointerCapture(pointerId);
        }
        this.startX = x;
        this.startY = y;
        this.onPointer(x, y);
    }
    static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: TileHandleDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
    static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.4", type: TileHandleDirective, isStandalone: true, selector: "[ngsTileHandle]", host: { listeners: { "pointerdown": "onStart($event)" } }, ngImport: i0 });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: TileHandleDirective, decorators: [{
            type: Directive,
            args: [{
                    selector: '[ngsTileHandle]',
                    host: {
                        '(pointerdown)': 'onStart($event)'
                    },
                }]
        }] });

/**
 * Generated bundle index. Do not edit.
 */

export { TILES_REORDER, Tile, TileHandleDirective, TileService, Tiles, tilesShift, tilesSwap };
//# sourceMappingURL=ngstarter-ui-components-tiles.mjs.map
