@ogidor/dashboard 1.0.3 → 1.0.5

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 (39) hide show
  1. package/README.md +283 -48
  2. package/app/app.module.d.ts +20 -0
  3. package/app/custom-grid.component.d.ts +93 -0
  4. package/app/dashboard-state.service.d.ts +70 -0
  5. package/app/dashboard.component.d.ts +48 -0
  6. package/app/models.d.ts +53 -0
  7. package/app/widget-renderer.component.d.ts +13 -0
  8. package/esm2020/app/app.module.mjs +57 -0
  9. package/esm2020/app/custom-grid.component.mjs +509 -0
  10. package/esm2020/app/dashboard-state.service.mjs +288 -0
  11. package/esm2020/app/dashboard.component.mjs +299 -0
  12. package/esm2020/app/models.mjs +2 -0
  13. package/esm2020/app/widget-renderer.component.mjs +83 -0
  14. package/esm2020/public-api.mjs +10 -0
  15. package/fesm2015/ogidor-dashboard.mjs +1233 -0
  16. package/fesm2015/ogidor-dashboard.mjs.map +1 -0
  17. package/fesm2020/ogidor-dashboard.mjs +1229 -0
  18. package/fesm2020/ogidor-dashboard.mjs.map +1 -0
  19. package/package.json +30 -44
  20. package/{dist-lib/public-api.d.ts → public-api.d.ts} +3 -2
  21. package/dist-lib/README.md +0 -103
  22. package/dist-lib/app/app.module.d.ts +0 -22
  23. package/dist-lib/app/dashboard-state.service.d.ts +0 -49
  24. package/dist-lib/app/dashboard.component.d.ts +0 -80
  25. package/dist-lib/app/models.d.ts +0 -73
  26. package/dist-lib/app/widget-renderer.component.d.ts +0 -40
  27. package/dist-lib/esm2020/app/app.module.mjs +0 -65
  28. package/dist-lib/esm2020/app/dashboard-state.service.mjs +0 -218
  29. package/dist-lib/esm2020/app/dashboard.component.mjs +0 -703
  30. package/dist-lib/esm2020/app/models.mjs +0 -2
  31. package/dist-lib/esm2020/app/widget-renderer.component.mjs +0 -163
  32. package/dist-lib/esm2020/public-api.mjs +0 -9
  33. package/dist-lib/fesm2015/ogidor-dashboard.mjs +0 -1154
  34. package/dist-lib/fesm2015/ogidor-dashboard.mjs.map +0 -1
  35. package/dist-lib/fesm2020/ogidor-dashboard.mjs +0 -1145
  36. package/dist-lib/fesm2020/ogidor-dashboard.mjs.map +0 -1
  37. package/dist-lib/package.json +0 -51
  38. /package/{dist-lib/esm2020 → esm2020}/ogidor-dashboard.mjs +0 -0
  39. /package/{dist-lib/index.d.ts → index.d.ts} +0 -0
package/README.md CHANGED
@@ -1,103 +1,338 @@
1
1
  # @ogidor/dashboard
2
2
 
3
- A professional, high-performance financial dashboard for Angular. Highly customizable, themeable via CSS variables, and powered by `angular-gridster2` and `ApexCharts`.
3
+ A lightweight, content-agnostic drag-and-drop dashboard library for Angular 15.
4
+ Drop it into any project, feed it your own card content, and get a fully functional multi-workspace grid with persistent layout, pop-out windows, and live cross-tab sync — out of the box.
5
+
6
+ ---
4
7
 
5
8
  ## Features
6
9
 
7
- - **Drag & Drop Layout:** Responsive grid system using `angular-gridster2`.
8
- - **Interactive Charts:** Beautiful Line, Bar, and Donut charts via `ng-apexcharts`.
9
- - **Multi-Workspace Support:** Manage multiple dashboard pages (workspaces).
10
- - **Deeply Themeable:** Customize every color and font using simple CSS variables or a configuration object.
11
- - **Standalone Ready:** Sidebar removed for easy integration into your own application's navigation.
10
+ - **Drag & Drop Grid** smooth cursor-following drag with animated collision resolution and auto-compaction
11
+ - **Resize** live resize with a bottom-right handle; other cards shift out of the way in real time
12
+ - **Multi-Workspace** unlimited tabbed workspaces, each with its own independent widget layout
13
+ - **Independent Pop-out Layouts** each window keeps its own card positions; drag cards in Window B and Window A is completely unaffected
14
+ - **Structural Sync** adding, removing, or editing a widget in any window immediately appears in all other open windows
15
+ - **Persistent Layout** — layout is saved to `localStorage` and restored on reload
16
+ - **Content-Agnostic Cards** — you own the card body; render any Angular component, chart, or HTML inside
17
+ - **Themeable** — override colors and fonts via a theme input or CSS custom properties
18
+
19
+ ---
12
20
 
13
21
  ## Installation
14
22
 
15
23
  ```bash
16
- npm install @ogidor/dashboard angular-gridster2 apexcharts ng-apexcharts line-awesome
24
+ npm install @ogidor/dashboard line-awesome
17
25
  ```
18
26
 
19
- Ensure you have `line-awesome` icons included in your global styles (e.g., `angular.json` or `styles.scss`):
27
+ Add the Line Awesome icon font to your global styles (`angular.json` or `styles.scss`):
20
28
 
21
29
  ```scss
22
30
  @import "line-awesome/dist/line-awesome/css/line-awesome.css";
23
31
  ```
24
32
 
25
- ## Usage
33
+ ---
34
+
35
+ ## Quick Start
26
36
 
27
- Import the `DashboardModule` in your `AppModule`:
37
+ ### 1. Import the module
28
38
 
29
39
  ```typescript
40
+ import { NgModule } from '@angular/core';
41
+ import { BrowserModule } from '@angular/platform-browser';
30
42
  import { DashboardModule } from '@ogidor/dashboard';
31
43
 
32
44
  @NgModule({
33
- imports: [
34
- DashboardModule,
35
- // ...
36
- ]
45
+ imports: [BrowserModule, DashboardModule],
46
+ bootstrap: [AppComponent],
37
47
  })
38
- export class AppModule { }
48
+ export class AppModule {}
39
49
  ```
40
50
 
41
- Use the component in your template:
51
+ ### 2. Use the component
42
52
 
43
53
  ```html
44
- <app-dashboard
45
- [initialLayout]="savedLayout"
46
- [theme]="myCustomTheme"
47
- ></app-dashboard>
54
+ <app-dashboard
55
+ (addWidgetRequested)="onAddWidget()"
56
+ (editWidgetRequested)="onEditWidget($event)"
57
+ >
58
+ <!-- Optional: custom card body rendered inside every widget -->
59
+ <ng-template gridCell let-widget="widget">
60
+ <div style="padding: 8px; color: white;">
61
+ {{ widget.data | json }}
62
+ </div>
63
+ </ng-template>
64
+ </app-dashboard>
48
65
  ```
49
66
 
67
+ ### 3. Add widgets programmatically
68
+
69
+ Inject `DashboardStateService` anywhere in your app:
70
+
71
+ ```typescript
72
+ import { DashboardStateService, Widget } from '@ogidor/dashboard';
73
+
74
+ @Component({ ... })
75
+ export class AppComponent {
76
+ constructor(private dashboard: DashboardStateService) {}
77
+
78
+ onAddWidget() {
79
+ this.dashboard.addWidget({
80
+ title: 'Sales Overview',
81
+ cols: 6,
82
+ rows: 4,
83
+ cardColor: '#1a1a2e', // optional — overrides the theme card color
84
+ data: { metric: 'revenue', period: 'Q1' }, // any shape you need
85
+ });
86
+ }
87
+
88
+ onEditWidget(widget: Widget) {
89
+ // open your own dialog, then:
90
+ this.dashboard.updateWidget(widget.id, {
91
+ title: 'Updated Title',
92
+ cardColor: '#0f3460',
93
+ data: { metric: 'users' },
94
+ });
95
+ }
96
+ }
97
+ ```
98
+
99
+ ---
100
+
50
101
  ## API Reference
51
102
 
52
- ### Inputs
103
+ ### `<app-dashboard>`
104
+
105
+ #### Inputs
106
+
107
+ | Input | Type | Default | Description |
108
+ |---|---|---|---|
109
+ | `initialLayout` | `string` | — | JSON string from a previous `serializeLayout()` call. Restores a full layout on load. |
110
+ | `theme` | `DashboardTheme` | See below | Override default colors and font. |
111
+
112
+ #### Outputs
113
+
114
+ | Output | Payload | Description |
115
+ |---|---|---|
116
+ | `addWidgetRequested` | `void` | Fires when the user clicks **Add Widget**. Open your own dialog here. |
117
+ | `editWidgetRequested` | `Widget` | Fires when the user clicks the edit icon on a card. Receives the full widget object. |
118
+
119
+ #### Content children
53
120
 
54
- | Input | Type | Description |
121
+ | Selector | Description |
122
+ |---|---|
123
+ | `<ng-template gridCell let-widget="widget">` | Optional template rendered inside every card body. The `widget` context variable gives you the full `Widget` object including your `data`. |
124
+
125
+ #### Methods (via `@ViewChild`)
126
+
127
+ ```typescript
128
+ @ViewChild(DashboardComponent) dash!: DashboardComponent;
129
+
130
+ // Serialize the current layout to a JSON string (e.g. to save to a backend)
131
+ const json = this.dash.serializeLayout();
132
+ ```
133
+
134
+ ---
135
+
136
+ ### `DashboardStateService`
137
+
138
+ The service is provided at root level. Inject it directly for full programmatic control.
139
+
140
+ ```typescript
141
+ constructor(private state: DashboardStateService) {}
142
+ ```
143
+
144
+ #### Methods
145
+
146
+ | Method | Signature | Description |
55
147
  |---|---|---|
56
- | `initialLayout` | `string` | A JSON string representing a previously exported layout. |
57
- | `theme` | `DashboardTheme` | An object to override default colors and fonts programmatically. |
148
+ | `addWidget` | `(widget: Partial<Widget> & { title: string }) => Widget` | Adds a widget to the active workspace. Returns the created widget with its generated `id`. |
149
+ | `updateWidget` | `(id: string, patch: Partial<Widget>) => void` | Update any widget field except `id`, `x`, `y`, `cols`, `rows`. |
150
+ | `removeWidget` | `(id: string) => void` | Remove a widget from the active workspace. |
151
+ | `updateWidgetPosition` | `(pageId, widgetId, x, y, cols, rows) => void` | Manually reposition/resize a widget. |
152
+ | `addPage` | `(name: string) => void` | Add a new workspace tab. |
153
+ | `removePage` | `(id: string) => void` | Remove a workspace tab (minimum 1 kept). |
154
+ | `setActivePage` | `(id: string) => void` | Switch to a workspace by id. |
155
+ | `popOutPage` | `(id: string) => void` | Open a workspace in its own browser window. |
156
+ | `loadLayout` | `(config: DashboardConfig) => void` | Load a full layout object (parsed from `serializeLayout`). |
157
+ | `serializeLayout` | `() => string` | Serialize the full layout to a JSON string. |
58
158
 
59
- ### DashboardTheme Interface
159
+ #### Observables
160
+
161
+ | Observable | Type | Description |
162
+ |---|---|---|
163
+ | `pages$` | `Observable<Page[]>` | Emits the full page list on every change. |
164
+ | `activePageId$` | `Observable<string>` | Emits the active workspace id on every change. |
165
+
166
+ ---
167
+
168
+ ### `Widget`
169
+
170
+ ```typescript
171
+ export interface Widget {
172
+ id: string; // auto-generated — do not set manually
173
+ x: number; // grid column (0-based)
174
+ y: number; // grid row (0-based)
175
+ cols: number; // column span (default: 4)
176
+ rows: number; // row span (default: 3)
177
+ title: string; // displayed in the card header
178
+ cardColor?: string; // optional background color for this card
179
+ data?: any; // your custom payload — stored & synced, never inspected
180
+ }
181
+ ```
182
+
183
+ ---
184
+
185
+ ### `DashboardTheme`
186
+
187
+ All fields are optional. Any omitted field falls back to its default.
60
188
 
61
189
  ```typescript
62
190
  export interface DashboardTheme {
63
- backgroundColor?: string;
64
- panelColor?: string;
65
- widgetCardColor?: string;
66
- chartForeColor?: string;
67
- accentColor?: string;
68
- dangerColor?: string;
69
- chartColors?: string[];
70
- fontFamily?: string;
191
+ backgroundColor?: string; // wrapper background default: #000000
192
+ panelColor?: string; // main panel background default: #1c1c1e
193
+ widgetCardColor?: string; // card background default: #2c2c2e
194
+ foreColor?: string; // text / labels default: #8e8e93
195
+ accentColor?: string; // buttons, active states default: #0a84ff
196
+ dangerColor?: string; // remove / close actions default: #ff453a
197
+ fontFamily?: string; // applied globally default: system-ui stack
71
198
  }
72
199
  ```
73
200
 
74
- ## Customization (Theming)
201
+ ---
75
202
 
76
- You can customize the dashboard's look and feel by overriding the following CSS variables in your global stylesheet:
203
+ ## CSS Custom Properties
204
+
205
+ You can also theme the dashboard entirely via CSS in your global stylesheet:
77
206
 
78
207
  ```css
79
- :root {
80
- --dash-bg: #000000; /* Overall background */
81
- --dash-panel-bg: #1c1c1e; /* Main content panel */
82
- --dash-card-bg: #2c2c2e; /* Widget card background */
83
- --dash-fore-color: #8e8e93; /* Primary text/chart labels */
84
- --dash-accent-color: #0a84ff; /* Action buttons & active states */
85
- --dash-danger-color: #ff453a; /* Delete/Close actions */
86
- --dash-font-family: 'Inter', sans-serif;
208
+ app-dashboard {
209
+ --dash-bg: #0d0d0d;
210
+ --dash-panel-bg: #161616;
211
+ --dash-card-bg: #1f1f1f;
212
+ --dash-fore-color: #a0a0a0;
213
+ --dash-accent-color: #6c63ff;
214
+ --dash-danger-color: #e84545;
215
+ --dash-font-family: 'Inter', sans-serif;
87
216
  }
88
217
  ```
89
218
 
90
- ## Methods (Public API)
219
+ ---
220
+
221
+ ## Pop-out Windows
222
+
223
+ Every workspace tab has a **pop-out** button (the arrow icon, visible on hover). Clicking it opens that workspace in a separate browser window.
224
+
225
+ The popped-out window:
226
+ - shows only that one workspace — no tabs, no "Add Widget" bar, just the grid
227
+ - has its **own independent card layout** — dragging and resizing cards there does not affect any other window
228
+ - **does** receive structural changes from other windows in real time
229
+
230
+ ### What syncs and what doesn't
91
231
 
92
- Access the dashboard instance via `@ViewChild` to trigger actions:
232
+ The library separates two kinds of state:
233
+
234
+ | State | Synced across windows? | Stored where |
235
+ |---|---|---|
236
+ | Page list (add / remove / rename workspace) | ✅ Yes | `ogidor_shared` in localStorage |
237
+ | Widget list (add / remove a card) | ✅ Yes | `ogidor_shared` in localStorage |
238
+ | Widget metadata (title, card color, data) | ✅ Yes | `ogidor_shared` in localStorage |
239
+ | Card positions and sizes (drag / resize) | ❌ No — local only | `ogidor_positions_<windowId>` |
240
+
241
+ **Example:** you have your main dashboard open in Tab A, and you pop out "Workspace 2" into Window B.
242
+
243
+ - You drag a card in **Window B** → it stays where you put it in B; Tab A is completely unaffected
244
+ - You add a widget from **Tab A** → the new card appears in **Window B** at its default position
245
+ - You delete a widget in **Window B** → it disappears from **Tab A** immediately
246
+ - Each window remembers its own layout between page reloads
247
+
248
+ To pop out a workspace programmatically:
93
249
 
94
250
  ```typescript
95
- @ViewChild(DashboardComponent) dashboard!: DashboardComponent;
251
+ this.state.popOutPage(pageId);
252
+ ```
253
+
254
+ ---
255
+
256
+ ## Persisting Layouts
257
+
258
+ The library automatically persists state to `localStorage` on every change using two keys:
96
259
 
97
- // Export the current layout as a JSON string
98
- const layout = this.dashboard.serializeLayout();
260
+ | Key | Contains |
261
+ |---|---|
262
+ | `ogidor_shared` | Page list and widget metadata (titles, colors, data). Shared across all windows. |
263
+ | `ogidor_positions_main` | Card positions/sizes for the main window. |
264
+ | `ogidor_positions_<pageId>` | Card positions/sizes for each pop-out window, keyed by the workspace id. |
265
+
266
+ To save the full current layout to a backend and restore it later:
267
+
268
+ ```typescript
269
+ // Save (includes this window's positions)
270
+ const json = this.dash.serializeLayout();
271
+ await myApi.saveLayout(json);
272
+
273
+ // Restore
274
+ const json = await myApi.loadLayout();
275
+ this.dash.stateService.loadLayout(JSON.parse(json));
276
+ ```
277
+
278
+ ---
279
+
280
+ ## Example: Full Integration
281
+
282
+ ```typescript
283
+ // app.component.ts
284
+ @Component({
285
+ selector: 'app-root',
286
+ template: `
287
+ <app-dashboard
288
+ [theme]="theme"
289
+ (addWidgetRequested)="openAddDialog()"
290
+ (editWidgetRequested)="openEditDialog($event)"
291
+ >
292
+ <ng-template gridCell let-widget="widget">
293
+ <my-chart [config]="widget.data"></my-chart>
294
+ </ng-template>
295
+ </app-dashboard>
296
+
297
+ <!-- Your own add/edit dialogs -->
298
+ <my-add-widget-dialog
299
+ *ngIf="addOpen"
300
+ (confirmed)="onAddConfirmed($event)"
301
+ (cancelled)="addOpen = false"
302
+ ></my-add-widget-dialog>
303
+
304
+ <my-edit-widget-dialog
305
+ *ngIf="editTarget"
306
+ [widget]="editTarget"
307
+ (confirmed)="onEditConfirmed($event)"
308
+ (cancelled)="editTarget = null"
309
+ ></my-edit-widget-dialog>
310
+ `,
311
+ })
312
+ export class AppComponent {
313
+ theme: DashboardTheme = { accentColor: '#6c63ff' };
314
+ addOpen = false;
315
+ editTarget: Widget | null = null;
316
+
317
+ constructor(private state: DashboardStateService) {}
318
+
319
+ openAddDialog() { this.addOpen = true; }
320
+ openEditDialog(w: Widget) { this.editTarget = w; }
321
+
322
+ onAddConfirmed(payload: { title: string; data: any; cols: number; rows: number }) {
323
+ this.state.addWidget(payload);
324
+ this.addOpen = false;
325
+ }
326
+
327
+ onEditConfirmed(patch: Partial<Widget>) {
328
+ this.state.updateWidget(this.editTarget!.id, patch);
329
+ this.editTarget = null;
330
+ }
331
+ }
99
332
  ```
100
333
 
334
+ ---
335
+
101
336
  ## License
102
337
 
103
338
  MIT
@@ -0,0 +1,20 @@
1
+ import * as i0 from "@angular/core";
2
+ import * as i1 from "./dashboard.component";
3
+ import * as i2 from "./widget-renderer.component";
4
+ import * as i3 from "./custom-grid.component";
5
+ import * as i4 from "@angular/common";
6
+ import * as i5 from "@angular/platform-browser";
7
+ export declare class DashboardModule {
8
+ static ɵfac: i0.ɵɵFactoryDeclaration<DashboardModule, never>;
9
+ static ɵmod: i0.ɵɵNgModuleDeclaration<DashboardModule, [typeof i1.DashboardComponent, typeof i2.WidgetRendererComponent, typeof i3.CustomGridComponent, typeof i3.GridCellDirective], [typeof i4.CommonModule], [typeof i1.DashboardComponent, typeof i2.WidgetRendererComponent, typeof i3.CustomGridComponent, typeof i3.GridCellDirective]>;
10
+ static ɵinj: i0.ɵɵInjectorDeclaration<DashboardModule>;
11
+ }
12
+ /**
13
+ * Root module for local demo / testing purposes only.
14
+ * Library consumers import DashboardModule, not AppModule.
15
+ */
16
+ export declare class AppModule {
17
+ static ɵfac: i0.ɵɵFactoryDeclaration<AppModule, never>;
18
+ static ɵmod: i0.ɵɵNgModuleDeclaration<AppModule, never, [typeof i5.BrowserModule, typeof DashboardModule], never>;
19
+ static ɵinj: i0.ɵɵInjectorDeclaration<AppModule>;
20
+ }
@@ -0,0 +1,93 @@
1
+ import { EventEmitter, ElementRef, OnDestroy, OnInit, NgZone, ChangeDetectorRef, TemplateRef, OnChanges, SimpleChanges } from '@angular/core';
2
+ import { Widget } from './models';
3
+ import * as i0 from "@angular/core";
4
+ /**
5
+ * Directive applied to the template that should be stamped inside each grid cell.
6
+ * Usage:
7
+ * <app-grid [widgets]="...">
8
+ * <ng-template gridCell let-widget="widget">
9
+ * <app-widget-renderer [widget]="widget" ...></app-widget-renderer>
10
+ * </ng-template>
11
+ * </app-grid>
12
+ */
13
+ export declare class GridCellDirective {
14
+ templateRef: TemplateRef<{
15
+ widget: Widget;
16
+ }>;
17
+ constructor(templateRef: TemplateRef<{
18
+ widget: Widget;
19
+ }>);
20
+ static ɵfac: i0.ɵɵFactoryDeclaration<GridCellDirective, never>;
21
+ static ɵdir: i0.ɵɵDirectiveDeclaration<GridCellDirective, "[gridCell]", never, {}, {}, never, never, false, never>;
22
+ }
23
+ export declare class CustomGridComponent implements OnInit, OnDestroy, OnChanges {
24
+ private zone;
25
+ private cdr;
26
+ widgets: Widget[];
27
+ columns: number;
28
+ gap: number;
29
+ rowHeight: number;
30
+ minItemCols: number;
31
+ minItemRows: number;
32
+ itemChanged: EventEmitter<Widget>;
33
+ layoutChanged: EventEmitter<Widget[]>;
34
+ gridContainer: ElementRef<HTMLDivElement>;
35
+ /** The ng-template decorated with gridCell from the parent */
36
+ cellTemplate: TemplateRef<{
37
+ widget: Widget;
38
+ }>;
39
+ placeholder: {
40
+ left: number;
41
+ top: number;
42
+ width: number;
43
+ height: number;
44
+ } | null;
45
+ containerHeight: number;
46
+ dragging: {
47
+ id: string;
48
+ offsetX: number;
49
+ offsetY: number;
50
+ currentPixelX: number;
51
+ currentPixelY: number;
52
+ } | null;
53
+ resizing: {
54
+ id: string;
55
+ startMouseX: number;
56
+ startMouseY: number;
57
+ startCols: number;
58
+ startRows: number;
59
+ currentPixelW: number;
60
+ currentPixelH: number;
61
+ } | null;
62
+ private previewWidgets;
63
+ private boundMouseMove;
64
+ private boundMouseUp;
65
+ constructor(zone: NgZone, cdr: ChangeDetectorRef);
66
+ ngOnInit(): void;
67
+ ngOnChanges(_changes: SimpleChanges): void;
68
+ ngOnDestroy(): void;
69
+ trackByFn(_index: number, item: Widget): string;
70
+ private get cellWidth();
71
+ private colToPixel;
72
+ private rowToPixel;
73
+ private pixelToCol;
74
+ private pixelToRow;
75
+ private colsToPixelWidth;
76
+ private rowsToPixelHeight;
77
+ getItemLeft(w: Widget): number;
78
+ getItemTop(w: Widget): number;
79
+ getItemWidth(w: Widget): number;
80
+ getItemHeight(w: Widget): number;
81
+ onDragStart(event: MouseEvent, widget: Widget): void;
82
+ onResizeStart(event: MouseEvent, widget: Widget): void;
83
+ private onMouseMove;
84
+ private onMouseUp;
85
+ private handleDragMove;
86
+ private finalizeDrag;
87
+ private handleResizeMove;
88
+ private finalizeResize;
89
+ private compactAndApply;
90
+ private updateContainerHeight;
91
+ static ɵfac: i0.ɵɵFactoryDeclaration<CustomGridComponent, never>;
92
+ static ɵcmp: i0.ɵɵComponentDeclaration<CustomGridComponent, "app-grid", never, { "widgets": "widgets"; "columns": "columns"; "gap": "gap"; "rowHeight": "rowHeight"; "minItemCols": "minItemCols"; "minItemRows": "minItemRows"; }, { "itemChanged": "itemChanged"; "layoutChanged": "layoutChanged"; }, ["cellTemplate"], never, false, never>;
93
+ }
@@ -0,0 +1,70 @@
1
+ import { NgZone, OnDestroy } from '@angular/core';
2
+ import { Page, Widget } from './models';
3
+ import * as i0 from "@angular/core";
4
+ export declare class DashboardStateService implements OnDestroy {
5
+ private zone;
6
+ /**
7
+ * Shared key — stores page list + widget metadata (no positions).
8
+ * Read and written by every window.
9
+ */
10
+ private readonly SHARED_KEY;
11
+ /**
12
+ * Per-window positions key — stores {pageId:widgetId → {x,y,cols,rows}}.
13
+ * Pop-out windows get a key scoped to their pageId so they never overwrite
14
+ * the main window or each other.
15
+ * main window → ogidor_positions_main
16
+ * pop-out #abc → ogidor_positions_abc
17
+ */
18
+ private readonly positionsKey;
19
+ private readonly CHANNEL_NAME;
20
+ private channel;
21
+ private readonly initialPages;
22
+ private pagesSubject;
23
+ private activePageIdSubject;
24
+ pages$: import("rxjs").Observable<Page[]>;
25
+ activePageId$: import("rxjs").Observable<string>;
26
+ constructor(zone: NgZone);
27
+ ngOnDestroy(): void;
28
+ getActivePage(): Page | undefined;
29
+ setActivePage(id: string): void;
30
+ addPage(name: string): void;
31
+ removePage(id: string): void;
32
+ addWidget(widget: Partial<Widget> & {
33
+ title: string;
34
+ }): Widget;
35
+ updateWidget(widgetId: string, patch: Partial<Omit<Widget, 'id' | 'x' | 'y' | 'cols' | 'rows'>>): void;
36
+ removeWidget(widgetId: string): void;
37
+ /**
38
+ * Update a widget's grid position/size.
39
+ * Saved only for this window — never broadcast to others.
40
+ */
41
+ updateWidgetPosition(pageId: string, widgetId: string, x: number, y: number, cols: number, rows: number): void;
42
+ serializeLayout(): string;
43
+ loadLayout(config: any): void;
44
+ popOutPage(pageId: string): void;
45
+ /**
46
+ * Save page list + widget metadata (titles, cardColor, data) — no positions.
47
+ */
48
+ private _saveShared;
49
+ /**
50
+ * Save this window's grid positions (x, y, cols, rows) per widget.
51
+ */
52
+ private _savePositions;
53
+ /**
54
+ * Overlay the positions saved for THIS window on top of the current pages.
55
+ */
56
+ private _restorePositions;
57
+ /**
58
+ * Apply a shared config object (page list + metadata) without touching
59
+ * this window's saved positions.
60
+ */
61
+ private _applyShared;
62
+ /**
63
+ * Handle a structural sync event arriving from another window.
64
+ * Position changes are never sent so we never receive them here.
65
+ */
66
+ private _onSyncEvent;
67
+ private _broadcast;
68
+ static ɵfac: i0.ɵɵFactoryDeclaration<DashboardStateService, never>;
69
+ static ɵprov: i0.ɵɵInjectableDeclaration<DashboardStateService>;
70
+ }
@@ -0,0 +1,48 @@
1
+ import { OnInit, OnDestroy, OnChanges, SimpleChanges, EventEmitter, TemplateRef } from '@angular/core';
2
+ import { DashboardStateService } from './dashboard-state.service';
3
+ import { Page, Widget, DashboardTheme } from './models';
4
+ import * as i0 from "@angular/core";
5
+ export declare class DashboardComponent implements OnInit, OnDestroy, OnChanges {
6
+ stateService: DashboardStateService;
7
+ initialLayout?: string;
8
+ theme?: DashboardTheme;
9
+ /**
10
+ * Emits when the user clicks "Add Widget".
11
+ * The consumer should open their own dialog and call `stateService.addWidget(...)`.
12
+ */
13
+ addWidgetRequested: EventEmitter<void>;
14
+ /**
15
+ * Emits the Widget when the user clicks the edit (pen) icon on a card.
16
+ * The consumer should open their own edit UI and call `stateService.updateWidget(...)`.
17
+ */
18
+ editWidgetRequested: EventEmitter<Widget>;
19
+ /**
20
+ * Optional: consumer can provide an `<ng-template widgetCell let-widget="widget">` child
21
+ * to render custom content inside every card body.
22
+ */
23
+ cellTemplate?: TemplateRef<{
24
+ widget: Widget;
25
+ }>;
26
+ resolvedTheme: Required<DashboardTheme>;
27
+ wrapperStyles: Record<string, string>;
28
+ pages: Page[];
29
+ activePageId: string;
30
+ activePage?: Page;
31
+ isPoppedOut: boolean;
32
+ private subs;
33
+ constructor(stateService: DashboardStateService);
34
+ ngOnChanges(changes: SimpleChanges): void;
35
+ ngOnInit(): void;
36
+ ngOnDestroy(): void;
37
+ onItemChanged(widget: Widget): void;
38
+ onRemoveWidget(widgetId: string): void;
39
+ onSelectPage(id: string): void;
40
+ onAddPage(): void;
41
+ onRemovePage(event: Event, id: string): void;
42
+ onPopOut(event: Event, pageId: string): void;
43
+ serializeLayout(): string;
44
+ private applyTheme;
45
+ private updateActivePage;
46
+ static ɵfac: i0.ɵɵFactoryDeclaration<DashboardComponent, never>;
47
+ static ɵcmp: i0.ɵɵComponentDeclaration<DashboardComponent, "app-dashboard", never, { "initialLayout": "initialLayout"; "theme": "theme"; }, { "addWidgetRequested": "addWidgetRequested"; "editWidgetRequested": "editWidgetRequested"; }, ["cellTemplate"], never, false, never>;
48
+ }
@@ -0,0 +1,53 @@
1
+ /**
2
+ * A single widget on the grid.
3
+ * The lib is content-agnostic — `data` can hold anything the consumer needs.
4
+ */
5
+ export interface Widget {
6
+ id: string;
7
+ /** Grid column (0-based) */
8
+ x: number;
9
+ /** Grid row (0-based) */
10
+ y: number;
11
+ /** Number of columns this widget spans */
12
+ cols: number;
13
+ /** Number of rows this widget spans */
14
+ rows: number;
15
+ /** Display title shown in the card header */
16
+ title: string;
17
+ /** Optional per-widget card background color. Overrides the global theme widgetCardColor. */
18
+ cardColor?: string;
19
+ /**
20
+ * Arbitrary consumer data.
21
+ * The lib stores and syncs this but never inspects it.
22
+ */
23
+ data?: any;
24
+ }
25
+ export interface Page {
26
+ id: string;
27
+ name: string;
28
+ widgets: Widget[];
29
+ }
30
+ export interface DashboardConfig {
31
+ pages: Page[];
32
+ activePageId: string;
33
+ }
34
+ /**
35
+ * Theme configuration for the dashboard.
36
+ * All properties are optional — any values not provided will fall back to defaults.
37
+ */
38
+ export interface DashboardTheme {
39
+ /** Background color of the overall dashboard wrapper. Default: `#000000` */
40
+ backgroundColor?: string;
41
+ /** Background color of the main content panel. Default: `#1c1c1e` */
42
+ panelColor?: string;
43
+ /** Background color of individual widget cards. Default: `#2c2c2e` */
44
+ widgetCardColor?: string;
45
+ /** Primary foreground/text color. Default: `#8e8e93` */
46
+ foreColor?: string;
47
+ /** Primary accent color (active tabs, buttons, etc.). Default: `#0a84ff` */
48
+ accentColor?: string;
49
+ /** Danger color used on remove-widget button hover. Default: `#ff453a` */
50
+ dangerColor?: string;
51
+ /** Font family applied to the whole dashboard. Default: system-ui stack */
52
+ fontFamily?: string;
53
+ }