@dragonworks/ngx-dashboard 22.0.0 → 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 = '22.
|
|
26
|
+
const NGX_DASHBOARD_VERSION = '22.1.0';
|
|
27
27
|
|
|
28
28
|
/**
|
|
29
29
|
* Maximum number of columns supported by the grid.
|
|
@@ -559,6 +559,38 @@ function applySelectionFilter(selection, allCells, options = {}) {
|
|
|
559
559
|
};
|
|
560
560
|
}
|
|
561
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
|
+
|
|
562
594
|
const initialGridConfigState = {
|
|
563
595
|
rows: 8,
|
|
564
596
|
columns: 16,
|
|
@@ -722,6 +754,9 @@ const initialDragDropState = {
|
|
|
722
754
|
hoveredDropZone: null,
|
|
723
755
|
};
|
|
724
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()),
|
|
725
760
|
// Highlighted zones during drag
|
|
726
761
|
highlightedZones: computed(() => calculateHighlightedZones(store.dragData(), store.hoveredDropZone())),
|
|
727
762
|
})), withComputed((store) => ({
|
|
@@ -948,6 +983,32 @@ const withResize = () => signalStoreFeature(withState(initialResizeState), withM
|
|
|
948
983
|
},
|
|
949
984
|
})));
|
|
950
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
|
+
|
|
951
1012
|
/** Returns the intended widget type ID, falling back to the factory's type ID */
|
|
952
1013
|
function effectiveWidgetTypeid(cell) {
|
|
953
1014
|
return cell.widgetTypeid ?? cell.widgetFactory.widgetTypeid;
|
|
@@ -957,9 +1018,15 @@ const initialState = {
|
|
|
957
1018
|
};
|
|
958
1019
|
const DashboardStore = signalStore(withState(initialState), withProps(() => ({
|
|
959
1020
|
dashboardService: inject(DashboardService),
|
|
960
|
-
})), withGridConfig(), withWidgetManagement(), withResize(), withDragDrop(),
|
|
1021
|
+
})), withGridConfig(), withWidgetManagement(), withResize(), withGridResize(), withDragDrop(),
|
|
961
1022
|
// Cross-feature computed properties (need access to multiple features)
|
|
962
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()),
|
|
963
1030
|
// Invalid zones (collision detection)
|
|
964
1031
|
invalidHighlightMap: computed(() => {
|
|
965
1032
|
const collisionInfo = calculateCollisionInfo(store.dragData(), store.hoveredDropZone(), store.cells(), store.rows(), store.columns());
|
|
@@ -1010,6 +1077,24 @@ withMethods((store) => ({
|
|
|
1010
1077
|
},
|
|
1011
1078
|
});
|
|
1012
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
|
+
},
|
|
1013
1098
|
// EXPORT/IMPORT METHODS (need access to multiple features)
|
|
1014
1099
|
exportDashboard(getCurrentWidgetStates, selection, selectionOptions) {
|
|
1015
1100
|
// Get live widget states if callback provided, otherwise use stored states
|
|
@@ -1105,6 +1190,27 @@ withMethods((store) => ({
|
|
|
1105
1190
|
});
|
|
1106
1191
|
},
|
|
1107
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
|
+
})),
|
|
1108
1214
|
// Cross-feature computed properties that depend on resize + widget data (using utility functions)
|
|
1109
1215
|
withComputed((store) => ({
|
|
1110
1216
|
// Compute preview cells during resize using utility function
|
|
@@ -1404,8 +1510,7 @@ class CellComponent {
|
|
|
1404
1510
|
: false;
|
|
1405
1511
|
}, /* @ts-ignore */
|
|
1406
1512
|
...(ngDevMode ? [{ debugName: "isResizing" }] : /* istanbul ignore next */ []));
|
|
1407
|
-
isDragActive =
|
|
1408
|
-
...(ngDevMode ? [{ debugName: "isDragActive" }] : /* istanbul ignore next */ []));
|
|
1513
|
+
isDragActive = this.#store.isDragActive;
|
|
1409
1514
|
resizeData = this.#store.resizeData;
|
|
1410
1515
|
gridCellDimensions = this.#store.gridCellDimensions;
|
|
1411
1516
|
resizeDirection = signal(null, /* @ts-ignore */
|
|
@@ -1647,7 +1752,7 @@ class CellComponent {
|
|
|
1647
1752
|
return this.widgetState();
|
|
1648
1753
|
}
|
|
1649
1754
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: CellComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1650
|
-
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: [":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}
|
|
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 });
|
|
1651
1756
|
}
|
|
1652
1757
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: CellComponent, decorators: [{
|
|
1653
1758
|
type: Component,
|
|
@@ -1657,7 +1762,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.4", ngImpor
|
|
|
1657
1762
|
'[class.is-dragging]': 'isDragging()',
|
|
1658
1763
|
'[class.drag-active]': 'isDragActive()',
|
|
1659
1764
|
'[class.flat]': 'flat() === true',
|
|
1660
|
-
}, 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}
|
|
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"] }]
|
|
1661
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 }] }] } });
|
|
1662
1767
|
|
|
1663
1768
|
/**
|
|
@@ -2370,6 +2475,8 @@ class DropZoneComponent {
|
|
|
2370
2475
|
...(ngDevMode ? [{ debugName: "highlightInvalid" }] : /* istanbul ignore next */ []));
|
|
2371
2476
|
highlightResize = input(false, /* @ts-ignore */
|
|
2372
2477
|
...(ngDevMode ? [{ debugName: "highlightResize" }] : /* istanbul ignore next */ []));
|
|
2478
|
+
highlightPreview = input(false, /* @ts-ignore */
|
|
2479
|
+
...(ngDevMode ? [{ debugName: "highlightPreview" }] : /* istanbul ignore next */ []));
|
|
2373
2480
|
editMode = input(false, /* @ts-ignore */
|
|
2374
2481
|
...(ngDevMode ? [{ debugName: "editMode" }] : /* istanbul ignore next */ []));
|
|
2375
2482
|
// Outputs
|
|
@@ -2475,12 +2582,12 @@ class DropZoneComponent {
|
|
|
2475
2582
|
}
|
|
2476
2583
|
}
|
|
2477
2584
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: DropZoneComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
2478
|
-
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 }, 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 });
|
|
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 });
|
|
2479
2586
|
}
|
|
2480
2587
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: DropZoneComponent, decorators: [{
|
|
2481
2588
|
type: Component,
|
|
2482
|
-
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"] }]
|
|
2483
|
-
}], 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"] }] } });
|
|
2484
2591
|
|
|
2485
2592
|
/**
|
|
2486
2593
|
* Context menu component for empty dashboard cells.
|
|
@@ -2709,6 +2816,155 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.4", ngImpor
|
|
|
2709
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"] }]
|
|
2710
2817
|
}], ctorParameters: () => [], propDecorators: { menuTrigger: [{ type: i0.ViewChild, args: ['menuTrigger', { ...{ read: MatMenuTrigger }, isSignal: true }] }] } });
|
|
2711
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
|
+
|
|
2712
2968
|
// dashboard-editor.component.ts
|
|
2713
2969
|
class DashboardEditorComponent {
|
|
2714
2970
|
bottomGridRef = viewChild.required('bottomGrid');
|
|
@@ -2725,8 +2981,8 @@ class DashboardEditorComponent {
|
|
|
2725
2981
|
...(ngDevMode ? [{ debugName: "columns" }] : /* istanbul ignore next */ []));
|
|
2726
2982
|
gutterSize = input('1em', /* @ts-ignore */
|
|
2727
2983
|
...(ngDevMode ? [{ debugName: "gutterSize" }] : /* istanbul ignore next */ []));
|
|
2728
|
-
|
|
2729
|
-
|
|
2984
|
+
// Emitted when a grid resize handle commits a new size (after clamp-to-content).
|
|
2985
|
+
gridResized = output();
|
|
2730
2986
|
// store signals
|
|
2731
2987
|
cells = this.#store.cells;
|
|
2732
2988
|
highlightedZones = this.#store.highlightedZones;
|
|
@@ -2734,22 +2990,48 @@ class DashboardEditorComponent {
|
|
|
2734
2990
|
invalidHighlightMap = this.#store.invalidHighlightMap;
|
|
2735
2991
|
hoveredDropZone = this.#store.hoveredDropZone;
|
|
2736
2992
|
resizePreviewMap = this.#store.resizePreviewMap;
|
|
2737
|
-
|
|
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).
|
|
2738
3012
|
dropzonePositions = computed(() => {
|
|
3013
|
+
const rows = this.effectiveRows();
|
|
3014
|
+
const columns = this.effectiveColumns();
|
|
2739
3015
|
const positions = [];
|
|
2740
|
-
for (let row = 1; row <=
|
|
2741
|
-
for (let col = 1; col <=
|
|
3016
|
+
for (let row = 1; row <= rows; row++) {
|
|
3017
|
+
for (let col = 1; col <= columns; col++) {
|
|
2742
3018
|
positions.push({
|
|
2743
3019
|
row,
|
|
2744
3020
|
col,
|
|
2745
3021
|
id: `dropzone-${row}-${col}`,
|
|
2746
|
-
index: (row - 1) *
|
|
3022
|
+
index: (row - 1) * columns + col,
|
|
2747
3023
|
});
|
|
2748
3024
|
}
|
|
2749
3025
|
}
|
|
2750
3026
|
return positions;
|
|
2751
3027
|
}, /* @ts-ignore */
|
|
2752
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
|
+
}
|
|
2753
3035
|
// Helper method for template
|
|
2754
3036
|
createCellId(row, col) {
|
|
2755
3037
|
return CellIdUtils.create(row, col);
|
|
@@ -2771,6 +3053,10 @@ class DashboardEditorComponent {
|
|
|
2771
3053
|
effect(() => {
|
|
2772
3054
|
this.#store.setEditMode(true);
|
|
2773
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());
|
|
2774
3060
|
}
|
|
2775
3061
|
#observeGridSize() {
|
|
2776
3062
|
const gridEl = this.bottomGridRef()?.nativeElement;
|
|
@@ -2821,10 +3107,22 @@ class DashboardEditorComponent {
|
|
|
2821
3107
|
this.#store.handleDrop(event.data, event.target);
|
|
2822
3108
|
// Note: Store handles all validation and error handling internally
|
|
2823
3109
|
}
|
|
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
|
+
}
|
|
2824
3122
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: DashboardEditorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
2825
|
-
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 } }, host: { properties: { "style.--rows": "
|
|
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: [
|
|
2826
3124
|
CellContextMenuService,
|
|
2827
|
-
], 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 });
|
|
2828
3126
|
}
|
|
2829
3127
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: DashboardEditorComponent, decorators: [{
|
|
2830
3128
|
type: Component,
|
|
@@ -2832,17 +3130,18 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.4", ngImpor
|
|
|
2832
3130
|
CellComponent,
|
|
2833
3131
|
DropZoneComponent,
|
|
2834
3132
|
CellContextMenuComponent,
|
|
2835
|
-
EmptyCellContextMenuComponent
|
|
3133
|
+
EmptyCellContextMenuComponent,
|
|
3134
|
+
GridResizeHandleComponent
|
|
2836
3135
|
], providers: [
|
|
2837
3136
|
CellContextMenuService,
|
|
2838
3137
|
], changeDetection: ChangeDetectionStrategy.OnPush, host: {
|
|
2839
|
-
'[style.--rows]': '
|
|
2840
|
-
'[style.--columns]': '
|
|
3138
|
+
'[style.--rows]': 'effectiveRows()',
|
|
3139
|
+
'[style.--columns]': 'effectiveColumns()',
|
|
2841
3140
|
'[style.--gutter-size]': 'gutterSize()',
|
|
2842
|
-
'[style.--gutters]': '
|
|
3141
|
+
'[style.--gutters]': 'effectiveColumns() + 1',
|
|
2843
3142
|
'[class.is-edit-mode]': 'true', // Always in edit mode
|
|
2844
|
-
}, 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"] }]
|
|
2845
|
-
}], 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"] }] } });
|
|
2846
3145
|
|
|
2847
3146
|
// dashboard-bridge.service.ts
|
|
2848
3147
|
/**
|
|
@@ -3061,9 +3360,10 @@ class DashboardViewportService {
|
|
|
3061
3360
|
*/
|
|
3062
3361
|
constraints = computed(() => {
|
|
3063
3362
|
const availableSize = this.availableSpace();
|
|
3064
|
-
//
|
|
3065
|
-
|
|
3066
|
-
const
|
|
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();
|
|
3067
3367
|
if (rows === 0 || columns === 0) {
|
|
3068
3368
|
return {
|
|
3069
3369
|
maxWidth: availableSize.width,
|
|
@@ -3133,6 +3433,7 @@ class DashboardComponent {
|
|
|
3133
3433
|
...(ngDevMode ? [{ debugName: "dragThreshold" }] : /* istanbul ignore next */ []));
|
|
3134
3434
|
// Component outputs
|
|
3135
3435
|
selectionComplete = output();
|
|
3436
|
+
gridResized = output();
|
|
3136
3437
|
// Store signals - shared by both child components
|
|
3137
3438
|
cells = this.#store.cells;
|
|
3138
3439
|
// ViewChild references for export/import functionality
|
|
@@ -3233,6 +3534,27 @@ class DashboardComponent {
|
|
|
3233
3534
|
clearDashboard() {
|
|
3234
3535
|
this.#store.clearDashboard();
|
|
3235
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
|
+
}
|
|
3236
3558
|
/**
|
|
3237
3559
|
* Forwards to the active viewer. No-op in edit mode.
|
|
3238
3560
|
* See `DashboardViewerComponent.clearSelection()`.
|
|
@@ -3277,20 +3599,20 @@ class DashboardComponent {
|
|
|
3277
3599
|
}
|
|
3278
3600
|
}
|
|
3279
3601
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: DashboardComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
3280
|
-
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" }, host: { properties: { "style.--rows": "store.
|
|
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 });
|
|
3281
3603
|
}
|
|
3282
3604
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: DashboardComponent, decorators: [{
|
|
3283
3605
|
type: Component,
|
|
3284
3606
|
args: [{ selector: 'ngx-dashboard', standalone: true, imports: [DashboardViewerComponent, DashboardEditorComponent], providers: [DashboardStore, DashboardViewportService], changeDetection: ChangeDetectionStrategy.OnPush, host: {
|
|
3285
|
-
'[style.--rows]': 'store.
|
|
3286
|
-
'[style.--columns]': 'store.
|
|
3607
|
+
'[style.--rows]': 'store.effectiveRows()',
|
|
3608
|
+
'[style.--columns]': 'store.effectiveColumns()',
|
|
3287
3609
|
'[style.--gutter-size]': 'store.gutterSize()',
|
|
3288
|
-
'[style.--gutters]': 'store.
|
|
3610
|
+
'[style.--gutters]': 'store.effectiveColumns() + 1',
|
|
3289
3611
|
'[class.is-edit-mode]': 'editMode()',
|
|
3290
3612
|
'[style.max-width.px]': 'viewport.constraints().maxWidth',
|
|
3291
3613
|
'[style.max-height.px]': 'viewport.constraints().maxHeight',
|
|
3292
|
-
}, 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"] }]
|
|
3293
|
-
}], 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 }] }] } });
|
|
3294
3616
|
|
|
3295
3617
|
// widget-list.component.ts
|
|
3296
3618
|
class WidgetListComponent {
|