@dragonworks/ngx-dashboard 20.0.6 → 20.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.
- package/fesm2022/dragonworks-ngx-dashboard.mjs +2250 -0
- package/fesm2022/dragonworks-ngx-dashboard.mjs.map +1 -0
- package/index.d.ts +727 -0
- package/package.json +45 -34
- package/ng-package.json +0 -7
- package/src/lib/__tests__/dashboard-component-widget-state-integration.spec.ts +0 -537
- package/src/lib/cell/__tests__/cell-resize.component.spec.ts +0 -442
- package/src/lib/cell/__tests__/cell.component.spec.ts +0 -541
- package/src/lib/cell/cell-context-menu.component.ts +0 -138
- package/src/lib/cell/cell-context-menu.service.ts +0 -36
- package/src/lib/cell/cell.component.html +0 -37
- package/src/lib/cell/cell.component.scss +0 -198
- package/src/lib/cell/cell.component.ts +0 -375
- package/src/lib/dashboard/dashboard.component.html +0 -18
- package/src/lib/dashboard/dashboard.component.scss +0 -17
- package/src/lib/dashboard/dashboard.component.ts +0 -187
- package/src/lib/dashboard-editor/dashboard-editor.component.html +0 -57
- package/src/lib/dashboard-editor/dashboard-editor.component.scss +0 -87
- package/src/lib/dashboard-editor/dashboard-editor.component.ts +0 -219
- package/src/lib/dashboard-viewer/__tests__/dashboard-viewer.component.spec.ts +0 -258
- package/src/lib/dashboard-viewer/dashboard-viewer.component.html +0 -20
- package/src/lib/dashboard-viewer/dashboard-viewer.component.scss +0 -50
- package/src/lib/dashboard-viewer/dashboard-viewer.component.ts +0 -70
- package/src/lib/drop-zone/__tests__/drop-zone.component.spec.ts +0 -465
- package/src/lib/drop-zone/drop-zone.component.html +0 -20
- package/src/lib/drop-zone/drop-zone.component.scss +0 -67
- package/src/lib/drop-zone/drop-zone.component.ts +0 -122
- package/src/lib/internal-widgets/unknown-widget/unknown-widget.component.ts +0 -72
- package/src/lib/models/cell-data.ts +0 -13
- package/src/lib/models/cell-dialog.ts +0 -7
- package/src/lib/models/cell-id.ts +0 -85
- package/src/lib/models/cell-position.ts +0 -15
- package/src/lib/models/dashboard-data.dto.ts +0 -44
- package/src/lib/models/dashboard-data.utils.ts +0 -49
- package/src/lib/models/drag-data.ts +0 -6
- package/src/lib/models/index.ts +0 -11
- package/src/lib/models/reserved-space.ts +0 -24
- package/src/lib/models/widget-factory.ts +0 -33
- package/src/lib/models/widget-id.ts +0 -70
- package/src/lib/models/widget.ts +0 -21
- package/src/lib/providers/cell-settings-dialog/cell-settings-dialog.component.ts +0 -127
- package/src/lib/providers/cell-settings-dialog/cell-settings-dialog.provider.ts +0 -15
- package/src/lib/providers/cell-settings-dialog/cell-settings-dialog.tokens.ts +0 -20
- package/src/lib/providers/cell-settings-dialog/default-cell-settings-dialog.provider.ts +0 -32
- package/src/lib/providers/cell-settings-dialog/index.ts +0 -3
- package/src/lib/providers/index.ts +0 -1
- package/src/lib/services/__tests__/dashboard-bridge.service.spec.ts +0 -220
- package/src/lib/services/__tests__/dashboard-viewport.service.spec.ts +0 -362
- package/src/lib/services/dashboard-bridge.service.ts +0 -155
- package/src/lib/services/dashboard-viewport.service.ts +0 -148
- package/src/lib/services/dashboard.service.ts +0 -62
- package/src/lib/store/__tests__/dashboard-store-collision-detection.spec.ts +0 -756
- package/src/lib/store/__tests__/dashboard-store-computed-properties.spec.ts +0 -974
- package/src/lib/store/__tests__/dashboard-store-drag-drop.spec.ts +0 -279
- package/src/lib/store/__tests__/dashboard-store-export-import.spec.ts +0 -780
- package/src/lib/store/__tests__/dashboard-store-grid-config.spec.ts +0 -128
- package/src/lib/store/__tests__/dashboard-store-query-methods.spec.ts +0 -229
- package/src/lib/store/__tests__/dashboard-store-resize-operations.spec.ts +0 -652
- package/src/lib/store/__tests__/dashboard-store-widget-management.spec.ts +0 -461
- package/src/lib/store/__tests__/dashboard-store-widget-state-preservation.spec.ts +0 -369
- package/src/lib/store/dashboard-store.ts +0 -239
- package/src/lib/store/features/drag-drop.feature.ts +0 -140
- package/src/lib/store/features/grid-config.feature.ts +0 -43
- package/src/lib/store/features/resize.feature.ts +0 -140
- package/src/lib/store/features/utils/collision.utils.ts +0 -89
- package/src/lib/store/features/utils/grid-query-internal.utils.ts +0 -37
- package/src/lib/store/features/utils/resize.utils.ts +0 -165
- package/src/lib/store/features/widget-management.feature.ts +0 -158
- package/src/lib/styles/_dashboard-grid-vars.scss +0 -11
- package/src/lib/widget-list/__tests__/widget-list-bridge-integration.spec.ts +0 -137
- package/src/lib/widget-list/widget-list.component.html +0 -22
- package/src/lib/widget-list/widget-list.component.scss +0 -154
- package/src/lib/widget-list/widget-list.component.ts +0 -106
- package/src/public-api.ts +0 -21
- package/src/test-setup.ts +0 -10
- package/tsconfig.lib.json +0 -15
- package/tsconfig.lib.prod.json +0 -11
- package/tsconfig.spec.json +0 -14
|
@@ -1,369 +0,0 @@
|
|
|
1
|
-
import { TestBed } from '@angular/core/testing';
|
|
2
|
-
import { Component, signal, ViewContainerRef } from '@angular/core';
|
|
3
|
-
import { DashboardService } from '../../services/dashboard.service';
|
|
4
|
-
import { DashboardStore } from '../dashboard-store';
|
|
5
|
-
import { CellIdUtils, WidgetIdUtils, CellData, WidgetFactory, DashboardDataDto, Widget, WidgetMetadata } from '../../models';
|
|
6
|
-
|
|
7
|
-
// Mock widget with changeable state to test state preservation
|
|
8
|
-
interface TestWidgetState {
|
|
9
|
-
value: string;
|
|
10
|
-
count: number;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
@Component({
|
|
14
|
-
selector: 'test-widget',
|
|
15
|
-
template: '<div>{{ state().value }} - {{ state().count }}</div>',
|
|
16
|
-
})
|
|
17
|
-
class TestWidgetComponent implements Widget {
|
|
18
|
-
static metadata: WidgetMetadata = {
|
|
19
|
-
widgetTypeid: 'test-widget',
|
|
20
|
-
name: 'Test Widget',
|
|
21
|
-
description: 'A test widget',
|
|
22
|
-
svgIcon: '<svg><rect width="10" height="10"/></svg>',
|
|
23
|
-
};
|
|
24
|
-
|
|
25
|
-
state = signal<TestWidgetState>({
|
|
26
|
-
value: 'initial',
|
|
27
|
-
count: 0,
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
dashboardSetState(state?: unknown) {
|
|
31
|
-
if (state) {
|
|
32
|
-
this.state.update((current) => ({
|
|
33
|
-
...current,
|
|
34
|
-
...(state as TestWidgetState),
|
|
35
|
-
}));
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
dashboardGetState(): TestWidgetState {
|
|
40
|
-
return { ...this.state() };
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
// Simulate widget state changes during runtime
|
|
44
|
-
updateState(newValue: string, newCount: number) {
|
|
45
|
-
this.state.set({ value: newValue, count: newCount });
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
describe('DashboardStore - Widget State Preservation Tests', () => {
|
|
50
|
-
let store: InstanceType<typeof DashboardStore>;
|
|
51
|
-
let mockWidgetFactory: WidgetFactory;
|
|
52
|
-
let mockDashboardService: jasmine.SpyObj<DashboardService>;
|
|
53
|
-
let testWidgetComponent: TestWidgetComponent;
|
|
54
|
-
|
|
55
|
-
beforeEach(() => {
|
|
56
|
-
const dashboardServiceSpy = jasmine.createSpyObj('DashboardService', ['getFactory']);
|
|
57
|
-
|
|
58
|
-
TestBed.configureTestingModule({
|
|
59
|
-
providers: [
|
|
60
|
-
DashboardStore,
|
|
61
|
-
{ provide: DashboardService, useValue: dashboardServiceSpy }
|
|
62
|
-
]
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
store = TestBed.inject(DashboardStore);
|
|
66
|
-
mockDashboardService = TestBed.inject(DashboardService) as jasmine.SpyObj<DashboardService>;
|
|
67
|
-
store.setGridConfig({ rows: 8, columns: 12 });
|
|
68
|
-
|
|
69
|
-
// Create a real widget instance for testing
|
|
70
|
-
testWidgetComponent = new TestWidgetComponent();
|
|
71
|
-
|
|
72
|
-
mockWidgetFactory = {
|
|
73
|
-
widgetTypeid: 'test-widget',
|
|
74
|
-
createInstance: jasmine.createSpy('createInstance').and.returnValue({
|
|
75
|
-
instance: testWidgetComponent,
|
|
76
|
-
destroy: jasmine.createSpy('destroy')
|
|
77
|
-
}),
|
|
78
|
-
createComponent: jasmine.createSpy('createComponent')
|
|
79
|
-
} as any;
|
|
80
|
-
|
|
81
|
-
mockDashboardService.getFactory.and.returnValue(mockWidgetFactory);
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
describe('Widget state preservation during export', () => {
|
|
85
|
-
it('should preserve initial widget state when no live changes made', () => {
|
|
86
|
-
const initialState: TestWidgetState = { value: 'initial', count: 0 };
|
|
87
|
-
|
|
88
|
-
const cell: CellData = {
|
|
89
|
-
widgetId: WidgetIdUtils.generate(),
|
|
90
|
-
cellId: CellIdUtils.create(2, 3),
|
|
91
|
-
row: 2,
|
|
92
|
-
col: 3,
|
|
93
|
-
rowSpan: 1,
|
|
94
|
-
colSpan: 1,
|
|
95
|
-
widgetFactory: mockWidgetFactory,
|
|
96
|
-
widgetState: initialState,
|
|
97
|
-
};
|
|
98
|
-
|
|
99
|
-
store.addWidget(cell);
|
|
100
|
-
|
|
101
|
-
// Test export without live state callback (backward compatibility)
|
|
102
|
-
const exported = store.exportDashboard();
|
|
103
|
-
|
|
104
|
-
expect(exported.cells.length).toBe(1);
|
|
105
|
-
expect(exported.cells[0].widgetState).toEqual(initialState);
|
|
106
|
-
});
|
|
107
|
-
|
|
108
|
-
it('should use live widget state when getCurrentWidgetStates callback is provided', () => {
|
|
109
|
-
const initialState: TestWidgetState = { value: 'initial', count: 0 };
|
|
110
|
-
const liveState: TestWidgetState = { value: 'updated', count: 5 };
|
|
111
|
-
|
|
112
|
-
const cell: CellData = {
|
|
113
|
-
widgetId: WidgetIdUtils.generate(),
|
|
114
|
-
cellId: CellIdUtils.create(2, 3),
|
|
115
|
-
row: 2,
|
|
116
|
-
col: 3,
|
|
117
|
-
rowSpan: 1,
|
|
118
|
-
colSpan: 1,
|
|
119
|
-
widgetFactory: mockWidgetFactory,
|
|
120
|
-
widgetState: initialState,
|
|
121
|
-
};
|
|
122
|
-
|
|
123
|
-
store.addWidget(cell);
|
|
124
|
-
|
|
125
|
-
// Simulate widget state change after initial creation
|
|
126
|
-
testWidgetComponent.updateState('updated', 5);
|
|
127
|
-
|
|
128
|
-
// Mock the live widget state collection that would come from CellComponent
|
|
129
|
-
const liveWidgetStates = new Map<string, unknown>();
|
|
130
|
-
liveWidgetStates.set('2-3', liveState);
|
|
131
|
-
|
|
132
|
-
const exported = store.exportDashboard(() => liveWidgetStates);
|
|
133
|
-
|
|
134
|
-
expect(exported.cells.length).toBe(1);
|
|
135
|
-
expect(exported.cells[0].widgetState).toEqual(liveState);
|
|
136
|
-
expect(exported.cells[0].widgetState).not.toEqual(initialState);
|
|
137
|
-
});
|
|
138
|
-
|
|
139
|
-
it('should fall back to stored state when live state is unavailable', () => {
|
|
140
|
-
const storedState: TestWidgetState = { value: 'stored', count: 2 };
|
|
141
|
-
|
|
142
|
-
const cell: CellData = {
|
|
143
|
-
widgetId: WidgetIdUtils.generate(),
|
|
144
|
-
cellId: CellIdUtils.create(4, 5),
|
|
145
|
-
row: 4,
|
|
146
|
-
col: 5,
|
|
147
|
-
rowSpan: 1,
|
|
148
|
-
colSpan: 1,
|
|
149
|
-
widgetFactory: mockWidgetFactory,
|
|
150
|
-
widgetState: storedState,
|
|
151
|
-
};
|
|
152
|
-
|
|
153
|
-
store.addWidget(cell);
|
|
154
|
-
|
|
155
|
-
// Mock live widget states that don't include this cell
|
|
156
|
-
const liveWidgetStates = new Map<string, unknown>();
|
|
157
|
-
// Intentionally not adding state for cell '4-5'
|
|
158
|
-
|
|
159
|
-
const exported = store.exportDashboard(() => liveWidgetStates);
|
|
160
|
-
|
|
161
|
-
expect(exported.cells.length).toBe(1);
|
|
162
|
-
expect(exported.cells[0].widgetState).toEqual(storedState);
|
|
163
|
-
});
|
|
164
|
-
|
|
165
|
-
it('should handle mixed scenario with some live and some stored states', () => {
|
|
166
|
-
const cell1: CellData = {
|
|
167
|
-
widgetId: WidgetIdUtils.generate(),
|
|
168
|
-
cellId: CellIdUtils.create(1, 1),
|
|
169
|
-
row: 1,
|
|
170
|
-
col: 1,
|
|
171
|
-
rowSpan: 1,
|
|
172
|
-
colSpan: 1,
|
|
173
|
-
widgetFactory: mockWidgetFactory,
|
|
174
|
-
widgetState: { value: 'stored1', count: 1 },
|
|
175
|
-
};
|
|
176
|
-
|
|
177
|
-
const cell2: CellData = {
|
|
178
|
-
widgetId: WidgetIdUtils.generate(),
|
|
179
|
-
cellId: CellIdUtils.create(2, 2),
|
|
180
|
-
row: 2,
|
|
181
|
-
col: 2,
|
|
182
|
-
rowSpan: 1,
|
|
183
|
-
colSpan: 1,
|
|
184
|
-
widgetFactory: mockWidgetFactory,
|
|
185
|
-
widgetState: { value: 'stored2', count: 2 },
|
|
186
|
-
};
|
|
187
|
-
|
|
188
|
-
store.addWidget(cell1);
|
|
189
|
-
store.addWidget(cell2);
|
|
190
|
-
|
|
191
|
-
// Only provide live state for first cell
|
|
192
|
-
const liveWidgetStates = new Map<string, unknown>();
|
|
193
|
-
liveWidgetStates.set('1-1', { value: 'live1', count: 10 });
|
|
194
|
-
|
|
195
|
-
const exported = store.exportDashboard(() => liveWidgetStates);
|
|
196
|
-
|
|
197
|
-
expect(exported.cells.length).toBe(2);
|
|
198
|
-
|
|
199
|
-
const cell1Export = exported.cells.find(c => c.row === 1 && c.col === 1);
|
|
200
|
-
const cell2Export = exported.cells.find(c => c.row === 2 && c.col === 2);
|
|
201
|
-
|
|
202
|
-
expect(cell1Export?.widgetState).toEqual({ value: 'live1', count: 10 });
|
|
203
|
-
expect(cell2Export?.widgetState).toEqual({ value: 'stored2', count: 2 });
|
|
204
|
-
});
|
|
205
|
-
|
|
206
|
-
it('should preserve complex widget states with nested objects', () => {
|
|
207
|
-
const complexState = {
|
|
208
|
-
value: 'complex',
|
|
209
|
-
count: 42,
|
|
210
|
-
config: {
|
|
211
|
-
nested: {
|
|
212
|
-
prop1: 'value1',
|
|
213
|
-
prop2: ['array', 'values'],
|
|
214
|
-
prop3: { deep: true }
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
};
|
|
218
|
-
|
|
219
|
-
const cell: CellData = {
|
|
220
|
-
widgetId: WidgetIdUtils.generate(),
|
|
221
|
-
cellId: CellIdUtils.create(3, 3),
|
|
222
|
-
row: 3,
|
|
223
|
-
col: 3,
|
|
224
|
-
rowSpan: 1,
|
|
225
|
-
colSpan: 1,
|
|
226
|
-
widgetFactory: mockWidgetFactory,
|
|
227
|
-
widgetState: complexState,
|
|
228
|
-
};
|
|
229
|
-
|
|
230
|
-
store.addWidget(cell);
|
|
231
|
-
|
|
232
|
-
const liveWidgetStates = new Map<string, unknown>();
|
|
233
|
-
liveWidgetStates.set('3-3', complexState);
|
|
234
|
-
|
|
235
|
-
const exported = store.exportDashboard(() => liveWidgetStates);
|
|
236
|
-
|
|
237
|
-
expect(exported.cells.length).toBe(1);
|
|
238
|
-
expect(exported.cells[0].widgetState).toEqual(complexState);
|
|
239
|
-
});
|
|
240
|
-
|
|
241
|
-
it('should handle undefined live state gracefully', () => {
|
|
242
|
-
const storedState: TestWidgetState = { value: 'stored', count: 3 };
|
|
243
|
-
|
|
244
|
-
const cell: CellData = {
|
|
245
|
-
widgetId: WidgetIdUtils.generate(),
|
|
246
|
-
cellId: CellIdUtils.create(5, 6),
|
|
247
|
-
row: 5,
|
|
248
|
-
col: 6,
|
|
249
|
-
rowSpan: 1,
|
|
250
|
-
colSpan: 1,
|
|
251
|
-
widgetFactory: mockWidgetFactory,
|
|
252
|
-
widgetState: storedState,
|
|
253
|
-
};
|
|
254
|
-
|
|
255
|
-
store.addWidget(cell);
|
|
256
|
-
|
|
257
|
-
const liveWidgetStates = new Map<string, unknown>();
|
|
258
|
-
liveWidgetStates.set('5-6', undefined); // Explicitly set to undefined
|
|
259
|
-
|
|
260
|
-
const exported = store.exportDashboard(() => liveWidgetStates);
|
|
261
|
-
|
|
262
|
-
expect(exported.cells.length).toBe(1);
|
|
263
|
-
expect(exported.cells[0].widgetState).toEqual(storedState);
|
|
264
|
-
});
|
|
265
|
-
|
|
266
|
-
it('should export state correctly after multiple widget updates', () => {
|
|
267
|
-
const cell: CellData = {
|
|
268
|
-
widgetId: WidgetIdUtils.generate(),
|
|
269
|
-
cellId: CellIdUtils.create(1, 2),
|
|
270
|
-
row: 1,
|
|
271
|
-
col: 2,
|
|
272
|
-
rowSpan: 1,
|
|
273
|
-
colSpan: 1,
|
|
274
|
-
widgetFactory: mockWidgetFactory,
|
|
275
|
-
widgetState: { value: 'initial', count: 0 },
|
|
276
|
-
};
|
|
277
|
-
|
|
278
|
-
store.addWidget(cell);
|
|
279
|
-
|
|
280
|
-
// Simulate multiple widget state changes
|
|
281
|
-
testWidgetComponent.updateState('first-update', 1);
|
|
282
|
-
testWidgetComponent.updateState('second-update', 2);
|
|
283
|
-
testWidgetComponent.updateState('final-state', 99);
|
|
284
|
-
|
|
285
|
-
const finalState = testWidgetComponent.dashboardGetState();
|
|
286
|
-
const liveWidgetStates = new Map<string, unknown>();
|
|
287
|
-
liveWidgetStates.set('1-2', finalState);
|
|
288
|
-
|
|
289
|
-
const exported = store.exportDashboard(() => liveWidgetStates);
|
|
290
|
-
|
|
291
|
-
expect(exported.cells.length).toBe(1);
|
|
292
|
-
expect(exported.cells[0].widgetState).toEqual({
|
|
293
|
-
value: 'final-state',
|
|
294
|
-
count: 99
|
|
295
|
-
});
|
|
296
|
-
});
|
|
297
|
-
});
|
|
298
|
-
|
|
299
|
-
describe('Round-trip export/import with widget states', () => {
|
|
300
|
-
it('should preserve widget state through export/import cycle', () => {
|
|
301
|
-
const originalState: TestWidgetState = { value: 'original', count: 42 };
|
|
302
|
-
|
|
303
|
-
const cell: CellData = {
|
|
304
|
-
widgetId: WidgetIdUtils.generate(),
|
|
305
|
-
cellId: CellIdUtils.create(2, 3),
|
|
306
|
-
row: 2,
|
|
307
|
-
col: 3,
|
|
308
|
-
rowSpan: 1,
|
|
309
|
-
colSpan: 1,
|
|
310
|
-
widgetFactory: mockWidgetFactory,
|
|
311
|
-
widgetState: originalState,
|
|
312
|
-
};
|
|
313
|
-
|
|
314
|
-
store.addWidget(cell);
|
|
315
|
-
|
|
316
|
-
// Export dashboard
|
|
317
|
-
const exported = store.exportDashboard();
|
|
318
|
-
|
|
319
|
-
// Clear dashboard
|
|
320
|
-
store.clearDashboard();
|
|
321
|
-
expect(store.cells().length).toBe(0);
|
|
322
|
-
|
|
323
|
-
// Import dashboard
|
|
324
|
-
store.loadDashboard(exported);
|
|
325
|
-
|
|
326
|
-
expect(store.cells().length).toBe(1);
|
|
327
|
-
const importedCell = store.cells()[0];
|
|
328
|
-
expect(importedCell.widgetState).toEqual(originalState);
|
|
329
|
-
expect(importedCell.row).toBe(2);
|
|
330
|
-
expect(importedCell.col).toBe(3);
|
|
331
|
-
});
|
|
332
|
-
|
|
333
|
-
it('should preserve live widget state through export/import cycle', () => {
|
|
334
|
-
const initialState: TestWidgetState = { value: 'initial', count: 0 };
|
|
335
|
-
const liveState: TestWidgetState = { value: 'live-updated', count: 100 };
|
|
336
|
-
|
|
337
|
-
const cell: CellData = {
|
|
338
|
-
widgetId: WidgetIdUtils.generate(),
|
|
339
|
-
cellId: CellIdUtils.create(1, 1),
|
|
340
|
-
row: 1,
|
|
341
|
-
col: 1,
|
|
342
|
-
rowSpan: 1,
|
|
343
|
-
colSpan: 1,
|
|
344
|
-
widgetFactory: mockWidgetFactory,
|
|
345
|
-
widgetState: initialState,
|
|
346
|
-
};
|
|
347
|
-
|
|
348
|
-
store.addWidget(cell);
|
|
349
|
-
|
|
350
|
-
// Simulate live widget state
|
|
351
|
-
const liveWidgetStates = new Map<string, unknown>();
|
|
352
|
-
liveWidgetStates.set('1-1', liveState);
|
|
353
|
-
|
|
354
|
-
// Export with live state
|
|
355
|
-
const exported = store.exportDashboard(() => liveWidgetStates);
|
|
356
|
-
|
|
357
|
-
// Verify exported state is the live state
|
|
358
|
-
expect(exported.cells[0].widgetState).toEqual(liveState);
|
|
359
|
-
|
|
360
|
-
// Clear and re-import
|
|
361
|
-
store.clearDashboard();
|
|
362
|
-
store.loadDashboard(exported);
|
|
363
|
-
|
|
364
|
-
// Verify imported state matches the live state that was exported
|
|
365
|
-
const importedCell = store.cells()[0];
|
|
366
|
-
expect(importedCell.widgetState).toEqual(liveState);
|
|
367
|
-
});
|
|
368
|
-
});
|
|
369
|
-
});
|
|
@@ -1,239 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
signalStore,
|
|
3
|
-
withProps,
|
|
4
|
-
withState,
|
|
5
|
-
withComputed,
|
|
6
|
-
withMethods,
|
|
7
|
-
patchState,
|
|
8
|
-
} from '@ngrx/signals';
|
|
9
|
-
import { DashboardService } from '../services/dashboard.service';
|
|
10
|
-
import { inject, computed } from '@angular/core';
|
|
11
|
-
import { calculateCollisionInfo } from './features/utils/collision.utils';
|
|
12
|
-
import {
|
|
13
|
-
CellId,
|
|
14
|
-
CellIdUtils,
|
|
15
|
-
CellData,
|
|
16
|
-
DragData,
|
|
17
|
-
DashboardDataDto,
|
|
18
|
-
WidgetId,
|
|
19
|
-
WidgetIdUtils,
|
|
20
|
-
} from '../models';
|
|
21
|
-
import { withGridConfig } from './features/grid-config.feature';
|
|
22
|
-
import { withWidgetManagement } from './features/widget-management.feature';
|
|
23
|
-
import { withDragDrop } from './features/drag-drop.feature';
|
|
24
|
-
import { withResize, ResizePreviewUtils } from './features/resize.feature';
|
|
25
|
-
|
|
26
|
-
type DashboardState = {
|
|
27
|
-
dashboardId: string;
|
|
28
|
-
};
|
|
29
|
-
|
|
30
|
-
const initialState: DashboardState = {
|
|
31
|
-
dashboardId: '',
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
export const DashboardStore = signalStore(
|
|
35
|
-
withState(initialState),
|
|
36
|
-
withProps(() => ({
|
|
37
|
-
dashboardService: inject(DashboardService),
|
|
38
|
-
})),
|
|
39
|
-
withGridConfig(),
|
|
40
|
-
withWidgetManagement(),
|
|
41
|
-
withResize(),
|
|
42
|
-
withDragDrop(),
|
|
43
|
-
|
|
44
|
-
// Cross-feature computed properties (need access to multiple features)
|
|
45
|
-
withComputed((store) => ({
|
|
46
|
-
// Invalid zones (collision detection)
|
|
47
|
-
invalidHighlightMap: computed(() => {
|
|
48
|
-
const collisionInfo = calculateCollisionInfo(
|
|
49
|
-
store.dragData(),
|
|
50
|
-
store.hoveredDropZone(),
|
|
51
|
-
store.cells(),
|
|
52
|
-
store.rows(),
|
|
53
|
-
store.columns()
|
|
54
|
-
);
|
|
55
|
-
|
|
56
|
-
return new Set(collisionInfo.invalidCells);
|
|
57
|
-
}),
|
|
58
|
-
|
|
59
|
-
// Check if placement would be valid (for drop validation)
|
|
60
|
-
isValidPlacement: computed(() => {
|
|
61
|
-
const collisionInfo = calculateCollisionInfo(
|
|
62
|
-
store.dragData(),
|
|
63
|
-
store.hoveredDropZone(),
|
|
64
|
-
store.cells(),
|
|
65
|
-
store.rows(),
|
|
66
|
-
store.columns()
|
|
67
|
-
);
|
|
68
|
-
|
|
69
|
-
return !collisionInfo.hasCollisions && !collisionInfo.outOfBounds;
|
|
70
|
-
}),
|
|
71
|
-
})),
|
|
72
|
-
|
|
73
|
-
// Cross-feature methods (need access to multiple features)
|
|
74
|
-
withMethods((store) => ({
|
|
75
|
-
// DROP HANDLING (delegate to drag-drop feature with dependency injection)
|
|
76
|
-
handleDrop(
|
|
77
|
-
dragData: DragData,
|
|
78
|
-
targetPosition: { row: number; col: number }
|
|
79
|
-
): boolean {
|
|
80
|
-
return store._handleDrop(dragData, targetPosition, {
|
|
81
|
-
cells: store.cells(),
|
|
82
|
-
rows: store.rows(),
|
|
83
|
-
columns: store.columns(),
|
|
84
|
-
dashboardService: store.dashboardService,
|
|
85
|
-
createWidget: store.createWidget,
|
|
86
|
-
updateWidgetPosition: store.updateWidgetPosition,
|
|
87
|
-
});
|
|
88
|
-
},
|
|
89
|
-
|
|
90
|
-
// RESIZE METHODS (delegate to resize feature with dependency injection)
|
|
91
|
-
startResize(cellId: CellId) {
|
|
92
|
-
store._startResize(cellId, {
|
|
93
|
-
cells: store.cells(),
|
|
94
|
-
});
|
|
95
|
-
},
|
|
96
|
-
|
|
97
|
-
updateResizePreview(direction: 'horizontal' | 'vertical', delta: number) {
|
|
98
|
-
store._updateResizePreview(direction, delta, {
|
|
99
|
-
cells: store.cells(),
|
|
100
|
-
rows: store.rows(),
|
|
101
|
-
columns: store.columns(),
|
|
102
|
-
});
|
|
103
|
-
},
|
|
104
|
-
|
|
105
|
-
endResize(apply: boolean) {
|
|
106
|
-
store._endResize(apply, {
|
|
107
|
-
updateWidgetSpan: (cellId: CellId, rowSpan: number, colSpan: number) => {
|
|
108
|
-
// Adapter: find widget by cellId and update using widgetId
|
|
109
|
-
const widget = store.cells().find(c => CellIdUtils.equals(c.cellId, cellId));
|
|
110
|
-
if (widget) {
|
|
111
|
-
store.updateWidgetSpan(widget.widgetId, rowSpan, colSpan);
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
});
|
|
115
|
-
},
|
|
116
|
-
|
|
117
|
-
// EXPORT/IMPORT METHODS (need access to multiple features)
|
|
118
|
-
exportDashboard(
|
|
119
|
-
getCurrentWidgetStates?: () => Map<string, unknown>
|
|
120
|
-
): DashboardDataDto {
|
|
121
|
-
// Get live widget states if callback provided, otherwise use stored states
|
|
122
|
-
const liveWidgetStates =
|
|
123
|
-
getCurrentWidgetStates?.() || new Map<string, unknown>();
|
|
124
|
-
|
|
125
|
-
return {
|
|
126
|
-
version: '1.0.0',
|
|
127
|
-
dashboardId: store.dashboardId(),
|
|
128
|
-
rows: store.rows(),
|
|
129
|
-
columns: store.columns(),
|
|
130
|
-
gutterSize: store.gutterSize(),
|
|
131
|
-
cells: store.cells()
|
|
132
|
-
.filter((cell) => cell.widgetFactory.widgetTypeid !== '__internal/unknown-widget')
|
|
133
|
-
.map((cell) => {
|
|
134
|
-
const cellIdString = CellIdUtils.toString(cell.cellId);
|
|
135
|
-
const currentState = liveWidgetStates.get(cellIdString);
|
|
136
|
-
|
|
137
|
-
return {
|
|
138
|
-
row: cell.row,
|
|
139
|
-
col: cell.col,
|
|
140
|
-
rowSpan: cell.rowSpan,
|
|
141
|
-
colSpan: cell.colSpan,
|
|
142
|
-
flat: cell.flat,
|
|
143
|
-
widgetTypeid: cell.widgetFactory.widgetTypeid,
|
|
144
|
-
widgetState:
|
|
145
|
-
currentState !== undefined ? currentState : cell.widgetState,
|
|
146
|
-
};
|
|
147
|
-
}),
|
|
148
|
-
};
|
|
149
|
-
},
|
|
150
|
-
|
|
151
|
-
loadDashboard(data: DashboardDataDto): void {
|
|
152
|
-
// Import full dashboard data with grid configuration
|
|
153
|
-
const widgetsById: Record<string, CellData> = {};
|
|
154
|
-
|
|
155
|
-
data.cells.forEach((cellData) => {
|
|
156
|
-
const factory = store.dashboardService.getFactory(
|
|
157
|
-
cellData.widgetTypeid
|
|
158
|
-
);
|
|
159
|
-
|
|
160
|
-
const widgetId = WidgetIdUtils.generate();
|
|
161
|
-
const cell: CellData = {
|
|
162
|
-
widgetId,
|
|
163
|
-
cellId: CellIdUtils.create(cellData.row, cellData.col),
|
|
164
|
-
row: cellData.row,
|
|
165
|
-
col: cellData.col,
|
|
166
|
-
rowSpan: cellData.rowSpan,
|
|
167
|
-
colSpan: cellData.colSpan,
|
|
168
|
-
flat: cellData.flat,
|
|
169
|
-
widgetFactory: factory,
|
|
170
|
-
widgetState: cellData.widgetState,
|
|
171
|
-
};
|
|
172
|
-
|
|
173
|
-
widgetsById[WidgetIdUtils.toString(widgetId)] = cell;
|
|
174
|
-
});
|
|
175
|
-
|
|
176
|
-
patchState(store, {
|
|
177
|
-
dashboardId: data.dashboardId,
|
|
178
|
-
rows: data.rows,
|
|
179
|
-
columns: data.columns,
|
|
180
|
-
gutterSize: data.gutterSize,
|
|
181
|
-
widgetsById,
|
|
182
|
-
});
|
|
183
|
-
},
|
|
184
|
-
|
|
185
|
-
// INITIALIZATION METHODS
|
|
186
|
-
initializeFromDto(dashboardData: DashboardDataDto): void {
|
|
187
|
-
// Inline the loadDashboard logic since it's defined later in the same methods block
|
|
188
|
-
const widgetsById: Record<string, CellData> = {};
|
|
189
|
-
|
|
190
|
-
dashboardData.cells.forEach((cellData) => {
|
|
191
|
-
const factory = store.dashboardService.getFactory(
|
|
192
|
-
cellData.widgetTypeid
|
|
193
|
-
);
|
|
194
|
-
|
|
195
|
-
const widgetId = WidgetIdUtils.generate();
|
|
196
|
-
const cell: CellData = {
|
|
197
|
-
widgetId,
|
|
198
|
-
cellId: CellIdUtils.create(cellData.row, cellData.col),
|
|
199
|
-
row: cellData.row,
|
|
200
|
-
col: cellData.col,
|
|
201
|
-
rowSpan: cellData.rowSpan,
|
|
202
|
-
colSpan: cellData.colSpan,
|
|
203
|
-
flat: cellData.flat,
|
|
204
|
-
widgetFactory: factory,
|
|
205
|
-
widgetState: cellData.widgetState,
|
|
206
|
-
};
|
|
207
|
-
|
|
208
|
-
widgetsById[WidgetIdUtils.toString(widgetId)] = cell;
|
|
209
|
-
});
|
|
210
|
-
|
|
211
|
-
patchState(store, {
|
|
212
|
-
dashboardId: dashboardData.dashboardId,
|
|
213
|
-
rows: dashboardData.rows,
|
|
214
|
-
columns: dashboardData.columns,
|
|
215
|
-
gutterSize: dashboardData.gutterSize,
|
|
216
|
-
widgetsById,
|
|
217
|
-
});
|
|
218
|
-
},
|
|
219
|
-
})),
|
|
220
|
-
|
|
221
|
-
// Cross-feature computed properties that depend on resize + widget data (using utility functions)
|
|
222
|
-
withComputed((store) => ({
|
|
223
|
-
// Compute preview cells during resize using utility function
|
|
224
|
-
resizePreviewCells: computed(() => {
|
|
225
|
-
return ResizePreviewUtils.computePreviewCells(
|
|
226
|
-
store.resizeData(),
|
|
227
|
-
store.cells()
|
|
228
|
-
);
|
|
229
|
-
}),
|
|
230
|
-
})),
|
|
231
|
-
|
|
232
|
-
// Second computed block that depends on the first
|
|
233
|
-
withComputed((store) => ({
|
|
234
|
-
// Map for resize preview highlighting using utility function
|
|
235
|
-
resizePreviewMap: computed(() => {
|
|
236
|
-
return ResizePreviewUtils.computePreviewMap(store.resizePreviewCells());
|
|
237
|
-
}),
|
|
238
|
-
}))
|
|
239
|
-
);
|