@dragonworks/ngx-dashboard 21.1.2 → 21.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -23,7 +23,7 @@ import { isPlatformBrowser } from '@angular/common';
23
23
 
24
24
  // Auto-generated by scripts/generate-versions.js
25
25
  // Do not edit manually
26
- const NGX_DASHBOARD_VERSION = '21.1.2';
26
+ const NGX_DASHBOARD_VERSION = '21.2.1';
27
27
 
28
28
  /**
29
29
  * Maximum number of columns supported by the grid.
@@ -1638,6 +1638,17 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.10", ngImpo
1638
1638
  }, template: "<!-- cell.component.html -->\n<div\n class=\"cell\"\n [class.is-resizing]=\"isResizing()\"\n [class.flat]=\"flat() === true\"\n [draggable]=\"draggable()\"\n (dragstart)=\"onDragStart($event)\"\n (dragend)=\"onDragEnd()\"\n (contextmenu)=\"onContextMenu($event)\"\n>\n <div class=\"content-area\">\n <ng-template #container></ng-template>\n </div>\n @if (isEditMode() && !isDragging()) {\n <!-- Right resize handle -->\n <div\n class=\"resize-handle resize-handle--right\"\n (mousedown)=\"onResizeStart($event, 'horizontal')\"\n >\n <div class=\"resize-handle-line\"></div>\n </div>\n <!-- Bottom resize handle -->\n <div\n class=\"resize-handle resize-handle--bottom\"\n (mousedown)=\"onResizeStart($event, 'vertical')\"\n >\n <div class=\"resize-handle-line\"></div>\n </div>\n }\n</div>\n\n@if (isResizing()) {\n<div class=\"resize-preview\" i18n=\"@@ngx.dashboard.cell.resize.dimensions\">\n {{ resizeData()?.previewColSpan ?? colSpan() }} \u00D7\n {{ resizeData()?.previewRowSpan ?? rowSpan() }}\n</div>\n}\n", styles: [":host{display:block;width:100%;height:100%;position:relative;z-index:1;container-type:inline-size}:host(.drag-active):not(.is-dragging){pointer-events:none}:host(.is-dragging){z-index:100;opacity:.5;pointer-events:none}:host(.is-dragging) .content-area{pointer-events:none}:host(:hover) .resize-handle{opacity:1}.cell{width:100%;height:100%;border-radius:4px;box-shadow:0 2px 6px #0000001a;padding:0;box-sizing:border-box;overflow:hidden;position:relative;container-type:inline-size}.cell:hover{box-shadow:0 4px 10px #00000026;transform:translateY(-2px)}.cell.flat{box-shadow:none;border:none}.cell.flat:hover{box-shadow:none;transform:none;border-color:#bdbdbd}.cell.resizing{-webkit-user-select:none;user-select:none}.content-area{width:100%;height:100%;overflow:auto;pointer-events:auto;position:relative;z-index:1}.content-area:hover{transform:initial}:host(:not(.is-dragging)) .cell.flat .content-area{pointer-events:auto}:host(:not(.is-dragging)) .cell.flat .content-area:hover{transform:initial}.resize-handle{position:absolute;z-index:20}.resize-handle--right{cursor:col-resize;width:16px;height:100%;right:-8px;top:0;display:flex;align-items:center;justify-content:center;opacity:0}.resize-handle--right:hover{opacity:1}.resize-handle--right:hover .resize-handle-line{background-color:var(--mat-sys-primary-container)}.resize-handle--bottom{cursor:row-resize;width:100%;height:16px;bottom:-8px;left:0;display:flex;align-items:center;justify-content:center;opacity:0}.resize-handle--bottom:hover{opacity:1}.resize-handle--bottom:hover .resize-handle-line{background-color:var(--mat-sys-primary-container)}.resize-handle-line{background-color:#0000001a}.resize-handle--right .resize-handle-line{width:8px;height:40px;border-radius:2px}.resize-handle--bottom .resize-handle-line{width:40px;height:8px;border-radius:2px}.resize-preview{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);background-color:var(--mat-sys-primary);color:var(--mat-sys-on-primary);padding:4px 12px;border-radius:4px;font-size:14px;font-weight:500;pointer-events:none;z-index:30}.cell.is-resizing{opacity:.6}.cell.is-resizing .resize-handle{background-color:#2196f380}:root .cursor-col-resize{cursor:col-resize!important}:root .cursor-row-resize{cursor:row-resize!important}\n"] }]
1639
1639
  }], ctorParameters: () => [], propDecorators: { widgetId: [{ type: i0.Input, args: [{ isSignal: true, alias: "widgetId", required: true }] }], cellId: [{ type: i0.Input, args: [{ isSignal: true, alias: "cellId", required: true }] }], widgetFactory: [{ type: i0.Input, args: [{ isSignal: true, alias: "widgetFactory", required: false }] }], widgetState: [{ type: i0.Input, args: [{ isSignal: true, alias: "widgetState", required: false }] }], isEditMode: [{ type: i0.Input, args: [{ isSignal: true, alias: "isEditMode", required: false }] }], flat: [{ type: i0.Input, args: [{ isSignal: true, alias: "flat", required: false }] }], row: [{ type: i0.Input, args: [{ isSignal: true, alias: "row", required: true }] }, { type: i0.Output, args: ["rowChange"] }], column: [{ type: i0.Input, args: [{ isSignal: true, alias: "column", required: true }] }, { type: i0.Output, args: ["columnChange"] }], rowSpan: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowSpan", required: false }] }], colSpan: [{ type: i0.Input, args: [{ isSignal: true, alias: "colSpan", required: false }] }], draggable: [{ type: i0.Input, args: [{ isSignal: true, alias: "draggable", required: false }] }], dragStart: [{ type: i0.Output, args: ["dragStart"] }], dragEnd: [{ type: i0.Output, args: ["dragEnd"] }], edit: [{ type: i0.Output, args: ["edit"] }], delete: [{ type: i0.Output, args: ["delete"] }], settings: [{ type: i0.Output, args: ["settings"] }], resizeStart: [{ type: i0.Output, args: ["resizeStart"] }], resizeMove: [{ type: i0.Output, args: ["resizeMove"] }], resizeEnd: [{ type: i0.Output, args: ["resizeEnd"] }], container: [{ type: i0.ViewChild, args: ['container', { ...{ read: ViewContainerRef }, isSignal: true }] }] } });
1640
1640
 
1641
+ /**
1642
+ * Map SelectionModifier values to KeyboardEvent.key values. Compared against
1643
+ * `event.key` directly (rather than the boolean `*Key` flags) so unrelated
1644
+ * keypresses while a modifier is held don't flip the modifier-held state.
1645
+ */
1646
+ const MODIFIER_KEY = {
1647
+ shift: 'Shift',
1648
+ ctrl: 'Control',
1649
+ alt: 'Alt',
1650
+ meta: 'Meta',
1651
+ };
1641
1652
  class DashboardViewerComponent {
1642
1653
  #store = inject(DashboardStore);
1643
1654
  #renderer = inject(Renderer2);
@@ -1650,6 +1661,16 @@ class DashboardViewerComponent {
1650
1661
  gutters = computed(() => this.columns() + 1, ...(ngDevMode ? [{ debugName: "gutters" }] : /* istanbul ignore next */ []));
1651
1662
  // Selection feature
1652
1663
  enableSelection = input(false, ...(ngDevMode ? [{ debugName: "enableSelection" }] : /* istanbul ignore next */ []));
1664
+ selectionModifier = input(null, ...(ngDevMode ? [{ debugName: "selectionModifier" }] : /* istanbul ignore next */ []));
1665
+ /**
1666
+ * Minimum pointer movement (in CSS pixels) between pointerdown and
1667
+ * pointerup required to emit `selectionComplete`. Below the threshold,
1668
+ * the gesture is treated as a click and no event is emitted.
1669
+ *
1670
+ * Default 4 — matches OS-native click-vs-drag thresholds. Set to 0 to
1671
+ * preserve the legacy behavior where every pointerup emits.
1672
+ */
1673
+ dragThreshold = input(4, ...(ngDevMode ? [{ debugName: "dragThreshold" }] : /* istanbul ignore next */ []));
1653
1674
  selectionComplete = output();
1654
1675
  // store signals - read-only
1655
1676
  cells = this.#store.cells;
@@ -1657,6 +1678,23 @@ class DashboardViewerComponent {
1657
1678
  isSelecting = signal(false, ...(ngDevMode ? [{ debugName: "isSelecting" }] : /* istanbul ignore next */ []));
1658
1679
  selectionStart = signal(null, ...(ngDevMode ? [{ debugName: "selectionStart" }] : /* istanbul ignore next */ []));
1659
1680
  selectionCurrent = signal(null, ...(ngDevMode ? [{ debugName: "selectionCurrent" }] : /* istanbul ignore next */ []));
1681
+ // Modifier-key gating state for selectionModifier
1682
+ #modifierHeld = signal(false, ...(ngDevMode ? [{ debugName: "#modifierHeld" }] : /* istanbul ignore next */ []));
1683
+ /**
1684
+ * Whether the selection overlay is currently interactive (intercepts
1685
+ * pointer events). Always false when `enableSelection` is false.
1686
+ * When `selectionModifier` is null, true whenever selection is enabled
1687
+ * (legacy behavior). Otherwise, true only while the configured modifier
1688
+ * is held or a drag is in progress (which keeps the overlay armed
1689
+ * across mid-drag modifier release).
1690
+ */
1691
+ armed = computed(() => {
1692
+ if (!this.enableSelection())
1693
+ return false;
1694
+ if (this.selectionModifier() === null)
1695
+ return true;
1696
+ return this.#modifierHeld() || this.isSelecting();
1697
+ }, ...(ngDevMode ? [{ debugName: "armed" }] : /* istanbul ignore next */ []));
1660
1698
  // Computed selection bounds (normalized)
1661
1699
  selectionBounds = computed(() => {
1662
1700
  const start = this.selectionStart();
@@ -1673,9 +1711,11 @@ class DashboardViewerComponent {
1673
1711
  // Generate array for template iteration
1674
1712
  rowNumbers = computed(() => Array.from({ length: this.rows() }, (_, i) => i + 1), ...(ngDevMode ? [{ debugName: "rowNumbers" }] : /* istanbul ignore next */ []));
1675
1713
  colNumbers = computed(() => Array.from({ length: this.columns() }, (_, i) => i + 1), ...(ngDevMode ? [{ debugName: "colNumbers" }] : /* istanbul ignore next */ []));
1676
- // Document-level event listeners (cleanup needed)
1677
- #mouseMoveListener;
1678
- #mouseUpListener;
1714
+ // Document-level pointer listeners (cleanup needed)
1715
+ #pointerMoveListener;
1716
+ #pointerUpListener;
1717
+ // Pointer position at the start of a drag, for dragThreshold checks
1718
+ #pointerDownPos = signal(null, ...(ngDevMode ? [{ debugName: "#pointerDownPos" }] : /* istanbul ignore next */ []));
1679
1719
  constructor() {
1680
1720
  // Sync grid configuration with store when inputs change
1681
1721
  effect(() => {
@@ -1693,6 +1733,38 @@ class DashboardViewerComponent {
1693
1733
  this.isSelecting.set(false);
1694
1734
  }
1695
1735
  });
1736
+ // Modifier-key tracking. Only registers document/window listeners when a
1737
+ // modifier is configured, so dashboards using the default (null) pay no
1738
+ // global keystroke cost. Cleans up listeners and resets state on
1739
+ // modifier change or component teardown.
1740
+ effect((onCleanup) => {
1741
+ const modifier = this.selectionModifier();
1742
+ if (modifier === null) {
1743
+ this.#modifierHeld.set(false);
1744
+ return;
1745
+ }
1746
+ const keyName = MODIFIER_KEY[modifier];
1747
+ const offKeyDown = this.#renderer.listen('document', 'keydown', (event) => {
1748
+ if (event.key === keyName) {
1749
+ this.#modifierHeld.set(true);
1750
+ }
1751
+ });
1752
+ const offKeyUp = this.#renderer.listen('document', 'keyup', (event) => {
1753
+ if (event.key === keyName) {
1754
+ this.#modifierHeld.set(false);
1755
+ }
1756
+ });
1757
+ // Cover focus-loss cases where keyup may never fire (Alt-Tab, etc.)
1758
+ const offBlur = this.#renderer.listen('window', 'blur', () => {
1759
+ this.#modifierHeld.set(false);
1760
+ });
1761
+ onCleanup(() => {
1762
+ offKeyDown();
1763
+ offKeyUp();
1764
+ offBlur();
1765
+ this.#modifierHeld.set(false);
1766
+ });
1767
+ });
1696
1768
  }
1697
1769
  // Selection methods
1698
1770
  /**
@@ -1708,78 +1780,105 @@ class DashboardViewerComponent {
1708
1780
  col <= bounds.endCol);
1709
1781
  }
1710
1782
  /**
1711
- * Handle mouse down on ghost cell to start selection
1783
+ * Handle pointer down on a ghost cell to start a selection. Uses
1784
+ * PointerEvent so mouse / touch / pen all work uniformly.
1712
1785
  */
1713
- onGhostCellMouseDown(event, row, col) {
1714
- if (!this.enableSelection())
1786
+ onGhostCellPointerDown(event, row, col) {
1787
+ if (!this.armed())
1788
+ return;
1789
+ if (event.pointerType === 'mouse' && event.button !== 0)
1715
1790
  return;
1716
- if (event.button !== 0)
1717
- return; // Only left button
1718
1791
  event.preventDefault();
1719
1792
  event.stopPropagation();
1720
1793
  this.isSelecting.set(true);
1721
1794
  this.selectionStart.set({ row, col });
1722
1795
  this.selectionCurrent.set({ row, col });
1723
- // Add document-level listeners for drag
1724
- this.#mouseMoveListener = this.#renderer.listen('document', 'mousemove', () => this.onDocumentMouseMove());
1725
- this.#mouseUpListener = this.#renderer.listen('document', 'mouseup', () => this.onDocumentMouseUp());
1726
- // Register cleanup
1796
+ this.#pointerDownPos.set({ x: event.clientX, y: event.clientY });
1797
+ const target = event.target;
1798
+ if (target instanceof Element &&
1799
+ typeof target.setPointerCapture === 'function') {
1800
+ try {
1801
+ target.setPointerCapture(event.pointerId);
1802
+ }
1803
+ catch {
1804
+ // Capture may be rejected for synthetic / invalid pointer ids;
1805
+ // document-level listeners still receive bubbled events.
1806
+ }
1807
+ }
1808
+ this.#pointerMoveListener = this.#renderer.listen('document', 'pointermove', (e) => this.onDocumentPointerMove(e));
1809
+ this.#pointerUpListener = this.#renderer.listen('document', 'pointerup', (e) => this.onDocumentPointerUp(e));
1727
1810
  this.#destroyRef.onDestroy(() => {
1728
1811
  this.cleanupListeners();
1729
1812
  });
1730
1813
  }
1731
1814
  /**
1732
- * Handle mouse enter on ghost cell during selection
1815
+ * Track the pointer across cell boundaries during a drag.
1816
+ *
1817
+ * Replaces the old per-cell `mouseenter` handler. Necessary because
1818
+ * pointer capture and (on touch) coalesced events make boundary
1819
+ * crossings unreliable when relying on per-element enter events.
1733
1820
  */
1734
- onGhostCellMouseEnter(row, col) {
1821
+ onDocumentPointerMove(event) {
1735
1822
  if (!this.isSelecting())
1736
1823
  return;
1737
- this.selectionCurrent.set({ row, col });
1738
- }
1739
- /**
1740
- * Handle document mouse move during selection
1741
- */
1742
- onDocumentMouseMove() {
1743
- if (!this.isSelecting())
1824
+ const el = document.elementFromPoint(event.clientX, event.clientY);
1825
+ const cell = el?.closest('.selection-ghost-cell');
1826
+ if (!cell)
1827
+ return;
1828
+ const row = Number(cell.dataset['row']);
1829
+ const col = Number(cell.dataset['col']);
1830
+ if (!Number.isFinite(row) || !Number.isFinite(col))
1744
1831
  return;
1745
- // The actual selection update is handled by onGhostCellMouseEnter
1746
- // This just ensures we capture the event
1832
+ // Skip updates while still inside the same cell — otherwise pointermove
1833
+ // (60–120 Hz) would re-emit a fresh object reference every frame and
1834
+ // trigger downstream rerenders across the whole overlay grid.
1835
+ const cur = this.selectionCurrent();
1836
+ if (cur && cur.row === row && cur.col === col)
1837
+ return;
1838
+ this.selectionCurrent.set({ row, col });
1747
1839
  }
1748
1840
  /**
1749
- * Handle document mouse up to complete selection
1841
+ * Complete a selection on pointerup. Emits `selectionComplete` only when
1842
+ * total pointer movement meets `dragThreshold` — sub-threshold gestures
1843
+ * are treated as clicks and discarded.
1750
1844
  */
1751
- onDocumentMouseUp() {
1845
+ onDocumentPointerUp(event) {
1752
1846
  if (!this.isSelecting())
1753
1847
  return;
1754
1848
  this.isSelecting.set(false);
1755
- // Emit selection event
1756
- const bounds = this.selectionBounds();
1757
- if (bounds) {
1758
- this.selectionComplete.emit({
1759
- topLeft: { row: bounds.startRow, col: bounds.startCol },
1760
- bottomRight: { row: bounds.endRow, col: bounds.endCol },
1761
- });
1849
+ const start = this.#pointerDownPos();
1850
+ const moved = start === null ||
1851
+ Math.hypot(event.clientX - start.x, event.clientY - start.y) >=
1852
+ this.dragThreshold();
1853
+ if (moved) {
1854
+ const bounds = this.selectionBounds();
1855
+ if (bounds) {
1856
+ this.selectionComplete.emit({
1857
+ topLeft: { row: bounds.startRow, col: bounds.startCol },
1858
+ bottomRight: { row: bounds.endRow, col: bounds.endCol },
1859
+ });
1860
+ }
1762
1861
  }
1763
- // Clean up listeners
1862
+ this.#pointerDownPos.set(null);
1764
1863
  this.cleanupListeners();
1765
- // Don't clear selection - let the parent control when to clear
1766
- // Selection remains visible until enableSelection becomes false
1864
+ // Don't clear selection - let the parent control when to clear.
1865
+ // Selection remains visible until enableSelection becomes false.
1767
1866
  }
1768
1867
  /**
1769
1868
  * Clean up document-level event listeners
1770
1869
  */
1771
1870
  cleanupListeners() {
1772
- if (this.#mouseMoveListener) {
1773
- this.#mouseMoveListener();
1774
- this.#mouseMoveListener = undefined;
1871
+ if (this.#pointerMoveListener) {
1872
+ this.#pointerMoveListener();
1873
+ this.#pointerMoveListener = undefined;
1775
1874
  }
1776
- if (this.#mouseUpListener) {
1777
- this.#mouseUpListener();
1778
- this.#mouseUpListener = undefined;
1875
+ if (this.#pointerUpListener) {
1876
+ this.#pointerUpListener();
1877
+ this.#pointerUpListener = undefined;
1779
1878
  }
1780
1879
  }
1781
1880
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: DashboardViewerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1782
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.10", type: DashboardViewerComponent, isStandalone: true, selector: "ngx-dashboard-viewer", inputs: { rows: { classPropertyName: "rows", publicName: "rows", isSignal: true, isRequired: true, transformFunction: null }, columns: { classPropertyName: "columns", publicName: "columns", isSignal: true, isRequired: true, transformFunction: null }, gutterSize: { classPropertyName: "gutterSize", publicName: "gutterSize", isSignal: true, isRequired: false, transformFunction: null }, enableSelection: { classPropertyName: "enableSelection", publicName: "enableSelection", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { selectionComplete: "selectionComplete" }, host: { properties: { "style.--rows": "rows()", "style.--columns": "columns()", "style.--gutter-size": "gutterSize()", "style.--gutters": "gutters()" } }, viewQueries: [{ propertyName: "cellComponents", predicate: CellComponent, descendants: true, isSignal: true }, { propertyName: "gridElement", first: true, predicate: ["gridElement"], descendants: true, isSignal: true }], ngImport: i0, template: "<!-- Dashboard viewer - read-only grid -->\n<div class=\"grid top-grid\" #gridElement>\n @for (cell of cells(); track cell.widgetId) {\n <lib-cell\n class=\"grid-cell\"\n [widgetId]=\"cell.widgetId\"\n [cellId]=\"cell.cellId\"\n [isEditMode]=\"false\"\n [draggable]=\"false\"\n [row]=\"cell.row\"\n [column]=\"cell.col\"\n [rowSpan]=\"cell.rowSpan\"\n [colSpan]=\"cell.colSpan\"\n [flat]=\"cell.flat\"\n [widgetFactory]=\"cell.widgetFactory\"\n [widgetState]=\"cell.widgetState\"\n >\n </lib-cell>\n }\n</div>\n\n<!-- Selection overlay grid - mirror of main grid for cell selection -->\n@if (enableSelection()) {\n <div class=\"selection-overlay-grid\">\n @for (row of rowNumbers(); track row) {\n @for (col of colNumbers(); track col) {\n <div\n class=\"selection-ghost-cell\"\n [class.selected]=\"isCellSelected(row, col)\"\n [class.selecting]=\"isSelecting()\"\n [style.grid-row]=\"row\"\n [style.grid-column]=\"col\"\n (mousedown)=\"onGhostCellMouseDown($event, row, col)\"\n (mouseenter)=\"onGhostCellMouseEnter(row, col)\"\n ></div>\n }\n }\n </div>\n}\n", styles: ["@charset \"UTF-8\";:host{--cell-size: calc( 100cqi / var(--columns) - var(--gutter-size) * var(--gutters) / var(--columns) );--tile-size: calc(var(--cell-size) + var(--gutter-size));--tile-offset: calc( var(--gutter-size) + var(--cell-size) + var(--gutter-size) / 2 );display:block;container-type:inline-size;box-sizing:border-box;aspect-ratio:var(--columns)/var(--rows);width:100%;height:auto;position:relative;background-color:var(--mat-sys-surface-container)}.grid{display:grid;gap:var(--gutter-size);padding:var(--gutter-size);width:100%;height:100%;box-sizing:border-box;grid-template-columns:repeat(var(--columns),var(--cell-size));grid-template-rows:repeat(var(--rows),var(--cell-size))}.grid-cell{pointer-events:none}.grid-cell:not(.flat){pointer-events:auto;cursor:default}.grid-cell:not(.flat) .content-area{pointer-events:none}.top-grid{z-index:2;pointer-events:none}.selection-overlay-grid{position:absolute;top:0;left:0;width:100%;height:100%;display:grid;gap:var(--gutter-size);padding:var(--gutter-size);grid-template-columns:repeat(var(--columns),var(--cell-size));grid-template-rows:repeat(var(--rows),var(--cell-size));z-index:5;pointer-events:auto;-webkit-user-select:none;user-select:none}.selection-ghost-cell{cursor:crosshair;transition:background-color .1s ease,border-radius .1s ease;border-radius:2px}.selection-ghost-cell:hover:not(.selecting){background-color:var(--mat-sys-primary);opacity:.08}.selection-ghost-cell.selected{background-color:var(--mat-sys-primary);opacity:.25;border-radius:4px}.selection-ghost-cell.selecting{cursor:crosshair}\n"], dependencies: [{ kind: "component", type: CellComponent, selector: "lib-cell", inputs: ["widgetId", "cellId", "widgetFactory", "widgetState", "isEditMode", "flat", "row", "column", "rowSpan", "colSpan", "draggable"], outputs: ["rowChange", "columnChange", "dragStart", "dragEnd", "edit", "delete", "settings", "resizeStart", "resizeMove", "resizeEnd"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1881
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.10", type: DashboardViewerComponent, isStandalone: true, selector: "ngx-dashboard-viewer", inputs: { rows: { classPropertyName: "rows", publicName: "rows", isSignal: true, isRequired: true, transformFunction: null }, columns: { classPropertyName: "columns", publicName: "columns", isSignal: true, isRequired: true, transformFunction: null }, gutterSize: { classPropertyName: "gutterSize", publicName: "gutterSize", isSignal: true, isRequired: false, transformFunction: null }, enableSelection: { classPropertyName: "enableSelection", publicName: "enableSelection", isSignal: true, isRequired: false, transformFunction: null }, selectionModifier: { classPropertyName: "selectionModifier", publicName: "selectionModifier", isSignal: true, isRequired: false, transformFunction: null }, dragThreshold: { classPropertyName: "dragThreshold", publicName: "dragThreshold", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { selectionComplete: "selectionComplete" }, host: { properties: { "style.--rows": "rows()", "style.--columns": "columns()", "style.--gutter-size": "gutterSize()", "style.--gutters": "gutters()" } }, viewQueries: [{ propertyName: "cellComponents", predicate: CellComponent, descendants: true, isSignal: true }, { propertyName: "gridElement", first: true, predicate: ["gridElement"], descendants: true, isSignal: true }], ngImport: i0, template: "<!-- Dashboard viewer - read-only grid -->\n<div class=\"grid top-grid\" #gridElement>\n @for (cell of cells(); track cell.widgetId) {\n <lib-cell\n class=\"grid-cell\"\n [widgetId]=\"cell.widgetId\"\n [cellId]=\"cell.cellId\"\n [isEditMode]=\"false\"\n [draggable]=\"false\"\n [row]=\"cell.row\"\n [column]=\"cell.col\"\n [rowSpan]=\"cell.rowSpan\"\n [colSpan]=\"cell.colSpan\"\n [flat]=\"cell.flat\"\n [widgetFactory]=\"cell.widgetFactory\"\n [widgetState]=\"cell.widgetState\"\n >\n </lib-cell>\n }\n</div>\n\n<!-- Selection overlay grid - mirror of main grid for cell selection -->\n@if (enableSelection()) {\n <div class=\"selection-overlay-grid\" [class.armed]=\"armed()\">\n @for (row of rowNumbers(); track row) {\n @for (col of colNumbers(); track col) {\n <div\n class=\"selection-ghost-cell\"\n [class.selected]=\"isCellSelected(row, col)\"\n [class.selecting]=\"isSelecting()\"\n [style.grid-row]=\"row\"\n [style.grid-column]=\"col\"\n [attr.data-row]=\"row\"\n [attr.data-col]=\"col\"\n (pointerdown)=\"onGhostCellPointerDown($event, row, col)\"\n ></div>\n }\n }\n </div>\n}\n", styles: ["@charset \"UTF-8\";:host{--cell-size: calc( 100cqi / var(--columns) - var(--gutter-size) * var(--gutters) / var(--columns) );--tile-size: calc(var(--cell-size) + var(--gutter-size));--tile-offset: calc( var(--gutter-size) + var(--cell-size) + var(--gutter-size) / 2 );display:block;container-type:inline-size;box-sizing:border-box;aspect-ratio:var(--columns)/var(--rows);width:100%;height:auto;position:relative;background-color:var(--mat-sys-surface-container)}.grid{display:grid;gap:var(--gutter-size);padding:var(--gutter-size);width:100%;height:100%;box-sizing:border-box;grid-template-columns:repeat(var(--columns),var(--cell-size));grid-template-rows:repeat(var(--rows),var(--cell-size))}.grid-cell{pointer-events:none}.grid-cell:not(.flat){pointer-events:auto;cursor:default}.grid-cell:not(.flat) .content-area{pointer-events:none}.top-grid{z-index:2;pointer-events:none}.selection-overlay-grid{position:absolute;top:0;left:0;width:100%;height:100%;display:grid;gap:var(--gutter-size);padding:var(--gutter-size);grid-template-columns:repeat(var(--columns),var(--cell-size));grid-template-rows:repeat(var(--rows),var(--cell-size));z-index:5;pointer-events:none;-webkit-user-select:none;user-select:none}.selection-overlay-grid.armed{pointer-events:auto}.selection-ghost-cell{transition:background-color .1s ease,border-radius .1s ease;border-radius:2px}.selection-ghost-cell.selected{background-color:var(--mat-sys-primary);opacity:.25;border-radius:4px}.selection-overlay-grid.armed .selection-ghost-cell{cursor:crosshair}.selection-overlay-grid.armed .selection-ghost-cell:hover:not(.selecting){background-color:var(--mat-sys-primary);opacity:.08}.selection-overlay-grid.armed .selection-ghost-cell.selecting{cursor:crosshair}\n"], dependencies: [{ kind: "component", type: CellComponent, selector: "lib-cell", inputs: ["widgetId", "cellId", "widgetFactory", "widgetState", "isEditMode", "flat", "row", "column", "rowSpan", "colSpan", "draggable"], outputs: ["rowChange", "columnChange", "dragStart", "dragEnd", "edit", "delete", "settings", "resizeStart", "resizeMove", "resizeEnd"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1783
1882
  }
1784
1883
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: DashboardViewerComponent, decorators: [{
1785
1884
  type: Component,
@@ -1788,8 +1887,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.10", ngImpo
1788
1887
  '[style.--columns]': 'columns()',
1789
1888
  '[style.--gutter-size]': 'gutterSize()',
1790
1889
  '[style.--gutters]': 'gutters()',
1791
- }, template: "<!-- Dashboard viewer - read-only grid -->\n<div class=\"grid top-grid\" #gridElement>\n @for (cell of cells(); track cell.widgetId) {\n <lib-cell\n class=\"grid-cell\"\n [widgetId]=\"cell.widgetId\"\n [cellId]=\"cell.cellId\"\n [isEditMode]=\"false\"\n [draggable]=\"false\"\n [row]=\"cell.row\"\n [column]=\"cell.col\"\n [rowSpan]=\"cell.rowSpan\"\n [colSpan]=\"cell.colSpan\"\n [flat]=\"cell.flat\"\n [widgetFactory]=\"cell.widgetFactory\"\n [widgetState]=\"cell.widgetState\"\n >\n </lib-cell>\n }\n</div>\n\n<!-- Selection overlay grid - mirror of main grid for cell selection -->\n@if (enableSelection()) {\n <div class=\"selection-overlay-grid\">\n @for (row of rowNumbers(); track row) {\n @for (col of colNumbers(); track col) {\n <div\n class=\"selection-ghost-cell\"\n [class.selected]=\"isCellSelected(row, col)\"\n [class.selecting]=\"isSelecting()\"\n [style.grid-row]=\"row\"\n [style.grid-column]=\"col\"\n (mousedown)=\"onGhostCellMouseDown($event, row, col)\"\n (mouseenter)=\"onGhostCellMouseEnter(row, col)\"\n ></div>\n }\n }\n </div>\n}\n", styles: ["@charset \"UTF-8\";:host{--cell-size: calc( 100cqi / var(--columns) - var(--gutter-size) * var(--gutters) / var(--columns) );--tile-size: calc(var(--cell-size) + var(--gutter-size));--tile-offset: calc( var(--gutter-size) + var(--cell-size) + var(--gutter-size) / 2 );display:block;container-type:inline-size;box-sizing:border-box;aspect-ratio:var(--columns)/var(--rows);width:100%;height:auto;position:relative;background-color:var(--mat-sys-surface-container)}.grid{display:grid;gap:var(--gutter-size);padding:var(--gutter-size);width:100%;height:100%;box-sizing:border-box;grid-template-columns:repeat(var(--columns),var(--cell-size));grid-template-rows:repeat(var(--rows),var(--cell-size))}.grid-cell{pointer-events:none}.grid-cell:not(.flat){pointer-events:auto;cursor:default}.grid-cell:not(.flat) .content-area{pointer-events:none}.top-grid{z-index:2;pointer-events:none}.selection-overlay-grid{position:absolute;top:0;left:0;width:100%;height:100%;display:grid;gap:var(--gutter-size);padding:var(--gutter-size);grid-template-columns:repeat(var(--columns),var(--cell-size));grid-template-rows:repeat(var(--rows),var(--cell-size));z-index:5;pointer-events:auto;-webkit-user-select:none;user-select:none}.selection-ghost-cell{cursor:crosshair;transition:background-color .1s ease,border-radius .1s ease;border-radius:2px}.selection-ghost-cell:hover:not(.selecting){background-color:var(--mat-sys-primary);opacity:.08}.selection-ghost-cell.selected{background-color:var(--mat-sys-primary);opacity:.25;border-radius:4px}.selection-ghost-cell.selecting{cursor:crosshair}\n"] }]
1792
- }], ctorParameters: () => [], propDecorators: { cellComponents: [{ type: i0.ViewChildren, args: [i0.forwardRef(() => CellComponent), { isSignal: true }] }], gridElement: [{ type: i0.ViewChild, args: ['gridElement', { isSignal: true }] }], rows: [{ type: i0.Input, args: [{ isSignal: true, alias: "rows", required: true }] }], columns: [{ type: i0.Input, args: [{ isSignal: true, alias: "columns", required: true }] }], gutterSize: [{ type: i0.Input, args: [{ isSignal: true, alias: "gutterSize", required: false }] }], enableSelection: [{ type: i0.Input, args: [{ isSignal: true, alias: "enableSelection", required: false }] }], selectionComplete: [{ type: i0.Output, args: ["selectionComplete"] }] } });
1890
+ }, template: "<!-- Dashboard viewer - read-only grid -->\n<div class=\"grid top-grid\" #gridElement>\n @for (cell of cells(); track cell.widgetId) {\n <lib-cell\n class=\"grid-cell\"\n [widgetId]=\"cell.widgetId\"\n [cellId]=\"cell.cellId\"\n [isEditMode]=\"false\"\n [draggable]=\"false\"\n [row]=\"cell.row\"\n [column]=\"cell.col\"\n [rowSpan]=\"cell.rowSpan\"\n [colSpan]=\"cell.colSpan\"\n [flat]=\"cell.flat\"\n [widgetFactory]=\"cell.widgetFactory\"\n [widgetState]=\"cell.widgetState\"\n >\n </lib-cell>\n }\n</div>\n\n<!-- Selection overlay grid - mirror of main grid for cell selection -->\n@if (enableSelection()) {\n <div class=\"selection-overlay-grid\" [class.armed]=\"armed()\">\n @for (row of rowNumbers(); track row) {\n @for (col of colNumbers(); track col) {\n <div\n class=\"selection-ghost-cell\"\n [class.selected]=\"isCellSelected(row, col)\"\n [class.selecting]=\"isSelecting()\"\n [style.grid-row]=\"row\"\n [style.grid-column]=\"col\"\n [attr.data-row]=\"row\"\n [attr.data-col]=\"col\"\n (pointerdown)=\"onGhostCellPointerDown($event, row, col)\"\n ></div>\n }\n }\n </div>\n}\n", styles: ["@charset \"UTF-8\";:host{--cell-size: calc( 100cqi / var(--columns) - var(--gutter-size) * var(--gutters) / var(--columns) );--tile-size: calc(var(--cell-size) + var(--gutter-size));--tile-offset: calc( var(--gutter-size) + var(--cell-size) + var(--gutter-size) / 2 );display:block;container-type:inline-size;box-sizing:border-box;aspect-ratio:var(--columns)/var(--rows);width:100%;height:auto;position:relative;background-color:var(--mat-sys-surface-container)}.grid{display:grid;gap:var(--gutter-size);padding:var(--gutter-size);width:100%;height:100%;box-sizing:border-box;grid-template-columns:repeat(var(--columns),var(--cell-size));grid-template-rows:repeat(var(--rows),var(--cell-size))}.grid-cell{pointer-events:none}.grid-cell:not(.flat){pointer-events:auto;cursor:default}.grid-cell:not(.flat) .content-area{pointer-events:none}.top-grid{z-index:2;pointer-events:none}.selection-overlay-grid{position:absolute;top:0;left:0;width:100%;height:100%;display:grid;gap:var(--gutter-size);padding:var(--gutter-size);grid-template-columns:repeat(var(--columns),var(--cell-size));grid-template-rows:repeat(var(--rows),var(--cell-size));z-index:5;pointer-events:none;-webkit-user-select:none;user-select:none}.selection-overlay-grid.armed{pointer-events:auto}.selection-ghost-cell{transition:background-color .1s ease,border-radius .1s ease;border-radius:2px}.selection-ghost-cell.selected{background-color:var(--mat-sys-primary);opacity:.25;border-radius:4px}.selection-overlay-grid.armed .selection-ghost-cell{cursor:crosshair}.selection-overlay-grid.armed .selection-ghost-cell:hover:not(.selecting){background-color:var(--mat-sys-primary);opacity:.08}.selection-overlay-grid.armed .selection-ghost-cell.selecting{cursor:crosshair}\n"] }]
1891
+ }], ctorParameters: () => [], propDecorators: { cellComponents: [{ type: i0.ViewChildren, args: [i0.forwardRef(() => CellComponent), { isSignal: true }] }], gridElement: [{ type: i0.ViewChild, args: ['gridElement', { isSignal: true }] }], rows: [{ type: i0.Input, args: [{ isSignal: true, alias: "rows", required: true }] }], columns: [{ type: i0.Input, args: [{ isSignal: true, alias: "columns", required: true }] }], gutterSize: [{ type: i0.Input, args: [{ isSignal: true, alias: "gutterSize", required: false }] }], enableSelection: [{ type: i0.Input, args: [{ isSignal: true, alias: "enableSelection", required: false }] }], selectionModifier: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectionModifier", required: false }] }], dragThreshold: [{ type: i0.Input, args: [{ isSignal: true, alias: "dragThreshold", required: false }] }], selectionComplete: [{ type: i0.Output, args: ["selectionComplete"] }] } });
1793
1892
 
1794
1893
  class CellContextMenuComponent {
1795
1894
  menuTrigger = viewChild.required('menuTrigger', { read: MatMenuTrigger });
@@ -2942,6 +3041,8 @@ class DashboardComponent {
2942
3041
  editMode = input(false, ...(ngDevMode ? [{ debugName: "editMode" }] : /* istanbul ignore next */ []));
2943
3042
  reservedSpace = input(...(ngDevMode ? [undefined, { debugName: "reservedSpace" }] : /* istanbul ignore next */ []));
2944
3043
  enableSelection = input(false, ...(ngDevMode ? [{ debugName: "enableSelection" }] : /* istanbul ignore next */ []));
3044
+ selectionModifier = input(null, ...(ngDevMode ? [{ debugName: "selectionModifier" }] : /* istanbul ignore next */ []));
3045
+ dragThreshold = input(4, ...(ngDevMode ? [{ debugName: "dragThreshold" }] : /* istanbul ignore next */ []));
2945
3046
  // Component outputs
2946
3047
  selectionComplete = output();
2947
3048
  // Store signals - shared by both child components
@@ -3079,7 +3180,7 @@ class DashboardComponent {
3079
3180
  }
3080
3181
  }
3081
3182
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: DashboardComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
3082
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.10", type: DashboardComponent, isStandalone: true, selector: "ngx-dashboard", inputs: { dashboardData: { classPropertyName: "dashboardData", publicName: "dashboardData", isSignal: true, isRequired: true, transformFunction: null }, editMode: { classPropertyName: "editMode", publicName: "editMode", isSignal: true, isRequired: false, transformFunction: null }, reservedSpace: { classPropertyName: "reservedSpace", publicName: "reservedSpace", isSignal: true, isRequired: false, transformFunction: null }, enableSelection: { classPropertyName: "enableSelection", publicName: "enableSelection", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { selectionComplete: "selectionComplete" }, host: { properties: { "style.--rows": "store.rows()", "style.--columns": "store.columns()", "style.--gutter-size": "store.gutterSize()", "style.--gutters": "store.columns() + 1", "class.is-edit-mode": "editMode()", "style.max-width.px": "viewport.constraints().maxWidth", "style.max-height.px": "viewport.constraints().maxHeight" } }, providers: [DashboardStore, DashboardViewportService], viewQueries: [{ propertyName: "dashboardEditor", first: true, predicate: DashboardEditorComponent, descendants: true, isSignal: true }, { propertyName: "dashboardViewer", first: true, predicate: DashboardViewerComponent, descendants: true, isSignal: true }], usesOnChanges: true, ngImport: i0, template: "<!-- dashboard.component.html -->\n<div class=\"grid-container\">\n @if (editMode()) {\n <!-- Full editor with drag & drop capabilities -->\n <ngx-dashboard-editor\n [rows]=\"store.rows()\"\n [columns]=\"store.columns()\"\n [gutterSize]=\"store.gutterSize()\"\n ></ngx-dashboard-editor>\n } @else {\n <!-- Read-only viewer -->\n <ngx-dashboard-viewer\n [rows]=\"store.rows()\"\n [columns]=\"store.columns()\"\n [gutterSize]=\"store.gutterSize()\"\n [enableSelection]=\"enableSelection()\"\n (selectionComplete)=\"selectionComplete.emit($event)\"\n ></ngx-dashboard-viewer>\n }\n</div>\n", styles: [":host{display:block;container-type:inline-size;box-sizing:border-box;aspect-ratio:var(--columns)/var(--rows);width:100%;height:auto}.grid-container{position:relative;width:100%;height:100%}\n"], dependencies: [{ kind: "component", type: DashboardViewerComponent, selector: "ngx-dashboard-viewer", inputs: ["rows", "columns", "gutterSize", "enableSelection"], outputs: ["selectionComplete"] }, { kind: "component", type: DashboardEditorComponent, selector: "ngx-dashboard-editor", inputs: ["rows", "columns", "gutterSize"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
3183
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.10", type: DashboardComponent, isStandalone: true, selector: "ngx-dashboard", inputs: { dashboardData: { classPropertyName: "dashboardData", publicName: "dashboardData", isSignal: true, isRequired: true, transformFunction: null }, editMode: { classPropertyName: "editMode", publicName: "editMode", isSignal: true, isRequired: false, transformFunction: null }, reservedSpace: { classPropertyName: "reservedSpace", publicName: "reservedSpace", isSignal: true, isRequired: false, transformFunction: null }, enableSelection: { classPropertyName: "enableSelection", publicName: "enableSelection", isSignal: true, isRequired: false, transformFunction: null }, selectionModifier: { classPropertyName: "selectionModifier", publicName: "selectionModifier", isSignal: true, isRequired: false, transformFunction: null }, dragThreshold: { classPropertyName: "dragThreshold", publicName: "dragThreshold", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { selectionComplete: "selectionComplete" }, host: { properties: { "style.--rows": "store.rows()", "style.--columns": "store.columns()", "style.--gutter-size": "store.gutterSize()", "style.--gutters": "store.columns() + 1", "class.is-edit-mode": "editMode()", "style.max-width.px": "viewport.constraints().maxWidth", "style.max-height.px": "viewport.constraints().maxHeight" } }, providers: [DashboardStore, DashboardViewportService], viewQueries: [{ propertyName: "dashboardEditor", first: true, predicate: DashboardEditorComponent, descendants: true, isSignal: true }, { propertyName: "dashboardViewer", first: true, predicate: DashboardViewerComponent, descendants: true, isSignal: true }], usesOnChanges: true, ngImport: i0, template: "<!-- dashboard.component.html -->\n<div class=\"grid-container\">\n @if (editMode()) {\n <!-- Full editor with drag & drop capabilities -->\n <ngx-dashboard-editor\n [rows]=\"store.rows()\"\n [columns]=\"store.columns()\"\n [gutterSize]=\"store.gutterSize()\"\n ></ngx-dashboard-editor>\n } @else {\n <!-- Read-only viewer -->\n <ngx-dashboard-viewer\n [rows]=\"store.rows()\"\n [columns]=\"store.columns()\"\n [gutterSize]=\"store.gutterSize()\"\n [enableSelection]=\"enableSelection()\"\n [selectionModifier]=\"selectionModifier()\"\n [dragThreshold]=\"dragThreshold()\"\n (selectionComplete)=\"selectionComplete.emit($event)\"\n ></ngx-dashboard-viewer>\n }\n</div>\n", styles: [":host{display:block;container-type:inline-size;box-sizing:border-box;aspect-ratio:var(--columns)/var(--rows);width:100%;height:auto}.grid-container{position:relative;width:100%;height:100%}\n"], dependencies: [{ kind: "component", type: DashboardViewerComponent, selector: "ngx-dashboard-viewer", inputs: ["rows", "columns", "gutterSize", "enableSelection", "selectionModifier", "dragThreshold"], outputs: ["selectionComplete"] }, { kind: "component", type: DashboardEditorComponent, selector: "ngx-dashboard-editor", inputs: ["rows", "columns", "gutterSize"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
3083
3184
  }
3084
3185
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: DashboardComponent, decorators: [{
3085
3186
  type: Component,
@@ -3091,8 +3192,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.10", ngImpo
3091
3192
  '[class.is-edit-mode]': 'editMode()',
3092
3193
  '[style.max-width.px]': 'viewport.constraints().maxWidth',
3093
3194
  '[style.max-height.px]': 'viewport.constraints().maxHeight',
3094
- }, template: "<!-- dashboard.component.html -->\n<div class=\"grid-container\">\n @if (editMode()) {\n <!-- Full editor with drag & drop capabilities -->\n <ngx-dashboard-editor\n [rows]=\"store.rows()\"\n [columns]=\"store.columns()\"\n [gutterSize]=\"store.gutterSize()\"\n ></ngx-dashboard-editor>\n } @else {\n <!-- Read-only viewer -->\n <ngx-dashboard-viewer\n [rows]=\"store.rows()\"\n [columns]=\"store.columns()\"\n [gutterSize]=\"store.gutterSize()\"\n [enableSelection]=\"enableSelection()\"\n (selectionComplete)=\"selectionComplete.emit($event)\"\n ></ngx-dashboard-viewer>\n }\n</div>\n", styles: [":host{display:block;container-type:inline-size;box-sizing:border-box;aspect-ratio:var(--columns)/var(--rows);width:100%;height:auto}.grid-container{position:relative;width:100%;height:100%}\n"] }]
3095
- }], ctorParameters: () => [], propDecorators: { dashboardData: [{ type: i0.Input, args: [{ isSignal: true, alias: "dashboardData", required: true }] }], editMode: [{ type: i0.Input, args: [{ isSignal: true, alias: "editMode", required: false }] }], reservedSpace: [{ type: i0.Input, args: [{ isSignal: true, alias: "reservedSpace", required: false }] }], enableSelection: [{ type: i0.Input, args: [{ isSignal: true, alias: "enableSelection", required: false }] }], selectionComplete: [{ type: i0.Output, args: ["selectionComplete"] }], dashboardEditor: [{ type: i0.ViewChild, args: [i0.forwardRef(() => DashboardEditorComponent), { isSignal: true }] }], dashboardViewer: [{ type: i0.ViewChild, args: [i0.forwardRef(() => DashboardViewerComponent), { isSignal: true }] }] } });
3195
+ }, template: "<!-- dashboard.component.html -->\n<div class=\"grid-container\">\n @if (editMode()) {\n <!-- Full editor with drag & drop capabilities -->\n <ngx-dashboard-editor\n [rows]=\"store.rows()\"\n [columns]=\"store.columns()\"\n [gutterSize]=\"store.gutterSize()\"\n ></ngx-dashboard-editor>\n } @else {\n <!-- Read-only viewer -->\n <ngx-dashboard-viewer\n [rows]=\"store.rows()\"\n [columns]=\"store.columns()\"\n [gutterSize]=\"store.gutterSize()\"\n [enableSelection]=\"enableSelection()\"\n [selectionModifier]=\"selectionModifier()\"\n [dragThreshold]=\"dragThreshold()\"\n (selectionComplete)=\"selectionComplete.emit($event)\"\n ></ngx-dashboard-viewer>\n }\n</div>\n", styles: [":host{display:block;container-type:inline-size;box-sizing:border-box;aspect-ratio:var(--columns)/var(--rows);width:100%;height:auto}.grid-container{position:relative;width:100%;height:100%}\n"] }]
3196
+ }], ctorParameters: () => [], propDecorators: { dashboardData: [{ type: i0.Input, args: [{ isSignal: true, alias: "dashboardData", required: true }] }], editMode: [{ type: i0.Input, args: [{ isSignal: true, alias: "editMode", required: false }] }], reservedSpace: [{ type: i0.Input, args: [{ isSignal: true, alias: "reservedSpace", required: false }] }], enableSelection: [{ type: i0.Input, args: [{ isSignal: true, alias: "enableSelection", required: false }] }], selectionModifier: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectionModifier", required: false }] }], dragThreshold: [{ type: i0.Input, args: [{ isSignal: true, alias: "dragThreshold", required: false }] }], selectionComplete: [{ type: i0.Output, args: ["selectionComplete"] }], dashboardEditor: [{ type: i0.ViewChild, args: [i0.forwardRef(() => DashboardEditorComponent), { isSignal: true }] }], dashboardViewer: [{ type: i0.ViewChild, args: [i0.forwardRef(() => DashboardViewerComponent), { isSignal: true }] }] } });
3096
3197
 
3097
3198
  // widget-list.component.ts
3098
3199
  class WidgetListComponent {