@dragonworks/ngx-dashboard 20.0.5 → 20.0.6
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.
- package/ng-package.json +7 -0
- package/package.json +34 -45
- package/src/lib/__tests__/dashboard-component-widget-state-integration.spec.ts +537 -0
- package/src/lib/cell/__tests__/cell-resize.component.spec.ts +442 -0
- package/src/lib/cell/__tests__/cell.component.spec.ts +541 -0
- package/src/lib/cell/cell-context-menu.component.ts +138 -0
- package/src/lib/cell/cell-context-menu.service.ts +36 -0
- package/src/lib/cell/cell.component.html +37 -0
- package/src/lib/cell/cell.component.scss +198 -0
- package/src/lib/cell/cell.component.ts +375 -0
- package/src/lib/dashboard/dashboard.component.html +18 -0
- package/src/lib/dashboard/dashboard.component.scss +17 -0
- package/src/lib/dashboard/dashboard.component.ts +187 -0
- package/src/lib/dashboard-editor/dashboard-editor.component.html +57 -0
- package/src/lib/dashboard-editor/dashboard-editor.component.scss +87 -0
- package/src/lib/dashboard-editor/dashboard-editor.component.ts +219 -0
- package/src/lib/dashboard-viewer/__tests__/dashboard-viewer.component.spec.ts +258 -0
- package/src/lib/dashboard-viewer/dashboard-viewer.component.html +20 -0
- package/src/lib/dashboard-viewer/dashboard-viewer.component.scss +50 -0
- package/src/lib/dashboard-viewer/dashboard-viewer.component.ts +70 -0
- package/src/lib/drop-zone/__tests__/drop-zone.component.spec.ts +465 -0
- package/src/lib/drop-zone/drop-zone.component.html +20 -0
- package/src/lib/drop-zone/drop-zone.component.scss +67 -0
- package/src/lib/drop-zone/drop-zone.component.ts +122 -0
- package/src/lib/internal-widgets/unknown-widget/unknown-widget.component.ts +72 -0
- package/src/lib/models/cell-data.ts +13 -0
- package/src/lib/models/cell-dialog.ts +7 -0
- package/src/lib/models/cell-id.ts +85 -0
- package/src/lib/models/cell-position.ts +15 -0
- package/src/lib/models/dashboard-data.dto.ts +44 -0
- package/src/lib/models/dashboard-data.utils.ts +49 -0
- package/src/lib/models/drag-data.ts +6 -0
- package/src/lib/models/index.ts +11 -0
- package/src/lib/models/reserved-space.ts +24 -0
- package/src/lib/models/widget-factory.ts +33 -0
- package/src/lib/models/widget-id.ts +70 -0
- package/src/lib/models/widget.ts +21 -0
- package/src/lib/providers/cell-settings-dialog/cell-settings-dialog.component.ts +127 -0
- package/src/lib/providers/cell-settings-dialog/cell-settings-dialog.provider.ts +15 -0
- package/src/lib/providers/cell-settings-dialog/cell-settings-dialog.tokens.ts +20 -0
- package/src/lib/providers/cell-settings-dialog/default-cell-settings-dialog.provider.ts +32 -0
- package/src/lib/providers/cell-settings-dialog/index.ts +3 -0
- package/src/lib/providers/index.ts +1 -0
- package/src/lib/services/__tests__/dashboard-bridge.service.spec.ts +220 -0
- package/src/lib/services/__tests__/dashboard-viewport.service.spec.ts +362 -0
- package/src/lib/services/dashboard-bridge.service.ts +155 -0
- package/src/lib/services/dashboard-viewport.service.ts +148 -0
- package/src/lib/services/dashboard.service.ts +62 -0
- package/src/lib/store/__tests__/dashboard-store-collision-detection.spec.ts +756 -0
- package/src/lib/store/__tests__/dashboard-store-computed-properties.spec.ts +974 -0
- package/src/lib/store/__tests__/dashboard-store-drag-drop.spec.ts +279 -0
- package/src/lib/store/__tests__/dashboard-store-export-import.spec.ts +780 -0
- package/src/lib/store/__tests__/dashboard-store-grid-config.spec.ts +128 -0
- package/src/lib/store/__tests__/dashboard-store-query-methods.spec.ts +229 -0
- package/src/lib/store/__tests__/dashboard-store-resize-operations.spec.ts +652 -0
- package/src/lib/store/__tests__/dashboard-store-widget-management.spec.ts +461 -0
- package/src/lib/store/__tests__/dashboard-store-widget-state-preservation.spec.ts +369 -0
- package/src/lib/store/dashboard-store.ts +239 -0
- package/src/lib/store/features/drag-drop.feature.ts +140 -0
- package/src/lib/store/features/grid-config.feature.ts +43 -0
- package/src/lib/store/features/resize.feature.ts +140 -0
- package/src/lib/store/features/utils/collision.utils.ts +89 -0
- package/src/lib/store/features/utils/grid-query-internal.utils.ts +37 -0
- package/src/lib/store/features/utils/resize.utils.ts +165 -0
- package/src/lib/store/features/widget-management.feature.ts +158 -0
- package/src/lib/styles/_dashboard-grid-vars.scss +11 -0
- package/src/lib/widget-list/__tests__/widget-list-bridge-integration.spec.ts +137 -0
- package/src/lib/widget-list/widget-list.component.html +22 -0
- package/src/lib/widget-list/widget-list.component.scss +154 -0
- package/src/lib/widget-list/widget-list.component.ts +106 -0
- package/src/public-api.ts +21 -0
- package/src/test-setup.ts +10 -0
- package/tsconfig.lib.json +15 -0
- package/tsconfig.lib.prod.json +11 -0
- package/tsconfig.spec.json +14 -0
- package/fesm2022/dragonworks-ngx-dashboard.mjs +0 -2178
- package/fesm2022/dragonworks-ngx-dashboard.mjs.map +0 -1
- package/index.d.ts +0 -678
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
|
2
|
+
import { Component, signal } from '@angular/core';
|
|
3
|
+
import { DashboardViewerComponent } from '../dashboard-viewer.component';
|
|
4
|
+
import { DashboardStore } from '../../store/dashboard-store';
|
|
5
|
+
import { CellIdUtils, WidgetIdUtils, CellData, WidgetFactory, Widget } from '../../models';
|
|
6
|
+
|
|
7
|
+
// Mock test widget component
|
|
8
|
+
@Component({
|
|
9
|
+
selector: 'lib-test-widget',
|
|
10
|
+
template: '<div class="test-widget">Test Widget: {{state().value}}</div>',
|
|
11
|
+
standalone: true,
|
|
12
|
+
})
|
|
13
|
+
class TestWidgetComponent implements Widget {
|
|
14
|
+
private state = signal<any>({ value: 'initial' });
|
|
15
|
+
|
|
16
|
+
dashboardGetState(): any {
|
|
17
|
+
return this.state();
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
dashboardSetState(state: any): void {
|
|
21
|
+
this.state.set(state);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
dashboardEditState(): void {
|
|
25
|
+
// Mock edit state method
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
describe('DashboardViewerComponent - Integration Tests', () => {
|
|
30
|
+
let component: DashboardViewerComponent;
|
|
31
|
+
let fixture: ComponentFixture<DashboardViewerComponent>;
|
|
32
|
+
let store: InstanceType<typeof DashboardStore>;
|
|
33
|
+
|
|
34
|
+
const mockWidgetFactory: WidgetFactory = {
|
|
35
|
+
widgetTypeid: 'test-widget',
|
|
36
|
+
name: 'Test Widget',
|
|
37
|
+
description: 'A test widget for testing',
|
|
38
|
+
svgIcon: '<svg></svg>',
|
|
39
|
+
createInstance: (container) => {
|
|
40
|
+
const componentRef = container.createComponent(TestWidgetComponent);
|
|
41
|
+
return componentRef;
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
beforeEach(async () => {
|
|
46
|
+
await TestBed.configureTestingModule({
|
|
47
|
+
imports: [DashboardViewerComponent],
|
|
48
|
+
providers: [DashboardStore]
|
|
49
|
+
}).compileComponents();
|
|
50
|
+
|
|
51
|
+
fixture = TestBed.createComponent(DashboardViewerComponent);
|
|
52
|
+
component = fixture.componentInstance;
|
|
53
|
+
store = TestBed.inject(DashboardStore);
|
|
54
|
+
|
|
55
|
+
// Set required inputs
|
|
56
|
+
fixture.componentRef.setInput('rows', 8);
|
|
57
|
+
fixture.componentRef.setInput('columns', 12);
|
|
58
|
+
fixture.componentRef.setInput('gutterSize', '1em');
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
describe('Complete Widget Rendering Workflow', () => {
|
|
62
|
+
it('should render widgets from store and export dashboard with current states', () => {
|
|
63
|
+
// SCENARIO: User loads dashboard with widgets, sees them rendered, exports dashboard
|
|
64
|
+
|
|
65
|
+
// Setup: Add widgets to store
|
|
66
|
+
const widget1: CellData = {
|
|
67
|
+
widgetId: WidgetIdUtils.generate(),
|
|
68
|
+
cellId: CellIdUtils.create(1, 1),
|
|
69
|
+
row: 1,
|
|
70
|
+
col: 1,
|
|
71
|
+
rowSpan: 1,
|
|
72
|
+
colSpan: 2,
|
|
73
|
+
flat: false,
|
|
74
|
+
widgetFactory: mockWidgetFactory,
|
|
75
|
+
widgetState: { value: 'Widget 1' }
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
const widget2: CellData = {
|
|
79
|
+
widgetId: WidgetIdUtils.generate(),
|
|
80
|
+
cellId: CellIdUtils.create(2, 3),
|
|
81
|
+
row: 2,
|
|
82
|
+
col: 3,
|
|
83
|
+
rowSpan: 2,
|
|
84
|
+
colSpan: 1,
|
|
85
|
+
flat: true,
|
|
86
|
+
widgetFactory: mockWidgetFactory,
|
|
87
|
+
widgetState: { value: 'Widget 2' }
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
// Action: Add widgets to store
|
|
91
|
+
store.addWidget(widget1);
|
|
92
|
+
store.addWidget(widget2);
|
|
93
|
+
fixture.detectChanges();
|
|
94
|
+
|
|
95
|
+
// Verify: Widgets are rendered
|
|
96
|
+
const cellElements = fixture.nativeElement.querySelectorAll('lib-cell');
|
|
97
|
+
expect(cellElements.length).toBe(2);
|
|
98
|
+
expect(cellElements[0]).toBeTruthy();
|
|
99
|
+
expect(cellElements[1]).toBeTruthy();
|
|
100
|
+
|
|
101
|
+
// Verify: Store integration works
|
|
102
|
+
expect(component.cells().length).toBe(2);
|
|
103
|
+
expect(component.cells()[0]).toEqual(widget1);
|
|
104
|
+
expect(component.cells()[1]).toEqual(widget2);
|
|
105
|
+
|
|
106
|
+
// Action: Export dashboard
|
|
107
|
+
const exported = component.exportDashboard();
|
|
108
|
+
|
|
109
|
+
// Verify: Export contains dashboard structure
|
|
110
|
+
expect(exported.version).toBe('1.0.0');
|
|
111
|
+
expect(exported.rows).toBe(8);
|
|
112
|
+
expect(exported.columns).toBe(12);
|
|
113
|
+
expect(exported.gutterSize).toBe('1em');
|
|
114
|
+
expect(exported.cells.length).toBe(2);
|
|
115
|
+
});
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
describe('Grid Configuration and Layout', () => {
|
|
119
|
+
it('should apply grid configuration and update CSS variables when inputs change', () => {
|
|
120
|
+
// SCENARIO: User changes grid configuration and sees layout update
|
|
121
|
+
|
|
122
|
+
// Initial setup
|
|
123
|
+
fixture.detectChanges();
|
|
124
|
+
|
|
125
|
+
// Verify: Initial CSS variables are set
|
|
126
|
+
const hostElement = fixture.nativeElement;
|
|
127
|
+
let computedStyle = getComputedStyle(hostElement);
|
|
128
|
+
expect(computedStyle.getPropertyValue('--rows')).toBe('8');
|
|
129
|
+
expect(computedStyle.getPropertyValue('--columns')).toBe('12');
|
|
130
|
+
expect(computedStyle.getPropertyValue('--gutter-size')).toBe('1em');
|
|
131
|
+
expect(computedStyle.getPropertyValue('--gutters')).toBe('13'); // columns + 1
|
|
132
|
+
|
|
133
|
+
// Action: Change grid configuration
|
|
134
|
+
fixture.componentRef.setInput('rows', 6);
|
|
135
|
+
fixture.componentRef.setInput('columns', 10);
|
|
136
|
+
fixture.componentRef.setInput('gutterSize', '2em');
|
|
137
|
+
fixture.detectChanges();
|
|
138
|
+
|
|
139
|
+
// Verify: CSS variables updated
|
|
140
|
+
computedStyle = getComputedStyle(hostElement);
|
|
141
|
+
expect(computedStyle.getPropertyValue('--rows')).toBe('6');
|
|
142
|
+
expect(computedStyle.getPropertyValue('--columns')).toBe('10');
|
|
143
|
+
expect(computedStyle.getPropertyValue('--gutter-size')).toBe('2em');
|
|
144
|
+
expect(computedStyle.getPropertyValue('--gutters')).toBe('11'); // columns + 1
|
|
145
|
+
|
|
146
|
+
// Verify: Store is updated
|
|
147
|
+
expect(store.rows()).toBe(6);
|
|
148
|
+
expect(store.columns()).toBe(10);
|
|
149
|
+
expect(store.gutterSize()).toBe('2em');
|
|
150
|
+
});
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
describe('Reactive Store Updates', () => {
|
|
154
|
+
it('should reactively update when store changes', () => {
|
|
155
|
+
// SCENARIO: User performs actions that modify store, sees immediate UI updates
|
|
156
|
+
|
|
157
|
+
// Initial state: No widgets
|
|
158
|
+
fixture.detectChanges();
|
|
159
|
+
expect(fixture.nativeElement.querySelectorAll('lib-cell').length).toBe(0);
|
|
160
|
+
|
|
161
|
+
// Action: Add widget to store
|
|
162
|
+
const widget: CellData = {
|
|
163
|
+
widgetId: WidgetIdUtils.generate(),
|
|
164
|
+
cellId: CellIdUtils.create(1, 1),
|
|
165
|
+
row: 1,
|
|
166
|
+
col: 1,
|
|
167
|
+
rowSpan: 1,
|
|
168
|
+
colSpan: 1,
|
|
169
|
+
flat: false,
|
|
170
|
+
widgetFactory: mockWidgetFactory,
|
|
171
|
+
widgetState: { value: 'Dynamic Widget' }
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
store.addWidget(widget);
|
|
175
|
+
fixture.detectChanges();
|
|
176
|
+
|
|
177
|
+
// Verify: Widget appears in UI
|
|
178
|
+
expect(fixture.nativeElement.querySelectorAll('lib-cell').length).toBe(1);
|
|
179
|
+
|
|
180
|
+
// Action: Remove widget from store
|
|
181
|
+
store.removeWidget(widget.widgetId);
|
|
182
|
+
fixture.detectChanges();
|
|
183
|
+
|
|
184
|
+
// Verify: Widget disappears from UI
|
|
185
|
+
expect(fixture.nativeElement.querySelectorAll('lib-cell').length).toBe(0);
|
|
186
|
+
});
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
describe('Empty State Handling', () => {
|
|
190
|
+
it('should handle empty dashboard gracefully', () => {
|
|
191
|
+
// SCENARIO: User loads empty dashboard or clears all widgets
|
|
192
|
+
|
|
193
|
+
// Action: Initialize with empty dashboard
|
|
194
|
+
fixture.detectChanges();
|
|
195
|
+
|
|
196
|
+
// Verify: No widgets rendered
|
|
197
|
+
expect(fixture.nativeElement.querySelectorAll('lib-cell').length).toBe(0);
|
|
198
|
+
expect(component.cells().length).toBe(0);
|
|
199
|
+
|
|
200
|
+
// Verify: Export works with empty state
|
|
201
|
+
const exported = component.exportDashboard();
|
|
202
|
+
expect(exported.cells.length).toBe(0);
|
|
203
|
+
expect(exported.version).toBe('1.0.0');
|
|
204
|
+
expect(exported.rows).toBe(8);
|
|
205
|
+
expect(exported.columns).toBe(12);
|
|
206
|
+
|
|
207
|
+
// Verify: Widget state collection works with no widgets
|
|
208
|
+
const widgetStates = component.getCurrentWidgetStates();
|
|
209
|
+
expect(widgetStates.size).toBe(0);
|
|
210
|
+
expect(widgetStates instanceof Map).toBe(true);
|
|
211
|
+
});
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
describe('Export Functionality with Live Widget States', () => {
|
|
215
|
+
it('should export dashboard with live widget states from component instances', () => {
|
|
216
|
+
// SCENARIO: User modifies widget states and exports - should get current states, not store states
|
|
217
|
+
|
|
218
|
+
// Setup: Add widget with initial state
|
|
219
|
+
const widget: CellData = {
|
|
220
|
+
widgetId: WidgetIdUtils.generate(),
|
|
221
|
+
cellId: CellIdUtils.create(1, 1),
|
|
222
|
+
row: 1,
|
|
223
|
+
col: 1,
|
|
224
|
+
rowSpan: 1,
|
|
225
|
+
colSpan: 1,
|
|
226
|
+
flat: false,
|
|
227
|
+
widgetFactory: mockWidgetFactory,
|
|
228
|
+
widgetState: { value: 'Initial State' }
|
|
229
|
+
};
|
|
230
|
+
|
|
231
|
+
store.addWidget(widget);
|
|
232
|
+
fixture.detectChanges();
|
|
233
|
+
|
|
234
|
+
// Verify: Widget is rendered
|
|
235
|
+
const cellElements = fixture.nativeElement.querySelectorAll('lib-cell');
|
|
236
|
+
expect(cellElements.length).toBe(1);
|
|
237
|
+
|
|
238
|
+
// Action: Export dashboard (should include live states)
|
|
239
|
+
const exported = component.exportDashboard();
|
|
240
|
+
|
|
241
|
+
// Verify: Export structure is correct
|
|
242
|
+
expect(exported.version).toBe('1.0.0');
|
|
243
|
+
expect(exported.cells.length).toBe(1);
|
|
244
|
+
expect(exported.cells[0].widgetTypeid).toBe('test-widget');
|
|
245
|
+
expect(exported.cells[0].row).toBe(1);
|
|
246
|
+
expect(exported.cells[0].col).toBe(1);
|
|
247
|
+
|
|
248
|
+
// Verify: getCurrentWidgetStates returns proper map structure
|
|
249
|
+
const widgetStates = component.getCurrentWidgetStates();
|
|
250
|
+
expect(widgetStates instanceof Map).toBe(true);
|
|
251
|
+
|
|
252
|
+
// The actual state collection depends on cell components being properly rendered
|
|
253
|
+
// This tests that the method exists and returns the right type with WidgetId keys
|
|
254
|
+
const widgetIdKey = WidgetIdUtils.toString(widget.widgetId);
|
|
255
|
+
expect(typeof widgetIdKey).toBe('string');
|
|
256
|
+
});
|
|
257
|
+
});
|
|
258
|
+
});
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
<!-- Dashboard viewer - read-only grid -->
|
|
2
|
+
<div class="grid top-grid">
|
|
3
|
+
@for (cell of cells(); track cell.widgetId) {
|
|
4
|
+
<lib-cell
|
|
5
|
+
class="grid-cell"
|
|
6
|
+
[widgetId]="cell.widgetId"
|
|
7
|
+
[cellId]="cell.cellId"
|
|
8
|
+
[isEditMode]="false"
|
|
9
|
+
[draggable]="false"
|
|
10
|
+
[row]="cell.row"
|
|
11
|
+
[column]="cell.col"
|
|
12
|
+
[rowSpan]="cell.rowSpan"
|
|
13
|
+
[colSpan]="cell.colSpan"
|
|
14
|
+
[flat]="cell.flat"
|
|
15
|
+
[widgetFactory]="cell.widgetFactory"
|
|
16
|
+
[widgetState]="cell.widgetState"
|
|
17
|
+
>
|
|
18
|
+
</lib-cell>
|
|
19
|
+
}
|
|
20
|
+
</div>
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/* dashboard-viewer.component.scss */
|
|
2
|
+
@use "../styles/dashboard-grid-vars" as *;
|
|
3
|
+
|
|
4
|
+
:host {
|
|
5
|
+
@include dashboard-grid-vars;
|
|
6
|
+
|
|
7
|
+
display: block;
|
|
8
|
+
container-type: inline-size;
|
|
9
|
+
box-sizing: border-box;
|
|
10
|
+
aspect-ratio: var(--columns) / var(--rows);
|
|
11
|
+
width: 100%;
|
|
12
|
+
height: auto;
|
|
13
|
+
|
|
14
|
+
background-color: var(--mat-sys-surface-container);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
.grid {
|
|
18
|
+
display: grid;
|
|
19
|
+
gap: var(--gutter-size);
|
|
20
|
+
padding: var(--gutter-size);
|
|
21
|
+
|
|
22
|
+
width: 100%;
|
|
23
|
+
height: 100%;
|
|
24
|
+
box-sizing: border-box;
|
|
25
|
+
|
|
26
|
+
/* ←―― single-source-of-truth for the cell geometry */
|
|
27
|
+
grid-template-columns: repeat(var(--columns), var(--cell-size));
|
|
28
|
+
grid-template-rows: repeat(var(--rows), var(--cell-size));
|
|
29
|
+
|
|
30
|
+
// background-color: var(--mat-sys-surface);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/* Interaction rules */
|
|
34
|
+
.grid-cell {
|
|
35
|
+
pointer-events: none;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
.grid-cell:not(.flat) {
|
|
39
|
+
pointer-events: auto;
|
|
40
|
+
cursor: default;
|
|
41
|
+
|
|
42
|
+
.content-area {
|
|
43
|
+
pointer-events: none;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
.top-grid {
|
|
48
|
+
z-index: 2;
|
|
49
|
+
pointer-events: none;
|
|
50
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { Component, computed, effect, inject, input, viewChildren, ChangeDetectionStrategy } from '@angular/core';
|
|
2
|
+
import { CommonModule } from '@angular/common';
|
|
3
|
+
import { CellComponent } from '../cell/cell.component';
|
|
4
|
+
import { DashboardStore } from '../store/dashboard-store';
|
|
5
|
+
import { CellIdUtils, WidgetIdUtils } from '../models';
|
|
6
|
+
|
|
7
|
+
@Component({
|
|
8
|
+
selector: 'ngx-dashboard-viewer',
|
|
9
|
+
standalone: true,
|
|
10
|
+
imports: [CellComponent, CommonModule],
|
|
11
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
12
|
+
templateUrl: './dashboard-viewer.component.html',
|
|
13
|
+
styleUrl: './dashboard-viewer.component.scss',
|
|
14
|
+
host: {
|
|
15
|
+
'[style.--rows]': 'rows()',
|
|
16
|
+
'[style.--columns]': 'columns()',
|
|
17
|
+
'[style.--gutter-size]': 'gutterSize()',
|
|
18
|
+
'[style.--gutters]': 'gutters()',
|
|
19
|
+
},
|
|
20
|
+
})
|
|
21
|
+
export class DashboardViewerComponent {
|
|
22
|
+
#store = inject(DashboardStore);
|
|
23
|
+
cellComponents = viewChildren(CellComponent);
|
|
24
|
+
|
|
25
|
+
rows = input.required<number>();
|
|
26
|
+
columns = input.required<number>();
|
|
27
|
+
gutterSize = input<string>('1em');
|
|
28
|
+
gutters = computed(() => this.columns() + 1);
|
|
29
|
+
|
|
30
|
+
// store signals - read-only
|
|
31
|
+
cells = this.#store.cells;
|
|
32
|
+
|
|
33
|
+
constructor() {
|
|
34
|
+
// Sync grid configuration with store when inputs change
|
|
35
|
+
effect(() => {
|
|
36
|
+
this.#store.setGridConfig({
|
|
37
|
+
rows: this.rows(),
|
|
38
|
+
columns: this.columns(),
|
|
39
|
+
gutterSize: this.gutterSize(),
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Get current widget states from all cell components.
|
|
46
|
+
* Used during dashboard export to get live widget states.
|
|
47
|
+
*/
|
|
48
|
+
getCurrentWidgetStates(): Map<string, unknown> {
|
|
49
|
+
const stateMap = new Map<string, unknown>();
|
|
50
|
+
|
|
51
|
+
const cells = this.cellComponents();
|
|
52
|
+
for (const cell of cells) {
|
|
53
|
+
const cellId = cell.cellId();
|
|
54
|
+
const currentState = cell.getCurrentWidgetState();
|
|
55
|
+
if (currentState !== undefined) {
|
|
56
|
+
stateMap.set(CellIdUtils.toString(cellId), currentState);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return stateMap;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Export dashboard with live widget states from current component instances.
|
|
65
|
+
* This ensures the most up-to-date widget states are captured.
|
|
66
|
+
*/
|
|
67
|
+
exportDashboard() {
|
|
68
|
+
return this.#store.exportDashboard(() => this.getCurrentWidgetStates());
|
|
69
|
+
}
|
|
70
|
+
}
|