@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,72 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
Component,
|
|
3
|
-
ChangeDetectionStrategy,
|
|
4
|
-
signal,
|
|
5
|
-
computed,
|
|
6
|
-
} from '@angular/core';
|
|
7
|
-
import { MatIconModule } from '@angular/material/icon';
|
|
8
|
-
import { MatTooltipModule } from '@angular/material/tooltip';
|
|
9
|
-
import { Widget, WidgetMetadata } from '../../models';
|
|
10
|
-
|
|
11
|
-
export interface UnknownWidgetState {
|
|
12
|
-
originalWidgetTypeid: string;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
@Component({
|
|
16
|
-
selector: 'lib-unknown-widget',
|
|
17
|
-
imports: [MatIconModule, MatTooltipModule],
|
|
18
|
-
template: `
|
|
19
|
-
<div class="unknown-widget-container" [matTooltip]="tooltipText()">
|
|
20
|
-
<mat-icon class="unknown-widget-icon">error_outline</mat-icon>
|
|
21
|
-
</div>
|
|
22
|
-
`,
|
|
23
|
-
styles: [
|
|
24
|
-
`
|
|
25
|
-
.unknown-widget-container {
|
|
26
|
-
display: flex;
|
|
27
|
-
align-items: center;
|
|
28
|
-
justify-content: center;
|
|
29
|
-
width: 100%;
|
|
30
|
-
height: 100%;
|
|
31
|
-
background-color: var(--mat-sys-error);
|
|
32
|
-
border-radius: 8px;
|
|
33
|
-
container-type: size;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
.unknown-widget-icon {
|
|
37
|
-
color: var(--mat-sys-on-error);
|
|
38
|
-
font-size: clamp(12px, 75cqmin, 68px);
|
|
39
|
-
width: clamp(12px, 75cqmin, 68px);
|
|
40
|
-
height: clamp(12px, 75cqmin, 68px);
|
|
41
|
-
}
|
|
42
|
-
`,
|
|
43
|
-
],
|
|
44
|
-
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
45
|
-
})
|
|
46
|
-
export class UnknownWidgetComponent implements Widget {
|
|
47
|
-
static metadata: WidgetMetadata = {
|
|
48
|
-
widgetTypeid: '__internal/unknown-widget',
|
|
49
|
-
name: 'Unknown Widget',
|
|
50
|
-
description: 'Fallback widget for unknown widget types',
|
|
51
|
-
svgIcon:
|
|
52
|
-
'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960" fill="currentColor"><path d="M480-280q17 0 28.5-11.5T520-320q0-17-11.5-28.5T480-360q-17 0-28.5 11.5T440-320q0 17 11.5 28.5T480-280Zm-40-160h80v-240h-80v240Zm40 360q-83 0-156-31.5T197-197q-54-54-85.5-127T80-480q0-83 31.5-156T197-763q54-54 127-85.5T480-880q83 0 156 31.5T763-763q54 54 85.5 127T880-480q0 83-31.5 156T763-197q-54 54-127 85.5T480-80Z"/></svg>',
|
|
53
|
-
};
|
|
54
|
-
|
|
55
|
-
readonly state = signal<UnknownWidgetState>({
|
|
56
|
-
originalWidgetTypeid: 'unknown',
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
readonly tooltipText = computed(() => `${this.state().originalWidgetTypeid}`);
|
|
60
|
-
|
|
61
|
-
dashboardSetState(state?: unknown): void {
|
|
62
|
-
if (state && typeof state === 'object' && 'originalWidgetTypeid' in state) {
|
|
63
|
-
this.state.set(state as UnknownWidgetState);
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
dashboardGetState(): UnknownWidgetState {
|
|
68
|
-
return this.state();
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
// No edit dialog for error widgets - method intentionally not implemented
|
|
72
|
-
}
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
// cell-data.ts
|
|
2
|
-
import { WidgetFactory } from './widget-factory';
|
|
3
|
-
import { CellId } from './cell-id';
|
|
4
|
-
import { WidgetId } from './widget-id';
|
|
5
|
-
import { CellPosition } from './cell-position';
|
|
6
|
-
|
|
7
|
-
export interface CellData extends CellPosition {
|
|
8
|
-
widgetId: WidgetId; // Unique identifier for the widget instance
|
|
9
|
-
cellId: CellId; // Current grid position (updates when widget moves)
|
|
10
|
-
flat?: boolean;
|
|
11
|
-
widgetFactory: WidgetFactory;
|
|
12
|
-
widgetState: unknown;
|
|
13
|
-
}
|
|
@@ -1,85 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Branded type for cell identifiers to ensure type safety when working with grid coordinates.
|
|
3
|
-
* This prevents accidentally mixing up row/column numbers with cell IDs.
|
|
4
|
-
*/
|
|
5
|
-
export type CellId = number & { __brand: 'CellId' };
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* Maximum number of columns supported by the grid.
|
|
9
|
-
* This determines the encoding scheme for cell coordinates.
|
|
10
|
-
*/
|
|
11
|
-
const MAX_COLUMNS = 1024;
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Utility functions for working with CellId branded type.
|
|
15
|
-
*/
|
|
16
|
-
export const CellIdUtils = {
|
|
17
|
-
/**
|
|
18
|
-
* Creates a CellId from row and column coordinates.
|
|
19
|
-
* @param row - The row number (1-based)
|
|
20
|
-
* @param col - The column number (1-based)
|
|
21
|
-
* @returns A branded CellId that encodes both coordinates
|
|
22
|
-
* @throws Error if row or col is less than 1, or if col exceeds MAX_COLUMNS
|
|
23
|
-
*/
|
|
24
|
-
create(row: number, col: number): CellId {
|
|
25
|
-
if (row < 1 || col < 1) {
|
|
26
|
-
throw new Error(
|
|
27
|
-
`Invalid cell coordinates: row=${row}, col=${col}. Both must be >= 1`,
|
|
28
|
-
);
|
|
29
|
-
}
|
|
30
|
-
if (col >= MAX_COLUMNS) {
|
|
31
|
-
throw new Error(`Column ${col} exceeds maximum of ${MAX_COLUMNS - 1}`);
|
|
32
|
-
}
|
|
33
|
-
return (row * MAX_COLUMNS + col) as CellId;
|
|
34
|
-
},
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* Decodes a CellId back into row and column coordinates.
|
|
38
|
-
* @param cellId - The CellId to decode
|
|
39
|
-
* @returns A tuple of [row, col] coordinates (1-based)
|
|
40
|
-
*/
|
|
41
|
-
decode(cellId: CellId): [number, number] {
|
|
42
|
-
const id = cellId as number;
|
|
43
|
-
const row = Math.floor(id / MAX_COLUMNS);
|
|
44
|
-
const col = id % MAX_COLUMNS;
|
|
45
|
-
return [row, col];
|
|
46
|
-
},
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
* Gets the row coordinate from a CellId.
|
|
50
|
-
* @param cellId - The CellId to extract row from
|
|
51
|
-
* @returns The row number (1-based)
|
|
52
|
-
*/
|
|
53
|
-
getRow(cellId: CellId): number {
|
|
54
|
-
return Math.floor((cellId as number) / MAX_COLUMNS);
|
|
55
|
-
},
|
|
56
|
-
|
|
57
|
-
/**
|
|
58
|
-
* Gets the column coordinate from a CellId.
|
|
59
|
-
* @param cellId - The CellId to extract column from
|
|
60
|
-
* @returns The column number (1-based)
|
|
61
|
-
*/
|
|
62
|
-
getCol(cellId: CellId): number {
|
|
63
|
-
return (cellId as number) % MAX_COLUMNS;
|
|
64
|
-
},
|
|
65
|
-
|
|
66
|
-
/**
|
|
67
|
-
* Creates a string representation of a CellId for debugging/display purposes.
|
|
68
|
-
* @param cellId - The CellId to convert to string
|
|
69
|
-
* @returns A string in the format "row-col"
|
|
70
|
-
*/
|
|
71
|
-
toString(cellId: CellId): string {
|
|
72
|
-
const [row, col] = this.decode(cellId);
|
|
73
|
-
return `${row}-${col}`;
|
|
74
|
-
},
|
|
75
|
-
|
|
76
|
-
/**
|
|
77
|
-
* Checks if two CellIds are equal.
|
|
78
|
-
* @param a - First CellId
|
|
79
|
-
* @param b - Second CellId
|
|
80
|
-
* @returns True if the CellIds represent the same cell
|
|
81
|
-
*/
|
|
82
|
-
equals(a: CellId, b: CellId): boolean {
|
|
83
|
-
return a === b;
|
|
84
|
-
},
|
|
85
|
-
};
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
// cell-position.ts
|
|
2
|
-
import { CellId } from './cell-id';
|
|
3
|
-
import { WidgetId } from './widget-id';
|
|
4
|
-
|
|
5
|
-
export interface CellPosition {
|
|
6
|
-
row: number;
|
|
7
|
-
col: number;
|
|
8
|
-
rowSpan: number;
|
|
9
|
-
colSpan: number;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export interface CellComponentPosition extends CellPosition {
|
|
13
|
-
cellId: CellId; // Current position
|
|
14
|
-
widgetId: WidgetId; // Widget instance identifier
|
|
15
|
-
}
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
// dashboard-data.dto.ts
|
|
2
|
-
/**
|
|
3
|
-
* Serializable data format for dashboard export/import functionality.
|
|
4
|
-
* This format can be safely converted to/from JSON for persistence.
|
|
5
|
-
* Corresponds to the persistent state from the dashboard store features.
|
|
6
|
-
*/
|
|
7
|
-
export interface DashboardDataDto {
|
|
8
|
-
/** Version for future compatibility and migration support */
|
|
9
|
-
version: string;
|
|
10
|
-
|
|
11
|
-
/** Unique dashboard identifier managed by the client */
|
|
12
|
-
dashboardId: string;
|
|
13
|
-
|
|
14
|
-
/** Grid dimensions */
|
|
15
|
-
rows: number;
|
|
16
|
-
columns: number;
|
|
17
|
-
gutterSize: string;
|
|
18
|
-
|
|
19
|
-
/** Array of serializable cell data */
|
|
20
|
-
cells: CellDataDto[];
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* Serializable version of CellData that can be safely JSON stringified.
|
|
25
|
-
* Converts non-serializable types (CellId, WidgetFactory) to serializable equivalents.
|
|
26
|
-
*/
|
|
27
|
-
export interface CellDataDto {
|
|
28
|
-
/** Grid position */
|
|
29
|
-
row: number;
|
|
30
|
-
col: number;
|
|
31
|
-
|
|
32
|
-
/** Cell span */
|
|
33
|
-
rowSpan: number;
|
|
34
|
-
colSpan: number;
|
|
35
|
-
|
|
36
|
-
/** Display settings */
|
|
37
|
-
flat?: boolean;
|
|
38
|
-
|
|
39
|
-
/** Widget type identifier for factory lookup during import */
|
|
40
|
-
widgetTypeid: string;
|
|
41
|
-
|
|
42
|
-
/** Raw widget state (must be JSON serializable) */
|
|
43
|
-
widgetState: unknown;
|
|
44
|
-
}
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
import { DashboardDataDto } from './dashboard-data.dto';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Creates an empty dashboard configuration with the specified dimensions.
|
|
5
|
-
* This is a convenience function for creating a basic dashboard without any cells.
|
|
6
|
-
*
|
|
7
|
-
* @param dashboardId - Unique identifier for the dashboard (managed by client)
|
|
8
|
-
* @param rows - Number of rows in the dashboard grid
|
|
9
|
-
* @param columns - Number of columns in the dashboard grid
|
|
10
|
-
* @param gutterSize - CSS size for the gutter between cells (default: '0.5em')
|
|
11
|
-
* @returns A DashboardDataDto configured with the specified dimensions and no cells
|
|
12
|
-
*
|
|
13
|
-
* @example
|
|
14
|
-
* // Create an 8x16 dashboard with default gutter
|
|
15
|
-
* const dashboard = createEmptyDashboard('my-dashboard-1', 8, 16);
|
|
16
|
-
*
|
|
17
|
-
* @example
|
|
18
|
-
* // Create a 5x10 dashboard with custom gutter
|
|
19
|
-
* const dashboard = createEmptyDashboard('my-dashboard-2', 5, 10, '0.5rem');
|
|
20
|
-
*/
|
|
21
|
-
export function createEmptyDashboard(
|
|
22
|
-
dashboardId: string,
|
|
23
|
-
rows: number,
|
|
24
|
-
columns: number,
|
|
25
|
-
gutterSize = '0.5em'
|
|
26
|
-
): DashboardDataDto {
|
|
27
|
-
return {
|
|
28
|
-
version: '1.0.0',
|
|
29
|
-
dashboardId,
|
|
30
|
-
rows,
|
|
31
|
-
columns,
|
|
32
|
-
gutterSize,
|
|
33
|
-
cells: [],
|
|
34
|
-
};
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* Creates a default dashboard configuration with standard dimensions.
|
|
39
|
-
* This provides a reasonable starting point for most use cases.
|
|
40
|
-
*
|
|
41
|
-
* @param dashboardId - Unique identifier for the dashboard (managed by client)
|
|
42
|
-
* @returns A DashboardDataDto with 8 rows, 16 columns, and 0.5em gutter
|
|
43
|
-
*
|
|
44
|
-
* @example
|
|
45
|
-
* const dashboard = createDefaultDashboard('my-dashboard-id');
|
|
46
|
-
*/
|
|
47
|
-
export function createDefaultDashboard(dashboardId: string): DashboardDataDto {
|
|
48
|
-
return createEmptyDashboard(dashboardId, 8, 16, '0.5em');
|
|
49
|
-
}
|
package/src/lib/models/index.ts
DELETED
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
export * from './cell-id';
|
|
2
|
-
export * from './widget-id';
|
|
3
|
-
export * from './cell-data';
|
|
4
|
-
export * from './cell-position';
|
|
5
|
-
export * from './cell-dialog';
|
|
6
|
-
export * from './dashboard-data.dto';
|
|
7
|
-
export * from './dashboard-data.utils';
|
|
8
|
-
export * from './drag-data';
|
|
9
|
-
export * from './reserved-space';
|
|
10
|
-
export * from './widget';
|
|
11
|
-
export * from './widget-factory';
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Defines space that should be reserved around the dashboard component
|
|
3
|
-
* when calculating viewport constraints.
|
|
4
|
-
*/
|
|
5
|
-
export interface ReservedSpace {
|
|
6
|
-
/** Space reserved at the top (e.g., toolbar height) */
|
|
7
|
-
top: number;
|
|
8
|
-
/** Space reserved on the right (e.g., padding, widget list) */
|
|
9
|
-
right: number;
|
|
10
|
-
/** Space reserved at the bottom (e.g., padding) */
|
|
11
|
-
bottom: number;
|
|
12
|
-
/** Space reserved on the left (e.g., padding) */
|
|
13
|
-
left: number;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* Default reserved space when none is specified
|
|
18
|
-
*/
|
|
19
|
-
export const DEFAULT_RESERVED_SPACE: ReservedSpace = {
|
|
20
|
-
top: 0,
|
|
21
|
-
right: 0,
|
|
22
|
-
bottom: 0,
|
|
23
|
-
left: 0
|
|
24
|
-
};
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
// widget-factory.ts
|
|
2
|
-
import { ComponentRef, ViewContainerRef } from '@angular/core';
|
|
3
|
-
import {
|
|
4
|
-
Widget,
|
|
5
|
-
WidgetComponentClass,
|
|
6
|
-
} from './widget';
|
|
7
|
-
|
|
8
|
-
export interface WidgetFactory<
|
|
9
|
-
T extends Widget = Widget
|
|
10
|
-
> {
|
|
11
|
-
widgetTypeid: string; // application wide unique ID
|
|
12
|
-
name: string;
|
|
13
|
-
description: string;
|
|
14
|
-
svgIcon: string; // SVG markup as string
|
|
15
|
-
createInstance(container: ViewContainerRef, state?: unknown): ComponentRef<T>;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export function createFactoryFromComponent<T extends Widget>(
|
|
19
|
-
component: WidgetComponentClass<T>
|
|
20
|
-
): WidgetFactory {
|
|
21
|
-
return {
|
|
22
|
-
...component.metadata,
|
|
23
|
-
|
|
24
|
-
createInstance(
|
|
25
|
-
container: ViewContainerRef,
|
|
26
|
-
state?: unknown
|
|
27
|
-
): ComponentRef<T> {
|
|
28
|
-
const ref = container.createComponent(component);
|
|
29
|
-
ref.instance.dashboardSetState?.(state);
|
|
30
|
-
return ref;
|
|
31
|
-
},
|
|
32
|
-
};
|
|
33
|
-
}
|
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Branded type for widget identifiers to ensure type safety when working with widget instances.
|
|
3
|
-
* This prevents accidentally mixing up widget IDs with other string values.
|
|
4
|
-
*/
|
|
5
|
-
export type WidgetId = string & { __brand: 'WidgetId' };
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* Utility functions for working with WidgetId branded type.
|
|
9
|
-
* WidgetIds are UUIDs that uniquely identify widget instances throughout their lifecycle,
|
|
10
|
-
* independent of their position on the dashboard grid.
|
|
11
|
-
*/
|
|
12
|
-
export const WidgetIdUtils = {
|
|
13
|
-
/**
|
|
14
|
-
* Generates a new unique WidgetId.
|
|
15
|
-
* Uses crypto.randomUUID() when available, falls back to timestamp-based ID for older browsers.
|
|
16
|
-
* @returns A new unique WidgetId
|
|
17
|
-
*/
|
|
18
|
-
generate(): WidgetId {
|
|
19
|
-
if (typeof crypto !== 'undefined' && crypto.randomUUID) {
|
|
20
|
-
return crypto.randomUUID() as WidgetId;
|
|
21
|
-
}
|
|
22
|
-
// Fallback for older browsers
|
|
23
|
-
return `widget-${Date.now()}-${Math.random().toString(36).substr(2, 9)}` as WidgetId;
|
|
24
|
-
},
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* Validates if a string is a valid WidgetId format.
|
|
28
|
-
* @param id - The string to validate
|
|
29
|
-
* @returns True if the string is a valid WidgetId format
|
|
30
|
-
*/
|
|
31
|
-
validate(id: string): id is WidgetId {
|
|
32
|
-
// UUID v4 format or fallback format
|
|
33
|
-
return (
|
|
34
|
-
/^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(id) ||
|
|
35
|
-
/^widget-\d+-[a-z0-9]{9}$/i.test(id)
|
|
36
|
-
);
|
|
37
|
-
},
|
|
38
|
-
|
|
39
|
-
/**
|
|
40
|
-
* Converts a WidgetId to a string for use as map keys or serialization.
|
|
41
|
-
* @param id - The WidgetId to convert
|
|
42
|
-
* @returns The string representation of the WidgetId
|
|
43
|
-
*/
|
|
44
|
-
toString(id: WidgetId): string {
|
|
45
|
-
return id;
|
|
46
|
-
},
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
* Creates a WidgetId from a string, validating the format.
|
|
50
|
-
* @param str - The string to convert to WidgetId
|
|
51
|
-
* @returns A WidgetId
|
|
52
|
-
* @throws Error if the string is not a valid WidgetId format
|
|
53
|
-
*/
|
|
54
|
-
fromString(str: string): WidgetId {
|
|
55
|
-
if (!this.validate(str)) {
|
|
56
|
-
throw new Error(`Invalid WidgetId format: ${str}`);
|
|
57
|
-
}
|
|
58
|
-
return str as WidgetId;
|
|
59
|
-
},
|
|
60
|
-
|
|
61
|
-
/**
|
|
62
|
-
* Checks if two WidgetIds are equal.
|
|
63
|
-
* @param a - First WidgetId
|
|
64
|
-
* @param b - Second WidgetId
|
|
65
|
-
* @returns True if the WidgetIds are the same
|
|
66
|
-
*/
|
|
67
|
-
equals(a: WidgetId, b: WidgetId): boolean {
|
|
68
|
-
return a === b;
|
|
69
|
-
},
|
|
70
|
-
};
|
package/src/lib/models/widget.ts
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
// widget.ts
|
|
2
|
-
import { Type } from '@angular/core';
|
|
3
|
-
|
|
4
|
-
export interface Widget {
|
|
5
|
-
dashboardGetState?(): unknown;
|
|
6
|
-
dashboardSetState?(state?: unknown): void;
|
|
7
|
-
dashboardEditState?(): void;
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
export interface WidgetMetadata {
|
|
11
|
-
widgetTypeid: string; // application wide unique ID
|
|
12
|
-
name: string; // to used in GUI
|
|
13
|
-
description: string; // short description for tooltip / GUI
|
|
14
|
-
svgIcon: string; // SVG markup as string
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export interface WidgetComponentClass<
|
|
18
|
-
T extends Widget = Widget
|
|
19
|
-
> extends Type<T> {
|
|
20
|
-
metadata: WidgetMetadata;
|
|
21
|
-
}
|
|
@@ -1,127 +0,0 @@
|
|
|
1
|
-
import { Component, Inject } from '@angular/core';
|
|
2
|
-
import { CommonModule } from '@angular/common';
|
|
3
|
-
import { FormsModule } from '@angular/forms';
|
|
4
|
-
import {
|
|
5
|
-
MAT_DIALOG_DATA,
|
|
6
|
-
MatDialogRef,
|
|
7
|
-
MatDialogModule,
|
|
8
|
-
} from '@angular/material/dialog';
|
|
9
|
-
import { MatButtonModule } from '@angular/material/button';
|
|
10
|
-
import { MatRadioModule } from '@angular/material/radio';
|
|
11
|
-
import { CellDisplayData } from '../../models/cell-dialog';
|
|
12
|
-
|
|
13
|
-
@Component({
|
|
14
|
-
selector: 'lib-cell-settings-dialog',
|
|
15
|
-
standalone: true,
|
|
16
|
-
imports: [
|
|
17
|
-
CommonModule,
|
|
18
|
-
FormsModule,
|
|
19
|
-
MatDialogModule,
|
|
20
|
-
MatButtonModule,
|
|
21
|
-
MatRadioModule,
|
|
22
|
-
],
|
|
23
|
-
template: `
|
|
24
|
-
<h2 mat-dialog-title>Cell Display Settings</h2>
|
|
25
|
-
<mat-dialog-content>
|
|
26
|
-
<p class="cell-info">Cell ID: <strong>{{ data.id }}</strong></p>
|
|
27
|
-
|
|
28
|
-
<div class="radio-group">
|
|
29
|
-
<mat-radio-group [(ngModel)]="selectedMode" name="displayMode">
|
|
30
|
-
<mat-radio-button value="normal">
|
|
31
|
-
<div class="radio-option">
|
|
32
|
-
<div class="option-title">Normal</div>
|
|
33
|
-
<div class="option-description">Standard cell display with full content visibility</div>
|
|
34
|
-
</div>
|
|
35
|
-
</mat-radio-button>
|
|
36
|
-
|
|
37
|
-
<mat-radio-button value="flat">
|
|
38
|
-
<div class="radio-option">
|
|
39
|
-
<div class="option-title">Flat</div>
|
|
40
|
-
<div class="option-description">Simplified display with reduced visual emphasis</div>
|
|
41
|
-
</div>
|
|
42
|
-
</mat-radio-button>
|
|
43
|
-
</mat-radio-group>
|
|
44
|
-
</div>
|
|
45
|
-
</mat-dialog-content>
|
|
46
|
-
<mat-dialog-actions align="end">
|
|
47
|
-
<button mat-button (click)="onCancel()">Cancel</button>
|
|
48
|
-
<button
|
|
49
|
-
mat-flat-button
|
|
50
|
-
(click)="save()"
|
|
51
|
-
[disabled]="selectedMode === currentMode">
|
|
52
|
-
Apply
|
|
53
|
-
</button>
|
|
54
|
-
</mat-dialog-actions>
|
|
55
|
-
`,
|
|
56
|
-
styles: [
|
|
57
|
-
`
|
|
58
|
-
mat-dialog-content {
|
|
59
|
-
display: block;
|
|
60
|
-
overflow-y: auto;
|
|
61
|
-
overflow-x: hidden;
|
|
62
|
-
padding-top: 0.5rem;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
.cell-info {
|
|
66
|
-
margin: 0 0 1.5rem 0;
|
|
67
|
-
padding-bottom: 1rem;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
.radio-group {
|
|
71
|
-
width: 100%;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
mat-radio-group {
|
|
75
|
-
display: block;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
mat-radio-button {
|
|
79
|
-
width: 100%;
|
|
80
|
-
display: block;
|
|
81
|
-
margin-bottom: 1rem;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
mat-radio-button:last-child {
|
|
85
|
-
margin-bottom: 0;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
.radio-option {
|
|
89
|
-
margin-left: 0.75rem;
|
|
90
|
-
padding: 0.25rem 0;
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
.option-title {
|
|
94
|
-
display: block;
|
|
95
|
-
margin-bottom: 0.25rem;
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
.option-description {
|
|
99
|
-
display: block;
|
|
100
|
-
}
|
|
101
|
-
`,
|
|
102
|
-
],
|
|
103
|
-
})
|
|
104
|
-
export class CellSettingsDialogComponent {
|
|
105
|
-
selectedMode: 'normal' | 'flat';
|
|
106
|
-
currentMode: 'normal' | 'flat';
|
|
107
|
-
|
|
108
|
-
constructor(
|
|
109
|
-
@Inject(MAT_DIALOG_DATA) public data: CellDisplayData,
|
|
110
|
-
private dialogRef: MatDialogRef<CellSettingsDialogComponent>
|
|
111
|
-
) {
|
|
112
|
-
this.currentMode = data.flat ? 'flat' : 'normal';
|
|
113
|
-
this.selectedMode = this.currentMode;
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
onCancel(): void {
|
|
117
|
-
this.dialogRef.close();
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
save(): void {
|
|
121
|
-
const newData: CellDisplayData = {
|
|
122
|
-
...this.data,
|
|
123
|
-
flat: this.selectedMode === 'flat'
|
|
124
|
-
};
|
|
125
|
-
this.dialogRef.close(newData);
|
|
126
|
-
}
|
|
127
|
-
}
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import { CellDisplayData } from '../../models/cell-dialog';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Abstract provider for cell settings dialogs.
|
|
5
|
-
* Implement this to provide custom dialog solutions.
|
|
6
|
-
*/
|
|
7
|
-
export abstract class CellSettingsDialogProvider {
|
|
8
|
-
/**
|
|
9
|
-
* Open a settings dialog for the given cell.
|
|
10
|
-
* Returns a promise that resolves to the new settings, or undefined if cancelled.
|
|
11
|
-
*/
|
|
12
|
-
abstract openCellSettings(
|
|
13
|
-
data: CellDisplayData
|
|
14
|
-
): Promise<CellDisplayData | undefined>;
|
|
15
|
-
}
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import { InjectionToken } from '@angular/core';
|
|
2
|
-
import { CellSettingsDialogProvider } from './cell-settings-dialog.provider';
|
|
3
|
-
import { DefaultCellSettingsDialogProvider } from './default-cell-settings-dialog.provider';
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Injection token for the cell dialog provider.
|
|
7
|
-
* Use this to provide your custom dialog implementation.
|
|
8
|
-
*
|
|
9
|
-
* @example
|
|
10
|
-
* ```typescript
|
|
11
|
-
* providers: [
|
|
12
|
-
* { provide: CELL_SETTINGS_DIALOG_PROVIDER, useClass: MyCellSettingsDialogProvider }
|
|
13
|
-
* ]
|
|
14
|
-
* ```
|
|
15
|
-
*/
|
|
16
|
-
export const CELL_SETTINGS_DIALOG_PROVIDER =
|
|
17
|
-
new InjectionToken<CellSettingsDialogProvider>('CellSettingsDialogProvider', {
|
|
18
|
-
providedIn: 'root',
|
|
19
|
-
factory: () => new DefaultCellSettingsDialogProvider(),
|
|
20
|
-
});
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
import { Injectable, inject } from '@angular/core';
|
|
2
|
-
import { MatDialog } from '@angular/material/dialog';
|
|
3
|
-
import { firstValueFrom } from 'rxjs';
|
|
4
|
-
import { CellDisplayData } from '../../models/cell-dialog';
|
|
5
|
-
import { CellSettingsDialogProvider } from './cell-settings-dialog.provider';
|
|
6
|
-
import { CellSettingsDialogComponent } from './cell-settings-dialog.component';
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* Default cell dialog provider that uses Material Design dialogs.
|
|
10
|
-
* Provides a modern, accessible dialog experience for cell settings.
|
|
11
|
-
*/
|
|
12
|
-
@Injectable({
|
|
13
|
-
providedIn: 'root',
|
|
14
|
-
})
|
|
15
|
-
export class DefaultCellSettingsDialogProvider extends CellSettingsDialogProvider {
|
|
16
|
-
private dialog = inject(MatDialog);
|
|
17
|
-
|
|
18
|
-
async openCellSettings(
|
|
19
|
-
data: CellDisplayData
|
|
20
|
-
): Promise<CellDisplayData | undefined> {
|
|
21
|
-
const dialogRef = this.dialog.open(CellSettingsDialogComponent, {
|
|
22
|
-
data,
|
|
23
|
-
width: '400px',
|
|
24
|
-
maxWidth: '90vw',
|
|
25
|
-
disableClose: false,
|
|
26
|
-
autoFocus: false,
|
|
27
|
-
});
|
|
28
|
-
|
|
29
|
-
const result = await firstValueFrom(dialogRef.afterClosed());
|
|
30
|
-
return result;
|
|
31
|
-
}
|
|
32
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from './cell-settings-dialog';
|