@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.
Files changed (78) hide show
  1. package/fesm2022/dragonworks-ngx-dashboard.mjs +2250 -0
  2. package/fesm2022/dragonworks-ngx-dashboard.mjs.map +1 -0
  3. package/index.d.ts +727 -0
  4. package/package.json +45 -34
  5. package/ng-package.json +0 -7
  6. package/src/lib/__tests__/dashboard-component-widget-state-integration.spec.ts +0 -537
  7. package/src/lib/cell/__tests__/cell-resize.component.spec.ts +0 -442
  8. package/src/lib/cell/__tests__/cell.component.spec.ts +0 -541
  9. package/src/lib/cell/cell-context-menu.component.ts +0 -138
  10. package/src/lib/cell/cell-context-menu.service.ts +0 -36
  11. package/src/lib/cell/cell.component.html +0 -37
  12. package/src/lib/cell/cell.component.scss +0 -198
  13. package/src/lib/cell/cell.component.ts +0 -375
  14. package/src/lib/dashboard/dashboard.component.html +0 -18
  15. package/src/lib/dashboard/dashboard.component.scss +0 -17
  16. package/src/lib/dashboard/dashboard.component.ts +0 -187
  17. package/src/lib/dashboard-editor/dashboard-editor.component.html +0 -57
  18. package/src/lib/dashboard-editor/dashboard-editor.component.scss +0 -87
  19. package/src/lib/dashboard-editor/dashboard-editor.component.ts +0 -219
  20. package/src/lib/dashboard-viewer/__tests__/dashboard-viewer.component.spec.ts +0 -258
  21. package/src/lib/dashboard-viewer/dashboard-viewer.component.html +0 -20
  22. package/src/lib/dashboard-viewer/dashboard-viewer.component.scss +0 -50
  23. package/src/lib/dashboard-viewer/dashboard-viewer.component.ts +0 -70
  24. package/src/lib/drop-zone/__tests__/drop-zone.component.spec.ts +0 -465
  25. package/src/lib/drop-zone/drop-zone.component.html +0 -20
  26. package/src/lib/drop-zone/drop-zone.component.scss +0 -67
  27. package/src/lib/drop-zone/drop-zone.component.ts +0 -122
  28. package/src/lib/internal-widgets/unknown-widget/unknown-widget.component.ts +0 -72
  29. package/src/lib/models/cell-data.ts +0 -13
  30. package/src/lib/models/cell-dialog.ts +0 -7
  31. package/src/lib/models/cell-id.ts +0 -85
  32. package/src/lib/models/cell-position.ts +0 -15
  33. package/src/lib/models/dashboard-data.dto.ts +0 -44
  34. package/src/lib/models/dashboard-data.utils.ts +0 -49
  35. package/src/lib/models/drag-data.ts +0 -6
  36. package/src/lib/models/index.ts +0 -11
  37. package/src/lib/models/reserved-space.ts +0 -24
  38. package/src/lib/models/widget-factory.ts +0 -33
  39. package/src/lib/models/widget-id.ts +0 -70
  40. package/src/lib/models/widget.ts +0 -21
  41. package/src/lib/providers/cell-settings-dialog/cell-settings-dialog.component.ts +0 -127
  42. package/src/lib/providers/cell-settings-dialog/cell-settings-dialog.provider.ts +0 -15
  43. package/src/lib/providers/cell-settings-dialog/cell-settings-dialog.tokens.ts +0 -20
  44. package/src/lib/providers/cell-settings-dialog/default-cell-settings-dialog.provider.ts +0 -32
  45. package/src/lib/providers/cell-settings-dialog/index.ts +0 -3
  46. package/src/lib/providers/index.ts +0 -1
  47. package/src/lib/services/__tests__/dashboard-bridge.service.spec.ts +0 -220
  48. package/src/lib/services/__tests__/dashboard-viewport.service.spec.ts +0 -362
  49. package/src/lib/services/dashboard-bridge.service.ts +0 -155
  50. package/src/lib/services/dashboard-viewport.service.ts +0 -148
  51. package/src/lib/services/dashboard.service.ts +0 -62
  52. package/src/lib/store/__tests__/dashboard-store-collision-detection.spec.ts +0 -756
  53. package/src/lib/store/__tests__/dashboard-store-computed-properties.spec.ts +0 -974
  54. package/src/lib/store/__tests__/dashboard-store-drag-drop.spec.ts +0 -279
  55. package/src/lib/store/__tests__/dashboard-store-export-import.spec.ts +0 -780
  56. package/src/lib/store/__tests__/dashboard-store-grid-config.spec.ts +0 -128
  57. package/src/lib/store/__tests__/dashboard-store-query-methods.spec.ts +0 -229
  58. package/src/lib/store/__tests__/dashboard-store-resize-operations.spec.ts +0 -652
  59. package/src/lib/store/__tests__/dashboard-store-widget-management.spec.ts +0 -461
  60. package/src/lib/store/__tests__/dashboard-store-widget-state-preservation.spec.ts +0 -369
  61. package/src/lib/store/dashboard-store.ts +0 -239
  62. package/src/lib/store/features/drag-drop.feature.ts +0 -140
  63. package/src/lib/store/features/grid-config.feature.ts +0 -43
  64. package/src/lib/store/features/resize.feature.ts +0 -140
  65. package/src/lib/store/features/utils/collision.utils.ts +0 -89
  66. package/src/lib/store/features/utils/grid-query-internal.utils.ts +0 -37
  67. package/src/lib/store/features/utils/resize.utils.ts +0 -165
  68. package/src/lib/store/features/widget-management.feature.ts +0 -158
  69. package/src/lib/styles/_dashboard-grid-vars.scss +0 -11
  70. package/src/lib/widget-list/__tests__/widget-list-bridge-integration.spec.ts +0 -137
  71. package/src/lib/widget-list/widget-list.component.html +0 -22
  72. package/src/lib/widget-list/widget-list.component.scss +0 -154
  73. package/src/lib/widget-list/widget-list.component.ts +0 -106
  74. package/src/public-api.ts +0 -21
  75. package/src/test-setup.ts +0 -10
  76. package/tsconfig.lib.json +0 -15
  77. package/tsconfig.lib.prod.json +0 -11
  78. package/tsconfig.spec.json +0 -14
@@ -1,158 +0,0 @@
1
- import {
2
- signalStoreFeature,
3
- withMethods,
4
- withState,
5
- withComputed,
6
- patchState,
7
- } from '@ngrx/signals';
8
- import { computed } from '@angular/core';
9
- import {
10
- CellId,
11
- CellIdUtils,
12
- CellData,
13
- WidgetFactory,
14
- WidgetId,
15
- WidgetIdUtils,
16
- } from '../../models';
17
-
18
- export interface WidgetManagementState {
19
- widgetsById: Record<string, CellData>;
20
- }
21
-
22
- const initialWidgetManagementState: WidgetManagementState = {
23
- widgetsById: {},
24
- };
25
-
26
- export const withWidgetManagement = () =>
27
- signalStoreFeature(
28
- withState<WidgetManagementState>(initialWidgetManagementState),
29
-
30
- // Computed cells array - lazy evaluation, automatic memoization
31
- withComputed((store) => ({
32
- cells: computed(() => Object.values(store.widgetsById())),
33
- })),
34
-
35
- withMethods((store) => ({
36
- addWidget(cell: CellData) {
37
- const widgetKey = WidgetIdUtils.toString(cell.widgetId);
38
- patchState(store, {
39
- widgetsById: { ...store.widgetsById(), [widgetKey]: cell },
40
- });
41
- },
42
-
43
- removeWidget(widgetId: WidgetId) {
44
- const widgetKey = WidgetIdUtils.toString(widgetId);
45
- const { [widgetKey]: removed, ...remaining } = store.widgetsById();
46
- patchState(store, { widgetsById: remaining });
47
- },
48
-
49
- updateWidgetPosition(widgetId: WidgetId, row: number, col: number) {
50
- const widgetKey = WidgetIdUtils.toString(widgetId);
51
- const existingWidget = store.widgetsById()[widgetKey];
52
-
53
- if (existingWidget) {
54
- // Update position and recalculate cellId based on new position
55
- const newCellId = CellIdUtils.create(row, col);
56
- patchState(store, {
57
- widgetsById: {
58
- ...store.widgetsById(),
59
- [widgetKey]: { ...existingWidget, row, col, cellId: newCellId },
60
- },
61
- });
62
- }
63
- },
64
-
65
- createWidget(
66
- row: number,
67
- col: number,
68
- widgetFactory: WidgetFactory,
69
- widgetState?: string
70
- ) {
71
- const widgetId = WidgetIdUtils.generate(); // Generate unique widget ID
72
- const cellId = CellIdUtils.create(row, col); // Calculate position-based cell ID
73
- const cell: CellData = {
74
- widgetId,
75
- cellId,
76
- row,
77
- col,
78
- rowSpan: 1,
79
- colSpan: 1,
80
- widgetFactory,
81
- widgetState,
82
- };
83
-
84
- const widgetKey = WidgetIdUtils.toString(widgetId);
85
- patchState(store, {
86
- widgetsById: { ...store.widgetsById(), [widgetKey]: cell },
87
- });
88
- },
89
-
90
- updateCellSettings(widgetId: WidgetId, flat: boolean) {
91
- const widgetKey = WidgetIdUtils.toString(widgetId);
92
- const existingWidget = store.widgetsById()[widgetKey];
93
-
94
- if (existingWidget) {
95
- patchState(store, {
96
- widgetsById: {
97
- ...store.widgetsById(),
98
- [widgetKey]: { ...existingWidget, flat },
99
- },
100
- });
101
- }
102
- },
103
-
104
- updateWidgetSpan(widgetId: WidgetId, rowSpan: number, colSpan: number) {
105
- const widgetKey = WidgetIdUtils.toString(widgetId);
106
- const existingWidget = store.widgetsById()[widgetKey];
107
-
108
- if (existingWidget) {
109
- patchState(store, {
110
- widgetsById: {
111
- ...store.widgetsById(),
112
- [widgetKey]: { ...existingWidget, rowSpan, colSpan },
113
- },
114
- });
115
- }
116
- },
117
-
118
- updateWidgetState(widgetId: WidgetId, widgetState: unknown) {
119
- const widgetKey = WidgetIdUtils.toString(widgetId);
120
- const existingWidget = store.widgetsById()[widgetKey];
121
-
122
- if (existingWidget) {
123
- patchState(store, {
124
- widgetsById: {
125
- ...store.widgetsById(),
126
- [widgetKey]: { ...existingWidget, widgetState },
127
- },
128
- });
129
- }
130
- },
131
-
132
- updateAllWidgetStates(cellStates: Map<string, unknown>) {
133
- const updatedWidgetsById = { ...store.widgetsById() };
134
-
135
- // Convert cell ID keys to widget IDs and update states
136
- for (const [cellIdString, newState] of cellStates) {
137
- // Find the widget with the matching cell ID
138
- const widget = Object.values(updatedWidgetsById).find(w =>
139
- CellIdUtils.toString(w.cellId) === cellIdString
140
- );
141
-
142
- if (widget) {
143
- const widgetIdString = WidgetIdUtils.toString(widget.widgetId);
144
- updatedWidgetsById[widgetIdString] = {
145
- ...updatedWidgetsById[widgetIdString],
146
- widgetState: newState,
147
- };
148
- }
149
- }
150
-
151
- patchState(store, { widgetsById: updatedWidgetsById });
152
- },
153
-
154
- clearDashboard() {
155
- patchState(store, { widgetsById: {} });
156
- },
157
- }))
158
- );
@@ -1,11 +0,0 @@
1
- @mixin dashboard-grid-vars {
2
- /* grid geometry derived once, reused everywhere */
3
- --cell-size: calc(
4
- 100cqi / var(--columns) - var(--gutter-size) * var(--gutters) /
5
- var(--columns)
6
- );
7
- --tile-size: calc(var(--cell-size) + var(--gutter-size));
8
- --tile-offset: calc(
9
- var(--gutter-size) + var(--cell-size) + var(--gutter-size) / 2
10
- );
11
- }
@@ -1,137 +0,0 @@
1
- // widget-list-bridge-integration.spec.ts
2
- import { TestBed } from '@angular/core/testing';
3
- import { Component, signal } from '@angular/core';
4
- import { WidgetListComponent } from '../widget-list.component';
5
- import { DashboardBridgeService } from '../../services/dashboard-bridge.service';
6
- import { DashboardService } from '../../services/dashboard.service';
7
- import { ArrowWidgetComponent } from '@dragonworks/ngx-dashboard-widgets';
8
-
9
- // Mock dashboard store
10
- function createMockDashboardStore(dashboardId = 'test-dashboard', dimensions = { width: 150, height: 100 }) {
11
- return {
12
- dashboardId: jasmine.createSpy('dashboardId').and.returnValue(dashboardId),
13
- gridCellDimensions: jasmine.createSpy('gridCellDimensions').and.returnValue(dimensions),
14
- startDrag: jasmine.createSpy('startDrag'),
15
- endDrag: jasmine.createSpy('endDrag')
16
- } as any;
17
- }
18
-
19
- // Test component that simulates dashboard registration
20
- @Component({
21
- template: '',
22
- providers: []
23
- })
24
- class MockDashboardComponent {
25
- private mockStore = createMockDashboardStore('test-dashboard-1', { width: 200, height: 120 });
26
-
27
- constructor(private bridge: DashboardBridgeService) {
28
- this.bridge.registerDashboard(this.mockStore);
29
- }
30
-
31
- destroy() {
32
- this.bridge.unregisterDashboard(this.mockStore);
33
- }
34
- }
35
-
36
- describe('WidgetListComponent Integration with DashboardBridgeService', () => {
37
- let component: WidgetListComponent;
38
- let fixture: any;
39
- let bridgeService: DashboardBridgeService;
40
- let dashboardService: DashboardService;
41
-
42
- beforeEach(async () => {
43
- await TestBed.configureTestingModule({
44
- imports: [WidgetListComponent],
45
- providers: [
46
- DashboardBridgeService,
47
- DashboardService
48
- ]
49
- }).compileComponents();
50
-
51
- // Register a test widget
52
- dashboardService = TestBed.inject(DashboardService);
53
- dashboardService.registerWidgetType(ArrowWidgetComponent);
54
-
55
- bridgeService = TestBed.inject(DashboardBridgeService);
56
- fixture = TestBed.createComponent(WidgetListComponent);
57
- component = fixture.componentInstance;
58
- });
59
-
60
- describe('Standalone Functionality', () => {
61
- it('should create without any registered dashboards', () => {
62
- expect(component).toBeTruthy();
63
- expect(bridgeService.dashboardCount()).toBe(0);
64
- });
65
-
66
- it('should use fallback dimensions when no dashboards registered', () => {
67
- fixture.detectChanges();
68
-
69
- expect(component.gridCellDimensions()).toEqual({ width: 100, height: 100 });
70
- });
71
-
72
- it('should display available widgets even without dashboards', () => {
73
- fixture.detectChanges();
74
-
75
- expect(component.widgets().length).toBeGreaterThan(0);
76
- expect(component.widgets()[0].widgetTypeid).toBe('@default/arrow-widget');
77
- });
78
-
79
- it('should handle drag start/end gracefully without dashboards', () => {
80
- const mockEvent = {
81
- dataTransfer: {
82
- effectAllowed: '',
83
- setDragImage: jasmine.createSpy('setDragImage')
84
- }
85
- } as any;
86
-
87
- const widget = component.widgets()[0];
88
-
89
- expect(() => {
90
- component.onDragStart(mockEvent, widget);
91
- component.onDragEnd();
92
- }).not.toThrow();
93
- });
94
- });
95
-
96
-
97
-
98
- describe('Drag Ghost Creation', () => {
99
- it('should create drag ghost with fallback dimensions when no dashboards', () => {
100
- fixture.detectChanges();
101
-
102
- const mockEvent = {
103
- dataTransfer: {
104
- effectAllowed: '',
105
- setDragImage: jasmine.createSpy('setDragImage')
106
- }
107
- } as any;
108
-
109
- const widget = component.widgets()[0];
110
- component.onDragStart(mockEvent, widget);
111
-
112
- // Should still work with fallback dimensions
113
- expect(mockEvent.dataTransfer.setDragImage).toHaveBeenCalled();
114
- });
115
- });
116
-
117
- describe('Widget State Management', () => {
118
- it('should manage active widget state independently of dashboard registration', () => {
119
- expect(component.activeWidget()).toBeNull();
120
-
121
- const mockEvent = {
122
- dataTransfer: {
123
- effectAllowed: '',
124
- setDragImage: jasmine.createSpy('setDragImage')
125
- }
126
- } as any;
127
-
128
- const widget = component.widgets()[0];
129
-
130
- component.onDragStart(mockEvent, widget);
131
- expect(component.activeWidget()).toBe(widget.widgetTypeid);
132
-
133
- component.onDragEnd();
134
- expect(component.activeWidget()).toBeNull();
135
- });
136
- });
137
- });
@@ -1,22 +0,0 @@
1
- <!-- widget-list.component.html -->
2
- <div class="widget-list" role="list" aria-label="Available widgets">
3
- @for (widget of widgets(); track widget.widgetTypeid) {
4
- <div
5
- class="widget-list-item"
6
- [class.active]="activeWidget() === widget.widgetTypeid"
7
- draggable="true"
8
- (dragstart)="onDragStart($event, widget)"
9
- (dragend)="onDragEnd()"
10
- role="listitem"
11
- [attr.aria-grabbed]="activeWidget() === widget.widgetTypeid"
12
- [attr.aria-label]="widget.name + ' widget: ' + widget.description"
13
- tabindex="0"
14
- >
15
- <div class="icon" [innerHTML]="widget.safeSvgIcon" aria-hidden="true"></div>
16
- <div class="content">
17
- <strong>{{ widget.name }}</strong>
18
- <small>{{ widget.description }}</small>
19
- </div>
20
- </div>
21
- }
22
- </div>
@@ -1,154 +0,0 @@
1
- // widget-list.component.scss
2
- :host {
3
- background-color: var(--mat-sys-surface-container, #f5f5f5);
4
- container-type: inline-size;
5
- }
6
-
7
- .widget-list {
8
- display: flex;
9
- flex-direction: column;
10
- gap: var(--mat-sys-spacing-2, 8px);
11
-
12
- // Responsive spacing based on container width
13
- @container (max-width: 200px) {
14
- gap: var(--mat-sys-spacing-1, 4px);
15
- }
16
-
17
- @container (min-width: 400px) {
18
- gap: var(--mat-sys-spacing-3, 12px);
19
- }
20
- }
21
-
22
- .widget-list-item {
23
- display: flex;
24
- align-items: start;
25
- gap: var(--mat-sys-spacing-3, 12px);
26
- background-color: var(--mat-sys-surface, #ffffff);
27
- border: 1px solid var(--mat-sys-outline-variant, #c7c7c7);
28
- padding: var(--mat-sys-spacing-3, 12px);
29
- border-radius: var(--mat-sys-corner-small, 4px);
30
- cursor: grab;
31
- transition: background-color var(--mat-sys-motion-duration-medium2, 300ms) var(--mat-sys-motion-easing-standard, ease-in-out),
32
- border-color var(--mat-sys-motion-duration-medium2, 300ms) var(--mat-sys-motion-easing-standard, ease-in-out),
33
- box-shadow var(--mat-sys-motion-duration-medium2, 300ms) var(--mat-sys-motion-easing-standard, ease-in-out);
34
- box-shadow: var(--mat-sys-elevation-level1, 0 1px 2px rgba(0, 0, 0, 0.05));
35
-
36
- // Responsive padding based on container width
37
- @container (max-width: 200px) {
38
- padding: var(--mat-sys-spacing-2, 8px);
39
- gap: var(--mat-sys-spacing-2, 8px);
40
- }
41
-
42
- @container (min-width: 400px) {
43
- padding: var(--mat-sys-spacing-4, 16px);
44
- gap: var(--mat-sys-spacing-4, 16px);
45
- }
46
-
47
- .icon {
48
- width: clamp(20px, 4vw, 28px);
49
- height: clamp(20px, 4vw, 28px);
50
- flex-shrink: 0;
51
-
52
- ::ng-deep svg {
53
- width: 100%;
54
- height: 100%;
55
- display: block;
56
- fill: var(--mat-sys-on-surface-variant, #5f5f5f);
57
- transition: fill var(--mat-sys-motion-duration-short2, 150ms) var(--mat-sys-motion-easing-standard, ease-in-out);
58
- }
59
- }
60
-
61
- .content {
62
- display: flex;
63
- flex-direction: column;
64
- line-height: 1.2;
65
- color: var(--mat-sys-on-surface, #1c1c1c);
66
- flex: 1;
67
- min-width: 0; // Allow text to truncate
68
-
69
- strong {
70
- color: var(--mat-sys-on-surface, #1c1c1c);
71
- font-weight: 500;
72
- font-size: clamp(0.875rem, 2.5vw, 1rem);
73
- overflow: hidden;
74
- text-overflow: ellipsis;
75
- white-space: nowrap;
76
- }
77
-
78
- small {
79
- color: var(--mat-sys-on-surface-variant, #5f5f5f);
80
- font-size: clamp(0.75rem, 2vw, 0.875rem);
81
- margin-top: var(--mat-sys-spacing-1, 4px);
82
- overflow: hidden;
83
- text-overflow: ellipsis;
84
- white-space: nowrap;
85
- }
86
- }
87
-
88
- &:hover {
89
- background-color: var(--mat-sys-surface-container-low, #f0f0f0);
90
- box-shadow: var(--mat-sys-elevation-level2, 0 2px 4px rgba(0, 0, 0, 0.1));
91
-
92
- .icon ::ng-deep svg {
93
- fill: var(--mat-sys-on-surface, #1c1c1c);
94
- }
95
- }
96
-
97
- &:active {
98
- cursor: grabbing;
99
- background-color: var(--mat-sys-surface-container, #f5f5f5);
100
- }
101
- }
102
-
103
- .widget-list-item.active {
104
- background-color: var(--mat-sys-primary-container, #e6f2ff);
105
- border-color: var(--mat-sys-primary, #1976d2);
106
- color: var(--mat-sys-on-primary-container, #004a99);
107
-
108
- .content {
109
- strong {
110
- color: var(--mat-sys-on-primary-container, #004a99);
111
- }
112
-
113
- small {
114
- color: var(--mat-sys-on-primary-container, #004a99);
115
- opacity: 0.8;
116
- }
117
- }
118
-
119
- .icon ::ng-deep svg {
120
- fill: var(--mat-sys-on-primary-container, #004a99);
121
- }
122
- }
123
-
124
- // Drag ghost styles
125
- .drag-ghost {
126
- position: absolute;
127
- top: 0;
128
- left: 0;
129
- z-index: 9999;
130
- margin: 0;
131
- pointer-events: none;
132
- display: flex;
133
- align-items: center;
134
- justify-content: center;
135
- box-sizing: border-box;
136
-
137
- background-color: var(--mat-sys-surface, #ffffff);
138
- border: 1px solid var(--mat-sys-outline-variant, #c7c7c7);
139
- border-radius: var(--mat-sys-corner-small, 4px);
140
- box-shadow: var(--mat-sys-elevation-level3, 0 4px 6px rgba(0, 0, 0, 0.15));
141
- opacity: 0.8;
142
-
143
- .icon {
144
- display: flex;
145
- align-items: center;
146
- justify-content: center;
147
-
148
- ::ng-deep svg {
149
- display: block;
150
- fill: var(--mat-sys-on-surface-variant, #5f5f5f);
151
- opacity: 0.6;
152
- }
153
- }
154
- }
@@ -1,106 +0,0 @@
1
- // widget-list.component.ts
2
- import {
3
- Component,
4
- computed,
5
- inject,
6
- Renderer2,
7
- signal,
8
- ChangeDetectionStrategy,
9
- afterNextRender,
10
- } from '@angular/core';
11
- import { DragData, WidgetMetadata } from '../models';
12
- import { DomSanitizer, SafeHtml } from '@angular/platform-browser';
13
- import { DashboardService } from '../services/dashboard.service';
14
- import { DashboardBridgeService } from '../services/dashboard-bridge.service';
15
-
16
- interface WidgetDisplayItem extends WidgetMetadata {
17
- safeSvgIcon?: SafeHtml;
18
- }
19
-
20
- @Component({
21
- selector: 'ngx-dashboard-widget-list',
22
- standalone: true,
23
- imports: [],
24
- changeDetection: ChangeDetectionStrategy.OnPush,
25
- templateUrl: './widget-list.component.html',
26
- styleUrl: './widget-list.component.scss',
27
- })
28
- export class WidgetListComponent {
29
- readonly #service = inject(DashboardService);
30
- readonly #sanitizer = inject(DomSanitizer);
31
- readonly #renderer = inject(Renderer2);
32
- readonly #bridge = inject(DashboardBridgeService);
33
-
34
- activeWidget = signal<string | null>(null);
35
-
36
- // Get grid cell dimensions from bridge service (uses first available dashboard)
37
- gridCellDimensions = this.#bridge.availableDimensions;
38
-
39
- widgets = computed(() =>
40
- this.#service.widgetTypes().map((w) => ({
41
- ...w.metadata,
42
- safeSvgIcon: this.#sanitizer.bypassSecurityTrustHtml(w.metadata.svgIcon),
43
- }))
44
- );
45
-
46
- onDragStart(event: DragEvent, widget: WidgetDisplayItem) {
47
- if (!event.dataTransfer) return;
48
- event.dataTransfer.effectAllowed = 'copy';
49
-
50
- const dragData: DragData = {
51
- kind: 'widget',
52
- content: widget,
53
- };
54
-
55
- this.activeWidget.set(widget.widgetTypeid);
56
- this.#bridge.startDrag(dragData);
57
-
58
- // Create custom drag ghost for better UX
59
- const ghost = this.#createDragGhost(widget.svgIcon);
60
- document.body.appendChild(ghost);
61
-
62
- // Force reflow to ensure element is rendered
63
- const _reflow = ghost.offsetHeight;
64
-
65
- event.dataTransfer.setDragImage(ghost, 10, 10);
66
-
67
- // Delay removal to ensure browser has time to snapshot the drag image
68
- setTimeout(() => ghost.remove());
69
- }
70
-
71
- onDragEnd(): void {
72
- this.activeWidget.set(null);
73
- this.#bridge.endDrag();
74
- }
75
-
76
- #createDragGhost(svgIcon: string | undefined): HTMLElement {
77
- const dimensions = this.gridCellDimensions();
78
-
79
- const el = this.#renderer.createElement('div');
80
- this.#renderer.addClass(el, 'drag-ghost');
81
-
82
- // Set dimensions using CSS custom properties for dynamic sizing
83
- this.#renderer.setStyle(el, 'width', `${dimensions.width}px`);
84
- this.#renderer.setStyle(el, 'height', `${dimensions.height}px`);
85
-
86
- if (svgIcon) {
87
- const iconWrapper = this.#renderer.createElement('div');
88
- this.#renderer.addClass(iconWrapper, 'icon');
89
-
90
- iconWrapper.innerHTML = svgIcon;
91
- const svg = iconWrapper.querySelector('svg');
92
-
93
- if (svg) {
94
- // Size the SVG to 80% of the cell dimensions
95
- svg.setAttribute('width', `${dimensions.width * 0.8}`);
96
- svg.setAttribute('height', `${dimensions.height * 0.8}`);
97
- // Remove hardcoded fill and opacity - let CSS handle styling
98
- svg.removeAttribute('fill');
99
- }
100
-
101
- el.appendChild(iconWrapper);
102
- }
103
-
104
- return el;
105
- }
106
- }
package/src/public-api.ts DELETED
@@ -1,21 +0,0 @@
1
- /*
2
- * Public API Surface of ngx-dashboard
3
- */
4
-
5
- // Main dashboard components
6
- export * from './lib/dashboard/dashboard.component';
7
- export * from './lib/dashboard-editor/dashboard-editor.component';
8
- export * from './lib/dashboard-viewer/dashboard-viewer.component';
9
- export * from './lib/widget-list/widget-list.component';
10
-
11
- // Public Services
12
- export * from './lib/services/dashboard.service';
13
-
14
- // Store is now internal - removed from public API
15
-
16
- // Public Models and interfaces
17
- // TODO: not everything should be exported
18
- export * from './lib/models';
19
-
20
- // Providers
21
- export * from './lib/providers';
package/src/test-setup.ts DELETED
@@ -1,10 +0,0 @@
1
- import 'zone.js';
2
- import 'zone.js/testing';
3
- import { getTestBed } from '@angular/core/testing';
4
- import { BrowserTestingModule, platformBrowserTesting } from '@angular/platform-browser/testing';
5
-
6
- // Initialize the Angular testing environment
7
- getTestBed().initTestEnvironment(
8
- BrowserTestingModule,
9
- platformBrowserTesting(),
10
- );
package/tsconfig.lib.json DELETED
@@ -1,15 +0,0 @@
1
- /* To learn more about Typescript configuration file: https://www.typescriptlang.org/docs/handbook/tsconfig-json.html. */
2
- /* To learn more about Angular compiler options: https://angular.dev/reference/configs/angular-compiler-options. */
3
- {
4
- "extends": "../../tsconfig.json",
5
- "compilerOptions": {
6
- "outDir": "../../out-tsc/lib",
7
- "declaration": true,
8
- "declarationMap": true,
9
- "inlineSources": true,
10
- "sourceMap": true,
11
- "types": []
12
- },
13
- "include": ["src/**/*.ts"],
14
- "exclude": ["**/*.spec.ts"]
15
- }
@@ -1,11 +0,0 @@
1
- /* To learn more about Typescript configuration file: https://www.typescriptlang.org/docs/handbook/tsconfig-json.html. */
2
- /* To learn more about Angular compiler options: https://angular.dev/reference/configs/angular-compiler-options. */
3
- {
4
- "extends": "./tsconfig.lib.json",
5
- "compilerOptions": {
6
- "declarationMap": false
7
- },
8
- "angularCompilerOptions": {
9
- "compilationMode": "partial"
10
- }
11
- }
@@ -1,14 +0,0 @@
1
- /* To learn more about Typescript configuration file: https://www.typescriptlang.org/docs/handbook/tsconfig-json.html. */
2
- /* To learn more about Angular compiler options: https://angular.dev/reference/configs/angular-compiler-options. */
3
- {
4
- "extends": "../../tsconfig.json",
5
- "compilerOptions": {
6
- "outDir": "../../out-tsc/spec",
7
- "types": [
8
- "jasmine"
9
- ]
10
- },
11
- "include": [
12
- "src/**/*.ts"
13
- ]
14
- }