@dragonworks/ngx-dashboard 21.2.2 → 22.1.0

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.2.2';
26
+ const NGX_DASHBOARD_VERSION = '22.1.0';
27
27
 
28
28
  /**
29
29
  * Maximum number of columns supported by the grid.
@@ -232,8 +232,10 @@ class UnknownWidgetComponent {
232
232
  };
233
233
  state = signal({
234
234
  originalWidgetTypeid: 'unknown',
235
- }, ...(ngDevMode ? [{ debugName: "state" }] : /* istanbul ignore next */ []));
236
- tooltipText = computed(() => `${this.state().originalWidgetTypeid}`, ...(ngDevMode ? [{ debugName: "tooltipText" }] : /* istanbul ignore next */ []));
235
+ }, /* @ts-ignore */
236
+ ...(ngDevMode ? [{ debugName: "state" }] : /* istanbul ignore next */ []));
237
+ tooltipText = computed(() => `${this.state().originalWidgetTypeid}`, /* @ts-ignore */
238
+ ...(ngDevMode ? [{ debugName: "tooltipText" }] : /* istanbul ignore next */ []));
237
239
  dashboardSetState(state) {
238
240
  if (state && typeof state === 'object' && 'originalWidgetTypeid' in state) {
239
241
  this.state.set(state);
@@ -242,14 +244,14 @@ class UnknownWidgetComponent {
242
244
  dashboardGetState() {
243
245
  return this.state();
244
246
  }
245
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: UnknownWidgetComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
246
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.10", type: UnknownWidgetComponent, isStandalone: true, selector: "lib-unknown-widget", ngImport: i0, template: `
247
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: UnknownWidgetComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
248
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "22.0.4", type: UnknownWidgetComponent, isStandalone: true, selector: "lib-unknown-widget", ngImport: i0, template: `
247
249
  <div class="unknown-widget-container" [matTooltip]="tooltipText()">
248
250
  <mat-icon class="unknown-widget-icon">error_outline</mat-icon>
249
251
  </div>
250
252
  `, isInline: true, styles: [".unknown-widget-container{display:flex;align-items:center;justify-content:center;width:100%;height:100%;background-color:var(--mat-sys-error);border-radius:8px;container-type:size}.unknown-widget-icon{color:var(--mat-sys-on-error);font-size:clamp(12px,75cqmin,68px);width:clamp(12px,75cqmin,68px);height:clamp(12px,75cqmin,68px)}\n"], dependencies: [{ kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i2.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i2$1.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
251
253
  }
252
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: UnknownWidgetComponent, decorators: [{
254
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: UnknownWidgetComponent, decorators: [{
253
255
  type: Component,
254
256
  args: [{ selector: 'lib-unknown-widget', imports: [MatIconModule, MatTooltipModule], template: `
255
257
  <div class="unknown-widget-container" [matTooltip]="tooltipText()">
@@ -260,7 +262,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.10", ngImpo
260
262
 
261
263
  // dashboard.service.ts
262
264
  class DashboardService {
263
- #widgetTypes = signal([], ...(ngDevMode ? [{ debugName: "#widgetTypes" }] : /* istanbul ignore next */ []));
265
+ #widgetTypes = signal([], /* @ts-ignore */
266
+ ...(ngDevMode ? [{ debugName: "#widgetTypes" }] : /* istanbul ignore next */ []));
264
267
  #widgetFactoryMap = new Map();
265
268
  #sharedStateProviders = new Map();
266
269
  #pendingSharedStates = new Map();
@@ -362,10 +365,10 @@ class DashboardService {
362
365
  }
363
366
  }
364
367
  }
365
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: DashboardService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
366
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: DashboardService, providedIn: 'root' });
368
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: DashboardService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
369
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: DashboardService, providedIn: 'root' });
367
370
  }
368
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: DashboardService, decorators: [{
371
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: DashboardService, decorators: [{
369
372
  type: Injectable,
370
373
  args: [{
371
374
  providedIn: 'root',
@@ -556,6 +559,38 @@ function applySelectionFilter(selection, allCells, options = {}) {
556
559
  };
557
560
  }
558
561
 
562
+ /** Floor a requested track count to a positive integer; non-finite -> 1. */
563
+ function sanitizeRequest(value) {
564
+ return Number.isFinite(value) ? Math.max(1, Math.floor(value)) : 1;
565
+ }
566
+ /**
567
+ * Clamp a requested grid size to the smallest size that still contains every
568
+ * widget's full footprint (the content floor). Pure: computes the result but
569
+ * does not mutate any state, so it is shared by both the committed resize
570
+ * (`setGridSize`) and the in-progress drag preview.
571
+ *
572
+ * Values below 1 are treated as 1; fractional values are floored.
573
+ */
574
+ function clampGridSize(requestedRows, requestedColumns, cells) {
575
+ // The bounding box's max edges are span-aware (a widget at col 14 spanning
576
+ // 3 columns needs at least 16 columns). null = empty dashboard.
577
+ const bounds = calculateMinimalBoundingBox(cells);
578
+ const minRows = bounds?.maxRow ?? 1;
579
+ const minColumns = bounds?.maxCol ?? 1;
580
+ // Sanitize non-finite input (NaN/Infinity from e.g. parseInt of empty user
581
+ // input) to 1 so it can never reach committed grid state; clamp-to-content
582
+ // then floors it at the occupied extent.
583
+ const reqRows = sanitizeRequest(requestedRows);
584
+ const reqColumns = sanitizeRequest(requestedColumns);
585
+ const rows = Math.max(minRows, reqRows);
586
+ const columns = Math.max(minColumns, reqColumns);
587
+ return {
588
+ rows,
589
+ columns,
590
+ clamped: rows !== reqRows || columns !== reqColumns,
591
+ };
592
+ }
593
+
559
594
  const initialGridConfigState = {
560
595
  rows: 8,
561
596
  columns: 16,
@@ -719,6 +754,9 @@ const initialDragDropState = {
719
754
  hoveredDropZone: null,
720
755
  };
721
756
  const withDragDrop = () => signalStoreFeature(withState(initialDragDropState), withComputed((store) => ({
757
+ // True while any widget drag is in progress. Shared by components that
758
+ // need to suppress conflicting affordances during a drag.
759
+ isDragActive: computed(() => !!store.dragData()),
722
760
  // Highlighted zones during drag
723
761
  highlightedZones: computed(() => calculateHighlightedZones(store.dragData(), store.hoveredDropZone())),
724
762
  })), withComputed((store) => ({
@@ -945,6 +983,32 @@ const withResize = () => signalStoreFeature(withState(initialResizeState), withM
945
983
  },
946
984
  })));
947
985
 
986
+ const initialGridResizeState = {
987
+ gridResizePreview: null,
988
+ };
989
+ const withGridResize = () => signalStoreFeature(withState(initialGridResizeState), withMethods((store) => ({
990
+ // Needs cross-feature dependencies (current size + cells for the floor),
991
+ // injected by the store wrapper. Skips the patch when the clamped result
992
+ // is unchanged — e.g. dragging further past the content floor yields new
993
+ // raw deltas but the same clamped preview — avoiding redundant re-renders.
994
+ _previewGridResize(deltaRows, deltaColumns, dependencies) {
995
+ const preview = clampGridSize(dependencies.rows + deltaRows, dependencies.columns + deltaColumns, dependencies.cells);
996
+ const current = store.gridResizePreview();
997
+ if (current &&
998
+ current.rows === preview.rows &&
999
+ current.columns === preview.columns &&
1000
+ current.clamped === preview.clamped) {
1001
+ return;
1002
+ }
1003
+ patchState(store, { gridResizePreview: preview });
1004
+ },
1005
+ clearGridResizePreview() {
1006
+ if (store.gridResizePreview() !== null) {
1007
+ patchState(store, { gridResizePreview: null });
1008
+ }
1009
+ },
1010
+ })));
1011
+
948
1012
  /** Returns the intended widget type ID, falling back to the factory's type ID */
949
1013
  function effectiveWidgetTypeid(cell) {
950
1014
  return cell.widgetTypeid ?? cell.widgetFactory.widgetTypeid;
@@ -954,9 +1018,15 @@ const initialState = {
954
1018
  };
955
1019
  const DashboardStore = signalStore(withState(initialState), withProps(() => ({
956
1020
  dashboardService: inject(DashboardService),
957
- })), withGridConfig(), withWidgetManagement(), withResize(), withDragDrop(),
1021
+ })), withGridConfig(), withWidgetManagement(), withResize(), withGridResize(), withDragDrop(),
958
1022
  // Cross-feature computed properties (need access to multiple features)
959
1023
  withComputed((store) => ({
1024
+ // Effective grid size: the live resize preview when a handle drag is in
1025
+ // progress, otherwise the committed size. Consumed wherever the rendered
1026
+ // grid size matters (editor template, outer frame, viewport letterboxing)
1027
+ // so they all reflow together during a drag.
1028
+ effectiveRows: computed(() => store.gridResizePreview()?.rows ?? store.rows()),
1029
+ effectiveColumns: computed(() => store.gridResizePreview()?.columns ?? store.columns()),
960
1030
  // Invalid zones (collision detection)
961
1031
  invalidHighlightMap: computed(() => {
962
1032
  const collisionInfo = calculateCollisionInfo(store.dragData(), store.hoveredDropZone(), store.cells(), store.rows(), store.columns());
@@ -1007,6 +1077,24 @@ withMethods((store) => ({
1007
1077
  },
1008
1078
  });
1009
1079
  },
1080
+ // GRID RESIZE (change row/column counts on a populated dashboard)
1081
+ // Clamp-to-content policy: a requested size that would push a widget out
1082
+ // of bounds is snapped up to the smallest size that still contains every
1083
+ // widget's full footprint, so shrinking never orphans a widget.
1084
+ setGridSize(rows, columns) {
1085
+ const result = clampGridSize(rows, columns, store.cells());
1086
+ store.setGridConfig({ rows: result.rows, columns: result.columns });
1087
+ return result;
1088
+ },
1089
+ // Preview a relative grid resize during a handle drag (no commit). Stores
1090
+ // the clamped target in gridResizePreview so the editor can live-reflow.
1091
+ previewGridResize(deltaRows, deltaColumns) {
1092
+ store._previewGridResize(deltaRows, deltaColumns, {
1093
+ rows: store.rows(),
1094
+ columns: store.columns(),
1095
+ cells: store.cells(),
1096
+ });
1097
+ },
1010
1098
  // EXPORT/IMPORT METHODS (need access to multiple features)
1011
1099
  exportDashboard(getCurrentWidgetStates, selection, selectionOptions) {
1012
1100
  // Get live widget states if callback provided, otherwise use stored states
@@ -1102,6 +1190,27 @@ withMethods((store) => ({
1102
1190
  });
1103
1191
  },
1104
1192
  })),
1193
+ // End a grid resize gesture atomically (mirrors withResize._endResize):
1194
+ // clear the live preview, no-op on a zero delta, otherwise commit the
1195
+ // relative resize. Returns null (no committed change) when the delta is zero
1196
+ // or clamp-to-content leaves the size unchanged, so callers don't signal a
1197
+ // resize that did nothing. Split into its own block so it can call the
1198
+ // absolute setGridSize above (siblings in one withMethods block aren't
1199
+ // visible to each other).
1200
+ withMethods((store) => ({
1201
+ endGridResize(deltaRows, deltaColumns) {
1202
+ store.clearGridResizePreview();
1203
+ if (deltaRows === 0 && deltaColumns === 0)
1204
+ return null;
1205
+ const beforeRows = store.rows();
1206
+ const beforeColumns = store.columns();
1207
+ const result = store.setGridSize(beforeRows + deltaRows, beforeColumns + deltaColumns);
1208
+ if (result.rows === beforeRows && result.columns === beforeColumns) {
1209
+ return null;
1210
+ }
1211
+ return result;
1212
+ },
1213
+ })),
1105
1214
  // Cross-feature computed properties that depend on resize + widget data (using utility functions)
1106
1215
  withComputed((store) => ({
1107
1216
  // Compute preview cells during resize using utility function
@@ -1143,8 +1252,8 @@ class CellSettingsDialogComponent {
1143
1252
  };
1144
1253
  this.dialogRef.close(newData);
1145
1254
  }
1146
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: CellSettingsDialogComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1147
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.10", type: CellSettingsDialogComponent, isStandalone: true, selector: "lib-cell-settings-dialog", ngImport: i0, template: `
1255
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: CellSettingsDialogComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1256
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "22.0.4", type: CellSettingsDialogComponent, isStandalone: true, selector: "lib-cell-settings-dialog", ngImport: i0, template: `
1148
1257
  <h2 mat-dialog-title i18n="@@ngx.dashboard.cell.settings.title">
1149
1258
  Cell Display Settings
1150
1259
  </h2>
@@ -1210,7 +1319,7 @@ class CellSettingsDialogComponent {
1210
1319
  </mat-dialog-actions>
1211
1320
  `, isInline: true, styles: ["mat-dialog-content{display:block;overflow-y:auto;overflow-x:hidden;padding-top:.5rem}.cell-info{margin:0 0 1.5rem;padding-bottom:1rem}.radio-group{width:100%}mat-radio-group{display:block}mat-radio-button{width:100%;display:block;margin-bottom:1rem}mat-radio-button:last-child{margin-bottom:0}.radio-option{margin-left:.75rem;padding:.25rem 0}.option-title{display:block;margin-bottom:.25rem}.option-description{display:block}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: MatDialogModule }, { kind: "directive", type: i2$2.MatDialogTitle, selector: "[mat-dialog-title], [matDialogTitle]", inputs: ["id"], exportAs: ["matDialogTitle"] }, { kind: "directive", type: i2$2.MatDialogActions, selector: "[mat-dialog-actions], mat-dialog-actions, [matDialogActions]", inputs: ["align"] }, { kind: "directive", type: i2$2.MatDialogContent, selector: "[mat-dialog-content], mat-dialog-content, [matDialogContent]" }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i4.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatRadioModule }, { kind: "directive", type: i4$1.MatRadioGroup, selector: "mat-radio-group", inputs: ["color", "name", "labelPosition", "value", "selected", "disabled", "required", "disabledInteractive"], outputs: ["change"], exportAs: ["matRadioGroup"] }, { kind: "component", type: i4$1.MatRadioButton, selector: "mat-radio-button", inputs: ["id", "name", "aria-label", "aria-labelledby", "aria-describedby", "disableRipple", "tabIndex", "checked", "value", "labelPosition", "disabled", "required", "color", "disabledInteractive"], outputs: ["change"], exportAs: ["matRadioButton"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1212
1321
  }
1213
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: CellSettingsDialogComponent, decorators: [{
1322
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: CellSettingsDialogComponent, decorators: [{
1214
1323
  type: Component,
1215
1324
  args: [{ selector: 'lib-cell-settings-dialog', changeDetection: ChangeDetectionStrategy.OnPush, imports: [
1216
1325
  FormsModule,
@@ -1301,10 +1410,10 @@ class DefaultCellSettingsDialogProvider extends CellSettingsDialogProvider {
1301
1410
  const result = await firstValueFrom(dialogRef.afterClosed());
1302
1411
  return result;
1303
1412
  }
1304
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: DefaultCellSettingsDialogProvider, deps: null, target: i0.ɵɵFactoryTarget.Injectable });
1305
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: DefaultCellSettingsDialogProvider, providedIn: 'root' });
1413
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: DefaultCellSettingsDialogProvider, deps: null, target: i0.ɵɵFactoryTarget.Injectable });
1414
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: DefaultCellSettingsDialogProvider, providedIn: 'root' });
1306
1415
  }
1307
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: DefaultCellSettingsDialogProvider, decorators: [{
1416
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: DefaultCellSettingsDialogProvider, decorators: [{
1308
1417
  type: Injectable,
1309
1418
  args: [{
1310
1419
  providedIn: 'root',
@@ -1328,7 +1437,8 @@ const CELL_SETTINGS_DIALOG_PROVIDER = new InjectionToken('CellSettingsDialogProv
1328
1437
  });
1329
1438
 
1330
1439
  class CellContextMenuService {
1331
- #activeMenu = signal(null, ...(ngDevMode ? [{ debugName: "#activeMenu" }] : /* istanbul ignore next */ []));
1440
+ #activeMenu = signal(null, /* @ts-ignore */
1441
+ ...(ngDevMode ? [{ debugName: "#activeMenu" }] : /* istanbul ignore next */ []));
1332
1442
  activeMenu = this.#activeMenu.asReadonly();
1333
1443
  show(x, y, items) {
1334
1444
  this.#activeMenu.set({ x, y, items });
@@ -1336,26 +1446,37 @@ class CellContextMenuService {
1336
1446
  hide() {
1337
1447
  this.#activeMenu.set(null);
1338
1448
  }
1339
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: CellContextMenuService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
1340
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: CellContextMenuService });
1449
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: CellContextMenuService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
1450
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: CellContextMenuService });
1341
1451
  }
1342
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: CellContextMenuService, decorators: [{
1452
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: CellContextMenuService, decorators: [{
1343
1453
  type: Injectable
1344
1454
  }] });
1345
1455
 
1346
1456
  // cell.component.ts
1347
1457
  class CellComponent {
1348
- widgetId = input.required(...(ngDevMode ? [{ debugName: "widgetId" }] : /* istanbul ignore next */ [])); // Unique widget instance identifier
1349
- cellId = input.required(...(ngDevMode ? [{ debugName: "cellId" }] : /* istanbul ignore next */ [])); // Current grid position
1350
- widgetFactory = input(undefined, ...(ngDevMode ? [{ debugName: "widgetFactory" }] : /* istanbul ignore next */ []));
1351
- widgetState = input(undefined, ...(ngDevMode ? [{ debugName: "widgetState" }] : /* istanbul ignore next */ []));
1352
- isEditMode = input(false, ...(ngDevMode ? [{ debugName: "isEditMode" }] : /* istanbul ignore next */ []));
1353
- flat = input(undefined, ...(ngDevMode ? [{ debugName: "flat" }] : /* istanbul ignore next */ []));
1354
- row = model.required(...(ngDevMode ? [{ debugName: "row" }] : /* istanbul ignore next */ []));
1355
- column = model.required(...(ngDevMode ? [{ debugName: "column" }] : /* istanbul ignore next */ []));
1356
- rowSpan = input(1, ...(ngDevMode ? [{ debugName: "rowSpan" }] : /* istanbul ignore next */ []));
1357
- colSpan = input(1, ...(ngDevMode ? [{ debugName: "colSpan" }] : /* istanbul ignore next */ []));
1358
- draggable = input(false, ...(ngDevMode ? [{ debugName: "draggable" }] : /* istanbul ignore next */ []));
1458
+ widgetId = input.required(/* @ts-ignore */
1459
+ ...(ngDevMode ? [{ debugName: "widgetId" }] : /* istanbul ignore next */ [])); // Unique widget instance identifier
1460
+ cellId = input.required(/* @ts-ignore */
1461
+ ...(ngDevMode ? [{ debugName: "cellId" }] : /* istanbul ignore next */ [])); // Current grid position
1462
+ widgetFactory = input(undefined, /* @ts-ignore */
1463
+ ...(ngDevMode ? [{ debugName: "widgetFactory" }] : /* istanbul ignore next */ []));
1464
+ widgetState = input(undefined, /* @ts-ignore */
1465
+ ...(ngDevMode ? [{ debugName: "widgetState" }] : /* istanbul ignore next */ []));
1466
+ isEditMode = input(false, /* @ts-ignore */
1467
+ ...(ngDevMode ? [{ debugName: "isEditMode" }] : /* istanbul ignore next */ []));
1468
+ flat = input(undefined, /* @ts-ignore */
1469
+ ...(ngDevMode ? [{ debugName: "flat" }] : /* istanbul ignore next */ []));
1470
+ row = model.required(/* @ts-ignore */
1471
+ ...(ngDevMode ? [{ debugName: "row" }] : /* istanbul ignore next */ []));
1472
+ column = model.required(/* @ts-ignore */
1473
+ ...(ngDevMode ? [{ debugName: "column" }] : /* istanbul ignore next */ []));
1474
+ rowSpan = input(1, /* @ts-ignore */
1475
+ ...(ngDevMode ? [{ debugName: "rowSpan" }] : /* istanbul ignore next */ []));
1476
+ colSpan = input(1, /* @ts-ignore */
1477
+ ...(ngDevMode ? [{ debugName: "colSpan" }] : /* istanbul ignore next */ []));
1478
+ draggable = input(false, /* @ts-ignore */
1479
+ ...(ngDevMode ? [{ debugName: "draggable" }] : /* istanbul ignore next */ []));
1359
1480
  dragStart = output();
1360
1481
  dragEnd = output();
1361
1482
  edit = output();
@@ -1376,20 +1497,26 @@ class CellComponent {
1376
1497
  // Document event listeners cleanup function
1377
1498
  // Performance: Only created when actively resizing, not for every cell
1378
1499
  #documentListeners;
1379
- isDragging = signal(false, ...(ngDevMode ? [{ debugName: "isDragging" }] : /* istanbul ignore next */ []));
1380
- gridRowStyle = computed(() => `${this.row()} / span ${this.rowSpan()}`, ...(ngDevMode ? [{ debugName: "gridRowStyle" }] : /* istanbul ignore next */ []));
1381
- gridColumnStyle = computed(() => `${this.column()} / span ${this.colSpan()}`, ...(ngDevMode ? [{ debugName: "gridColumnStyle" }] : /* istanbul ignore next */ []));
1500
+ isDragging = signal(false, /* @ts-ignore */
1501
+ ...(ngDevMode ? [{ debugName: "isDragging" }] : /* istanbul ignore next */ []));
1502
+ gridRowStyle = computed(() => `${this.row()} / span ${this.rowSpan()}`, /* @ts-ignore */
1503
+ ...(ngDevMode ? [{ debugName: "gridRowStyle" }] : /* istanbul ignore next */ []));
1504
+ gridColumnStyle = computed(() => `${this.column()} / span ${this.colSpan()}`, /* @ts-ignore */
1505
+ ...(ngDevMode ? [{ debugName: "gridColumnStyle" }] : /* istanbul ignore next */ []));
1382
1506
  isResizing = computed(() => {
1383
1507
  const resizeData = this.#store.resizeData();
1384
1508
  return resizeData
1385
1509
  ? CellIdUtils.equals(resizeData.cellId, this.cellId())
1386
1510
  : false;
1387
- }, ...(ngDevMode ? [{ debugName: "isResizing" }] : /* istanbul ignore next */ []));
1388
- isDragActive = computed(() => !!this.#store.dragData(), ...(ngDevMode ? [{ debugName: "isDragActive" }] : /* istanbul ignore next */ []));
1511
+ }, /* @ts-ignore */
1512
+ ...(ngDevMode ? [{ debugName: "isResizing" }] : /* istanbul ignore next */ []));
1513
+ isDragActive = this.#store.isDragActive;
1389
1514
  resizeData = this.#store.resizeData;
1390
1515
  gridCellDimensions = this.#store.gridCellDimensions;
1391
- resizeDirection = signal(null, ...(ngDevMode ? [{ debugName: "resizeDirection" }] : /* istanbul ignore next */ []));
1392
- resizeStartPos = signal({ x: 0, y: 0 }, ...(ngDevMode ? [{ debugName: "resizeStartPos" }] : /* istanbul ignore next */ []));
1516
+ resizeDirection = signal(null, /* @ts-ignore */
1517
+ ...(ngDevMode ? [{ debugName: "resizeDirection" }] : /* istanbul ignore next */ []));
1518
+ resizeStartPos = signal({ x: 0, y: 0 }, /* @ts-ignore */
1519
+ ...(ngDevMode ? [{ debugName: "resizeStartPos" }] : /* istanbul ignore next */ []));
1393
1520
  constructor() {
1394
1521
  // widget creation - triggers when factory or state changes
1395
1522
  effect(() => {
@@ -1624,10 +1751,10 @@ class CellComponent {
1624
1751
  // Fall back to stored state if widget doesn't implement dashboardGetState
1625
1752
  return this.widgetState();
1626
1753
  }
1627
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: CellComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1628
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.10", type: CellComponent, isStandalone: true, selector: "lib-cell", inputs: { widgetId: { classPropertyName: "widgetId", publicName: "widgetId", isSignal: true, isRequired: true, transformFunction: null }, cellId: { classPropertyName: "cellId", publicName: "cellId", isSignal: true, isRequired: true, transformFunction: null }, widgetFactory: { classPropertyName: "widgetFactory", publicName: "widgetFactory", isSignal: true, isRequired: false, transformFunction: null }, widgetState: { classPropertyName: "widgetState", publicName: "widgetState", isSignal: true, isRequired: false, transformFunction: null }, isEditMode: { classPropertyName: "isEditMode", publicName: "isEditMode", isSignal: true, isRequired: false, transformFunction: null }, flat: { classPropertyName: "flat", publicName: "flat", isSignal: true, isRequired: false, transformFunction: null }, row: { classPropertyName: "row", publicName: "row", isSignal: true, isRequired: true, transformFunction: null }, column: { classPropertyName: "column", publicName: "column", isSignal: true, isRequired: true, transformFunction: null }, rowSpan: { classPropertyName: "rowSpan", publicName: "rowSpan", isSignal: true, isRequired: false, transformFunction: null }, colSpan: { classPropertyName: "colSpan", publicName: "colSpan", isSignal: true, isRequired: false, transformFunction: null }, draggable: { classPropertyName: "draggable", publicName: "draggable", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { row: "rowChange", column: "columnChange", dragStart: "dragStart", dragEnd: "dragEnd", edit: "edit", delete: "delete", settings: "settings", resizeStart: "resizeStart", resizeMove: "resizeMove", resizeEnd: "resizeEnd" }, host: { properties: { "style.grid-row": "gridRowStyle()", "style.grid-column": "gridColumnStyle()", "class.is-dragging": "isDragging()", "class.drag-active": "isDragActive()", "class.flat": "flat() === true" } }, viewQueries: [{ propertyName: "container", first: true, predicate: ["container"], descendants: true, read: ViewContainerRef, isSignal: true }], ngImport: i0, 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"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1754
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: CellComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1755
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.4", type: CellComponent, isStandalone: true, selector: "lib-cell", inputs: { widgetId: { classPropertyName: "widgetId", publicName: "widgetId", isSignal: true, isRequired: true, transformFunction: null }, cellId: { classPropertyName: "cellId", publicName: "cellId", isSignal: true, isRequired: true, transformFunction: null }, widgetFactory: { classPropertyName: "widgetFactory", publicName: "widgetFactory", isSignal: true, isRequired: false, transformFunction: null }, widgetState: { classPropertyName: "widgetState", publicName: "widgetState", isSignal: true, isRequired: false, transformFunction: null }, isEditMode: { classPropertyName: "isEditMode", publicName: "isEditMode", isSignal: true, isRequired: false, transformFunction: null }, flat: { classPropertyName: "flat", publicName: "flat", isSignal: true, isRequired: false, transformFunction: null }, row: { classPropertyName: "row", publicName: "row", isSignal: true, isRequired: true, transformFunction: null }, column: { classPropertyName: "column", publicName: "column", isSignal: true, isRequired: true, transformFunction: null }, rowSpan: { classPropertyName: "rowSpan", publicName: "rowSpan", isSignal: true, isRequired: false, transformFunction: null }, colSpan: { classPropertyName: "colSpan", publicName: "colSpan", isSignal: true, isRequired: false, transformFunction: null }, draggable: { classPropertyName: "draggable", publicName: "draggable", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { row: "rowChange", column: "columnChange", dragStart: "dragStart", dragEnd: "dragEnd", edit: "edit", delete: "delete", settings: "settings", resizeStart: "resizeStart", resizeMove: "resizeMove", resizeEnd: "resizeEnd" }, host: { properties: { "style.grid-row": "gridRowStyle()", "style.grid-column": "gridColumnStyle()", "class.is-dragging": "isDragging()", "class.drag-active": "isDragActive()", "class.flat": "flat() === true" } }, viewQueries: [{ propertyName: "container", first: true, predicate: ["container"], descendants: true, read: ViewContainerRef, isSignal: true }], ngImport: i0, 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: [":root .cursor-col-resize{cursor:col-resize!important}:root .cursor-row-resize{cursor:row-resize!important}:root .cursor-nwse-resize{cursor:nwse-resize!important}: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}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1629
1756
  }
1630
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: CellComponent, decorators: [{
1757
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: CellComponent, decorators: [{
1631
1758
  type: Component,
1632
1759
  args: [{ selector: 'lib-cell', standalone: true, imports: [], changeDetection: ChangeDetectionStrategy.OnPush, host: {
1633
1760
  '[style.grid-row]': 'gridRowStyle()',
@@ -1635,7 +1762,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.10", ngImpo
1635
1762
  '[class.is-dragging]': 'isDragging()',
1636
1763
  '[class.drag-active]': 'isDragActive()',
1637
1764
  '[class.flat]': 'flat() === true',
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"] }]
1765
+ }, 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: [":root .cursor-col-resize{cursor:col-resize!important}:root .cursor-row-resize{cursor:row-resize!important}:root .cursor-nwse-resize{cursor:nwse-resize!important}: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}\n"] }]
1639
1766
  }], 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
1767
 
1641
1768
  /**
@@ -1653,15 +1780,23 @@ class DashboardViewerComponent {
1653
1780
  #store = inject(DashboardStore);
1654
1781
  #renderer = inject(Renderer2);
1655
1782
  #destroyRef = inject(DestroyRef);
1656
- cellComponents = viewChildren(CellComponent, ...(ngDevMode ? [{ debugName: "cellComponents" }] : /* istanbul ignore next */ []));
1657
- gridElement = viewChild('gridElement', ...(ngDevMode ? [{ debugName: "gridElement" }] : /* istanbul ignore next */ []));
1658
- rows = input.required(...(ngDevMode ? [{ debugName: "rows" }] : /* istanbul ignore next */ []));
1659
- columns = input.required(...(ngDevMode ? [{ debugName: "columns" }] : /* istanbul ignore next */ []));
1660
- gutterSize = input('1em', ...(ngDevMode ? [{ debugName: "gutterSize" }] : /* istanbul ignore next */ []));
1661
- gutters = computed(() => this.columns() + 1, ...(ngDevMode ? [{ debugName: "gutters" }] : /* istanbul ignore next */ []));
1783
+ cellComponents = viewChildren(CellComponent, /* @ts-ignore */
1784
+ ...(ngDevMode ? [{ debugName: "cellComponents" }] : /* istanbul ignore next */ []));
1785
+ gridElement = viewChild('gridElement', /* @ts-ignore */
1786
+ ...(ngDevMode ? [{ debugName: "gridElement" }] : /* istanbul ignore next */ []));
1787
+ rows = input.required(/* @ts-ignore */
1788
+ ...(ngDevMode ? [{ debugName: "rows" }] : /* istanbul ignore next */ []));
1789
+ columns = input.required(/* @ts-ignore */
1790
+ ...(ngDevMode ? [{ debugName: "columns" }] : /* istanbul ignore next */ []));
1791
+ gutterSize = input('1em', /* @ts-ignore */
1792
+ ...(ngDevMode ? [{ debugName: "gutterSize" }] : /* istanbul ignore next */ []));
1793
+ gutters = computed(() => this.columns() + 1, /* @ts-ignore */
1794
+ ...(ngDevMode ? [{ debugName: "gutters" }] : /* istanbul ignore next */ []));
1662
1795
  // Selection feature
1663
- enableSelection = input(false, ...(ngDevMode ? [{ debugName: "enableSelection" }] : /* istanbul ignore next */ []));
1664
- selectionModifier = input(null, ...(ngDevMode ? [{ debugName: "selectionModifier" }] : /* istanbul ignore next */ []));
1796
+ enableSelection = input(false, /* @ts-ignore */
1797
+ ...(ngDevMode ? [{ debugName: "enableSelection" }] : /* istanbul ignore next */ []));
1798
+ selectionModifier = input(null, /* @ts-ignore */
1799
+ ...(ngDevMode ? [{ debugName: "selectionModifier" }] : /* istanbul ignore next */ []));
1665
1800
  /**
1666
1801
  * Minimum pointer movement (in CSS pixels) between pointerdown and
1667
1802
  * pointerup required to emit `selectionComplete`. Below the threshold,
@@ -1670,16 +1805,21 @@ class DashboardViewerComponent {
1670
1805
  * Default 4 — matches OS-native click-vs-drag thresholds. Set to 0 to
1671
1806
  * preserve the legacy behavior where every pointerup emits.
1672
1807
  */
1673
- dragThreshold = input(4, ...(ngDevMode ? [{ debugName: "dragThreshold" }] : /* istanbul ignore next */ []));
1808
+ dragThreshold = input(4, /* @ts-ignore */
1809
+ ...(ngDevMode ? [{ debugName: "dragThreshold" }] : /* istanbul ignore next */ []));
1674
1810
  selectionComplete = output();
1675
1811
  // store signals - read-only
1676
1812
  cells = this.#store.cells;
1677
1813
  // Selection state
1678
- isSelecting = signal(false, ...(ngDevMode ? [{ debugName: "isSelecting" }] : /* istanbul ignore next */ []));
1679
- selectionStart = signal(null, ...(ngDevMode ? [{ debugName: "selectionStart" }] : /* istanbul ignore next */ []));
1680
- selectionCurrent = signal(null, ...(ngDevMode ? [{ debugName: "selectionCurrent" }] : /* istanbul ignore next */ []));
1814
+ isSelecting = signal(false, /* @ts-ignore */
1815
+ ...(ngDevMode ? [{ debugName: "isSelecting" }] : /* istanbul ignore next */ []));
1816
+ selectionStart = signal(null, /* @ts-ignore */
1817
+ ...(ngDevMode ? [{ debugName: "selectionStart" }] : /* istanbul ignore next */ []));
1818
+ selectionCurrent = signal(null, /* @ts-ignore */
1819
+ ...(ngDevMode ? [{ debugName: "selectionCurrent" }] : /* istanbul ignore next */ []));
1681
1820
  // Modifier-key gating state for selectionModifier
1682
- #modifierHeld = signal(false, ...(ngDevMode ? [{ debugName: "#modifierHeld" }] : /* istanbul ignore next */ []));
1821
+ #modifierHeld = signal(false, /* @ts-ignore */
1822
+ ...(ngDevMode ? [{ debugName: "#modifierHeld" }] : /* istanbul ignore next */ []));
1683
1823
  /**
1684
1824
  * Whether the selection overlay is currently interactive (intercepts
1685
1825
  * pointer events). Always false when `enableSelection` is false.
@@ -1694,7 +1834,8 @@ class DashboardViewerComponent {
1694
1834
  if (this.selectionModifier() === null)
1695
1835
  return true;
1696
1836
  return this.#modifierHeld() || this.isSelecting();
1697
- }, ...(ngDevMode ? [{ debugName: "armed" }] : /* istanbul ignore next */ []));
1837
+ }, /* @ts-ignore */
1838
+ ...(ngDevMode ? [{ debugName: "armed" }] : /* istanbul ignore next */ []));
1698
1839
  // Computed selection bounds (normalized)
1699
1840
  selectionBounds = computed(() => {
1700
1841
  const start = this.selectionStart();
@@ -1707,15 +1848,19 @@ class DashboardViewerComponent {
1707
1848
  startCol: Math.min(start.col, current.col),
1708
1849
  endCol: Math.max(start.col, current.col),
1709
1850
  };
1710
- }, ...(ngDevMode ? [{ debugName: "selectionBounds" }] : /* istanbul ignore next */ []));
1851
+ }, /* @ts-ignore */
1852
+ ...(ngDevMode ? [{ debugName: "selectionBounds" }] : /* istanbul ignore next */ []));
1711
1853
  // Generate array for template iteration
1712
- rowNumbers = computed(() => Array.from({ length: this.rows() }, (_, i) => i + 1), ...(ngDevMode ? [{ debugName: "rowNumbers" }] : /* istanbul ignore next */ []));
1713
- colNumbers = computed(() => Array.from({ length: this.columns() }, (_, i) => i + 1), ...(ngDevMode ? [{ debugName: "colNumbers" }] : /* istanbul ignore next */ []));
1854
+ rowNumbers = computed(() => Array.from({ length: this.rows() }, (_, i) => i + 1), /* @ts-ignore */
1855
+ ...(ngDevMode ? [{ debugName: "rowNumbers" }] : /* istanbul ignore next */ []));
1856
+ colNumbers = computed(() => Array.from({ length: this.columns() }, (_, i) => i + 1), /* @ts-ignore */
1857
+ ...(ngDevMode ? [{ debugName: "colNumbers" }] : /* istanbul ignore next */ []));
1714
1858
  // Document-level pointer listeners (cleanup needed)
1715
1859
  #pointerMoveListener;
1716
1860
  #pointerUpListener;
1717
1861
  // Pointer position at the start of a drag, for dragThreshold checks
1718
- #pointerDownPos = signal(null, ...(ngDevMode ? [{ debugName: "#pointerDownPos" }] : /* istanbul ignore next */ []));
1862
+ #pointerDownPos = signal(null, /* @ts-ignore */
1863
+ ...(ngDevMode ? [{ debugName: "#pointerDownPos" }] : /* istanbul ignore next */ []));
1719
1864
  constructor() {
1720
1865
  // Sync grid configuration with store when inputs change
1721
1866
  effect(() => {
@@ -1886,10 +2031,10 @@ class DashboardViewerComponent {
1886
2031
  this.#pointerUpListener = undefined;
1887
2032
  }
1888
2033
  }
1889
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: DashboardViewerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1890
- 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:color-mix(in srgb,var(--mat-sys-primary) 25%,transparent);border-radius:4px}.selection-overlay-grid.armed .selection-ghost-cell{cursor:crosshair}.selection-overlay-grid.armed .selection-ghost-cell:hover:not(.selecting){background-color:color-mix(in srgb,var(--mat-sys-primary) 8%,transparent)}.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 });
2034
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: DashboardViewerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2035
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.4", 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:color-mix(in srgb,var(--mat-sys-primary) 25%,transparent);border-radius:4px}.selection-overlay-grid.armed .selection-ghost-cell{cursor:crosshair}.selection-overlay-grid.armed .selection-ghost-cell:hover:not(.selecting){background-color:color-mix(in srgb,var(--mat-sys-primary) 8%,transparent)}.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 });
1891
2036
  }
1892
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: DashboardViewerComponent, decorators: [{
2037
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: DashboardViewerComponent, decorators: [{
1893
2038
  type: Component,
1894
2039
  args: [{ selector: 'ngx-dashboard-viewer', standalone: true, imports: [CellComponent], changeDetection: ChangeDetectionStrategy.OnPush, host: {
1895
2040
  '[style.--rows]': 'rows()',
@@ -1907,11 +2052,13 @@ class CellContextMenuComponent {
1907
2052
  menuPosition = computed(() => {
1908
2053
  const menu = this.menuService.activeMenu();
1909
2054
  return menu ? { left: `${menu.x}px`, top: `${menu.y}px` } : { left: '0px', top: '0px' };
1910
- }, ...(ngDevMode ? [{ debugName: "menuPosition" }] : /* istanbul ignore next */ []));
2055
+ }, /* @ts-ignore */
2056
+ ...(ngDevMode ? [{ debugName: "menuPosition" }] : /* istanbul ignore next */ []));
1911
2057
  menuItems = computed(() => {
1912
2058
  const menu = this.menuService.activeMenu();
1913
2059
  return menu?.items || [];
1914
- }, ...(ngDevMode ? [{ debugName: "menuItems" }] : /* istanbul ignore next */ []));
2060
+ }, /* @ts-ignore */
2061
+ ...(ngDevMode ? [{ debugName: "menuItems" }] : /* istanbul ignore next */ []));
1915
2062
  constructor() {
1916
2063
  // Material Menu has a backdrop that blocks events from reaching other elements.
1917
2064
  // When any right-click occurs while menu is open, we need to:
@@ -1972,8 +2119,8 @@ class CellContextMenuComponent {
1972
2119
  this.menuService.hide();
1973
2120
  }
1974
2121
  }
1975
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: CellContextMenuComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1976
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.10", type: CellContextMenuComponent, isStandalone: true, selector: "lib-cell-context-menu", viewQueries: [{ propertyName: "menuTrigger", first: true, predicate: ["menuTrigger"], descendants: true, read: MatMenuTrigger, isSignal: true }], ngImport: i0, template: `
2122
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: CellContextMenuComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2123
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.4", type: CellContextMenuComponent, isStandalone: true, selector: "lib-cell-context-menu", viewQueries: [{ propertyName: "menuTrigger", first: true, predicate: ["menuTrigger"], descendants: true, read: MatMenuTrigger, isSignal: true }], ngImport: i0, template: `
1977
2124
  <!-- Hidden trigger for menu positioned at exact mouse coordinates
1978
2125
 
1979
2126
  IMPORTANT: Angular Material applies its own positioning logic to menus,
@@ -2027,7 +2174,7 @@ class CellContextMenuComponent {
2027
2174
  </mat-menu>
2028
2175
  `, isInline: true, styles: [":host{display:contents}\n"], dependencies: [{ kind: "ngmodule", type: MatMenuModule }, { kind: "component", type: i1$1.MatMenu, selector: "mat-menu", inputs: ["backdropClass", "aria-label", "aria-labelledby", "aria-describedby", "xPosition", "yPosition", "overlapTrigger", "hasBackdrop", "class", "classList"], outputs: ["closed", "close"], exportAs: ["matMenu"] }, { kind: "component", type: i1$1.MatMenuItem, selector: "[mat-menu-item]", inputs: ["role", "disabled", "disableRipple"], exportAs: ["matMenuItem"] }, { kind: "directive", type: i1$1.MatMenuTrigger, selector: "[mat-menu-trigger-for], [matMenuTriggerFor]", inputs: ["mat-menu-trigger-for", "matMenuTriggerFor", "matMenuTriggerData", "matMenuTriggerRestoreFocus"], outputs: ["menuOpened", "onMenuOpen", "menuClosed", "onMenuClose"], exportAs: ["matMenuTrigger"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i2.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatDividerModule }, { kind: "component", type: i3.MatDivider, selector: "mat-divider", inputs: ["vertical", "inset"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i4.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
2029
2176
  }
2030
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: CellContextMenuComponent, decorators: [{
2177
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: CellContextMenuComponent, decorators: [{
2031
2178
  type: Component,
2032
2179
  args: [{ selector: 'lib-cell-context-menu', standalone: true, imports: [MatMenuModule, MatIconModule, MatDividerModule, MatButtonModule], changeDetection: ChangeDetectionStrategy.OnPush, template: `
2033
2180
  <!-- Hidden trigger for menu positioned at exact mouse coordinates
@@ -2118,10 +2265,10 @@ class DefaultEmptyCellContextProvider extends EmptyCellContextProvider {
2118
2265
  handleEmptyCellContext() {
2119
2266
  // Default behavior: do nothing
2120
2267
  }
2121
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: DefaultEmptyCellContextProvider, deps: null, target: i0.ɵɵFactoryTarget.Injectable });
2122
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: DefaultEmptyCellContextProvider, providedIn: 'root' });
2268
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: DefaultEmptyCellContextProvider, deps: null, target: i0.ɵɵFactoryTarget.Injectable });
2269
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: DefaultEmptyCellContextProvider, providedIn: 'root' });
2123
2270
  }
2124
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: DefaultEmptyCellContextProvider, decorators: [{
2271
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: DefaultEmptyCellContextProvider, decorators: [{
2125
2272
  type: Injectable,
2126
2273
  args: [{
2127
2274
  providedIn: 'root',
@@ -2156,8 +2303,10 @@ const EMPTY_CELL_CONTEXT_PROVIDER = new InjectionToken('EmptyCellContextProvider
2156
2303
  * of the context menu that appears when right-clicking on empty dashboard cells.
2157
2304
  */
2158
2305
  class EmptyCellContextMenuService {
2159
- #activeMenu = signal(null, ...(ngDevMode ? [{ debugName: "#activeMenu" }] : /* istanbul ignore next */ []));
2160
- #lastSelectedWidgetTypeId = signal(null, ...(ngDevMode ? [{ debugName: "#lastSelectedWidgetTypeId" }] : /* istanbul ignore next */ []));
2306
+ #activeMenu = signal(null, /* @ts-ignore */
2307
+ ...(ngDevMode ? [{ debugName: "#activeMenu" }] : /* istanbul ignore next */ []));
2308
+ #lastSelectedWidgetTypeId = signal(null, /* @ts-ignore */
2309
+ ...(ngDevMode ? [{ debugName: "#lastSelectedWidgetTypeId" }] : /* istanbul ignore next */ []));
2161
2310
  activeMenu = this.#activeMenu.asReadonly();
2162
2311
  lastSelectedWidgetTypeId = this.#lastSelectedWidgetTypeId.asReadonly();
2163
2312
  /**
@@ -2185,10 +2334,10 @@ class EmptyCellContextMenuService {
2185
2334
  setLastSelection(widgetTypeId) {
2186
2335
  this.#lastSelectedWidgetTypeId.set(widgetTypeId);
2187
2336
  }
2188
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: EmptyCellContextMenuService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
2189
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: EmptyCellContextMenuService, providedIn: 'root' });
2337
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: EmptyCellContextMenuService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
2338
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: EmptyCellContextMenuService, providedIn: 'root' });
2190
2339
  }
2191
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: EmptyCellContextMenuService, decorators: [{
2340
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: EmptyCellContextMenuService, decorators: [{
2192
2341
  type: Injectable,
2193
2342
  args: [{
2194
2343
  providedIn: 'root',
@@ -2303,37 +2452,49 @@ class WidgetListContextMenuProvider extends EmptyCellContextProvider {
2303
2452
  'Ensure you are using a compatible version of the dashboard component.');
2304
2453
  }
2305
2454
  }
2306
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: WidgetListContextMenuProvider, deps: null, target: i0.ɵɵFactoryTarget.Injectable });
2307
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: WidgetListContextMenuProvider });
2455
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: WidgetListContextMenuProvider, deps: null, target: i0.ɵɵFactoryTarget.Injectable });
2456
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: WidgetListContextMenuProvider });
2308
2457
  }
2309
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: WidgetListContextMenuProvider, decorators: [{
2458
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: WidgetListContextMenuProvider, decorators: [{
2310
2459
  type: Injectable
2311
2460
  }] });
2312
2461
 
2313
2462
  // drop-zone.component.ts
2314
2463
  class DropZoneComponent {
2315
2464
  // Required inputs
2316
- row = input.required(...(ngDevMode ? [{ debugName: "row" }] : /* istanbul ignore next */ []));
2317
- col = input.required(...(ngDevMode ? [{ debugName: "col" }] : /* istanbul ignore next */ []));
2318
- index = input.required(...(ngDevMode ? [{ debugName: "index" }] : /* istanbul ignore next */ []));
2465
+ row = input.required(/* @ts-ignore */
2466
+ ...(ngDevMode ? [{ debugName: "row" }] : /* istanbul ignore next */ []));
2467
+ col = input.required(/* @ts-ignore */
2468
+ ...(ngDevMode ? [{ debugName: "col" }] : /* istanbul ignore next */ []));
2469
+ index = input.required(/* @ts-ignore */
2470
+ ...(ngDevMode ? [{ debugName: "index" }] : /* istanbul ignore next */ []));
2319
2471
  // Optional inputs with defaults
2320
- highlight = input(false, ...(ngDevMode ? [{ debugName: "highlight" }] : /* istanbul ignore next */ []));
2321
- highlightInvalid = input(false, ...(ngDevMode ? [{ debugName: "highlightInvalid" }] : /* istanbul ignore next */ []));
2322
- highlightResize = input(false, ...(ngDevMode ? [{ debugName: "highlightResize" }] : /* istanbul ignore next */ []));
2323
- editMode = input(false, ...(ngDevMode ? [{ debugName: "editMode" }] : /* istanbul ignore next */ []));
2472
+ highlight = input(false, /* @ts-ignore */
2473
+ ...(ngDevMode ? [{ debugName: "highlight" }] : /* istanbul ignore next */ []));
2474
+ highlightInvalid = input(false, /* @ts-ignore */
2475
+ ...(ngDevMode ? [{ debugName: "highlightInvalid" }] : /* istanbul ignore next */ []));
2476
+ highlightResize = input(false, /* @ts-ignore */
2477
+ ...(ngDevMode ? [{ debugName: "highlightResize" }] : /* istanbul ignore next */ []));
2478
+ highlightPreview = input(false, /* @ts-ignore */
2479
+ ...(ngDevMode ? [{ debugName: "highlightPreview" }] : /* istanbul ignore next */ []));
2480
+ editMode = input(false, /* @ts-ignore */
2481
+ ...(ngDevMode ? [{ debugName: "editMode" }] : /* istanbul ignore next */ []));
2324
2482
  // Outputs
2325
2483
  dragEnter = output();
2326
2484
  dragExit = output();
2327
2485
  dragOver = output();
2328
2486
  dragDrop = output();
2329
2487
  // Computed properties
2330
- dropZoneId = computed(() => `drop-zone-${this.row()}-${this.col()}`, ...(ngDevMode ? [{ debugName: "dropZoneId" }] : /* istanbul ignore next */ []));
2488
+ dropZoneId = computed(() => `drop-zone-${this.row()}-${this.col()}`, /* @ts-ignore */
2489
+ ...(ngDevMode ? [{ debugName: "dropZoneId" }] : /* istanbul ignore next */ []));
2331
2490
  dropData = computed(() => ({
2332
2491
  row: this.row(),
2333
2492
  col: this.col(),
2334
- }), ...(ngDevMode ? [{ debugName: "dropData" }] : /* istanbul ignore next */ []));
2493
+ }), /* @ts-ignore */
2494
+ ...(ngDevMode ? [{ debugName: "dropData" }] : /* istanbul ignore next */ []));
2335
2495
  // Abstract drag state from store
2336
- dragData = computed(() => this.#store.dragData(), ...(ngDevMode ? [{ debugName: "dragData" }] : /* istanbul ignore next */ []));
2496
+ dragData = computed(() => this.#store.dragData(), /* @ts-ignore */
2497
+ ...(ngDevMode ? [{ debugName: "dragData" }] : /* istanbul ignore next */ []));
2337
2498
  // Computed drop effect based on drag data and validity
2338
2499
  dropEffect = computed(() => {
2339
2500
  const data = this.dragData();
@@ -2341,7 +2502,8 @@ class DropZoneComponent {
2341
2502
  return 'none';
2342
2503
  }
2343
2504
  return data.kind === 'cell' ? 'move' : 'copy';
2344
- }, ...(ngDevMode ? [{ debugName: "dropEffect" }] : /* istanbul ignore next */ []));
2505
+ }, /* @ts-ignore */
2506
+ ...(ngDevMode ? [{ debugName: "dropEffect" }] : /* istanbul ignore next */ []));
2345
2507
  #store = inject(DashboardStore);
2346
2508
  #elementRef = inject(ElementRef);
2347
2509
  #dashboardService = inject(DashboardService);
@@ -2419,13 +2581,13 @@ class DropZoneComponent {
2419
2581
  });
2420
2582
  }
2421
2583
  }
2422
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: DropZoneComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2423
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.10", type: DropZoneComponent, isStandalone: true, selector: "lib-drop-zone", inputs: { row: { classPropertyName: "row", publicName: "row", isSignal: true, isRequired: true, transformFunction: null }, col: { classPropertyName: "col", publicName: "col", isSignal: true, isRequired: true, transformFunction: null }, index: { classPropertyName: "index", publicName: "index", isSignal: true, isRequired: true, transformFunction: null }, highlight: { classPropertyName: "highlight", publicName: "highlight", isSignal: true, isRequired: false, transformFunction: null }, highlightInvalid: { classPropertyName: "highlightInvalid", publicName: "highlightInvalid", isSignal: true, isRequired: false, transformFunction: null }, highlightResize: { classPropertyName: "highlightResize", publicName: "highlightResize", isSignal: true, isRequired: false, transformFunction: null }, editMode: { classPropertyName: "editMode", publicName: "editMode", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { dragEnter: "dragEnter", dragExit: "dragExit", dragOver: "dragOver", dragDrop: "dragDrop" }, ngImport: i0, template: "<!-- drop-zone.component.html -->\n<div\n class=\"drop-zone\"\n [class.drop-zone--highlight]=\"highlight() && !highlightInvalid()\"\n [class.drop-zone--invalid]=\"highlightInvalid()\"\n [class.drop-zone--resize]=\"highlightResize()\"\n [style.grid-row]=\"row()\"\n [style.grid-column]=\"col()\"\n (dragenter)=\"onDragEnter($event)\"\n (dragover)=\"onDragOver($event)\"\n (dragleave)=\"onDragLeave($event)\"\n (drop)=\"onDrop($event)\"\n (contextmenu)=\"onContextMenu($event)\"\n>\n @if (editMode()) {\n <div class=\"edit-mode-cell-number\">\n {{ index() }}<br />\n {{ row() }},{{ col() }}\n </div>\n }\n</div>\n", styles: [".drop-zone{width:100%;height:100%;z-index:0;align-self:stretch;justify-self:stretch;display:block;box-sizing:border-box}.drop-zone--active,.drop-zone--highlight{background-color:#80808080}.drop-zone--invalid{background-color:light-dark(color-mix(in srgb,var(--mat-sys-error) 40%,white),color-mix(in srgb,var(--mat-sys-error) 80%,black))}.drop-zone--resize{background-color:#2196f34d;outline:1px solid rgba(33,150,243,.6)}.edit-mode-cell-number{font-size:10px;line-height:1.1;color:#64646499;pointer-events:none;-webkit-user-select:none;user-select:none;z-index:-1;display:flex;flex-direction:column;align-items:center;justify-content:center;text-align:center;width:100%;height:100%}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
2584
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: DropZoneComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2585
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.4", type: DropZoneComponent, isStandalone: true, selector: "lib-drop-zone", inputs: { row: { classPropertyName: "row", publicName: "row", isSignal: true, isRequired: true, transformFunction: null }, col: { classPropertyName: "col", publicName: "col", isSignal: true, isRequired: true, transformFunction: null }, index: { classPropertyName: "index", publicName: "index", isSignal: true, isRequired: true, transformFunction: null }, highlight: { classPropertyName: "highlight", publicName: "highlight", isSignal: true, isRequired: false, transformFunction: null }, highlightInvalid: { classPropertyName: "highlightInvalid", publicName: "highlightInvalid", isSignal: true, isRequired: false, transformFunction: null }, highlightResize: { classPropertyName: "highlightResize", publicName: "highlightResize", isSignal: true, isRequired: false, transformFunction: null }, highlightPreview: { classPropertyName: "highlightPreview", publicName: "highlightPreview", isSignal: true, isRequired: false, transformFunction: null }, editMode: { classPropertyName: "editMode", publicName: "editMode", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { dragEnter: "dragEnter", dragExit: "dragExit", dragOver: "dragOver", dragDrop: "dragDrop" }, ngImport: i0, template: "<!-- drop-zone.component.html -->\n<div\n class=\"drop-zone\"\n [class.drop-zone--highlight]=\"highlight() && !highlightInvalid()\"\n [class.drop-zone--invalid]=\"highlightInvalid()\"\n [class.drop-zone--resize]=\"highlightResize()\"\n [class.drop-zone--preview]=\"highlightPreview()\"\n [style.grid-row]=\"row()\"\n [style.grid-column]=\"col()\"\n (dragenter)=\"onDragEnter($event)\"\n (dragover)=\"onDragOver($event)\"\n (dragleave)=\"onDragLeave($event)\"\n (drop)=\"onDrop($event)\"\n (contextmenu)=\"onContextMenu($event)\"\n>\n @if (editMode()) {\n <div class=\"edit-mode-cell-number\">\n {{ index() }}<br />\n {{ row() }},{{ col() }}\n </div>\n }\n</div>\n", styles: [".drop-zone{width:100%;height:100%;z-index:0;align-self:stretch;justify-self:stretch;display:block;box-sizing:border-box}.drop-zone--active,.drop-zone--highlight{background-color:#80808080}.drop-zone--invalid{background-color:light-dark(color-mix(in srgb,var(--mat-sys-error) 40%,white),color-mix(in srgb,var(--mat-sys-error) 80%,black))}.drop-zone--resize{background-color:#2196f34d;outline:1px solid rgba(33,150,243,.6)}.drop-zone--preview{background-color:color-mix(in srgb,var(--mat-sys-primary) 18%,transparent);outline:1px dashed color-mix(in srgb,var(--mat-sys-primary) 50%,transparent)}.edit-mode-cell-number{font-size:10px;line-height:1.1;color:#64646499;pointer-events:none;-webkit-user-select:none;user-select:none;z-index:-1;display:flex;flex-direction:column;align-items:center;justify-content:center;text-align:center;width:100%;height:100%}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
2424
2586
  }
2425
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: DropZoneComponent, decorators: [{
2587
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: DropZoneComponent, decorators: [{
2426
2588
  type: Component,
2427
- args: [{ selector: 'lib-drop-zone', standalone: true, imports: [], changeDetection: ChangeDetectionStrategy.OnPush, template: "<!-- drop-zone.component.html -->\n<div\n class=\"drop-zone\"\n [class.drop-zone--highlight]=\"highlight() && !highlightInvalid()\"\n [class.drop-zone--invalid]=\"highlightInvalid()\"\n [class.drop-zone--resize]=\"highlightResize()\"\n [style.grid-row]=\"row()\"\n [style.grid-column]=\"col()\"\n (dragenter)=\"onDragEnter($event)\"\n (dragover)=\"onDragOver($event)\"\n (dragleave)=\"onDragLeave($event)\"\n (drop)=\"onDrop($event)\"\n (contextmenu)=\"onContextMenu($event)\"\n>\n @if (editMode()) {\n <div class=\"edit-mode-cell-number\">\n {{ index() }}<br />\n {{ row() }},{{ col() }}\n </div>\n }\n</div>\n", styles: [".drop-zone{width:100%;height:100%;z-index:0;align-self:stretch;justify-self:stretch;display:block;box-sizing:border-box}.drop-zone--active,.drop-zone--highlight{background-color:#80808080}.drop-zone--invalid{background-color:light-dark(color-mix(in srgb,var(--mat-sys-error) 40%,white),color-mix(in srgb,var(--mat-sys-error) 80%,black))}.drop-zone--resize{background-color:#2196f34d;outline:1px solid rgba(33,150,243,.6)}.edit-mode-cell-number{font-size:10px;line-height:1.1;color:#64646499;pointer-events:none;-webkit-user-select:none;user-select:none;z-index:-1;display:flex;flex-direction:column;align-items:center;justify-content:center;text-align:center;width:100%;height:100%}\n"] }]
2428
- }], propDecorators: { row: [{ type: i0.Input, args: [{ isSignal: true, alias: "row", required: true }] }], col: [{ type: i0.Input, args: [{ isSignal: true, alias: "col", required: true }] }], index: [{ type: i0.Input, args: [{ isSignal: true, alias: "index", required: true }] }], highlight: [{ type: i0.Input, args: [{ isSignal: true, alias: "highlight", required: false }] }], highlightInvalid: [{ type: i0.Input, args: [{ isSignal: true, alias: "highlightInvalid", required: false }] }], highlightResize: [{ type: i0.Input, args: [{ isSignal: true, alias: "highlightResize", required: false }] }], editMode: [{ type: i0.Input, args: [{ isSignal: true, alias: "editMode", required: false }] }], dragEnter: [{ type: i0.Output, args: ["dragEnter"] }], dragExit: [{ type: i0.Output, args: ["dragExit"] }], dragOver: [{ type: i0.Output, args: ["dragOver"] }], dragDrop: [{ type: i0.Output, args: ["dragDrop"] }] } });
2589
+ args: [{ selector: 'lib-drop-zone', standalone: true, imports: [], changeDetection: ChangeDetectionStrategy.OnPush, template: "<!-- drop-zone.component.html -->\n<div\n class=\"drop-zone\"\n [class.drop-zone--highlight]=\"highlight() && !highlightInvalid()\"\n [class.drop-zone--invalid]=\"highlightInvalid()\"\n [class.drop-zone--resize]=\"highlightResize()\"\n [class.drop-zone--preview]=\"highlightPreview()\"\n [style.grid-row]=\"row()\"\n [style.grid-column]=\"col()\"\n (dragenter)=\"onDragEnter($event)\"\n (dragover)=\"onDragOver($event)\"\n (dragleave)=\"onDragLeave($event)\"\n (drop)=\"onDrop($event)\"\n (contextmenu)=\"onContextMenu($event)\"\n>\n @if (editMode()) {\n <div class=\"edit-mode-cell-number\">\n {{ index() }}<br />\n {{ row() }},{{ col() }}\n </div>\n }\n</div>\n", styles: [".drop-zone{width:100%;height:100%;z-index:0;align-self:stretch;justify-self:stretch;display:block;box-sizing:border-box}.drop-zone--active,.drop-zone--highlight{background-color:#80808080}.drop-zone--invalid{background-color:light-dark(color-mix(in srgb,var(--mat-sys-error) 40%,white),color-mix(in srgb,var(--mat-sys-error) 80%,black))}.drop-zone--resize{background-color:#2196f34d;outline:1px solid rgba(33,150,243,.6)}.drop-zone--preview{background-color:color-mix(in srgb,var(--mat-sys-primary) 18%,transparent);outline:1px dashed color-mix(in srgb,var(--mat-sys-primary) 50%,transparent)}.edit-mode-cell-number{font-size:10px;line-height:1.1;color:#64646499;pointer-events:none;-webkit-user-select:none;user-select:none;z-index:-1;display:flex;flex-direction:column;align-items:center;justify-content:center;text-align:center;width:100%;height:100%}\n"] }]
2590
+ }], propDecorators: { row: [{ type: i0.Input, args: [{ isSignal: true, alias: "row", required: true }] }], col: [{ type: i0.Input, args: [{ isSignal: true, alias: "col", required: true }] }], index: [{ type: i0.Input, args: [{ isSignal: true, alias: "index", required: true }] }], highlight: [{ type: i0.Input, args: [{ isSignal: true, alias: "highlight", required: false }] }], highlightInvalid: [{ type: i0.Input, args: [{ isSignal: true, alias: "highlightInvalid", required: false }] }], highlightResize: [{ type: i0.Input, args: [{ isSignal: true, alias: "highlightResize", required: false }] }], highlightPreview: [{ type: i0.Input, args: [{ isSignal: true, alias: "highlightPreview", required: false }] }], editMode: [{ type: i0.Input, args: [{ isSignal: true, alias: "editMode", required: false }] }], dragEnter: [{ type: i0.Output, args: ["dragEnter"] }], dragExit: [{ type: i0.Output, args: ["dragExit"] }], dragOver: [{ type: i0.Output, args: ["dragOver"] }], dragDrop: [{ type: i0.Output, args: ["dragDrop"] }] } });
2429
2591
 
2430
2592
  /**
2431
2593
  * Context menu component for empty dashboard cells.
@@ -2447,11 +2609,13 @@ class EmptyCellContextMenuComponent {
2447
2609
  return menu
2448
2610
  ? { left: `${menu.x}px`, top: `${menu.y}px` }
2449
2611
  : { left: '0px', top: '0px' };
2450
- }, ...(ngDevMode ? [{ debugName: "menuPosition" }] : /* istanbul ignore next */ []));
2612
+ }, /* @ts-ignore */
2613
+ ...(ngDevMode ? [{ debugName: "menuPosition" }] : /* istanbul ignore next */ []));
2451
2614
  menuItems = computed(() => {
2452
2615
  const menu = this.menuService.activeMenu();
2453
2616
  return menu?.items || [];
2454
- }, ...(ngDevMode ? [{ debugName: "menuItems" }] : /* istanbul ignore next */ []));
2617
+ }, /* @ts-ignore */
2618
+ ...(ngDevMode ? [{ debugName: "menuItems" }] : /* istanbul ignore next */ []));
2455
2619
  constructor() {
2456
2620
  // Material Menu has a backdrop that blocks events from reaching other elements.
2457
2621
  // When any right-click occurs while menu is open, we need to:
@@ -2523,8 +2687,8 @@ class EmptyCellContextMenuComponent {
2523
2687
  sanitizeSvg(svg) {
2524
2688
  return this.#sanitizer.bypassSecurityTrustHtml(svg);
2525
2689
  }
2526
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: EmptyCellContextMenuComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2527
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.10", type: EmptyCellContextMenuComponent, isStandalone: true, selector: "lib-empty-cell-context-menu", viewQueries: [{ propertyName: "menuTrigger", first: true, predicate: ["menuTrigger"], descendants: true, read: MatMenuTrigger, isSignal: true }], ngImport: i0, template: `
2690
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: EmptyCellContextMenuComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2691
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.4", type: EmptyCellContextMenuComponent, isStandalone: true, selector: "lib-empty-cell-context-menu", viewQueries: [{ propertyName: "menuTrigger", first: true, predicate: ["menuTrigger"], descendants: true, read: MatMenuTrigger, isSignal: true }], ngImport: i0, template: `
2528
2692
  <!-- Hidden trigger for menu positioned at exact mouse coordinates
2529
2693
 
2530
2694
  IMPORTANT: Angular Material applies its own positioning logic to menus,
@@ -2584,7 +2748,7 @@ class EmptyCellContextMenuComponent {
2584
2748
  </mat-menu>
2585
2749
  `, isInline: true, styles: [":host{display:contents}.empty-cell-widget-menu{max-height:400px;overflow-y:auto}.widget-icon{width:24px;height:24px;display:inline-flex;align-items:center;justify-content:center;flex-shrink:0;vertical-align:middle}.widget-icon :deep(svg){width:20px;height:20px;fill:currentColor}\n"], dependencies: [{ kind: "ngmodule", type: MatMenuModule }, { kind: "component", type: i1$1.MatMenu, selector: "mat-menu", inputs: ["backdropClass", "aria-label", "aria-labelledby", "aria-describedby", "xPosition", "yPosition", "overlapTrigger", "hasBackdrop", "class", "classList"], outputs: ["closed", "close"], exportAs: ["matMenu"] }, { kind: "component", type: i1$1.MatMenuItem, selector: "[mat-menu-item]", inputs: ["role", "disabled", "disableRipple"], exportAs: ["matMenuItem"] }, { kind: "directive", type: i1$1.MatMenuTrigger, selector: "[mat-menu-trigger-for], [matMenuTriggerFor]", inputs: ["mat-menu-trigger-for", "matMenuTriggerFor", "matMenuTriggerData", "matMenuTriggerRestoreFocus"], outputs: ["menuOpened", "onMenuOpen", "menuClosed", "onMenuClose"], exportAs: ["matMenuTrigger"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i2.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatDividerModule }, { kind: "component", type: i3.MatDivider, selector: "mat-divider", inputs: ["vertical", "inset"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i4.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
2586
2750
  }
2587
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: EmptyCellContextMenuComponent, decorators: [{
2751
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: EmptyCellContextMenuComponent, decorators: [{
2588
2752
  type: Component,
2589
2753
  args: [{ selector: 'lib-empty-cell-context-menu', standalone: true, imports: [
2590
2754
  MatMenuModule,
@@ -2652,18 +2816,173 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.10", ngImpo
2652
2816
  `, styles: [":host{display:contents}.empty-cell-widget-menu{max-height:400px;overflow-y:auto}.widget-icon{width:24px;height:24px;display:inline-flex;align-items:center;justify-content:center;flex-shrink:0;vertical-align:middle}.widget-icon :deep(svg){width:20px;height:20px;fill:currentColor}\n"] }]
2653
2817
  }], ctorParameters: () => [], propDecorators: { menuTrigger: [{ type: i0.ViewChild, args: ['menuTrigger', { ...{ read: MatMenuTrigger }, isSignal: true }] }] } });
2654
2818
 
2819
+ // grid-resize-handle.component.ts
2820
+ //
2821
+ // A small, self-contained drag handle for resizing the dashboard grid's
2822
+ // row/column counts from the editor. Uses PointerEvents (with pointer capture)
2823
+ // so mouse, touch and pen all work uniformly, and reports a whole-grid delta
2824
+ // in track counts rather than a per-widget span change.
2825
+ //
2826
+ // The handle is intentionally store-agnostic: it converts pointer movement
2827
+ // into track-count deltas using the cell footprint passed in, and emits them.
2828
+ // The editor owns the actual grid mutation (clamp-to-content lives there).
2829
+ /**
2830
+ * Round half away from zero so inward and outward half-cell drags behave
2831
+ * symmetrically. `Math.round` rounds 0.5 to 1 but -0.5 to 0, which makes a
2832
+ * half-cell shrink "stick" while a half-cell grow responds.
2833
+ */
2834
+ function symmetricRound(value) {
2835
+ return Math.sign(value) * Math.round(Math.abs(value));
2836
+ }
2837
+ class GridResizeHandleComponent {
2838
+ axis = input.required(/* @ts-ignore */
2839
+ ...(ngDevMode ? [{ debugName: "axis" }] : /* istanbul ignore next */ []));
2840
+ /** Current grid cell footprint in px; converts pointer delta to track counts. */
2841
+ cellWidth = input.required(/* @ts-ignore */
2842
+ ...(ngDevMode ? [{ debugName: "cellWidth" }] : /* istanbul ignore next */ []));
2843
+ cellHeight = input.required(/* @ts-ignore */
2844
+ ...(ngDevMode ? [{ debugName: "cellHeight" }] : /* istanbul ignore next */ []));
2845
+ resizeMove = output();
2846
+ resizeEnd = output();
2847
+ #host = inject(ElementRef);
2848
+ #renderer = inject(Renderer2);
2849
+ #destroyRef = inject(DestroyRef);
2850
+ isActive = signal(false, /* @ts-ignore */
2851
+ ...(ngDevMode ? [{ debugName: "isActive" }] : /* istanbul ignore next */ []));
2852
+ #startX = 0;
2853
+ #startY = 0;
2854
+ // Cell footprint snapshot taken at gesture start. Reading the live signal
2855
+ // would feed back on itself: the editor's live preview shrinks cells as
2856
+ // columns are added, which would shrink this px->track divisor mid-drag and
2857
+ // make the grid overshoot.
2858
+ #cellWidth = 0;
2859
+ #cellHeight = 0;
2860
+ #pointerId = null;
2861
+ #delta = { deltaColumns: 0, deltaRows: 0 };
2862
+ #cleanup;
2863
+ constructor() {
2864
+ // Defensive: drop listeners / capture if the handle is torn down mid-drag.
2865
+ this.#destroyRef.onDestroy(() => this.#stop());
2866
+ }
2867
+ onResizeStart(event) {
2868
+ event.preventDefault();
2869
+ event.stopPropagation();
2870
+ this.#startX = event.clientX;
2871
+ this.#startY = event.clientY;
2872
+ this.#cellWidth = this.cellWidth();
2873
+ this.#cellHeight = this.cellHeight();
2874
+ this.#delta = { deltaColumns: 0, deltaRows: 0 };
2875
+ this.isActive.set(true);
2876
+ // Capture so the gesture keeps receiving events even if the pointer leaves
2877
+ // the thin handle strip. try/catch tolerates environments without capture
2878
+ // support (TypeError) and synthetic test events with no active pointer.
2879
+ try {
2880
+ this.#host.nativeElement.setPointerCapture(event.pointerId);
2881
+ this.#pointerId = event.pointerId;
2882
+ }
2883
+ catch {
2884
+ // No capture; the document listeners still drive the gesture.
2885
+ }
2886
+ // Document/window listeners only exist while actively dragging this handle.
2887
+ const unlistenMove = this.#renderer.listen('document', 'pointermove', this.#onMove);
2888
+ const unlistenUp = this.#renderer.listen('document', 'pointerup', this.#onUp);
2889
+ const unlistenCancel = this.#renderer.listen('document', 'pointercancel', this.#onAbort);
2890
+ // A pointerup that lands outside the window never fires; window blur is the
2891
+ // backstop that aborts a gesture the browser has otherwise abandoned.
2892
+ const unlistenBlur = this.#renderer.listen('window', 'blur', this.#onAbort);
2893
+ this.#cleanup = () => {
2894
+ unlistenMove();
2895
+ unlistenUp();
2896
+ unlistenCancel();
2897
+ unlistenBlur();
2898
+ };
2899
+ this.#renderer.addClass(document.body, this.#cursorClass());
2900
+ }
2901
+ // Arrow fields keep `this` bound when used as Renderer2 listener callbacks.
2902
+ #onMove = (event) => {
2903
+ const axis = this.axis();
2904
+ const next = {
2905
+ deltaColumns: axis !== 'vertical' && this.#cellWidth > 0
2906
+ ? symmetricRound((event.clientX - this.#startX) / this.#cellWidth)
2907
+ : 0,
2908
+ deltaRows: axis !== 'horizontal' && this.#cellHeight > 0
2909
+ ? symmetricRound((event.clientY - this.#startY) / this.#cellHeight)
2910
+ : 0,
2911
+ };
2912
+ // Only emit when the rounded track delta actually changes, so sub-cell
2913
+ // pointer movement doesn't spam the live preview.
2914
+ if (next.deltaColumns === this.#delta.deltaColumns &&
2915
+ next.deltaRows === this.#delta.deltaRows) {
2916
+ return;
2917
+ }
2918
+ this.#delta = next;
2919
+ this.resizeMove.emit(next);
2920
+ };
2921
+ #onUp = () => this.#finish(this.#delta);
2922
+ // Abort (pointercancel / window blur): discard the in-progress delta so the
2923
+ // editor clears the preview without committing a half-finished gesture.
2924
+ #onAbort = () => this.#finish({ deltaColumns: 0, deltaRows: 0 });
2925
+ #finish(delta) {
2926
+ if (!this.isActive())
2927
+ return;
2928
+ this.#stop();
2929
+ this.resizeEnd.emit(delta);
2930
+ }
2931
+ #stop() {
2932
+ this.isActive.set(false);
2933
+ this.#renderer.removeClass(document.body, this.#cursorClass());
2934
+ if (this.#pointerId !== null) {
2935
+ try {
2936
+ this.#host.nativeElement.releasePointerCapture(this.#pointerId);
2937
+ }
2938
+ catch {
2939
+ // Capture may already be gone (e.g. pointercancel released it).
2940
+ }
2941
+ }
2942
+ this.#pointerId = null;
2943
+ this.#cleanup?.();
2944
+ this.#cleanup = undefined;
2945
+ }
2946
+ #cursorClass() {
2947
+ switch (this.axis()) {
2948
+ case 'horizontal':
2949
+ return 'cursor-col-resize';
2950
+ case 'vertical':
2951
+ return 'cursor-row-resize';
2952
+ default:
2953
+ return 'cursor-nwse-resize';
2954
+ }
2955
+ }
2956
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: GridResizeHandleComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2957
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.0.4", type: GridResizeHandleComponent, isStandalone: true, selector: "lib-grid-resize-handle", inputs: { axis: { classPropertyName: "axis", publicName: "axis", isSignal: true, isRequired: true, transformFunction: null }, cellWidth: { classPropertyName: "cellWidth", publicName: "cellWidth", isSignal: true, isRequired: true, transformFunction: null }, cellHeight: { classPropertyName: "cellHeight", publicName: "cellHeight", isSignal: true, isRequired: true, transformFunction: null } }, outputs: { resizeMove: "resizeMove", resizeEnd: "resizeEnd" }, host: { listeners: { "pointerdown": "onResizeStart($event)" }, properties: { "class": "'axis-' + axis()", "class.is-active": "isActive()" } }, ngImport: i0, template: "<!-- grid-resize-handle.component.html -->\n<div class=\"grip-line\"></div>\n", styles: [":root .cursor-col-resize{cursor:col-resize!important}:root .cursor-row-resize{cursor:row-resize!important}:root .cursor-nwse-resize{cursor:nwse-resize!important}:host{position:absolute;z-index:25;display:flex;align-items:center;justify-content:center;opacity:.3;transition:opacity .15s ease;touch-action:none}:host(.axis-horizontal){cursor:col-resize;top:0;right:0;width:max(var(--gutter-size, 8px),6px);height:100%}:host(.axis-vertical){cursor:row-resize;left:0;bottom:0;width:100%;height:max(var(--gutter-size, 8px),6px)}:host(.axis-both){cursor:nwse-resize;right:0;bottom:0;width:max(var(--gutter-size, 8px),8px);height:max(var(--gutter-size, 8px),8px);z-index:26}:host(:hover),:host(.is-active){opacity:1}.grip-line{background-color:var(--mat-sys-outline-variant);border-radius:2px}:host(.axis-horizontal) .grip-line{width:4px;height:48px;max-height:60%}:host(.axis-vertical) .grip-line{width:48px;max-width:60%;height:4px}:host(.axis-both) .grip-line{width:6px;height:6px;border-bottom-right-radius:3px}:host(:hover) .grip-line,:host(.is-active) .grip-line{background-color:var(--mat-sys-primary)}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
2958
+ }
2959
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: GridResizeHandleComponent, decorators: [{
2960
+ type: Component,
2961
+ args: [{ selector: 'lib-grid-resize-handle', standalone: true, imports: [], changeDetection: ChangeDetectionStrategy.OnPush, host: {
2962
+ '[class]': "'axis-' + axis()",
2963
+ '[class.is-active]': 'isActive()',
2964
+ '(pointerdown)': 'onResizeStart($event)',
2965
+ }, template: "<!-- grid-resize-handle.component.html -->\n<div class=\"grip-line\"></div>\n", styles: [":root .cursor-col-resize{cursor:col-resize!important}:root .cursor-row-resize{cursor:row-resize!important}:root .cursor-nwse-resize{cursor:nwse-resize!important}:host{position:absolute;z-index:25;display:flex;align-items:center;justify-content:center;opacity:.3;transition:opacity .15s ease;touch-action:none}:host(.axis-horizontal){cursor:col-resize;top:0;right:0;width:max(var(--gutter-size, 8px),6px);height:100%}:host(.axis-vertical){cursor:row-resize;left:0;bottom:0;width:100%;height:max(var(--gutter-size, 8px),6px)}:host(.axis-both){cursor:nwse-resize;right:0;bottom:0;width:max(var(--gutter-size, 8px),8px);height:max(var(--gutter-size, 8px),8px);z-index:26}:host(:hover),:host(.is-active){opacity:1}.grip-line{background-color:var(--mat-sys-outline-variant);border-radius:2px}:host(.axis-horizontal) .grip-line{width:4px;height:48px;max-height:60%}:host(.axis-vertical) .grip-line{width:48px;max-width:60%;height:4px}:host(.axis-both) .grip-line{width:6px;height:6px;border-bottom-right-radius:3px}:host(:hover) .grip-line,:host(.is-active) .grip-line{background-color:var(--mat-sys-primary)}\n"] }]
2966
+ }], ctorParameters: () => [], propDecorators: { axis: [{ type: i0.Input, args: [{ isSignal: true, alias: "axis", required: true }] }], cellWidth: [{ type: i0.Input, args: [{ isSignal: true, alias: "cellWidth", required: true }] }], cellHeight: [{ type: i0.Input, args: [{ isSignal: true, alias: "cellHeight", required: true }] }], resizeMove: [{ type: i0.Output, args: ["resizeMove"] }], resizeEnd: [{ type: i0.Output, args: ["resizeEnd"] }] } });
2967
+
2655
2968
  // dashboard-editor.component.ts
2656
2969
  class DashboardEditorComponent {
2657
2970
  bottomGridRef = viewChild.required('bottomGrid');
2658
- dropZones = viewChildren(DropZoneComponent, ...(ngDevMode ? [{ debugName: "dropZones" }] : /* istanbul ignore next */ []));
2659
- cellComponents = viewChildren(CellComponent, ...(ngDevMode ? [{ debugName: "cellComponents" }] : /* istanbul ignore next */ []));
2971
+ dropZones = viewChildren(DropZoneComponent, /* @ts-ignore */
2972
+ ...(ngDevMode ? [{ debugName: "dropZones" }] : /* istanbul ignore next */ []));
2973
+ cellComponents = viewChildren(CellComponent, /* @ts-ignore */
2974
+ ...(ngDevMode ? [{ debugName: "cellComponents" }] : /* istanbul ignore next */ []));
2660
2975
  #store = inject(DashboardStore);
2661
2976
  #destroyRef = inject(DestroyRef);
2662
2977
  #resizeObserver;
2663
- rows = input.required(...(ngDevMode ? [{ debugName: "rows" }] : /* istanbul ignore next */ []));
2664
- columns = input.required(...(ngDevMode ? [{ debugName: "columns" }] : /* istanbul ignore next */ []));
2665
- gutterSize = input('1em', ...(ngDevMode ? [{ debugName: "gutterSize" }] : /* istanbul ignore next */ []));
2666
- gutters = computed(() => this.columns() + 1, ...(ngDevMode ? [{ debugName: "gutters" }] : /* istanbul ignore next */ []));
2978
+ rows = input.required(/* @ts-ignore */
2979
+ ...(ngDevMode ? [{ debugName: "rows" }] : /* istanbul ignore next */ []));
2980
+ columns = input.required(/* @ts-ignore */
2981
+ ...(ngDevMode ? [{ debugName: "columns" }] : /* istanbul ignore next */ []));
2982
+ gutterSize = input('1em', /* @ts-ignore */
2983
+ ...(ngDevMode ? [{ debugName: "gutterSize" }] : /* istanbul ignore next */ []));
2984
+ // Emitted when a grid resize handle commits a new size (after clamp-to-content).
2985
+ gridResized = output();
2667
2986
  // store signals
2668
2987
  cells = this.#store.cells;
2669
2988
  highlightedZones = this.#store.highlightedZones;
@@ -2671,21 +2990,48 @@ class DashboardEditorComponent {
2671
2990
  invalidHighlightMap = this.#store.invalidHighlightMap;
2672
2991
  hoveredDropZone = this.#store.hoveredDropZone;
2673
2992
  resizePreviewMap = this.#store.resizePreviewMap;
2674
- // Generate all possible cell positions for the grid
2993
+ cellDimensions = this.#store.gridCellDimensions;
2994
+ gridResizePreview = this.#store.gridResizePreview;
2995
+ // Effective grid size (live preview when dragging, else committed) — shared
2996
+ // from the store so the editor grid, the outer frame and the viewport
2997
+ // letterboxing all reflow together. Drives the grid template, aspect-ratio
2998
+ // and drop zones so the editor reflows under the handle without committing.
2999
+ effectiveRows = this.#store.effectiveRows;
3000
+ effectiveColumns = this.#store.effectiveColumns;
3001
+ // Hide grid resize handles while a widget drag is in progress to avoid
3002
+ // conflicting gestures.
3003
+ isDragActive = this.#store.isDragActive;
3004
+ // Axes rendered as grid resize handles (right edge, bottom edge, corner).
3005
+ resizeAxes = [
3006
+ 'horizontal',
3007
+ 'vertical',
3008
+ 'both',
3009
+ ];
3010
+ // Generate all possible cell positions for the grid (using the effective
3011
+ // size so the drop-zone grid grows/shrinks with the live resize preview).
2675
3012
  dropzonePositions = computed(() => {
3013
+ const rows = this.effectiveRows();
3014
+ const columns = this.effectiveColumns();
2676
3015
  const positions = [];
2677
- for (let row = 1; row <= this.rows(); row++) {
2678
- for (let col = 1; col <= this.columns(); col++) {
3016
+ for (let row = 1; row <= rows; row++) {
3017
+ for (let col = 1; col <= columns; col++) {
2679
3018
  positions.push({
2680
3019
  row,
2681
3020
  col,
2682
3021
  id: `dropzone-${row}-${col}`,
2683
- index: (row - 1) * this.columns() + col,
3022
+ index: (row - 1) * columns + col,
2684
3023
  });
2685
3024
  }
2686
3025
  }
2687
3026
  return positions;
2688
- }, ...(ngDevMode ? [{ debugName: "dropzonePositions" }] : /* istanbul ignore next */ []));
3027
+ }, /* @ts-ignore */
3028
+ ...(ngDevMode ? [{ debugName: "dropzonePositions" }] : /* istanbul ignore next */ []));
3029
+ // True for drop zones in the about-to-be-added region during a grow preview,
3030
+ // so the template can tint the tracks the resize is adding.
3031
+ isPreviewAdded(row, col) {
3032
+ return (this.gridResizePreview() !== null &&
3033
+ (row > this.rows() || col > this.columns()));
3034
+ }
2689
3035
  // Helper method for template
2690
3036
  createCellId(row, col) {
2691
3037
  return CellIdUtils.create(row, col);
@@ -2707,6 +3053,10 @@ class DashboardEditorComponent {
2707
3053
  effect(() => {
2708
3054
  this.#store.setEditMode(true);
2709
3055
  });
3056
+ // Drop any in-progress resize preview if the editor is torn down mid-drag
3057
+ // (e.g. editMode toggled off): the store outlives this component, so a
3058
+ // stale preview would otherwise render a phantom grid on the next mount.
3059
+ this.#destroyRef.onDestroy(() => this.#store.clearGridResizePreview());
2710
3060
  }
2711
3061
  #observeGridSize() {
2712
3062
  const gridEl = this.bottomGridRef()?.nativeElement;
@@ -2757,28 +3107,41 @@ class DashboardEditorComponent {
2757
3107
  this.#store.handleDrop(event.data, event.target);
2758
3108
  // Note: Store handles all validation and error handling internally
2759
3109
  }
2760
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: DashboardEditorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2761
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.10", type: DashboardEditorComponent, isStandalone: true, selector: "ngx-dashboard-editor", 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 } }, host: { properties: { "style.--rows": "rows()", "style.--columns": "columns()", "style.--gutter-size": "gutterSize()", "style.--gutters": "gutters()", "class.is-edit-mode": "true" } }, providers: [
3110
+ // Live preview while dragging a handle: the store reflows the grid to the
3111
+ // clamped target size without committing.
3112
+ onGridResizeMove(delta) {
3113
+ this.#store.previewGridResize(delta.deltaRows, delta.deltaColumns);
3114
+ }
3115
+ // Commit a grid resize gesture. The store clears the preview, no-ops on a
3116
+ // zero delta, and otherwise commits the clamped relative resize.
3117
+ onGridResizeEnd(delta) {
3118
+ const result = this.#store.endGridResize(delta.deltaRows, delta.deltaColumns);
3119
+ if (result)
3120
+ this.gridResized.emit(result);
3121
+ }
3122
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: DashboardEditorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
3123
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.4", type: DashboardEditorComponent, isStandalone: true, selector: "ngx-dashboard-editor", 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 } }, outputs: { gridResized: "gridResized" }, host: { properties: { "style.--rows": "effectiveRows()", "style.--columns": "effectiveColumns()", "style.--gutter-size": "gutterSize()", "style.--gutters": "effectiveColumns() + 1", "class.is-edit-mode": "true" } }, providers: [
2762
3124
  CellContextMenuService,
2763
- ], viewQueries: [{ propertyName: "bottomGridRef", first: true, predicate: ["bottomGrid"], descendants: true, isSignal: true }, { propertyName: "dropZones", predicate: DropZoneComponent, descendants: true, isSignal: true }, { propertyName: "cellComponents", predicate: CellComponent, descendants: true, isSignal: true }], ngImport: i0, template: "<!-- dashboard-editor.component.html -->\n<div class=\"grid-container\">\n <!-- Bottom grid with drop zones -->\n <div class=\"grid\" id=\"bottom-grid\" #bottomGrid>\n @for (position of dropzonePositions(); track position.id) {\n <lib-drop-zone\n class=\"drop-zone\"\n [row]=\"position.row\"\n [col]=\"position.col\"\n [index]=\"position.index\"\n [highlight]=\"highlightMap().has(createCellId(position.row, position.col))\"\n [highlightInvalid]=\"\n invalidHighlightMap().has(createCellId(position.row, position.col))\n \"\n [highlightResize]=\"\n resizePreviewMap().has(createCellId(position.row, position.col))\n \"\n [editMode]=\"true\"\n (dragEnter)=\"onDragEnter($event)\"\n (dragExit)=\"onDragExit()\"\n (dragOver)=\"onDragOver($event)\"\n (dragDrop)=\"onDragDrop($event)\"\n ></lib-drop-zone>\n }\n </div>\n\n <!-- Top grid with interactive cells -->\n <div class=\"grid\" id=\"top-grid\">\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]=\"true\"\n [draggable]=\"true\"\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 (dragStart)=\"onCellDragStart($event)\"\n (dragEnd)=\"dragEnd()\"\n (delete)=\"onCellDelete($event)\"\n (settings)=\"onCellSettings($event)\"\n (resizeStart)=\"onCellResizeStart($event)\"\n (resizeMove)=\"onCellResizeMove($event)\"\n (resizeEnd)=\"onCellResizeEnd($event)\"\n >\n </lib-cell>\n }\n </div>\n</div>\n\n<!-- Context menus -->\n<lib-cell-context-menu></lib-cell-context-menu>\n<lib-empty-cell-context-menu></lib-empty-cell-context-menu>\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}:host .grid{background-image:linear-gradient(to right,rgba(100,100,100,.12) 1px,transparent 1px),linear-gradient(to bottom,rgba(100,100,100,.12) 1px,transparent 1px),linear-gradient(to bottom,rgba(100,100,100,.12) 1px,transparent 1px);background-size:var(--tile-size) var(--tile-size),var(--tile-size) var(--tile-size),100% 1px;background-position:var(--tile-offset) var(--tile-offset),var(--tile-offset) var(--tile-offset),bottom;background-repeat:repeat,repeat,no-repeat}.grid-container{position:relative;width:100%;height:100%}.grid{display:grid;gap:var(--gutter-size);padding:var(--gutter-size);position:absolute;inset:0;width:100%;height:100%;box-sizing:border-box;align-items:stretch;justify-items:stretch;grid-template-columns:repeat(var(--columns),var(--cell-size));grid-template-rows:repeat(var(--rows),var(--cell-size))}#bottom-grid{z-index:1}#top-grid{z-index:2;pointer-events:none}.grid-cell{pointer-events:auto}.grid-cell.is-dragging{pointer-events:none;opacity:.5}\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"] }, { kind: "component", type: DropZoneComponent, selector: "lib-drop-zone", inputs: ["row", "col", "index", "highlight", "highlightInvalid", "highlightResize", "editMode"], outputs: ["dragEnter", "dragExit", "dragOver", "dragDrop"] }, { kind: "component", type: CellContextMenuComponent, selector: "lib-cell-context-menu" }, { kind: "component", type: EmptyCellContextMenuComponent, selector: "lib-empty-cell-context-menu" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
3125
+ ], viewQueries: [{ propertyName: "bottomGridRef", first: true, predicate: ["bottomGrid"], descendants: true, isSignal: true }, { propertyName: "dropZones", predicate: DropZoneComponent, descendants: true, isSignal: true }, { propertyName: "cellComponents", predicate: CellComponent, descendants: true, isSignal: true }], ngImport: i0, template: "<!-- dashboard-editor.component.html -->\n<div class=\"grid-container\">\n <!-- Bottom grid with drop zones -->\n <div class=\"grid\" id=\"bottom-grid\" #bottomGrid>\n @for (position of dropzonePositions(); track position.id) {\n <lib-drop-zone\n class=\"drop-zone\"\n [row]=\"position.row\"\n [col]=\"position.col\"\n [index]=\"position.index\"\n [highlight]=\"highlightMap().has(createCellId(position.row, position.col))\"\n [highlightInvalid]=\"\n invalidHighlightMap().has(createCellId(position.row, position.col))\n \"\n [highlightResize]=\"\n resizePreviewMap().has(createCellId(position.row, position.col))\n \"\n [highlightPreview]=\"isPreviewAdded(position.row, position.col)\"\n [editMode]=\"true\"\n (dragEnter)=\"onDragEnter($event)\"\n (dragExit)=\"onDragExit()\"\n (dragOver)=\"onDragOver($event)\"\n (dragDrop)=\"onDragDrop($event)\"\n ></lib-drop-zone>\n }\n </div>\n\n <!-- Grid resize handles (right edge, bottom edge, bottom-right corner).\n Hidden during a widget drag to avoid conflicting gestures. -->\n @if (!isDragActive()) {\n @for (axis of resizeAxes; track axis) {\n <lib-grid-resize-handle\n [axis]=\"axis\"\n [cellWidth]=\"cellDimensions().width\"\n [cellHeight]=\"cellDimensions().height\"\n (resizeMove)=\"onGridResizeMove($event)\"\n (resizeEnd)=\"onGridResizeEnd($event)\"\n ></lib-grid-resize-handle>\n }\n }\n\n <!-- Live size badge shown while a resize handle is being dragged. -->\n @if (gridResizePreview(); as preview) {\n <div\n class=\"grid-resize-badge\"\n [class.grid-resize-badge--clamped]=\"preview.clamped\"\n i18n=\"@@ngx.dashboard.editor.gridResize.dimensions\"\n >\n {{ preview.columns }} \u00D7 {{ preview.rows }}\n </div>\n }\n\n <!-- Top grid with interactive cells -->\n <div class=\"grid\" id=\"top-grid\">\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]=\"true\"\n [draggable]=\"true\"\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 (dragStart)=\"onCellDragStart($event)\"\n (dragEnd)=\"dragEnd()\"\n (delete)=\"onCellDelete($event)\"\n (settings)=\"onCellSettings($event)\"\n (resizeStart)=\"onCellResizeStart($event)\"\n (resizeMove)=\"onCellResizeMove($event)\"\n (resizeEnd)=\"onCellResizeEnd($event)\"\n >\n </lib-cell>\n }\n </div>\n</div>\n\n<!-- Context menus -->\n<lib-cell-context-menu></lib-cell-context-menu>\n<lib-empty-cell-context-menu></lib-empty-cell-context-menu>\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}:host .grid{background-image:linear-gradient(to right,rgba(100,100,100,.12) 1px,transparent 1px),linear-gradient(to bottom,rgba(100,100,100,.12) 1px,transparent 1px),linear-gradient(to bottom,rgba(100,100,100,.12) 1px,transparent 1px);background-size:var(--tile-size) var(--tile-size),var(--tile-size) var(--tile-size),100% 1px;background-position:var(--tile-offset) var(--tile-offset),var(--tile-offset) var(--tile-offset),bottom;background-repeat:repeat,repeat,no-repeat}.grid-container{position:relative;width:100%;height:100%}.grid{display:grid;gap:var(--gutter-size);padding:var(--gutter-size);position:absolute;inset:0;width:100%;height:100%;box-sizing:border-box;align-items:stretch;justify-items:stretch;grid-template-columns:repeat(var(--columns),var(--cell-size));grid-template-rows:repeat(var(--rows),var(--cell-size))}#bottom-grid{z-index:1}#top-grid{z-index:2;pointer-events:none}.grid-cell{pointer-events:auto}.grid-cell.is-dragging{pointer-events:none;opacity:.5}.grid-resize-badge{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);z-index:30;padding:4px 12px;border-radius:4px;font-size:14px;font-weight:500;pointer-events:none;background-color:var(--mat-sys-primary);color:var(--mat-sys-on-primary)}.grid-resize-badge--clamped{background-color:var(--mat-sys-error);color:var(--mat-sys-on-error)}\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"] }, { kind: "component", type: DropZoneComponent, selector: "lib-drop-zone", inputs: ["row", "col", "index", "highlight", "highlightInvalid", "highlightResize", "highlightPreview", "editMode"], outputs: ["dragEnter", "dragExit", "dragOver", "dragDrop"] }, { kind: "component", type: CellContextMenuComponent, selector: "lib-cell-context-menu" }, { kind: "component", type: EmptyCellContextMenuComponent, selector: "lib-empty-cell-context-menu" }, { kind: "component", type: GridResizeHandleComponent, selector: "lib-grid-resize-handle", inputs: ["axis", "cellWidth", "cellHeight"], outputs: ["resizeMove", "resizeEnd"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
2764
3126
  }
2765
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: DashboardEditorComponent, decorators: [{
3127
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: DashboardEditorComponent, decorators: [{
2766
3128
  type: Component,
2767
3129
  args: [{ selector: 'ngx-dashboard-editor', standalone: true, imports: [
2768
3130
  CellComponent,
2769
3131
  DropZoneComponent,
2770
3132
  CellContextMenuComponent,
2771
- EmptyCellContextMenuComponent
3133
+ EmptyCellContextMenuComponent,
3134
+ GridResizeHandleComponent
2772
3135
  ], providers: [
2773
3136
  CellContextMenuService,
2774
3137
  ], changeDetection: ChangeDetectionStrategy.OnPush, host: {
2775
- '[style.--rows]': 'rows()',
2776
- '[style.--columns]': 'columns()',
3138
+ '[style.--rows]': 'effectiveRows()',
3139
+ '[style.--columns]': 'effectiveColumns()',
2777
3140
  '[style.--gutter-size]': 'gutterSize()',
2778
- '[style.--gutters]': 'gutters()',
3141
+ '[style.--gutters]': 'effectiveColumns() + 1',
2779
3142
  '[class.is-edit-mode]': 'true', // Always in edit mode
2780
- }, template: "<!-- dashboard-editor.component.html -->\n<div class=\"grid-container\">\n <!-- Bottom grid with drop zones -->\n <div class=\"grid\" id=\"bottom-grid\" #bottomGrid>\n @for (position of dropzonePositions(); track position.id) {\n <lib-drop-zone\n class=\"drop-zone\"\n [row]=\"position.row\"\n [col]=\"position.col\"\n [index]=\"position.index\"\n [highlight]=\"highlightMap().has(createCellId(position.row, position.col))\"\n [highlightInvalid]=\"\n invalidHighlightMap().has(createCellId(position.row, position.col))\n \"\n [highlightResize]=\"\n resizePreviewMap().has(createCellId(position.row, position.col))\n \"\n [editMode]=\"true\"\n (dragEnter)=\"onDragEnter($event)\"\n (dragExit)=\"onDragExit()\"\n (dragOver)=\"onDragOver($event)\"\n (dragDrop)=\"onDragDrop($event)\"\n ></lib-drop-zone>\n }\n </div>\n\n <!-- Top grid with interactive cells -->\n <div class=\"grid\" id=\"top-grid\">\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]=\"true\"\n [draggable]=\"true\"\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 (dragStart)=\"onCellDragStart($event)\"\n (dragEnd)=\"dragEnd()\"\n (delete)=\"onCellDelete($event)\"\n (settings)=\"onCellSettings($event)\"\n (resizeStart)=\"onCellResizeStart($event)\"\n (resizeMove)=\"onCellResizeMove($event)\"\n (resizeEnd)=\"onCellResizeEnd($event)\"\n >\n </lib-cell>\n }\n </div>\n</div>\n\n<!-- Context menus -->\n<lib-cell-context-menu></lib-cell-context-menu>\n<lib-empty-cell-context-menu></lib-empty-cell-context-menu>\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}:host .grid{background-image:linear-gradient(to right,rgba(100,100,100,.12) 1px,transparent 1px),linear-gradient(to bottom,rgba(100,100,100,.12) 1px,transparent 1px),linear-gradient(to bottom,rgba(100,100,100,.12) 1px,transparent 1px);background-size:var(--tile-size) var(--tile-size),var(--tile-size) var(--tile-size),100% 1px;background-position:var(--tile-offset) var(--tile-offset),var(--tile-offset) var(--tile-offset),bottom;background-repeat:repeat,repeat,no-repeat}.grid-container{position:relative;width:100%;height:100%}.grid{display:grid;gap:var(--gutter-size);padding:var(--gutter-size);position:absolute;inset:0;width:100%;height:100%;box-sizing:border-box;align-items:stretch;justify-items:stretch;grid-template-columns:repeat(var(--columns),var(--cell-size));grid-template-rows:repeat(var(--rows),var(--cell-size))}#bottom-grid{z-index:1}#top-grid{z-index:2;pointer-events:none}.grid-cell{pointer-events:auto}.grid-cell.is-dragging{pointer-events:none;opacity:.5}\n"] }]
2781
- }], ctorParameters: () => [], propDecorators: { bottomGridRef: [{ type: i0.ViewChild, args: ['bottomGrid', { isSignal: true }] }], dropZones: [{ type: i0.ViewChildren, args: [i0.forwardRef(() => DropZoneComponent), { isSignal: true }] }], cellComponents: [{ type: i0.ViewChildren, args: [i0.forwardRef(() => CellComponent), { 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 }] }] } });
3143
+ }, template: "<!-- dashboard-editor.component.html -->\n<div class=\"grid-container\">\n <!-- Bottom grid with drop zones -->\n <div class=\"grid\" id=\"bottom-grid\" #bottomGrid>\n @for (position of dropzonePositions(); track position.id) {\n <lib-drop-zone\n class=\"drop-zone\"\n [row]=\"position.row\"\n [col]=\"position.col\"\n [index]=\"position.index\"\n [highlight]=\"highlightMap().has(createCellId(position.row, position.col))\"\n [highlightInvalid]=\"\n invalidHighlightMap().has(createCellId(position.row, position.col))\n \"\n [highlightResize]=\"\n resizePreviewMap().has(createCellId(position.row, position.col))\n \"\n [highlightPreview]=\"isPreviewAdded(position.row, position.col)\"\n [editMode]=\"true\"\n (dragEnter)=\"onDragEnter($event)\"\n (dragExit)=\"onDragExit()\"\n (dragOver)=\"onDragOver($event)\"\n (dragDrop)=\"onDragDrop($event)\"\n ></lib-drop-zone>\n }\n </div>\n\n <!-- Grid resize handles (right edge, bottom edge, bottom-right corner).\n Hidden during a widget drag to avoid conflicting gestures. -->\n @if (!isDragActive()) {\n @for (axis of resizeAxes; track axis) {\n <lib-grid-resize-handle\n [axis]=\"axis\"\n [cellWidth]=\"cellDimensions().width\"\n [cellHeight]=\"cellDimensions().height\"\n (resizeMove)=\"onGridResizeMove($event)\"\n (resizeEnd)=\"onGridResizeEnd($event)\"\n ></lib-grid-resize-handle>\n }\n }\n\n <!-- Live size badge shown while a resize handle is being dragged. -->\n @if (gridResizePreview(); as preview) {\n <div\n class=\"grid-resize-badge\"\n [class.grid-resize-badge--clamped]=\"preview.clamped\"\n i18n=\"@@ngx.dashboard.editor.gridResize.dimensions\"\n >\n {{ preview.columns }} \u00D7 {{ preview.rows }}\n </div>\n }\n\n <!-- Top grid with interactive cells -->\n <div class=\"grid\" id=\"top-grid\">\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]=\"true\"\n [draggable]=\"true\"\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 (dragStart)=\"onCellDragStart($event)\"\n (dragEnd)=\"dragEnd()\"\n (delete)=\"onCellDelete($event)\"\n (settings)=\"onCellSettings($event)\"\n (resizeStart)=\"onCellResizeStart($event)\"\n (resizeMove)=\"onCellResizeMove($event)\"\n (resizeEnd)=\"onCellResizeEnd($event)\"\n >\n </lib-cell>\n }\n </div>\n</div>\n\n<!-- Context menus -->\n<lib-cell-context-menu></lib-cell-context-menu>\n<lib-empty-cell-context-menu></lib-empty-cell-context-menu>\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}:host .grid{background-image:linear-gradient(to right,rgba(100,100,100,.12) 1px,transparent 1px),linear-gradient(to bottom,rgba(100,100,100,.12) 1px,transparent 1px),linear-gradient(to bottom,rgba(100,100,100,.12) 1px,transparent 1px);background-size:var(--tile-size) var(--tile-size),var(--tile-size) var(--tile-size),100% 1px;background-position:var(--tile-offset) var(--tile-offset),var(--tile-offset) var(--tile-offset),bottom;background-repeat:repeat,repeat,no-repeat}.grid-container{position:relative;width:100%;height:100%}.grid{display:grid;gap:var(--gutter-size);padding:var(--gutter-size);position:absolute;inset:0;width:100%;height:100%;box-sizing:border-box;align-items:stretch;justify-items:stretch;grid-template-columns:repeat(var(--columns),var(--cell-size));grid-template-rows:repeat(var(--rows),var(--cell-size))}#bottom-grid{z-index:1}#top-grid{z-index:2;pointer-events:none}.grid-cell{pointer-events:auto}.grid-cell.is-dragging{pointer-events:none;opacity:.5}.grid-resize-badge{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);z-index:30;padding:4px 12px;border-radius:4px;font-size:14px;font-weight:500;pointer-events:none;background-color:var(--mat-sys-primary);color:var(--mat-sys-on-primary)}.grid-resize-badge--clamped{background-color:var(--mat-sys-error);color:var(--mat-sys-on-error)}\n"] }]
3144
+ }], ctorParameters: () => [], propDecorators: { bottomGridRef: [{ type: i0.ViewChild, args: ['bottomGrid', { isSignal: true }] }], dropZones: [{ type: i0.ViewChildren, args: [i0.forwardRef(() => DropZoneComponent), { isSignal: true }] }], cellComponents: [{ type: i0.ViewChildren, args: [i0.forwardRef(() => CellComponent), { 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 }] }], gridResized: [{ type: i0.Output, args: ["gridResized"] }] } });
2782
3145
 
2783
3146
  // dashboard-bridge.service.ts
2784
3147
  /**
@@ -2789,7 +3152,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.10", ngImpo
2789
3152
  */
2790
3153
  class DashboardBridgeService {
2791
3154
  // Map of registered dashboard instances with their reactive dimensions
2792
- dashboards = signal(new Map(), ...(ngDevMode ? [{ debugName: "dashboards" }] : /* istanbul ignore next */ []));
3155
+ dashboards = signal(new Map(), /* @ts-ignore */
3156
+ ...(ngDevMode ? [{ debugName: "dashboards" }] : /* istanbul ignore next */ []));
2793
3157
  /**
2794
3158
  * Register a dashboard store instance using its dashboard ID
2795
3159
  */
@@ -2840,7 +3204,8 @@ class DashboardBridgeService {
2840
3204
  }
2841
3205
  // Return dimensions from first available dashboard with fallback for undefined
2842
3206
  return dashboardEntries[0].dimensions() || { width: 100, height: 100 };
2843
- }, ...(ngDevMode ? [{ debugName: "availableDimensions" }] : /* istanbul ignore next */ []));
3207
+ }, /* @ts-ignore */
3208
+ ...(ngDevMode ? [{ debugName: "availableDimensions" }] : /* istanbul ignore next */ []));
2844
3209
  /**
2845
3210
  * Start drag operation on the first available dashboard
2846
3211
  * (Widget lists need some dashboard to coordinate with during drag)
@@ -2863,15 +3228,18 @@ class DashboardBridgeService {
2863
3228
  /**
2864
3229
  * Get all registered dashboard IDs
2865
3230
  */
2866
- registeredDashboards = computed(() => Array.from(this.dashboards().keys()), ...(ngDevMode ? [{ debugName: "registeredDashboards" }] : /* istanbul ignore next */ []));
3231
+ registeredDashboards = computed(() => Array.from(this.dashboards().keys()), /* @ts-ignore */
3232
+ ...(ngDevMode ? [{ debugName: "registeredDashboards" }] : /* istanbul ignore next */ []));
2867
3233
  /**
2868
3234
  * Get the number of registered dashboards
2869
3235
  */
2870
- dashboardCount = computed(() => this.dashboards().size, ...(ngDevMode ? [{ debugName: "dashboardCount" }] : /* istanbul ignore next */ []));
3236
+ dashboardCount = computed(() => this.dashboards().size, /* @ts-ignore */
3237
+ ...(ngDevMode ? [{ debugName: "dashboardCount" }] : /* istanbul ignore next */ []));
2871
3238
  /**
2872
3239
  * Check if any dashboards are registered
2873
3240
  */
2874
- hasDashboards = computed(() => this.dashboards().size > 0, ...(ngDevMode ? [{ debugName: "hasDashboards" }] : /* istanbul ignore next */ []));
3241
+ hasDashboards = computed(() => this.dashboards().size > 0, /* @ts-ignore */
3242
+ ...(ngDevMode ? [{ debugName: "hasDashboards" }] : /* istanbul ignore next */ []));
2875
3243
  /**
2876
3244
  * Update registration for a dashboard store when its ID becomes available
2877
3245
  */
@@ -2906,10 +3274,10 @@ class DashboardBridgeService {
2906
3274
  getAllDashboards() {
2907
3275
  return this.dashboards();
2908
3276
  }
2909
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: DashboardBridgeService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
2910
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: DashboardBridgeService, providedIn: 'root' });
3277
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: DashboardBridgeService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
3278
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: DashboardBridgeService, providedIn: 'root' });
2911
3279
  }
2912
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: DashboardBridgeService, decorators: [{
3280
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: DashboardBridgeService, decorators: [{
2913
3281
  type: Injectable,
2914
3282
  args: [{ providedIn: 'root' }]
2915
3283
  }] });
@@ -2924,8 +3292,10 @@ class DashboardViewportService {
2924
3292
  platformId = inject(PLATFORM_ID);
2925
3293
  destroyRef = inject(DestroyRef);
2926
3294
  store = inject(DashboardStore);
2927
- viewportSize = signal({ width: 0, height: 0 }, ...(ngDevMode ? [{ debugName: "viewportSize" }] : /* istanbul ignore next */ []));
2928
- reservedSpace = signal(DEFAULT_RESERVED_SPACE, ...(ngDevMode ? [{ debugName: "reservedSpace" }] : /* istanbul ignore next */ []));
3295
+ viewportSize = signal({ width: 0, height: 0 }, /* @ts-ignore */
3296
+ ...(ngDevMode ? [{ debugName: "viewportSize" }] : /* istanbul ignore next */ []));
3297
+ reservedSpace = signal(DEFAULT_RESERVED_SPACE, /* @ts-ignore */
3298
+ ...(ngDevMode ? [{ debugName: "reservedSpace" }] : /* istanbul ignore next */ []));
2929
3299
  resizeObserver = null;
2930
3300
  constructor() {
2931
3301
  if (isPlatformBrowser(this.platformId)) {
@@ -2983,15 +3353,17 @@ class DashboardViewportService {
2983
3353
  width: Math.max(0, viewport.width - reserved.left - reserved.right),
2984
3354
  height: Math.max(0, viewport.height - reserved.top - reserved.bottom)
2985
3355
  };
2986
- }, ...(ngDevMode ? [{ debugName: "availableSpace" }] : /* istanbul ignore next */ []));
3356
+ }, /* @ts-ignore */
3357
+ ...(ngDevMode ? [{ debugName: "availableSpace" }] : /* istanbul ignore next */ []));
2987
3358
  /**
2988
3359
  * Calculate dashboard constraints for this dashboard instance
2989
3360
  */
2990
3361
  constraints = computed(() => {
2991
3362
  const availableSize = this.availableSpace();
2992
- // Get grid configuration from our component's store
2993
- const rows = this.store.rows();
2994
- const columns = this.store.columns();
3363
+ // Use the effective (preview-aware) size so the letterboxed frame reflows
3364
+ // to fit during a grid-resize drag instead of overflowing/scrolling.
3365
+ const rows = this.store.effectiveRows();
3366
+ const columns = this.store.effectiveColumns();
2995
3367
  if (rows === 0 || columns === 0) {
2996
3368
  return {
2997
3369
  maxWidth: availableSize.width,
@@ -3024,11 +3396,12 @@ class DashboardViewportService {
3024
3396
  maxHeight: Math.max(0, maxHeight),
3025
3397
  constrainedBy
3026
3398
  };
3027
- }, ...(ngDevMode ? [{ debugName: "constraints" }] : /* istanbul ignore next */ []));
3028
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: DashboardViewportService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
3029
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: DashboardViewportService });
3399
+ }, /* @ts-ignore */
3400
+ ...(ngDevMode ? [{ debugName: "constraints" }] : /* istanbul ignore next */ []));
3401
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: DashboardViewportService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
3402
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: DashboardViewportService });
3030
3403
  }
3031
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: DashboardViewportService, decorators: [{
3404
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: DashboardViewportService, decorators: [{
3032
3405
  type: Injectable
3033
3406
  }], ctorParameters: () => [] });
3034
3407
 
@@ -3046,19 +3419,28 @@ class DashboardComponent {
3046
3419
  store = this.#store;
3047
3420
  viewport = this.#viewport;
3048
3421
  // Component inputs
3049
- dashboardData = input.required(...(ngDevMode ? [{ debugName: "dashboardData" }] : /* istanbul ignore next */ []));
3050
- editMode = input(false, ...(ngDevMode ? [{ debugName: "editMode" }] : /* istanbul ignore next */ []));
3051
- reservedSpace = input(...(ngDevMode ? [undefined, { debugName: "reservedSpace" }] : /* istanbul ignore next */ []));
3052
- enableSelection = input(false, ...(ngDevMode ? [{ debugName: "enableSelection" }] : /* istanbul ignore next */ []));
3053
- selectionModifier = input(null, ...(ngDevMode ? [{ debugName: "selectionModifier" }] : /* istanbul ignore next */ []));
3054
- dragThreshold = input(4, ...(ngDevMode ? [{ debugName: "dragThreshold" }] : /* istanbul ignore next */ []));
3422
+ dashboardData = input.required(/* @ts-ignore */
3423
+ ...(ngDevMode ? [{ debugName: "dashboardData" }] : /* istanbul ignore next */ []));
3424
+ editMode = input(false, /* @ts-ignore */
3425
+ ...(ngDevMode ? [{ debugName: "editMode" }] : /* istanbul ignore next */ []));
3426
+ reservedSpace = input(/* @ts-ignore */
3427
+ ...(ngDevMode ? [undefined, { debugName: "reservedSpace" }] : /* istanbul ignore next */ []));
3428
+ enableSelection = input(false, /* @ts-ignore */
3429
+ ...(ngDevMode ? [{ debugName: "enableSelection" }] : /* istanbul ignore next */ []));
3430
+ selectionModifier = input(null, /* @ts-ignore */
3431
+ ...(ngDevMode ? [{ debugName: "selectionModifier" }] : /* istanbul ignore next */ []));
3432
+ dragThreshold = input(4, /* @ts-ignore */
3433
+ ...(ngDevMode ? [{ debugName: "dragThreshold" }] : /* istanbul ignore next */ []));
3055
3434
  // Component outputs
3056
3435
  selectionComplete = output();
3436
+ gridResized = output();
3057
3437
  // Store signals - shared by both child components
3058
3438
  cells = this.#store.cells;
3059
3439
  // ViewChild references for export/import functionality
3060
- dashboardEditor = viewChild(DashboardEditorComponent, ...(ngDevMode ? [{ debugName: "dashboardEditor" }] : /* istanbul ignore next */ []));
3061
- dashboardViewer = viewChild(DashboardViewerComponent, ...(ngDevMode ? [{ debugName: "dashboardViewer" }] : /* istanbul ignore next */ []));
3440
+ dashboardEditor = viewChild(DashboardEditorComponent, /* @ts-ignore */
3441
+ ...(ngDevMode ? [{ debugName: "dashboardEditor" }] : /* istanbul ignore next */ []));
3442
+ dashboardViewer = viewChild(DashboardViewerComponent, /* @ts-ignore */
3443
+ ...(ngDevMode ? [{ debugName: "dashboardViewer" }] : /* istanbul ignore next */ []));
3062
3444
  // Track if we're in the middle of preserving states
3063
3445
  #isPreservingStates = false;
3064
3446
  // Track if component has been initialized
@@ -3152,6 +3534,27 @@ class DashboardComponent {
3152
3534
  clearDashboard() {
3153
3535
  this.#store.clearDashboard();
3154
3536
  }
3537
+ /**
3538
+ * Resize the dashboard grid to the given row/column counts.
3539
+ *
3540
+ * Uses a clamp-to-content policy: a size that would push an existing widget
3541
+ * out of bounds is snapped up to the smallest size that still contains every
3542
+ * widget, so shrinking never orphans a widget. The applied size (which may
3543
+ * differ from the request when clamped) is returned and emitted via
3544
+ * `gridResized`. Values below 1 are treated as 1; fractional values are
3545
+ * floored.
3546
+ */
3547
+ setGridSize(rows, columns) {
3548
+ const beforeRows = this.#store.rows();
3549
+ const beforeColumns = this.#store.columns();
3550
+ const result = this.#store.setGridSize(rows, columns);
3551
+ // Only signal a resize when the committed size actually changed, matching
3552
+ // the handle-drag path (store.endGridResize).
3553
+ if (result.rows !== beforeRows || result.columns !== beforeColumns) {
3554
+ this.gridResized.emit(result);
3555
+ }
3556
+ return result;
3557
+ }
3155
3558
  /**
3156
3559
  * Forwards to the active viewer. No-op in edit mode.
3157
3560
  * See `DashboardViewerComponent.clearSelection()`.
@@ -3195,21 +3598,21 @@ class DashboardComponent {
3195
3598
  this.#isPreservingStates = false;
3196
3599
  }
3197
3600
  }
3198
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: DashboardComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
3199
- 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 });
3601
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: DashboardComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
3602
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.4", 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", gridResized: "gridResized" }, host: { properties: { "style.--rows": "store.effectiveRows()", "style.--columns": "store.effectiveColumns()", "style.--gutter-size": "store.gutterSize()", "style.--gutters": "store.effectiveColumns() + 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 (gridResized)=\"gridResized.emit($event)\"\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"], outputs: ["gridResized"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
3200
3603
  }
3201
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: DashboardComponent, decorators: [{
3604
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: DashboardComponent, decorators: [{
3202
3605
  type: Component,
3203
3606
  args: [{ selector: 'ngx-dashboard', standalone: true, imports: [DashboardViewerComponent, DashboardEditorComponent], providers: [DashboardStore, DashboardViewportService], changeDetection: ChangeDetectionStrategy.OnPush, host: {
3204
- '[style.--rows]': 'store.rows()',
3205
- '[style.--columns]': 'store.columns()',
3607
+ '[style.--rows]': 'store.effectiveRows()',
3608
+ '[style.--columns]': 'store.effectiveColumns()',
3206
3609
  '[style.--gutter-size]': 'store.gutterSize()',
3207
- '[style.--gutters]': 'store.columns() + 1',
3610
+ '[style.--gutters]': 'store.effectiveColumns() + 1',
3208
3611
  '[class.is-edit-mode]': 'editMode()',
3209
3612
  '[style.max-width.px]': 'viewport.constraints().maxWidth',
3210
3613
  '[style.max-height.px]': 'viewport.constraints().maxHeight',
3211
- }, 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"] }]
3212
- }], 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 }] }] } });
3614
+ }, 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 (gridResized)=\"gridResized.emit($event)\"\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"] }]
3615
+ }], 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"] }], gridResized: [{ type: i0.Output, args: ["gridResized"] }], dashboardEditor: [{ type: i0.ViewChild, args: [i0.forwardRef(() => DashboardEditorComponent), { isSignal: true }] }], dashboardViewer: [{ type: i0.ViewChild, args: [i0.forwardRef(() => DashboardViewerComponent), { isSignal: true }] }] } });
3213
3616
 
3214
3617
  // widget-list.component.ts
3215
3618
  class WidgetListComponent {
@@ -3218,14 +3621,17 @@ class WidgetListComponent {
3218
3621
  #renderer = inject(Renderer2);
3219
3622
  #bridge = inject(DashboardBridgeService);
3220
3623
  // Input to track collapsed state for tooltip display
3221
- collapsed = input(false, ...(ngDevMode ? [{ debugName: "collapsed" }] : /* istanbul ignore next */ []));
3222
- activeWidget = signal(null, ...(ngDevMode ? [{ debugName: "activeWidget" }] : /* istanbul ignore next */ []));
3624
+ collapsed = input(false, /* @ts-ignore */
3625
+ ...(ngDevMode ? [{ debugName: "collapsed" }] : /* istanbul ignore next */ []));
3626
+ activeWidget = signal(null, /* @ts-ignore */
3627
+ ...(ngDevMode ? [{ debugName: "activeWidget" }] : /* istanbul ignore next */ []));
3223
3628
  // Get grid cell dimensions from bridge service (uses first available dashboard)
3224
3629
  gridCellDimensions = this.#bridge.availableDimensions;
3225
3630
  widgets = computed(() => this.#service.widgetTypes().map((w) => ({
3226
3631
  ...w.metadata,
3227
3632
  safeSvgIcon: this.#sanitizer.bypassSecurityTrustHtml(w.metadata.svgIcon),
3228
- })), ...(ngDevMode ? [{ debugName: "widgets" }] : /* istanbul ignore next */ []));
3633
+ })), /* @ts-ignore */
3634
+ ...(ngDevMode ? [{ debugName: "widgets" }] : /* istanbul ignore next */ []));
3229
3635
  onDragStart(event, widget) {
3230
3636
  if (!event.dataTransfer)
3231
3637
  return;
@@ -3277,10 +3683,10 @@ class WidgetListComponent {
3277
3683
  // Using $localize for the template pattern
3278
3684
  return $localize `:@@ngx.dashboard.widget.list.item.ariaLabel:${widget.name} widget: ${widget.description}`;
3279
3685
  }
3280
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: WidgetListComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
3281
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.10", type: WidgetListComponent, isStandalone: true, selector: "ngx-dashboard-widget-list", inputs: { collapsed: { classPropertyName: "collapsed", publicName: "collapsed", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<!-- widget-list.component.html -->\n<div\n class=\"widget-list\"\n role=\"list\"\n i18n-aria-label=\"@@ngx.dashboard.widget.list.available\"\n aria-label=\"Available widgets\"\n>\n @for (widget of widgets(); track widget.widgetTypeid) {\n <div\n class=\"widget-list-item\"\n [class.active]=\"activeWidget() === widget.widgetTypeid\"\n draggable=\"true\"\n (dragstart)=\"onDragStart($event, widget)\"\n (dragend)=\"onDragEnd()\"\n role=\"listitem\"\n [attr.aria-grabbed]=\"activeWidget() === widget.widgetTypeid\"\n [attr.aria-label]=\"getWidgetAriaLabel(widget)\"\n [matTooltip]=\"widget.description\"\n [matTooltipDisabled]=\"!collapsed()\"\n matTooltipPosition=\"right\"\n tabindex=\"0\"\n >\n <div class=\"icon\" [innerHTML]=\"widget.safeSvgIcon\" aria-hidden=\"true\"></div>\n <div class=\"content\">\n <strong>{{ widget.name }}</strong>\n <small>{{ widget.description }}</small>\n </div>\n </div>\n }\n</div>\n", styles: [":host{background-color:var(--mat-sys-surface-container, #f5f5f5);container-type:inline-size}.widget-list{display:flex;flex-direction:column;gap:var(--mat-sys-spacing-2, 8px)}@container (max-width: 200px){.widget-list{gap:var(--mat-sys-spacing-1, 4px)}}@container (min-width: 400px){.widget-list{gap:var(--mat-sys-spacing-3, 12px)}}.widget-list-item{display:flex;align-items:start;gap:var(--mat-sys-spacing-3, 12px);background-color:var(--mat-sys-surface, #ffffff);border:1px solid var(--mat-sys-outline-variant, #c7c7c7);padding:var(--mat-sys-spacing-3, 12px);border-radius:var(--mat-sys-corner-small, 4px);cursor:grab;transition:background-color var(--mat-sys-motion-duration-medium2, .3s) var(--mat-sys-motion-easing-standard, ease-in-out),border-color var(--mat-sys-motion-duration-medium2, .3s) var(--mat-sys-motion-easing-standard, ease-in-out),box-shadow var(--mat-sys-motion-duration-medium2, .3s) var(--mat-sys-motion-easing-standard, ease-in-out);box-shadow:var(--mat-sys-elevation-level1, 0 1px 2px rgba(0, 0, 0, .05))}@container (max-width: 200px){.widget-list-item{padding:var(--mat-sys-spacing-2, 8px);gap:var(--mat-sys-spacing-2, 8px)}}@container (min-width: 400px){.widget-list-item{padding:var(--mat-sys-spacing-4, 16px);gap:var(--mat-sys-spacing-4, 16px)}}.widget-list-item .icon{width:clamp(20px,4vw,28px);height:clamp(20px,4vw,28px);flex-shrink:0;color:var(--mat-sys-on-surface-variant, #5f5f5f);transition:color var(--mat-sys-motion-duration-short2, .15s) var(--mat-sys-motion-easing-standard, ease-in-out)}.widget-list-item .icon ::ng-deep svg{width:100%;height:100%;display:block}.widget-list-item .content{display:flex;flex-direction:column;line-height:1.2;color:var(--mat-sys-on-surface, #1c1c1c);flex:1;min-width:0}.widget-list-item .content strong{color:var(--mat-sys-on-surface, #1c1c1c);font-weight:500;font-size:clamp(.875rem,2.5vw,1rem);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.widget-list-item .content small{color:var(--mat-sys-on-surface-variant, #5f5f5f);font-size:clamp(.75rem,2vw,.875rem);margin-top:var(--mat-sys-spacing-1, 4px);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.widget-list-item:hover{background-color:var(--mat-sys-surface-container-low, #f0f0f0);box-shadow:var(--mat-sys-elevation-level2, 0 2px 4px rgba(0, 0, 0, .1))}.widget-list-item:hover .icon{color:var(--mat-sys-on-surface, #1c1c1c)}.widget-list-item:active{cursor:grabbing;background-color:var(--mat-sys-surface-container, #f5f5f5)}.widget-list-item.active{background-color:var(--mat-sys-primary-container, #e6f2ff);border-color:var(--mat-sys-primary, #1976d2);color:var(--mat-sys-on-primary-container, #004a99)}.widget-list-item.active .content strong{color:var(--mat-sys-on-primary-container, #004a99)}.widget-list-item.active .content small{color:var(--mat-sys-on-primary-container, #004a99);opacity:.8}.widget-list-item.active .icon{color:var(--mat-sys-on-primary-container, #004a99)}.drag-ghost{position:absolute;top:0;left:0;z-index:9999;margin:0;pointer-events:none;display:flex;align-items:center;justify-content:center;box-sizing:border-box;background-color:var(--mat-sys-surface, #ffffff);border:1px solid var(--mat-sys-outline-variant, #c7c7c7);border-radius:var(--mat-sys-corner-small, 4px);box-shadow:var(--mat-sys-elevation-level3, 0 4px 6px rgba(0, 0, 0, .15));opacity:.8}.drag-ghost .icon{display:flex;align-items:center;justify-content:center;color:var(--mat-sys-on-surface-variant, #5f5f5f);opacity:.6}.drag-ghost .icon ::ng-deep svg{display:block}\n"], dependencies: [{ kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i2$1.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
3686
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: WidgetListComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
3687
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.4", type: WidgetListComponent, isStandalone: true, selector: "ngx-dashboard-widget-list", inputs: { collapsed: { classPropertyName: "collapsed", publicName: "collapsed", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<!-- widget-list.component.html -->\n<div\n class=\"widget-list\"\n role=\"list\"\n i18n-aria-label=\"@@ngx.dashboard.widget.list.available\"\n aria-label=\"Available widgets\"\n>\n @for (widget of widgets(); track widget.widgetTypeid) {\n <div\n class=\"widget-list-item\"\n [class.active]=\"activeWidget() === widget.widgetTypeid\"\n draggable=\"true\"\n (dragstart)=\"onDragStart($event, widget)\"\n (dragend)=\"onDragEnd()\"\n role=\"listitem\"\n [attr.aria-grabbed]=\"activeWidget() === widget.widgetTypeid\"\n [attr.aria-label]=\"getWidgetAriaLabel(widget)\"\n [matTooltip]=\"widget.description\"\n [matTooltipDisabled]=\"!collapsed()\"\n matTooltipPosition=\"right\"\n tabindex=\"0\"\n >\n <div class=\"icon\" [innerHTML]=\"widget.safeSvgIcon\" aria-hidden=\"true\"></div>\n <div class=\"content\">\n <strong>{{ widget.name }}</strong>\n <small>{{ widget.description }}</small>\n </div>\n </div>\n }\n</div>\n", styles: [":host{background-color:var(--mat-sys-surface-container, #f5f5f5);container-type:inline-size}.widget-list{display:flex;flex-direction:column;gap:var(--mat-sys-spacing-2, 8px)}@container (max-width: 200px){.widget-list{gap:var(--mat-sys-spacing-1, 4px)}}@container (min-width: 400px){.widget-list{gap:var(--mat-sys-spacing-3, 12px)}}.widget-list-item{display:flex;align-items:start;gap:var(--mat-sys-spacing-3, 12px);background-color:var(--mat-sys-surface, #ffffff);border:1px solid var(--mat-sys-outline-variant, #c7c7c7);padding:var(--mat-sys-spacing-3, 12px);border-radius:var(--mat-sys-corner-small, 4px);cursor:grab;transition:background-color var(--mat-sys-motion-duration-medium2, .3s) var(--mat-sys-motion-easing-standard, ease-in-out),border-color var(--mat-sys-motion-duration-medium2, .3s) var(--mat-sys-motion-easing-standard, ease-in-out),box-shadow var(--mat-sys-motion-duration-medium2, .3s) var(--mat-sys-motion-easing-standard, ease-in-out);box-shadow:var(--mat-sys-elevation-level1, 0 1px 2px rgba(0, 0, 0, .05))}@container (max-width: 200px){.widget-list-item{padding:var(--mat-sys-spacing-2, 8px);gap:var(--mat-sys-spacing-2, 8px)}}@container (min-width: 400px){.widget-list-item{padding:var(--mat-sys-spacing-4, 16px);gap:var(--mat-sys-spacing-4, 16px)}}.widget-list-item .icon{width:clamp(20px,4vw,28px);height:clamp(20px,4vw,28px);flex-shrink:0;color:var(--mat-sys-on-surface-variant, #5f5f5f);transition:color var(--mat-sys-motion-duration-short2, .15s) var(--mat-sys-motion-easing-standard, ease-in-out)}.widget-list-item .icon ::ng-deep svg{width:100%;height:100%;display:block}.widget-list-item .content{display:flex;flex-direction:column;line-height:1.2;color:var(--mat-sys-on-surface, #1c1c1c);flex:1;min-width:0}.widget-list-item .content strong{color:var(--mat-sys-on-surface, #1c1c1c);font-weight:500;font-size:clamp(.875rem,2.5vw,1rem);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.widget-list-item .content small{color:var(--mat-sys-on-surface-variant, #5f5f5f);font-size:clamp(.75rem,2vw,.875rem);margin-top:var(--mat-sys-spacing-1, 4px);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.widget-list-item:hover{background-color:var(--mat-sys-surface-container-low, #f0f0f0);box-shadow:var(--mat-sys-elevation-level2, 0 2px 4px rgba(0, 0, 0, .1))}.widget-list-item:hover .icon{color:var(--mat-sys-on-surface, #1c1c1c)}.widget-list-item:active{cursor:grabbing;background-color:var(--mat-sys-surface-container, #f5f5f5)}.widget-list-item.active{background-color:var(--mat-sys-primary-container, #e6f2ff);border-color:var(--mat-sys-primary, #1976d2);color:var(--mat-sys-on-primary-container, #004a99)}.widget-list-item.active .content strong{color:var(--mat-sys-on-primary-container, #004a99)}.widget-list-item.active .content small{color:var(--mat-sys-on-primary-container, #004a99);opacity:.8}.widget-list-item.active .icon{color:var(--mat-sys-on-primary-container, #004a99)}.drag-ghost{position:absolute;top:0;left:0;z-index:9999;margin:0;pointer-events:none;display:flex;align-items:center;justify-content:center;box-sizing:border-box;background-color:var(--mat-sys-surface, #ffffff);border:1px solid var(--mat-sys-outline-variant, #c7c7c7);border-radius:var(--mat-sys-corner-small, 4px);box-shadow:var(--mat-sys-elevation-level3, 0 4px 6px rgba(0, 0, 0, .15));opacity:.8}.drag-ghost .icon{display:flex;align-items:center;justify-content:center;color:var(--mat-sys-on-surface-variant, #5f5f5f);opacity:.6}.drag-ghost .icon ::ng-deep svg{display:block}\n"], dependencies: [{ kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i2$1.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
3282
3688
  }
3283
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: WidgetListComponent, decorators: [{
3689
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: WidgetListComponent, decorators: [{
3284
3690
  type: Component,
3285
3691
  args: [{ selector: 'ngx-dashboard-widget-list', standalone: true, imports: [MatTooltipModule], changeDetection: ChangeDetectionStrategy.OnPush, template: "<!-- widget-list.component.html -->\n<div\n class=\"widget-list\"\n role=\"list\"\n i18n-aria-label=\"@@ngx.dashboard.widget.list.available\"\n aria-label=\"Available widgets\"\n>\n @for (widget of widgets(); track widget.widgetTypeid) {\n <div\n class=\"widget-list-item\"\n [class.active]=\"activeWidget() === widget.widgetTypeid\"\n draggable=\"true\"\n (dragstart)=\"onDragStart($event, widget)\"\n (dragend)=\"onDragEnd()\"\n role=\"listitem\"\n [attr.aria-grabbed]=\"activeWidget() === widget.widgetTypeid\"\n [attr.aria-label]=\"getWidgetAriaLabel(widget)\"\n [matTooltip]=\"widget.description\"\n [matTooltipDisabled]=\"!collapsed()\"\n matTooltipPosition=\"right\"\n tabindex=\"0\"\n >\n <div class=\"icon\" [innerHTML]=\"widget.safeSvgIcon\" aria-hidden=\"true\"></div>\n <div class=\"content\">\n <strong>{{ widget.name }}</strong>\n <small>{{ widget.description }}</small>\n </div>\n </div>\n }\n</div>\n", styles: [":host{background-color:var(--mat-sys-surface-container, #f5f5f5);container-type:inline-size}.widget-list{display:flex;flex-direction:column;gap:var(--mat-sys-spacing-2, 8px)}@container (max-width: 200px){.widget-list{gap:var(--mat-sys-spacing-1, 4px)}}@container (min-width: 400px){.widget-list{gap:var(--mat-sys-spacing-3, 12px)}}.widget-list-item{display:flex;align-items:start;gap:var(--mat-sys-spacing-3, 12px);background-color:var(--mat-sys-surface, #ffffff);border:1px solid var(--mat-sys-outline-variant, #c7c7c7);padding:var(--mat-sys-spacing-3, 12px);border-radius:var(--mat-sys-corner-small, 4px);cursor:grab;transition:background-color var(--mat-sys-motion-duration-medium2, .3s) var(--mat-sys-motion-easing-standard, ease-in-out),border-color var(--mat-sys-motion-duration-medium2, .3s) var(--mat-sys-motion-easing-standard, ease-in-out),box-shadow var(--mat-sys-motion-duration-medium2, .3s) var(--mat-sys-motion-easing-standard, ease-in-out);box-shadow:var(--mat-sys-elevation-level1, 0 1px 2px rgba(0, 0, 0, .05))}@container (max-width: 200px){.widget-list-item{padding:var(--mat-sys-spacing-2, 8px);gap:var(--mat-sys-spacing-2, 8px)}}@container (min-width: 400px){.widget-list-item{padding:var(--mat-sys-spacing-4, 16px);gap:var(--mat-sys-spacing-4, 16px)}}.widget-list-item .icon{width:clamp(20px,4vw,28px);height:clamp(20px,4vw,28px);flex-shrink:0;color:var(--mat-sys-on-surface-variant, #5f5f5f);transition:color var(--mat-sys-motion-duration-short2, .15s) var(--mat-sys-motion-easing-standard, ease-in-out)}.widget-list-item .icon ::ng-deep svg{width:100%;height:100%;display:block}.widget-list-item .content{display:flex;flex-direction:column;line-height:1.2;color:var(--mat-sys-on-surface, #1c1c1c);flex:1;min-width:0}.widget-list-item .content strong{color:var(--mat-sys-on-surface, #1c1c1c);font-weight:500;font-size:clamp(.875rem,2.5vw,1rem);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.widget-list-item .content small{color:var(--mat-sys-on-surface-variant, #5f5f5f);font-size:clamp(.75rem,2vw,.875rem);margin-top:var(--mat-sys-spacing-1, 4px);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.widget-list-item:hover{background-color:var(--mat-sys-surface-container-low, #f0f0f0);box-shadow:var(--mat-sys-elevation-level2, 0 2px 4px rgba(0, 0, 0, .1))}.widget-list-item:hover .icon{color:var(--mat-sys-on-surface, #1c1c1c)}.widget-list-item:active{cursor:grabbing;background-color:var(--mat-sys-surface-container, #f5f5f5)}.widget-list-item.active{background-color:var(--mat-sys-primary-container, #e6f2ff);border-color:var(--mat-sys-primary, #1976d2);color:var(--mat-sys-on-primary-container, #004a99)}.widget-list-item.active .content strong{color:var(--mat-sys-on-primary-container, #004a99)}.widget-list-item.active .content small{color:var(--mat-sys-on-primary-container, #004a99);opacity:.8}.widget-list-item.active .icon{color:var(--mat-sys-on-primary-container, #004a99)}.drag-ghost{position:absolute;top:0;left:0;z-index:9999;margin:0;pointer-events:none;display:flex;align-items:center;justify-content:center;box-sizing:border-box;background-color:var(--mat-sys-surface, #ffffff);border:1px solid var(--mat-sys-outline-variant, #c7c7c7);border-radius:var(--mat-sys-corner-small, 4px);box-shadow:var(--mat-sys-elevation-level3, 0 4px 6px rgba(0, 0, 0, .15));opacity:.8}.drag-ghost .icon{display:flex;align-items:center;justify-content:center;color:var(--mat-sys-on-surface-variant, #5f5f5f);opacity:.6}.drag-ghost .icon ::ng-deep svg{display:block}\n"] }]
3286
3692
  }], propDecorators: { collapsed: [{ type: i0.Input, args: [{ isSignal: true, alias: "collapsed", required: false }] }] } });