@grafloria/element 0.4.1 → 0.4.3
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/package.json +1 -1
- package/src/lib/dashboard-kit/dashboard.d.ts +181 -1
- package/src/lib/dashboard-kit/dashboard.js +685 -92
- package/src/lib/dashboard-kit/grid-binder.d.ts +43 -0
- package/src/lib/dashboard-kit/grid-binder.js +736 -109
- package/src/lib/dashboard-kit/index.d.ts +2 -0
- package/src/lib/dashboard-kit/index.js +2 -0
- package/src/lib/dashboard-kit/split-binder.d.ts +69 -0
- package/src/lib/dashboard-kit/split-binder.js +1042 -0
- package/src/lib/dashboard-kit/split-layout.d.ts +128 -0
- package/src/lib/dashboard-kit/split-layout.js +425 -0
- package/src/lib/dashboard-kit/styles.js +137 -20
- package/src/lib/dashboard-kit/widgets.d.ts +16 -1
- package/src/lib/dashboard-kit/widgets.js +146 -33
- package/src/lib/diagram-kit/card.d.ts +46 -0
- package/src/lib/diagram-kit/card.js +149 -0
- package/src/lib/diagram-kit/er.js +5 -3
- package/src/lib/diagram-kit/styles.js +40 -5
- package/src/lib/diagram-kit/uml.js +3 -4
- package/src/lib/diagram-kit/update.js +7 -3
- package/src/lib/grafloria.js +4 -4
- package/src/lib/load.d.ts +5 -0
- package/src/lib/load.js +125 -21
package/package.json
CHANGED
|
@@ -44,6 +44,7 @@
|
|
|
44
44
|
*/
|
|
45
45
|
import { GroupModel, NodeModel, type GridColumnLayout } from '@grafloria/engine';
|
|
46
46
|
import { type DashboardGridHandle, type DashboardGridOptions, type DashboardResponsiveOptions } from './grid-binder.js';
|
|
47
|
+
import type { SplitNode } from './split-layout.js';
|
|
47
48
|
/** A widget, declared as data. */
|
|
48
49
|
export interface DashboardWidgetSpec {
|
|
49
50
|
id: string;
|
|
@@ -60,10 +61,50 @@ export interface DashboardWidgetSpec {
|
|
|
60
61
|
y?: number;
|
|
61
62
|
/** Pinned: never pushed, refuses the mover, survives every reflow. */
|
|
62
63
|
pinned?: boolean;
|
|
64
|
+
/**
|
|
65
|
+
* SIZE LIMITS in cells (gridstack's minW/maxW/minH/maxH). A resize — by
|
|
66
|
+
* hand, by the API, or by a column change scaling widths — clamps to them.
|
|
67
|
+
* `maxRows` here is the WIDGET's row limit; a container's inner row count is
|
|
68
|
+
* its own `maxRows` field one level up, which is why these live in `limits`.
|
|
69
|
+
*/
|
|
70
|
+
limits?: {
|
|
71
|
+
minSpan?: number;
|
|
72
|
+
maxSpan?: number;
|
|
73
|
+
minRows?: number;
|
|
74
|
+
maxRows?: number;
|
|
75
|
+
};
|
|
76
|
+
/** May the user drag it? Default true. The API can always move it. */
|
|
77
|
+
movable?: boolean;
|
|
78
|
+
/** May the user resize it? Default true (no handle when false). The API can always resize it. */
|
|
79
|
+
resizable?: boolean;
|
|
63
80
|
/** Your payload — passed straight back to `renderWidget`. */
|
|
64
81
|
data?: Record<string, unknown>;
|
|
65
82
|
/** Optional title used by the built-in fallback renderer. */
|
|
66
83
|
title?: string;
|
|
84
|
+
/**
|
|
85
|
+
* CONTAINMENT. A widget carrying `widgets` is a CONTAINER: it mounts as a
|
|
86
|
+
* member group (a locked slab in its parent's grid, exactly like a view's
|
|
87
|
+
* board one level down) with its own nested pack grid bound on it. Children
|
|
88
|
+
* lay out inside its frame; dragging a tile across the boundary adopts it
|
|
89
|
+
* live in either direction, and one undo restores the whole gesture.
|
|
90
|
+
* Containers may nest — tested to TWO levels; deeper is not exercised by
|
|
91
|
+
* the gates and rides at your own risk. A container renders no card of its
|
|
92
|
+
* own (`kind`/`data` are carried for your bookkeeping and serialization,
|
|
93
|
+
* not painted).
|
|
94
|
+
*/
|
|
95
|
+
widgets?: DashboardWidgetSpec[];
|
|
96
|
+
/**
|
|
97
|
+
* Container only: column count of the INNER grid (default: the parent
|
|
98
|
+
* board's column count).
|
|
99
|
+
*/
|
|
100
|
+
columns?: number;
|
|
101
|
+
/**
|
|
102
|
+
* Container only: the inner grid's designed row count. A child resized past
|
|
103
|
+
* it ESCALATES — the container's slab grows a row in the parent board (the
|
|
104
|
+
* ratchet), instead of the child overflowing the frame. Default: the row
|
|
105
|
+
* extent of the declared children.
|
|
106
|
+
*/
|
|
107
|
+
maxRows?: number;
|
|
67
108
|
}
|
|
68
109
|
/** One board. Multiple views are the tab pattern: only one is on-camera. */
|
|
69
110
|
export interface DashboardViewSpec {
|
|
@@ -74,14 +115,41 @@ export interface DashboardViewSpec {
|
|
|
74
115
|
columns?: number;
|
|
75
116
|
width?: number;
|
|
76
117
|
height?: number;
|
|
118
|
+
/** Per-view layout (default: the dashboard-level `layout`). `toJSON()` writes it per view. */
|
|
119
|
+
layout?: 'grid' | 'split';
|
|
120
|
+
/**
|
|
121
|
+
* SPLIT layout only: the authored splitter tree (see `layout`). Omit it and
|
|
122
|
+
* the tree is derived from the widgets' cells, so a grid-authored view keeps
|
|
123
|
+
* its proportions when it opens as a split board. `toJSON()` writes it back.
|
|
124
|
+
*/
|
|
125
|
+
tree?: SplitNode | null;
|
|
77
126
|
}
|
|
78
127
|
export interface DashboardOptions {
|
|
79
128
|
/** Column count for every view (default 12). */
|
|
80
129
|
columns?: number;
|
|
81
130
|
/** Gap between widgets AND the board padding, px (default 8). */
|
|
82
131
|
gap?: number;
|
|
83
|
-
/**
|
|
132
|
+
/**
|
|
133
|
+
* Sizing mode. 'grow': rows keep `rowHeight` and the board extends
|
|
134
|
+
* downward — the default on a FLUID board, and what every grid library
|
|
135
|
+
* does: dragging one tile never resizes another. 'fit': the board keeps its
|
|
136
|
+
* height and rows squeeze so everything stays on one screen (bounded, see
|
|
137
|
+
* `overflow`) — the default on a FIXED board, and the choice for a designer
|
|
138
|
+
* who wants the whole dashboard visible at once.
|
|
139
|
+
*/
|
|
84
140
|
sizing?: 'fit' | 'grow';
|
|
141
|
+
/**
|
|
142
|
+
* HOW THE BOARD IS LAID OUT (the DevExpress question, decided 6 Sep 2026).
|
|
143
|
+
* 'grid' (the default): the cell grid — columns, spans, push, gravity, the
|
|
144
|
+
* gridstack model. 'split': a splitter tree — the board is always covered;
|
|
145
|
+
* one widget fills it, a second halves it, a third halves the larger half
|
|
146
|
+
* the other way; dividers drag as percentages; a drag lifts the widget out
|
|
147
|
+
* and an insertion line on the nearest edge says where it lands; a removed
|
|
148
|
+
* widget's slot goes to its siblings. Sizing is always fit under 'split'.
|
|
149
|
+
* Switch live with `handle.setLayout()`: grid cells become a tree by
|
|
150
|
+
* guillotine cuts, a tree becomes cells by snapping to the columns.
|
|
151
|
+
*/
|
|
152
|
+
layout?: 'grid' | 'split';
|
|
85
153
|
/** Row height in 'grow' mode, px (default 130). */
|
|
86
154
|
rowHeight?: number;
|
|
87
155
|
/** Board size, px (default 1180 × 660). */
|
|
@@ -89,6 +157,37 @@ export interface DashboardOptions {
|
|
|
89
157
|
height?: number;
|
|
90
158
|
/** Engine float mode (default false → gravity packs upward). */
|
|
91
159
|
float?: boolean;
|
|
160
|
+
/**
|
|
161
|
+
* DIAGRAM OR LAYOUT — the one switch (decision of 2026-09-06).
|
|
162
|
+
*
|
|
163
|
+
* 'fluid' (the default): the board is 100% of its container, laid out at
|
|
164
|
+
* real CSS pixels; zoom is pinned at 1; a plain wheel scrolls; in 'fit' the
|
|
165
|
+
* height follows the container too. What every grid library does, and
|
|
166
|
+
* what "responsive" means to a dashboard author.
|
|
167
|
+
* 'fixed': the authored `width`/`height` are the world, and the camera frames
|
|
168
|
+
* them — today's behaviour, kept for a dashboard embedded inside a larger
|
|
169
|
+
* diagram. An explicit `width` implies 'fixed', so existing boards keep
|
|
170
|
+
* their behaviour without naming a mode.
|
|
171
|
+
*/
|
|
172
|
+
mode?: 'fluid' | 'fixed';
|
|
173
|
+
/**
|
|
174
|
+
* FIT MEANS BOUNDED. In 'fit' the board never changes size; widgets do. Past
|
|
175
|
+
* the row floor the design height is a CAPACITY: a drop, resize or
|
|
176
|
+
* `addWidget()` that would need one row too many is refused (the placeholder
|
|
177
|
+
* stays put, the palette chip dims, `addWidget` returns undefined) — at
|
|
178
|
+
* design time, instead of tiles painted past the frame. A board that already
|
|
179
|
+
* holds more than fits (a grow→fit switch, a loaded document) squeezes its
|
|
180
|
+
* rows below the floor: a bounded fit board NEVER scrolls. 'scroll' is the
|
|
181
|
+
* opt-in for boards that want more than fits: the frame extends to hold the
|
|
182
|
+
* rows at the floor height and the canvas pans.
|
|
183
|
+
*/
|
|
184
|
+
overflow?: 'bounded' | 'scroll';
|
|
185
|
+
/**
|
|
186
|
+
* STATIC board (gridstack's `staticGrid`): no drag, no resize, no handles —
|
|
187
|
+
* the viewer's mode. The API (moveTo, resize, addWidget, undo) still works,
|
|
188
|
+
* so a designer/viewer pair is one flag apart. Live: `handle.setStatic()`.
|
|
189
|
+
*/
|
|
190
|
+
static?: boolean;
|
|
92
191
|
/**
|
|
93
192
|
* RIGHT-TO-LEFT boards: column x=0 renders at the RIGHT edge and columns run
|
|
94
193
|
* leftwards. Cells are untouched — the same `widgets` array describes the
|
|
@@ -128,6 +227,14 @@ export interface DashboardSpec {
|
|
|
128
227
|
finalize: (api: unknown) => void;
|
|
129
228
|
/** Live handle, populated by finalize(). */
|
|
130
229
|
readonly handle: DashboardHandle;
|
|
230
|
+
/**
|
|
231
|
+
* Instance options the spec asks `render()` to apply — a fluid board pins
|
|
232
|
+
* the zoom range to 1 so the layout can never become a scaled picture.
|
|
233
|
+
*/
|
|
234
|
+
renderOptions?: {
|
|
235
|
+
minZoom?: number;
|
|
236
|
+
maxZoom?: number;
|
|
237
|
+
};
|
|
131
238
|
}
|
|
132
239
|
/**
|
|
133
240
|
* A whole board as plain data: every `DashboardOptions` field except the
|
|
@@ -152,6 +259,14 @@ export interface DashboardHandle {
|
|
|
152
259
|
widget(id: string): WidgetHandle | undefined;
|
|
153
260
|
/** Every widget handle of a view (default: the active one). */
|
|
154
261
|
widgetsOf(viewId?: string): WidgetHandle[];
|
|
262
|
+
/**
|
|
263
|
+
* Switch a view (default: the active one) between the cell grid and the
|
|
264
|
+
* split tree, live and keeping the picture: cells → tree by guillotine cuts,
|
|
265
|
+
* tree → cells by snapping to the columns. Persisted on the board, so a
|
|
266
|
+
* saved document reopens in the layout it was left in.
|
|
267
|
+
*/
|
|
268
|
+
setLayout(layout: 'grid' | 'split', viewId?: string): void;
|
|
269
|
+
getLayout(viewId?: string): 'grid' | 'split';
|
|
155
270
|
/** Live sizing/float switches — the two prototype toggles. */
|
|
156
271
|
setSizing(mode: 'fit' | 'grow'): void;
|
|
157
272
|
getSizing(): 'fit' | 'grow';
|
|
@@ -169,6 +284,9 @@ export interface DashboardHandle {
|
|
|
169
284
|
/** RTL mirroring, live — pixels only, cells never change. */
|
|
170
285
|
setRtl(on: boolean): void;
|
|
171
286
|
getRtl(): boolean;
|
|
287
|
+
/** Static (read-only for the pointer) mode, live — the viewer/designer switch. */
|
|
288
|
+
setStatic(on: boolean): void;
|
|
289
|
+
getStatic(): boolean;
|
|
172
290
|
/**
|
|
173
291
|
* Add a widget to a view. CREATES the node (you do not pre-build one), wires
|
|
174
292
|
* its metadata, and commits node + membership as ONE undoable step.
|
|
@@ -287,15 +405,38 @@ export interface DashboardApiRef {
|
|
|
287
405
|
addGroup(g: GroupModel): void;
|
|
288
406
|
getGroup(id: string): GroupModel | undefined;
|
|
289
407
|
removeGroup?(id: string): unknown;
|
|
408
|
+
removeNode?(id: string): unknown;
|
|
409
|
+
/** Derived writes (a layout switch) bypass the history like the binder's own. */
|
|
410
|
+
runSystemWrite?(fn: () => void): void;
|
|
290
411
|
};
|
|
291
412
|
getEngine?: () => {
|
|
292
413
|
commandManager: {
|
|
293
414
|
execute(c: unknown): unknown;
|
|
294
415
|
};
|
|
416
|
+
/** The engine's bus — the kit listens for history events on it (D3). */
|
|
417
|
+
eventBus?: {
|
|
418
|
+
on(event: string, handler: (...args: unknown[]) => void): () => void;
|
|
419
|
+
};
|
|
295
420
|
};
|
|
296
421
|
renderNow(): void;
|
|
297
422
|
viewport?: {
|
|
298
423
|
fitToBounds(r: unknown, pad: number, o?: unknown): void;
|
|
424
|
+
/** Fluid boards pin the camera instead of framing the board. */
|
|
425
|
+
setZoom?(z: number): unknown;
|
|
426
|
+
getViewport?(): {
|
|
427
|
+
x: number;
|
|
428
|
+
y: number;
|
|
429
|
+
width: number;
|
|
430
|
+
height: number;
|
|
431
|
+
};
|
|
432
|
+
setViewport?(r: {
|
|
433
|
+
x: number;
|
|
434
|
+
y: number;
|
|
435
|
+
width: number;
|
|
436
|
+
height: number;
|
|
437
|
+
}): void;
|
|
438
|
+
/** Camera changes (wheel, drag-pan, a canvas resize) — the fluid clamp listens. */
|
|
439
|
+
onChange?(listener: (state: unknown) => void): () => void;
|
|
299
440
|
};
|
|
300
441
|
}
|
|
301
442
|
/**
|
|
@@ -316,7 +457,17 @@ export interface DashboardHandleContext {
|
|
|
316
457
|
groups: Map<string, GroupModel>;
|
|
317
458
|
binders: Map<string, DashboardGridHandle>;
|
|
318
459
|
specById: Map<string, DashboardWidgetSpec>;
|
|
460
|
+
/** Widget id → the BOARD that owns it (a view id, or a container id). */
|
|
319
461
|
viewOfWidget: Map<string, string>;
|
|
462
|
+
/** Every board group — the views PLUS every container. Views also live in
|
|
463
|
+
* `groups` (the parking map showView drives); containers deliberately do
|
|
464
|
+
* NOT — parking flings a group to OFFSCREEN_X, and a container must follow
|
|
465
|
+
* its parent, not travel on its own. */
|
|
466
|
+
boardGroups: Map<string, GroupModel>;
|
|
467
|
+
/** Board id → its authored widgets array (views and containers alike). */
|
|
468
|
+
boardWidgets: Map<string, DashboardWidgetSpec[]>;
|
|
469
|
+
/** Board id → the VIEW it belongs to (identity for views). */
|
|
470
|
+
viewOfBoard: Map<string, string>;
|
|
320
471
|
hosts: Map<string, HTMLElement>;
|
|
321
472
|
renderWidget: (widget: DashboardWidgetSpec, host: HTMLElement) => void;
|
|
322
473
|
columns: number;
|
|
@@ -324,6 +475,13 @@ export interface DashboardHandleContext {
|
|
|
324
475
|
rowHeight: number;
|
|
325
476
|
boardW: number;
|
|
326
477
|
boardH: number;
|
|
478
|
+
/** See DashboardOptions.mode / overflow. */
|
|
479
|
+
mode: 'fluid' | 'fixed';
|
|
480
|
+
overflow: 'bounded' | 'scroll';
|
|
481
|
+
/** See DashboardOptions.layout — per view. */
|
|
482
|
+
layoutOf: Map<string, 'grid' | 'split'>;
|
|
483
|
+
/** Set by finalize: re-bind a VIEW's board under the given layout (setLayout). */
|
|
484
|
+
rebindView?: (viewId: string, layout: 'grid' | 'split') => void;
|
|
327
485
|
/**
|
|
328
486
|
* Spread verbatim into `toJSON()` output — carries width/height/responsive
|
|
329
487
|
* and any other authored option so a new `DashboardOptions` field round-trips
|
|
@@ -335,6 +493,28 @@ export interface DashboardHandleContext {
|
|
|
335
493
|
active: string;
|
|
336
494
|
/** MUTABLE — set by the caller's finalize once the render API exists. */
|
|
337
495
|
apiRef: DashboardApiRef | null;
|
|
496
|
+
/** The consumer's layout hook, if any (dashboard() passes its option). */
|
|
497
|
+
onLayoutChange?: (viewId: string, widgets: DashboardWidgetSpec[]) => void;
|
|
498
|
+
/**
|
|
499
|
+
* Set by createDashboardHandle. `reportChanged()` fires `onLayoutChange` for
|
|
500
|
+
* every view whose layout differs from the last report — ONE reporter for
|
|
501
|
+
* pointer commits, API calls, undo/redo and column changes alike, so a
|
|
502
|
+
* consumer's autosave sees every change and never the same change twice.
|
|
503
|
+
* `attachHistory()` is what finalize calls once `apiRef` exists: it
|
|
504
|
+
* subscribes the boards to the command history so an undo re-syncs them
|
|
505
|
+
* without the consumer calling refresh().
|
|
506
|
+
*/
|
|
507
|
+
reportChanged?: () => void;
|
|
508
|
+
attachHistory?: () => void;
|
|
509
|
+
/** Unsubscribers dispose() runs. */
|
|
510
|
+
subscriptions?: Array<() => void>;
|
|
511
|
+
/**
|
|
512
|
+
* Re-bind a CONTAINER whose group came back through the history (undo of a
|
|
513
|
+
* container removal restores the group as a fresh GroupModel, which the old
|
|
514
|
+
* binder cannot see). Set by the two finalizes; called by the history
|
|
515
|
+
* handler for any board group the model holds without a binder.
|
|
516
|
+
*/
|
|
517
|
+
rebindContainer?: (id: string) => void;
|
|
338
518
|
}
|
|
339
519
|
/**
|
|
340
520
|
* Build THE `DashboardHandle` — the one and only implementation, shared by
|