@grafloria/element 0.4.2 → 0.4.4
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 +147 -1
- package/src/lib/dashboard-kit/dashboard.js +524 -113
- package/src/lib/dashboard-kit/grid-binder.d.ts +43 -0
- package/src/lib/dashboard-kit/grid-binder.js +719 -106
- 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 +1078 -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 +72 -8
|
@@ -43,8 +43,9 @@
|
|
|
43
43
|
* round-trips with no extra work — same as every other kit.
|
|
44
44
|
*/
|
|
45
45
|
import { __awaiter } from "tslib";
|
|
46
|
-
import {
|
|
46
|
+
import { BatchCommand, BringNodeToFrontCommand, Command, GroupModel, NodeModel, RemoveFromGroupCommand, RemoveGroupCommand, RemoveNodeCommand, SendNodeToBackCommand, } from '@grafloria/engine';
|
|
47
47
|
import { bindDashboardGrid, } from './grid-binder.js';
|
|
48
|
+
import { bindDashboardSplit, SPLIT_TREE_KEY } from './split-binder.js';
|
|
48
49
|
import { gridItemFromCell } from './grid-mapping.js';
|
|
49
50
|
import { ensureDashboardKitStyles } from './styles.js';
|
|
50
51
|
import { defaultWidgetRenderer } from './widgets.js';
|
|
@@ -60,26 +61,41 @@ import { defaultWidgetRenderer } from './widgets.js';
|
|
|
60
61
|
* unwinds both in undo().
|
|
61
62
|
*/
|
|
62
63
|
class AddWidgetCommand extends Command {
|
|
63
|
-
|
|
64
|
+
/**
|
|
65
|
+
* `registry` is the kit's bookkeeping for the widget (see
|
|
66
|
+
* RegisterWidgetCommand): it is applied INSIDE this command rather than in a
|
|
67
|
+
* batch beside it, because a batch runs its members across awaits and the
|
|
68
|
+
* node would reach the model a microtask after the caller's `addWidget()`
|
|
69
|
+
* returned — every consumer that read the model right after would have
|
|
70
|
+
* broken. `nodeWasInModel` covers re-adding a node that already exists
|
|
71
|
+
* (membership only), the case that used to be a separate AddToGroupCommand.
|
|
72
|
+
*/
|
|
73
|
+
constructor(node, groupId, registry, nodeWasInModel = false) {
|
|
64
74
|
super('Add widget');
|
|
65
75
|
this.node = node;
|
|
66
76
|
this.groupId = groupId;
|
|
77
|
+
this.registry = registry;
|
|
78
|
+
this.nodeWasInModel = nodeWasInModel;
|
|
67
79
|
}
|
|
68
80
|
execute(context) {
|
|
69
|
-
var _a;
|
|
81
|
+
var _a, _b;
|
|
70
82
|
const diagram = context.diagram;
|
|
71
83
|
if (!diagram)
|
|
72
84
|
return;
|
|
73
|
-
|
|
74
|
-
|
|
85
|
+
(_a = this.registry) === null || _a === void 0 ? void 0 : _a.register();
|
|
86
|
+
if (!this.nodeWasInModel && !diagram.getNode(this.node.id))
|
|
87
|
+
diagram.addNode(this.node);
|
|
88
|
+
(_b = diagram.getGroup(this.groupId)) === null || _b === void 0 ? void 0 : _b.addMember(this.node.id);
|
|
75
89
|
}
|
|
76
90
|
undo(context) {
|
|
77
|
-
var _a;
|
|
91
|
+
var _a, _b;
|
|
78
92
|
const diagram = context.diagram;
|
|
79
93
|
if (!diagram)
|
|
80
94
|
return;
|
|
81
95
|
(_a = diagram.getGroup(this.groupId)) === null || _a === void 0 ? void 0 : _a.removeMember(this.node.id);
|
|
82
|
-
|
|
96
|
+
if (!this.nodeWasInModel)
|
|
97
|
+
diagram.removeNode(this.node.id);
|
|
98
|
+
(_b = this.registry) === null || _b === void 0 ? void 0 : _b.unregister();
|
|
83
99
|
}
|
|
84
100
|
serialize() {
|
|
85
101
|
return {
|
|
@@ -90,6 +106,81 @@ class AddWidgetCommand extends Command {
|
|
|
90
106
|
};
|
|
91
107
|
}
|
|
92
108
|
}
|
|
109
|
+
/**
|
|
110
|
+
* The kit's BOOKKEEPING for a widget — its spec, its board, its slot in the
|
|
111
|
+
* authored array — travels through the history WITH the node.
|
|
112
|
+
*
|
|
113
|
+
* It did not, and that was D2 of the 2026-09-06 review: `remove()` deleted the
|
|
114
|
+
* spec synchronously while the removal itself was a command, so undo restored
|
|
115
|
+
* the node and the membership through the model and the painter — asked to
|
|
116
|
+
* paint a node whose id the kit no longer knew — returned without drawing. A
|
|
117
|
+
* blank host where the donut was, `widget(id)` undefined, not listed. The
|
|
118
|
+
* comment on AddWidgetCommand already warns that the painter needs the spec
|
|
119
|
+
* BEFORE the node reaches the model; the same holds on the way back in.
|
|
120
|
+
*
|
|
121
|
+
* `register`/`unregister` are idempotent so the handle may also apply them
|
|
122
|
+
* synchronously (execute() is async, and the caller reads the handle right
|
|
123
|
+
* after) without double-counting when the command runs.
|
|
124
|
+
*/
|
|
125
|
+
class RegisterWidgetCommand extends Command {
|
|
126
|
+
constructor(registry, direction) {
|
|
127
|
+
super(direction === 'register' ? 'Register widget' : 'Unregister widget');
|
|
128
|
+
this.registry = registry;
|
|
129
|
+
this.direction = direction;
|
|
130
|
+
}
|
|
131
|
+
execute() {
|
|
132
|
+
if (this.direction === 'register')
|
|
133
|
+
this.registry.register();
|
|
134
|
+
else
|
|
135
|
+
this.registry.unregister();
|
|
136
|
+
}
|
|
137
|
+
undo() {
|
|
138
|
+
if (this.direction === 'register')
|
|
139
|
+
this.registry.unregister();
|
|
140
|
+
else
|
|
141
|
+
this.registry.register();
|
|
142
|
+
}
|
|
143
|
+
serialize() {
|
|
144
|
+
return { id: this.id, name: this.name, timestamp: this.timestamp, data: { direction: this.direction } };
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Pin as ONE undoable step. `pin()` wrote the node's lock directly, which made
|
|
149
|
+
* it the only layout mutation outside the history (Ctrl-Z after a pin undid the
|
|
150
|
+
* gesture before it) and the reason toJSON() never saw it — D5. `before` is
|
|
151
|
+
* captured at construction so the handle can apply the lock synchronously and
|
|
152
|
+
* let the command re-apply it idempotently when it runs.
|
|
153
|
+
*/
|
|
154
|
+
class SetWidgetLockCommand extends Command {
|
|
155
|
+
constructor(nodeId, before, after) {
|
|
156
|
+
super(after ? 'Pin widget' : 'Unpin widget');
|
|
157
|
+
this.nodeId = nodeId;
|
|
158
|
+
this.before = before;
|
|
159
|
+
this.after = after;
|
|
160
|
+
}
|
|
161
|
+
apply(context, locked) {
|
|
162
|
+
var _a;
|
|
163
|
+
const diagram = context.diagram;
|
|
164
|
+
(_a = diagram === null || diagram === void 0 ? void 0 : diagram.getNode(this.nodeId)) === null || _a === void 0 ? void 0 : _a.setState({ locked });
|
|
165
|
+
}
|
|
166
|
+
execute(context) {
|
|
167
|
+
this.apply(context, this.after);
|
|
168
|
+
}
|
|
169
|
+
undo(context) {
|
|
170
|
+
this.apply(context, this.before);
|
|
171
|
+
}
|
|
172
|
+
serialize() {
|
|
173
|
+
return {
|
|
174
|
+
id: this.id,
|
|
175
|
+
name: this.name,
|
|
176
|
+
timestamp: this.timestamp,
|
|
177
|
+
data: { nodeId: this.nodeId, before: this.before, after: this.after },
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
/** The history events a layout can change on — the engine's DiagramEventTypes
|
|
182
|
+
* values, spelled out so the kit needs no import from the engine's type bag. */
|
|
183
|
+
const HISTORY_EVENTS = ['command:executed', 'command:undone', 'command:redone'];
|
|
93
184
|
const DEFAULTS = { columns: 12, gap: 8, rowHeight: 130, width: 1180, height: 660 };
|
|
94
185
|
const OFFSCREEN_X = -20000;
|
|
95
186
|
let autoId = 0;
|
|
@@ -115,6 +206,15 @@ function buildWidgetNode(w, rowHeight) {
|
|
|
115
206
|
node.setMetadata('widgetTitle', w.title);
|
|
116
207
|
node.setMetadata('columnSpan', (_c = w.span) !== null && _c !== void 0 ? _c : 3);
|
|
117
208
|
node.setMetadata('rowSpan', (_d = w.rows) !== null && _d !== void 0 ? _d : 1);
|
|
209
|
+
// Limits and the two pointer flags reach the node too, so the binder reads
|
|
210
|
+
// them and a reload rebuilds them (only when authored — an unconstrained
|
|
211
|
+
// widget serialises exactly as it always did).
|
|
212
|
+
if (w.limits !== undefined)
|
|
213
|
+
node.setMetadata('widgetLimits', Object.assign({}, w.limits));
|
|
214
|
+
if (w.movable === false)
|
|
215
|
+
node.setMetadata('widgetMovable', false);
|
|
216
|
+
if (w.resizable === false)
|
|
217
|
+
node.setMetadata('widgetResizable', false);
|
|
118
218
|
if (w.x !== undefined && w.y !== undefined) {
|
|
119
219
|
node.setGridItem({
|
|
120
220
|
columnStart: w.x + 1,
|
|
@@ -157,13 +257,24 @@ function assignCellsDeep(widgets, columns) {
|
|
|
157
257
|
}
|
|
158
258
|
/** Flow widgets that declared no cell: left-to-right, wrapping at `columns`. */
|
|
159
259
|
function assignCells(widgets, columns) {
|
|
160
|
-
var _a, _b;
|
|
260
|
+
var _a, _b, _c;
|
|
161
261
|
let x = 0;
|
|
162
262
|
let y = 0;
|
|
163
263
|
let rowMax = 0;
|
|
164
264
|
for (const w of widgets) {
|
|
165
|
-
const
|
|
166
|
-
|
|
265
|
+
const lim = (_a = w.limits) !== null && _a !== void 0 ? _a : {};
|
|
266
|
+
let span = Math.max(1, Math.min(columns, (_b = w.span) !== null && _b !== void 0 ? _b : 3));
|
|
267
|
+
if (lim.minSpan !== undefined)
|
|
268
|
+
span = Math.max(span, lim.minSpan);
|
|
269
|
+
if (lim.maxSpan !== undefined)
|
|
270
|
+
span = Math.min(span, lim.maxSpan);
|
|
271
|
+
span = Math.max(1, Math.min(columns, span));
|
|
272
|
+
let rows = Math.max(1, (_c = w.rows) !== null && _c !== void 0 ? _c : 1);
|
|
273
|
+
if (lim.minRows !== undefined)
|
|
274
|
+
rows = Math.max(rows, lim.minRows);
|
|
275
|
+
if (lim.maxRows !== undefined)
|
|
276
|
+
rows = Math.min(rows, lim.maxRows);
|
|
277
|
+
rows = Math.max(1, rows);
|
|
167
278
|
if (w.x === undefined || w.y === undefined) {
|
|
168
279
|
if (x + span > columns) {
|
|
169
280
|
x = 0;
|
|
@@ -187,6 +298,58 @@ function assignCells(widgets, columns) {
|
|
|
187
298
|
export function createDashboardHandle(ctx) {
|
|
188
299
|
const { views, groups, binders, specById, viewOfWidget } = ctx;
|
|
189
300
|
const hostOf = (id) => ctx.hosts.get(id);
|
|
301
|
+
/**
|
|
302
|
+
* Put the camera on a view. FLUID: the board IS the container, so the camera
|
|
303
|
+
* sits at zoom 1 with the board's origin at the top-left — never a fit, which
|
|
304
|
+
* is what made a dashboard a scaled picture (D1). FIXED: frame the board.
|
|
305
|
+
*/
|
|
306
|
+
const frameView = (g) => {
|
|
307
|
+
var _a, _b, _c;
|
|
308
|
+
const vp = (_a = ctx.apiRef) === null || _a === void 0 ? void 0 : _a.viewport;
|
|
309
|
+
if (!vp)
|
|
310
|
+
return;
|
|
311
|
+
const gs = (_b = g.size) !== null && _b !== void 0 ? _b : { width: ctx.boardW, height: ctx.boardH };
|
|
312
|
+
if (ctx.mode === 'fluid' && vp.setViewport && vp.getViewport) {
|
|
313
|
+
(_c = vp.setZoom) === null || _c === void 0 ? void 0 : _c.call(vp, 1);
|
|
314
|
+
const cur = vp.getViewport();
|
|
315
|
+
vp.setViewport({ x: g.position.x, y: g.position.y, width: cur.width, height: cur.height });
|
|
316
|
+
return;
|
|
317
|
+
}
|
|
318
|
+
vp.fitToBounds({ x: g.position.x, y: g.position.y, width: gs.width, height: gs.height }, 26, { maxZoom: 1 });
|
|
319
|
+
};
|
|
320
|
+
/**
|
|
321
|
+
* FLUID: the camera is a SCROLL POSITION over the board, never a free pan.
|
|
322
|
+
* Bounded to the active board's frame — the page-scroll model of every grid
|
|
323
|
+
* library — so a wheel cannot run past the last row, and a frame that
|
|
324
|
+
* shrinks under a scrolled camera (Fit after a scroll in Grow; an undo that
|
|
325
|
+
* removes rows) pulls the camera back into the board. Found by the user
|
|
326
|
+
* switching Fit and Grow on the live page: Fit shrank the board to the
|
|
327
|
+
* canvas while the camera stayed 600 px down — the top half of the board
|
|
328
|
+
* out of view above an empty canvas. FIXED boards are diagrams and pan free.
|
|
329
|
+
*/
|
|
330
|
+
let clamping = false;
|
|
331
|
+
const clampCamera = () => {
|
|
332
|
+
var _a, _b;
|
|
333
|
+
if (ctx.mode !== 'fluid' || clamping)
|
|
334
|
+
return;
|
|
335
|
+
const vp = (_a = ctx.apiRef) === null || _a === void 0 ? void 0 : _a.viewport;
|
|
336
|
+
const g = groups.get(ctx.active);
|
|
337
|
+
if (!(vp === null || vp === void 0 ? void 0 : vp.getViewport) || !vp.setViewport || !g)
|
|
338
|
+
return;
|
|
339
|
+
const cur = vp.getViewport();
|
|
340
|
+
const gs = (_b = g.size) !== null && _b !== void 0 ? _b : { width: ctx.boardW, height: ctx.boardH };
|
|
341
|
+
const x = Math.min(Math.max(cur.x, g.position.x), g.position.x + Math.max(0, gs.width - cur.width));
|
|
342
|
+
const y = Math.min(Math.max(cur.y, g.position.y), g.position.y + Math.max(0, gs.height - cur.height));
|
|
343
|
+
if (Math.abs(x - cur.x) < 0.5 && Math.abs(y - cur.y) < 0.5)
|
|
344
|
+
return;
|
|
345
|
+
clamping = true;
|
|
346
|
+
try {
|
|
347
|
+
vp.setViewport({ x, y, width: cur.width, height: cur.height });
|
|
348
|
+
}
|
|
349
|
+
finally {
|
|
350
|
+
clamping = false;
|
|
351
|
+
}
|
|
352
|
+
};
|
|
190
353
|
/**
|
|
191
354
|
* A board's widgets as a NESTED tree, derived from LIVE membership — not the
|
|
192
355
|
* authored arrays. A cross-boundary drag moves membership through commands
|
|
@@ -195,7 +358,7 @@ export function createDashboardHandle(ctx) {
|
|
|
195
358
|
* tile under the container it is actually in, in every one of those states.
|
|
196
359
|
*/
|
|
197
360
|
const treeOf = (boardId) => {
|
|
198
|
-
var _a;
|
|
361
|
+
var _a, _b, _c, _d;
|
|
199
362
|
const g = ctx.boardGroups.get(boardId);
|
|
200
363
|
const b = binders.get(boardId);
|
|
201
364
|
if (!g)
|
|
@@ -213,12 +376,97 @@ export function createDashboardHandle(ctx) {
|
|
|
213
376
|
entries.push(Object.assign(Object.assign(Object.assign({ id: memberId }, (spec !== null && spec !== void 0 ? spec : {})), at), { widgets: treeOf(memberId) }));
|
|
214
377
|
}
|
|
215
378
|
else if (spec) {
|
|
216
|
-
|
|
379
|
+
// `pinned` is read from the NODE's lock, not the authored spec: pin()
|
|
380
|
+
// changes the node, and a saved board must come back pinned the way
|
|
381
|
+
// the user left it (D5). Written only when true, so an unpinned
|
|
382
|
+
// widget serialises exactly as it always did.
|
|
383
|
+
const entry = Object.assign(Object.assign({}, spec), at);
|
|
384
|
+
if (((_d = (_c = (_b = ctx.apiRef) === null || _b === void 0 ? void 0 : _b.getModel().getNode(memberId)) === null || _c === void 0 ? void 0 : _c.state) === null || _d === void 0 ? void 0 : _d.locked) === true)
|
|
385
|
+
entry.pinned = true;
|
|
386
|
+
else
|
|
387
|
+
delete entry.pinned;
|
|
388
|
+
entries.push(entry);
|
|
217
389
|
}
|
|
218
390
|
}
|
|
219
391
|
entries.sort((p1, p2) => { var _a, _b, _c, _d; return ((_a = p1.y) !== null && _a !== void 0 ? _a : 0) - ((_b = p2.y) !== null && _b !== void 0 ? _b : 0) || ((_c = p1.x) !== null && _c !== void 0 ? _c : 0) - ((_d = p2.x) !== null && _d !== void 0 ? _d : 0); });
|
|
220
392
|
return entries;
|
|
221
393
|
};
|
|
394
|
+
/** Bookkeeping closures for one widget, shared by the add and remove
|
|
395
|
+
* commands (and applied synchronously by the handle — idempotent). */
|
|
396
|
+
const registryOf = (id, boardId, spec) => {
|
|
397
|
+
let slot = -1;
|
|
398
|
+
return {
|
|
399
|
+
register: () => {
|
|
400
|
+
specById.set(id, spec);
|
|
401
|
+
viewOfWidget.set(id, boardId);
|
|
402
|
+
const arr = ctx.boardWidgets.get(boardId);
|
|
403
|
+
if (arr && !arr.some((w) => w.id === id)) {
|
|
404
|
+
arr.splice(slot < 0 ? arr.length : Math.min(slot, arr.length), 0, spec);
|
|
405
|
+
}
|
|
406
|
+
},
|
|
407
|
+
unregister: () => {
|
|
408
|
+
specById.delete(id);
|
|
409
|
+
viewOfWidget.delete(id);
|
|
410
|
+
const arr = ctx.boardWidgets.get(boardId);
|
|
411
|
+
if (arr) {
|
|
412
|
+
const i = arr.findIndex((w) => w.id === id);
|
|
413
|
+
if (i >= 0) {
|
|
414
|
+
slot = i; // remembered so undo puts it back where it was
|
|
415
|
+
arr.splice(i, 1);
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
},
|
|
419
|
+
};
|
|
420
|
+
};
|
|
421
|
+
// ONE reporter for every path that changes a layout (D3). Diff-based: a
|
|
422
|
+
// pointer commit reports synchronously, the history event that follows finds
|
|
423
|
+
// nothing new and stays quiet.
|
|
424
|
+
const lastReported = new Map();
|
|
425
|
+
const reportChanged = () => {
|
|
426
|
+
if (!ctx.onLayoutChange || !ctx.apiRef)
|
|
427
|
+
return;
|
|
428
|
+
for (const v of handle.toJSON().views) {
|
|
429
|
+
const key = JSON.stringify(v.widgets);
|
|
430
|
+
if (lastReported.get(v.id) === key)
|
|
431
|
+
continue;
|
|
432
|
+
lastReported.set(v.id, key);
|
|
433
|
+
ctx.onLayoutChange(v.id, v.widgets);
|
|
434
|
+
}
|
|
435
|
+
};
|
|
436
|
+
ctx.reportChanged = reportChanged;
|
|
437
|
+
// The boards follow the HISTORY, not the consumer's memory of it: after any
|
|
438
|
+
// command lands, is undone or redone, every binder re-reads the model and the
|
|
439
|
+
// reporter runs. This is what retires "call refresh() after undo".
|
|
440
|
+
ctx.attachHistory = () => {
|
|
441
|
+
var _a, _b, _c, _d, _e;
|
|
442
|
+
const bus = (_c = (_b = (_a = ctx.apiRef) === null || _a === void 0 ? void 0 : _a.getEngine) === null || _b === void 0 ? void 0 : _b.call(_a)) === null || _c === void 0 ? void 0 : _c.eventBus;
|
|
443
|
+
if (!bus)
|
|
444
|
+
return;
|
|
445
|
+
// Prime the reporter so the boot layout is never reported as a change.
|
|
446
|
+
for (const v of handle.toJSON().views)
|
|
447
|
+
lastReported.set(v.id, JSON.stringify(v.widgets));
|
|
448
|
+
const onHistory = () => {
|
|
449
|
+
var _a;
|
|
450
|
+
if (!ctx.apiRef)
|
|
451
|
+
return;
|
|
452
|
+
const model = ctx.apiRef.getModel();
|
|
453
|
+
for (const id of [...ctx.boardGroups.keys()]) {
|
|
454
|
+
if (!binders.has(id) && model.getGroup(id))
|
|
455
|
+
(_a = ctx.rebindContainer) === null || _a === void 0 ? void 0 : _a.call(ctx, id);
|
|
456
|
+
}
|
|
457
|
+
for (const b of binders.values())
|
|
458
|
+
b.sync();
|
|
459
|
+
clampCamera();
|
|
460
|
+
ctx.apiRef.renderNow();
|
|
461
|
+
reportChanged();
|
|
462
|
+
};
|
|
463
|
+
ctx.subscriptions = (_d = ctx.subscriptions) !== null && _d !== void 0 ? _d : [];
|
|
464
|
+
for (const ev of HISTORY_EVENTS)
|
|
465
|
+
ctx.subscriptions.push(bus.on(ev, onHistory));
|
|
466
|
+
const vp = (_e = ctx.apiRef) === null || _e === void 0 ? void 0 : _e.viewport;
|
|
467
|
+
if (ctx.mode === 'fluid' && (vp === null || vp === void 0 ? void 0 : vp.onChange))
|
|
468
|
+
ctx.subscriptions.push(vp.onChange(() => clampCamera()));
|
|
469
|
+
};
|
|
222
470
|
const execCommand = (cmd) => {
|
|
223
471
|
var _a, _b, _c, _d;
|
|
224
472
|
try {
|
|
@@ -239,7 +487,7 @@ export function createDashboardHandle(ctx) {
|
|
|
239
487
|
return ctx.active;
|
|
240
488
|
},
|
|
241
489
|
showView(id) {
|
|
242
|
-
var _a, _b, _c
|
|
490
|
+
var _a, _b, _c;
|
|
243
491
|
if (!groups.has(id))
|
|
244
492
|
return;
|
|
245
493
|
ctx.active = id;
|
|
@@ -251,9 +499,7 @@ export function createDashboardHandle(ctx) {
|
|
|
251
499
|
}
|
|
252
500
|
(_b = binders.get(id)) === null || _b === void 0 ? void 0 : _b.sync();
|
|
253
501
|
(_c = ctx.apiRef) === null || _c === void 0 ? void 0 : _c.renderNow();
|
|
254
|
-
|
|
255
|
-
const gs = (_d = g.size) !== null && _d !== void 0 ? _d : { width: ctx.boardW, height: ctx.boardH };
|
|
256
|
-
(_f = (_e = ctx.apiRef) === null || _e === void 0 ? void 0 : _e.viewport) === null || _f === void 0 ? void 0 : _f.fitToBounds({ x: g.position.x, y: g.position.y, width: gs.width, height: gs.height }, 26, { maxZoom: 1 });
|
|
502
|
+
frameView(groups.get(id));
|
|
257
503
|
},
|
|
258
504
|
widget(id) {
|
|
259
505
|
return makeWidgetHandle(id);
|
|
@@ -263,18 +509,33 @@ export function createDashboardHandle(ctx) {
|
|
|
263
509
|
const v = views.find((x) => x.id === (viewId !== null && viewId !== void 0 ? viewId : ctx.active));
|
|
264
510
|
return ((_a = v === null || v === void 0 ? void 0 : v.widgets) !== null && _a !== void 0 ? _a : []).map((w) => makeWidgetHandle(w.id)).filter(Boolean);
|
|
265
511
|
},
|
|
512
|
+
setLayout(layout, viewId) {
|
|
513
|
+
var _a, _b, _c;
|
|
514
|
+
const vid = viewId !== null && viewId !== void 0 ? viewId : ctx.active;
|
|
515
|
+
if (!views.some((v) => v.id === vid))
|
|
516
|
+
return;
|
|
517
|
+
if (((_a = ctx.layoutOf.get(vid)) !== null && _a !== void 0 ? _a : 'grid') === layout)
|
|
518
|
+
return;
|
|
519
|
+
(_b = ctx.rebindView) === null || _b === void 0 ? void 0 : _b.call(ctx, vid, layout);
|
|
520
|
+
clampCamera();
|
|
521
|
+
(_c = ctx.apiRef) === null || _c === void 0 ? void 0 : _c.renderNow();
|
|
522
|
+
reportChanged();
|
|
523
|
+
},
|
|
524
|
+
getLayout: (viewId) => { var _a; return (_a = ctx.layoutOf.get(viewId !== null && viewId !== void 0 ? viewId : ctx.active)) !== null && _a !== void 0 ? _a : 'grid'; },
|
|
266
525
|
setSizing(mode) {
|
|
267
526
|
var _a;
|
|
268
527
|
for (const b of binders.values())
|
|
269
528
|
b.setSizing(mode);
|
|
529
|
+
clampCamera();
|
|
270
530
|
(_a = ctx.apiRef) === null || _a === void 0 ? void 0 : _a.renderNow();
|
|
271
531
|
},
|
|
272
|
-
getSizing: () => { var _a, _b, _c; return (_b = (_a = binders.get(ctx.active)) === null || _a === void 0 ? void 0 : _a.getSizing()) !== null && _b !== void 0 ? _b :
|
|
532
|
+
getSizing: () => { var _a, _b, _c; return (_c = (_b = (_a = binders.get(ctx.active)) === null || _a === void 0 ? void 0 : _a.getSizing()) !== null && _b !== void 0 ? _b : ctx.optionsBase.sizing) !== null && _c !== void 0 ? _c : (ctx.mode === 'fluid' ? 'grow' : 'fit'); },
|
|
273
533
|
setFloat(on) {
|
|
274
534
|
var _a;
|
|
275
535
|
for (const b of binders.values())
|
|
276
536
|
b.setFloat(on);
|
|
277
537
|
(_a = ctx.apiRef) === null || _a === void 0 ? void 0 : _a.renderNow();
|
|
538
|
+
reportChanged(); // gravity re-packs when float turns off
|
|
278
539
|
},
|
|
279
540
|
getFloat: () => { var _a, _b, _c; return (_b = (_a = binders.get(ctx.active)) === null || _a === void 0 ? void 0 : _a.getFloat()) !== null && _b !== void 0 ? _b : ((_c = ctx.optionsBase.float) !== null && _c !== void 0 ? _c : false); },
|
|
280
541
|
setColumns(n, layout, viewId) {
|
|
@@ -283,6 +544,7 @@ export function createDashboardHandle(ctx) {
|
|
|
283
544
|
for (const b of targets)
|
|
284
545
|
b === null || b === void 0 ? void 0 : b.setColumns(n, layout);
|
|
285
546
|
(_a = ctx.apiRef) === null || _a === void 0 ? void 0 : _a.renderNow();
|
|
547
|
+
reportChanged(); // derived state, never a command — report it here
|
|
286
548
|
},
|
|
287
549
|
getColumns: (viewId) => { var _a, _b; return (_b = (_a = binders.get(viewId !== null && viewId !== void 0 ? viewId : ctx.active)) === null || _a === void 0 ? void 0 : _a.getColumns()) !== null && _b !== void 0 ? _b : ctx.columns; },
|
|
288
550
|
setRtl(on) {
|
|
@@ -292,8 +554,15 @@ export function createDashboardHandle(ctx) {
|
|
|
292
554
|
(_a = ctx.apiRef) === null || _a === void 0 ? void 0 : _a.renderNow();
|
|
293
555
|
},
|
|
294
556
|
getRtl: () => { var _a, _b, _c; return (_b = (_a = binders.get(ctx.active)) === null || _a === void 0 ? void 0 : _a.getRtl()) !== null && _b !== void 0 ? _b : ((_c = ctx.optionsBase.rtl) !== null && _c !== void 0 ? _c : false); },
|
|
557
|
+
setStatic(on) {
|
|
558
|
+
var _a;
|
|
559
|
+
for (const b of binders.values())
|
|
560
|
+
b.setStatic(on);
|
|
561
|
+
(_a = ctx.apiRef) === null || _a === void 0 ? void 0 : _a.renderNow();
|
|
562
|
+
},
|
|
563
|
+
getStatic: () => { var _a, _b, _c; return (_b = (_a = binders.get(ctx.active)) === null || _a === void 0 ? void 0 : _a.getStatic()) !== null && _b !== void 0 ? _b : ((_c = ctx.optionsBase.static) !== null && _c !== void 0 ? _c : false); },
|
|
295
564
|
addWidget(spec, viewId) {
|
|
296
|
-
var _a, _b, _c, _d, _e;
|
|
565
|
+
var _a, _b, _c, _d, _e, _f;
|
|
297
566
|
const vid = viewId !== null && viewId !== void 0 ? viewId : ctx.active;
|
|
298
567
|
// `vid` may name a view OR a container — both are boards with a group,
|
|
299
568
|
// a binder and an authored array.
|
|
@@ -306,17 +575,22 @@ export function createDashboardHandle(ctx) {
|
|
|
306
575
|
// REGISTER FIRST: a custom node mounts exactly once, and the painter
|
|
307
576
|
// returns early for an id the spec does not know — so the widget must be
|
|
308
577
|
// known before the node reaches the model, or it paints blank forever.
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
578
|
+
// The registration ALSO rides in the batch, so undo un-lists the widget
|
|
579
|
+
// and redo lists it again before the node comes back.
|
|
580
|
+
// A bounded fit board with no room says so HERE, before anything is
|
|
581
|
+
// created: undefined, the same answer as an unknown board.
|
|
582
|
+
if (((_d = binders.get(vid)) === null || _d === void 0 ? void 0 : _d.willItFit(w.span, w.rows)) === false)
|
|
583
|
+
return undefined;
|
|
584
|
+
const registry = registryOf(w.id, vid, w);
|
|
585
|
+
registry.register();
|
|
312
586
|
const existing = model.getNode(w.id);
|
|
313
587
|
const node = existing !== null && existing !== void 0 ? existing : buildWidgetNode(w, ctx.rowHeight);
|
|
314
588
|
if (w.pinned)
|
|
315
589
|
node.setState({ locked: true });
|
|
316
|
-
// ONE undoable step (see AddWidgetCommand
|
|
317
|
-
execCommand(
|
|
318
|
-
(
|
|
319
|
-
(
|
|
590
|
+
// ONE undoable step, registration included (see AddWidgetCommand).
|
|
591
|
+
execCommand(new AddWidgetCommand(node, group.id, registry, !!existing));
|
|
592
|
+
(_e = binders.get(vid)) === null || _e === void 0 ? void 0 : _e.sync();
|
|
593
|
+
(_f = ctx.apiRef) === null || _f === void 0 ? void 0 : _f.renderNow();
|
|
320
594
|
return makeWidgetHandle(w.id);
|
|
321
595
|
},
|
|
322
596
|
refresh() {
|
|
@@ -326,12 +600,9 @@ export function createDashboardHandle(ctx) {
|
|
|
326
600
|
(_a = ctx.apiRef) === null || _a === void 0 ? void 0 : _a.renderNow();
|
|
327
601
|
},
|
|
328
602
|
fit(viewId) {
|
|
329
|
-
var _a, _b, _c;
|
|
330
603
|
const g = groups.get(viewId !== null && viewId !== void 0 ? viewId : ctx.active);
|
|
331
|
-
if (
|
|
332
|
-
|
|
333
|
-
const gs = (_a = g.size) !== null && _a !== void 0 ? _a : { width: ctx.boardW, height: ctx.boardH };
|
|
334
|
-
(_c = (_b = ctx.apiRef) === null || _b === void 0 ? void 0 : _b.viewport) === null || _c === void 0 ? void 0 : _c.fitToBounds({ x: g.position.x, y: g.position.y, width: gs.width, height: gs.height }, 26, { maxZoom: 1 });
|
|
604
|
+
if (g)
|
|
605
|
+
frameView(g);
|
|
335
606
|
},
|
|
336
607
|
metrics(viewId) {
|
|
337
608
|
var _a;
|
|
@@ -371,34 +642,47 @@ export function createDashboardHandle(ctx) {
|
|
|
371
642
|
// layout its user authored — and the view's `columns` is that count, so
|
|
372
643
|
// feeding this straight back into dashboard() rebuilds the wide board.
|
|
373
644
|
const savedViews = views.map((v) => {
|
|
374
|
-
var _a, _b, _c, _d;
|
|
645
|
+
var _a, _b, _c, _d, _e;
|
|
375
646
|
const saved = (_a = binders.get(v.id)) === null || _a === void 0 ? void 0 : _a.saveLayout();
|
|
376
647
|
const live = treeOf(v.id);
|
|
377
|
-
|
|
648
|
+
const layout = (_b = ctx.layoutOf.get(v.id)) !== null && _b !== void 0 ? _b : 'grid';
|
|
649
|
+
const split = binders.get(v.id);
|
|
650
|
+
const tree = layout === 'split' && (split === null || split === void 0 ? void 0 : split.getSplitTree) ? split.getSplitTree() : undefined;
|
|
651
|
+
return Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({}, v), (saved ? { columns: saved.columns } : {})), { layout }), (tree !== undefined ? { tree } : {})), {
|
|
378
652
|
// Live membership when the view is mounted; the authored tree before
|
|
379
653
|
// finalize (a spec serialised without ever rendering keeps its shape).
|
|
380
|
-
widgets: live.length > 0 || ((
|
|
654
|
+
widgets: live.length > 0 || ((_e = (_d = (_c = ctx.boardGroups.get(v.id)) === null || _c === void 0 ? void 0 : _c.members) === null || _d === void 0 ? void 0 : _d.size) !== null && _e !== void 0 ? _e : 0) > 0 ? live : v.widgets.map((w) => (Object.assign({}, w))) });
|
|
381
655
|
});
|
|
382
656
|
// Board options come off the LIVE board wherever the handle can see it —
|
|
383
657
|
// `sizing` and `float` are the two a user changes from the toolbar, and
|
|
384
658
|
// reading them from the authored literal would restore the board they
|
|
385
659
|
// started with rather than the one they are looking at.
|
|
386
|
-
return Object.assign(Object.assign({}, ctx.optionsBase), { renderWidget: undefined, onLayoutChange: undefined, columns: ctx.columns, gap: ctx.gap, rowHeight: ctx.rowHeight, sizing: handle.getSizing(), float: handle.getFloat(), rtl: handle.getRtl(), views: savedViews });
|
|
660
|
+
return Object.assign(Object.assign({}, ctx.optionsBase), { renderWidget: undefined, onLayoutChange: undefined, columns: ctx.columns, gap: ctx.gap, rowHeight: ctx.rowHeight, mode: ctx.mode, overflow: ctx.overflow, sizing: handle.getSizing(), float: handle.getFloat(), rtl: handle.getRtl(), static: handle.getStatic(), layout: handle.getLayout(), views: savedViews });
|
|
387
661
|
},
|
|
388
662
|
dispose() {
|
|
389
|
-
var _a, _b, _c;
|
|
663
|
+
var _a, _b, _c, _d, _e;
|
|
664
|
+
for (const off of (_a = ctx.subscriptions) !== null && _a !== void 0 ? _a : [])
|
|
665
|
+
off();
|
|
666
|
+
ctx.subscriptions = [];
|
|
390
667
|
for (const b of binders.values())
|
|
391
668
|
b.dispose();
|
|
392
669
|
binders.clear();
|
|
393
670
|
// The groups finalize() created are ours to clean up — leaving them
|
|
394
671
|
// behind made a rebuild stack a second set of boards on the first.
|
|
395
|
-
const model = (
|
|
672
|
+
const model = (_b = ctx.apiRef) === null || _b === void 0 ? void 0 : _b.getModel();
|
|
673
|
+
// …and so are the widget NODES (D9): they used to stay behind with live
|
|
674
|
+
// hosts, so a consumer switching dashboards in one canvas accumulated
|
|
675
|
+
// orphans, and a rebuild with the same ids re-added nodes that existed.
|
|
676
|
+
for (const id of specById.keys()) {
|
|
677
|
+
if (!ctx.boardGroups.has(id))
|
|
678
|
+
(_c = model === null || model === void 0 ? void 0 : model.removeNode) === null || _c === void 0 ? void 0 : _c.call(model, id);
|
|
679
|
+
}
|
|
396
680
|
// Deepest first: a container group removed after its parent is an orphan
|
|
397
681
|
// the model never saw inside a board.
|
|
398
682
|
for (const id of [...ctx.boardGroups.keys()].reverse())
|
|
399
|
-
(
|
|
683
|
+
(_d = model === null || model === void 0 ? void 0 : model.removeGroup) === null || _d === void 0 ? void 0 : _d.call(model, id);
|
|
400
684
|
for (const id of groups.keys())
|
|
401
|
-
(
|
|
685
|
+
(_e = model === null || model === void 0 ? void 0 : model.removeGroup) === null || _e === void 0 ? void 0 : _e.call(model, id);
|
|
402
686
|
groups.clear();
|
|
403
687
|
ctx.boardGroups.clear();
|
|
404
688
|
ctx.hosts.clear();
|
|
@@ -449,12 +733,20 @@ export function createDashboardHandle(ctx) {
|
|
|
449
733
|
const n = node();
|
|
450
734
|
if (!n)
|
|
451
735
|
return;
|
|
452
|
-
|
|
736
|
+
const before = ((_a = n.state) === null || _a === void 0 ? void 0 : _a.locked) === true;
|
|
737
|
+
const after = on !== null && on !== void 0 ? on : !before;
|
|
738
|
+
if (after === before)
|
|
739
|
+
return;
|
|
740
|
+
// Applied now so the handle reads back correctly at once, AND recorded
|
|
741
|
+
// as one undoable step (D5) — the command re-applies idempotently.
|
|
742
|
+
n.setState({ locked: after });
|
|
743
|
+
execCommand(new SetWidgetLockCommand(id, before, after));
|
|
453
744
|
// Re-sync so the ENGINE's locked flag (never pushed, drags refused)
|
|
454
745
|
// and the hidden corner handle take effect on this frame, not the next
|
|
455
746
|
// gesture.
|
|
456
747
|
(_b = binder()) === null || _b === void 0 ? void 0 : _b.sync();
|
|
457
748
|
(_c = ctx.apiRef) === null || _c === void 0 ? void 0 : _c.renderNow();
|
|
749
|
+
reportChanged();
|
|
458
750
|
},
|
|
459
751
|
bringToFront() {
|
|
460
752
|
var _a;
|
|
@@ -478,12 +770,55 @@ export function createDashboardHandle(ctx) {
|
|
|
478
770
|
ctx.renderWidget(spec, host);
|
|
479
771
|
},
|
|
480
772
|
remove(displaced) {
|
|
481
|
-
var _a, _b;
|
|
773
|
+
var _a, _b, _c, _d, _e, _f;
|
|
482
774
|
if (spec.widgets) {
|
|
483
|
-
// A
|
|
484
|
-
//
|
|
485
|
-
//
|
|
486
|
-
|
|
775
|
+
// A CONTAINER (review D12): its subtree, its own group, its slab in
|
|
776
|
+
// the parent board and the parent's re-pack, as ONE undoable batch.
|
|
777
|
+
// Undo restores the groups as fresh GroupModels, so the history
|
|
778
|
+
// handler re-binds the container's grid (ctx.rebindContainer).
|
|
779
|
+
const parentGroup = ctx.boardGroups.get(viewId);
|
|
780
|
+
const parentBinder = binders.get(viewId);
|
|
781
|
+
const model = (_a = ctx.apiRef) === null || _a === void 0 ? void 0 : _a.getModel();
|
|
782
|
+
if (!parentGroup || !parentBinder || !model)
|
|
783
|
+
return;
|
|
784
|
+
// ORDER MATTERS FOR UNDO. A batch may only undo when EVERY member
|
|
785
|
+
// can, checked before any of them runs — and RemoveFromGroupCommand
|
|
786
|
+
// can undo only while its group exists. So no membership commands
|
|
787
|
+
// for the members of a group that is going: the group is removed
|
|
788
|
+
// FIRST (its serialized members ride with it and come back on undo),
|
|
789
|
+
// its nodes after; on undo the nodes return, then the group with its
|
|
790
|
+
// membership, then the slab's membership in the parent.
|
|
791
|
+
const cmds = [
|
|
792
|
+
...((_b = displaced) !== null && _b !== void 0 ? _b : parentBinder.planRemoval(id)),
|
|
793
|
+
new RemoveFromGroupCommand(parentGroup.id, id),
|
|
794
|
+
];
|
|
795
|
+
const nodeRemovals = [];
|
|
796
|
+
const unregisters = [];
|
|
797
|
+
const removeSubtree = (boardId) => {
|
|
798
|
+
var _a, _b;
|
|
799
|
+
const g = ctx.boardGroups.get(boardId);
|
|
800
|
+
cmds.push(new RemoveGroupCommand(boardId));
|
|
801
|
+
for (const m of [...((_a = g === null || g === void 0 ? void 0 : g.members) !== null && _a !== void 0 ? _a : [])]) {
|
|
802
|
+
const mSpec = specById.get(m);
|
|
803
|
+
if (mSpec)
|
|
804
|
+
unregisters.push(new RegisterWidgetCommand(registryOf(m, boardId, mSpec), 'unregister'));
|
|
805
|
+
if (ctx.boardGroups.has(m))
|
|
806
|
+
removeSubtree(m);
|
|
807
|
+
else
|
|
808
|
+
nodeRemovals.push(new RemoveNodeCommand(m));
|
|
809
|
+
(_b = binders.get(m)) === null || _b === void 0 ? void 0 : _b.dispose();
|
|
810
|
+
binders.delete(m);
|
|
811
|
+
}
|
|
812
|
+
};
|
|
813
|
+
removeSubtree(id);
|
|
814
|
+
const registry = registryOf(id, viewId, spec);
|
|
815
|
+
cmds.push(...nodeRemovals, ...unregisters, new RegisterWidgetCommand(registry, 'unregister'));
|
|
816
|
+
(_c = binders.get(id)) === null || _c === void 0 ? void 0 : _c.dispose();
|
|
817
|
+
binders.delete(id);
|
|
818
|
+
void execCommand(new BatchCommand('Remove section', cmds));
|
|
819
|
+
registry.unregister();
|
|
820
|
+
parentBinder.sync();
|
|
821
|
+
(_d = ctx.apiRef) === null || _d === void 0 ? void 0 : _d.renderNow();
|
|
487
822
|
return;
|
|
488
823
|
}
|
|
489
824
|
const n = node();
|
|
@@ -496,19 +831,21 @@ export function createDashboardHandle(ctx) {
|
|
|
496
831
|
// computed the survivors passes them in: after a drag-out the tile is
|
|
497
832
|
// gone from the engine, so planRemoval() would return [] and the
|
|
498
833
|
// survivors' cells would never commit.
|
|
499
|
-
const survivors = (
|
|
500
|
-
|
|
834
|
+
const survivors = (_e = displaced) !== null && _e !== void 0 ? _e : b.planRemoval(id);
|
|
835
|
+
// The un-registration is the LAST command so that undo — which runs
|
|
836
|
+
// the batch in reverse — re-registers the spec BEFORE the node and its
|
|
837
|
+
// membership come back and the painter is asked to paint it (D2).
|
|
838
|
+
const registry = registryOf(id, viewId, spec);
|
|
839
|
+
const cmds = [
|
|
840
|
+
...survivors,
|
|
841
|
+
new RemoveFromGroupCommand(group.id, id),
|
|
842
|
+
new RemoveNodeCommand(id),
|
|
843
|
+
new RegisterWidgetCommand(registry, 'unregister'),
|
|
844
|
+
];
|
|
501
845
|
void execCommand(new BatchCommand('Remove widget', cmds));
|
|
502
|
-
|
|
503
|
-
if (arr) {
|
|
504
|
-
const i = arr.findIndex((w) => w.id === id);
|
|
505
|
-
if (i >= 0)
|
|
506
|
-
arr.splice(i, 1);
|
|
507
|
-
}
|
|
508
|
-
specById.delete(id);
|
|
509
|
-
viewOfWidget.delete(id);
|
|
846
|
+
registry.unregister(); // now, for the caller reading the handle next
|
|
510
847
|
b.sync();
|
|
511
|
-
(
|
|
848
|
+
(_f = ctx.apiRef) === null || _f === void 0 ? void 0 : _f.renderNow();
|
|
512
849
|
},
|
|
513
850
|
repaint() {
|
|
514
851
|
const host = hostOf(id);
|
|
@@ -522,18 +859,25 @@ export function createDashboardHandle(ctx) {
|
|
|
522
859
|
return handle;
|
|
523
860
|
}
|
|
524
861
|
export function dashboard(options) {
|
|
525
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
|
|
862
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p;
|
|
526
863
|
ensureDashboardKitStyles();
|
|
527
864
|
const columns = (_a = options.columns) !== null && _a !== void 0 ? _a : DEFAULTS.columns;
|
|
528
865
|
const gap = (_b = options.gap) !== null && _b !== void 0 ? _b : DEFAULTS.gap;
|
|
529
866
|
const rowHeight = (_c = options.rowHeight) !== null && _c !== void 0 ? _c : DEFAULTS.rowHeight;
|
|
530
867
|
const boardW = (_d = options.width) !== null && _d !== void 0 ? _d : DEFAULTS.width;
|
|
531
868
|
const boardH = (_e = options.height) !== null && _e !== void 0 ? _e : DEFAULTS.height;
|
|
869
|
+
// An explicit width is a fixed world; everything else lays out fluid.
|
|
870
|
+
const mode = (_f = options.mode) !== null && _f !== void 0 ? _f : (options.width !== undefined ? 'fixed' : 'fluid');
|
|
871
|
+
const overflow = (_g = options.overflow) !== null && _g !== void 0 ? _g : 'bounded';
|
|
872
|
+
// A fluid board grows (fixed row heights, the board extends); a fixed board
|
|
873
|
+
// fits (its authored height is the picture).
|
|
874
|
+
const sizing = (_h = options.sizing) !== null && _h !== void 0 ? _h : (mode === 'fluid' ? 'grow' : 'fit');
|
|
875
|
+
const layout = (_j = options.layout) !== null && _j !== void 0 ? _j : 'grid';
|
|
532
876
|
const views = options.views
|
|
533
877
|
? options.views.map((v) => (Object.assign(Object.assign({}, v), { widgets: cloneWidgets(v.widgets) })))
|
|
534
|
-
: [{ id: 'main', widgets: cloneWidgets((
|
|
878
|
+
: [{ id: 'main', widgets: cloneWidgets((_k = options.widgets) !== null && _k !== void 0 ? _k : []) }];
|
|
535
879
|
for (const v of views)
|
|
536
|
-
assignCellsDeep(v.widgets, (
|
|
880
|
+
assignCellsDeep(v.widgets, (_l = v.columns) !== null && _l !== void 0 ? _l : columns);
|
|
537
881
|
// -- the render spec: one custom-HTML node per widget ----------------------
|
|
538
882
|
const nodes = [];
|
|
539
883
|
const specById = new Map();
|
|
@@ -565,7 +909,7 @@ export function dashboard(options) {
|
|
|
565
909
|
position: { x: 0, y: 0 },
|
|
566
910
|
size: { width: 100, height: rowHeight },
|
|
567
911
|
custom: true,
|
|
568
|
-
metadata: Object.assign(Object.assign({ useHTMLLayer: true, widgetKind: (_a = w.kind) !== null && _a !== void 0 ? _a : 'widget', widgetSpec: (_b = w.data) !== null && _b !== void 0 ? _b : {} }, (w.title !== undefined ? { widgetTitle: w.title } : {})), { columnSpan: w.span, rowSpan: w.rows, gridItem: { columnStart: w.x + 1, columnEnd: w.x + 1 + w.span, rowStart: w.y + 1, rowEnd: w.y + 1 + w.rows } }),
|
|
912
|
+
metadata: Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({ useHTMLLayer: true, widgetKind: (_a = w.kind) !== null && _a !== void 0 ? _a : 'widget', widgetSpec: (_b = w.data) !== null && _b !== void 0 ? _b : {} }, (w.title !== undefined ? { widgetTitle: w.title } : {})), (w.limits !== undefined ? { widgetLimits: Object.assign({}, w.limits) } : {})), (w.movable === false ? { widgetMovable: false } : {})), (w.resizable === false ? { widgetResizable: false } : {})), { columnSpan: w.span, rowSpan: w.rows, gridItem: { columnStart: w.x + 1, columnEnd: w.x + 1 + w.span, rowStart: w.y + 1, rowEnd: w.y + 1 + w.rows } }),
|
|
569
913
|
});
|
|
570
914
|
}
|
|
571
915
|
};
|
|
@@ -577,7 +921,7 @@ export function dashboard(options) {
|
|
|
577
921
|
// No renderWidget → the built-in renderers draw the declared `kind` from the
|
|
578
922
|
// developer's own `data` (widgets.ts), unknown kinds landing on the titled
|
|
579
923
|
// frame they always did.
|
|
580
|
-
const renderWidget = (
|
|
924
|
+
const renderWidget = (_m = options.renderWidget) !== null && _m !== void 0 ? _m : defaultWidgetRenderer;
|
|
581
925
|
// -- runtime: ONE shared handle over a boxed context -----------------------
|
|
582
926
|
// The two MUTABLE cells the handle used to close over as free `let`s —
|
|
583
927
|
// `active` (showView reassigns it) and `apiRef` (finalize sets it) — are boxed
|
|
@@ -600,37 +944,44 @@ export function dashboard(options) {
|
|
|
600
944
|
rowHeight,
|
|
601
945
|
boardW,
|
|
602
946
|
boardH,
|
|
947
|
+
mode,
|
|
948
|
+
overflow,
|
|
949
|
+
layoutOf: new Map(views.map((v) => { var _a; return [v.id, (_a = v.layout) !== null && _a !== void 0 ? _a : layout]; })),
|
|
603
950
|
optionsBase: options,
|
|
604
|
-
active: (
|
|
951
|
+
active: (_p = (_o = views[0]) === null || _o === void 0 ? void 0 : _o.id) !== null && _p !== void 0 ? _p : 'main',
|
|
605
952
|
apiRef: null,
|
|
953
|
+
onLayoutChange: options.onLayoutChange,
|
|
606
954
|
};
|
|
607
955
|
const { binders, groups } = ctx;
|
|
608
956
|
const handle = createDashboardHandle(ctx);
|
|
609
|
-
return {
|
|
610
|
-
nodes,
|
|
611
|
-
edges: [],
|
|
612
|
-
renderCustomNode: (node, host) => {
|
|
957
|
+
return Object.assign(Object.assign({ nodes, edges: [] }, (mode === 'fluid' ? { renderOptions: { minZoom: 1, maxZoom: 1 } } : {})), { renderCustomNode: (node, host) => {
|
|
613
958
|
const n = node;
|
|
614
959
|
const spec = specById.get(n.id);
|
|
615
960
|
if (!spec)
|
|
616
961
|
return;
|
|
617
962
|
ctx.hosts.set(n.id, host);
|
|
618
963
|
renderWidget(spec, host);
|
|
619
|
-
},
|
|
620
|
-
get handle() {
|
|
964
|
+
}, get handle() {
|
|
621
965
|
return handle;
|
|
622
|
-
},
|
|
623
|
-
|
|
624
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p;
|
|
966
|
+
}, finalize: (api) => {
|
|
967
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
|
|
625
968
|
const a = api;
|
|
626
969
|
if (!a)
|
|
627
970
|
return;
|
|
628
971
|
ctx.apiRef = a;
|
|
629
972
|
const model = a.getModel();
|
|
973
|
+
// FLUID: the board starts at the container's box when it can be measured
|
|
974
|
+
// (the binder keeps it there); the authored defaults only fill in for a
|
|
975
|
+
// container with no size yet.
|
|
976
|
+
const box = mode === 'fluid'
|
|
977
|
+
? { w: ((_a = a.container) === null || _a === void 0 ? void 0 : _a.clientWidth) || 0, h: ((_b = a.container) === null || _b === void 0 ? void 0 : _b.clientHeight) || 0 }
|
|
978
|
+
: { w: 0, h: 0 };
|
|
979
|
+
const viewW = (v) => { var _a; return (mode === 'fluid' && box.w > 0 ? box.w : (_a = v.width) !== null && _a !== void 0 ? _a : boardW); };
|
|
980
|
+
const viewH = (v) => { var _a; return (mode === 'fluid' && box.h > 0 ? box.h : (_a = v.height) !== null && _a !== void 0 ? _a : boardH); };
|
|
630
981
|
for (const v of views) {
|
|
631
982
|
// One board per view; the group is a pure LAYOUT CONTAINER, so its
|
|
632
983
|
// chrome is suppressed (frameChrome) exactly as a dashboard needs.
|
|
633
|
-
const g = new GroupModel({ id: v.id, name: (
|
|
984
|
+
const g = new GroupModel({ id: v.id, name: (_c = v.name) !== null && _c !== void 0 ? _c : v.id });
|
|
634
985
|
model.addGroup(g);
|
|
635
986
|
g.setMetadata('frameChrome', 'none');
|
|
636
987
|
// THE BOARD'S GEOMETRY, PERSISTED. Cells alone do not describe a board:
|
|
@@ -640,28 +991,72 @@ export function dashboard(options) {
|
|
|
640
991
|
// reload would be a picture of a dashboard rather than a dashboard.
|
|
641
992
|
// Serializable values only: the binder's callbacks belong to the app.
|
|
642
993
|
g.setMetadata('dashboardBoard', {
|
|
643
|
-
columns: (
|
|
994
|
+
columns: (_d = v.columns) !== null && _d !== void 0 ? _d : columns,
|
|
644
995
|
gap,
|
|
645
996
|
padding: gap,
|
|
646
|
-
sizing
|
|
997
|
+
sizing,
|
|
647
998
|
baseRowHeight: rowHeight,
|
|
648
|
-
designHeight: (
|
|
999
|
+
designHeight: viewH(v),
|
|
649
1000
|
float: (_e = options.float) !== null && _e !== void 0 ? _e : false,
|
|
650
1001
|
rtl: (_f = options.rtl) !== null && _f !== void 0 ? _f : false,
|
|
1002
|
+
fluid: mode === 'fluid',
|
|
1003
|
+
overflow,
|
|
1004
|
+
static: (_g = options.static) !== null && _g !== void 0 ? _g : false,
|
|
1005
|
+
layout: (_h = ctx.layoutOf.get(v.id)) !== null && _h !== void 0 ? _h : layout,
|
|
651
1006
|
});
|
|
652
|
-
|
|
1007
|
+
if (((_j = ctx.layoutOf.get(v.id)) !== null && _j !== void 0 ? _j : layout) === 'split' && v.tree !== undefined)
|
|
1008
|
+
g.setMetadata(SPLIT_TREE_KEY, v.tree);
|
|
1009
|
+
g.size = { width: viewW(v), height: viewH(v), depth: 0 };
|
|
653
1010
|
g.position = { x: v.id === ctx.active ? 0 : OFFSCREEN_X, y: 0 };
|
|
654
1011
|
groups.set(v.id, g);
|
|
655
1012
|
ctx.boardGroups.set(v.id, g);
|
|
656
1013
|
mountBoard(v.id, v.id, v.widgets, g);
|
|
657
|
-
binders.set(v.id,
|
|
658
|
-
var _a, _b;
|
|
659
|
-
if (e.type === 'commit')
|
|
660
|
-
reportLayoutChange(v.id);
|
|
661
|
-
(_b = (_a = options.binder) === null || _a === void 0 ? void 0 : _a.onGesture) === null || _b === void 0 ? void 0 : _b.call(_a, e);
|
|
662
|
-
} })));
|
|
1014
|
+
binders.set(v.id, bindView(v, g, (_k = ctx.layoutOf.get(v.id)) !== null && _k !== void 0 ? _k : layout));
|
|
663
1015
|
}
|
|
1016
|
+
// LIVE LAYOUT SWITCH (setLayout): keep the picture, swap the binder.
|
|
1017
|
+
// Either way the outgoing binder's cells are written where the grid
|
|
1018
|
+
// binder reads members' cells (gridItem metadata): grid → split derives
|
|
1019
|
+
// its tree from exactly those, so any stale tree is cleared first;
|
|
1020
|
+
// split → grid rebuilds from them, so the column cache goes too.
|
|
1021
|
+
ctx.rebindView = (viewId, next) => {
|
|
1022
|
+
var _a;
|
|
1023
|
+
const v = views.find((x) => x.id === viewId);
|
|
1024
|
+
const g = groups.get(viewId);
|
|
1025
|
+
const b = binders.get(viewId);
|
|
1026
|
+
if (!v || !g || !b)
|
|
1027
|
+
return;
|
|
1028
|
+
const cells = b.saveLayout().cells;
|
|
1029
|
+
b.dispose();
|
|
1030
|
+
const write = (fn) => (model.runSystemWrite ? model.runSystemWrite(fn) : fn());
|
|
1031
|
+
write(() => {
|
|
1032
|
+
var _a, _b;
|
|
1033
|
+
for (const [id, cell] of cells) {
|
|
1034
|
+
const n = model.getNode(id);
|
|
1035
|
+
if (n)
|
|
1036
|
+
n.setMetadata('gridItem', gridItemFromCell(cell));
|
|
1037
|
+
else
|
|
1038
|
+
(_a = model.getGroup(id)) === null || _a === void 0 ? void 0 : _a.setMetadata('gridItem', gridItemFromCell(cell));
|
|
1039
|
+
}
|
|
1040
|
+
g.setMetadata(SPLIT_TREE_KEY, undefined);
|
|
1041
|
+
g.setMetadata('dashboardLayouts', undefined);
|
|
1042
|
+
const board = (_b = g.getMetadata('dashboardBoard')) !== null && _b !== void 0 ? _b : {};
|
|
1043
|
+
g.setMetadata('dashboardBoard', Object.assign(Object.assign({}, board), { layout: next }));
|
|
1044
|
+
});
|
|
1045
|
+
ctx.layoutOf.set(viewId, next);
|
|
1046
|
+
binders.set(viewId, bindView(v, g, next));
|
|
1047
|
+
(_a = binders.get(viewId)) === null || _a === void 0 ? void 0 : _a.sync();
|
|
1048
|
+
};
|
|
664
1049
|
handle.showView(ctx.active);
|
|
1050
|
+
ctx.rebindContainer = (id) => {
|
|
1051
|
+
var _a;
|
|
1052
|
+
const g = model.getGroup(id);
|
|
1053
|
+
const w = specById.get(id);
|
|
1054
|
+
if (!g || !w || !w.widgets)
|
|
1055
|
+
return;
|
|
1056
|
+
ctx.boardGroups.set(id, g);
|
|
1057
|
+
bindContainer(g, w, (_a = ctx.viewOfBoard.get(id)) !== null && _a !== void 0 ? _a : ctx.active);
|
|
1058
|
+
};
|
|
1059
|
+
(_l = ctx.attachHistory) === null || _l === void 0 ? void 0 : _l.call(ctx);
|
|
665
1060
|
return;
|
|
666
1061
|
/**
|
|
667
1062
|
* Mount one board's widgets into its group — and recurse for CONTAINERS.
|
|
@@ -673,7 +1068,7 @@ export function dashboard(options) {
|
|
|
673
1068
|
* hit-testing and the height-escalation ratchet all apply unchanged.
|
|
674
1069
|
*/
|
|
675
1070
|
function mountBoard(boardId, viewId, widgets, boardGroup) {
|
|
676
|
-
var _a, _b, _c, _d, _e
|
|
1071
|
+
var _a, _b, _c, _d, _e;
|
|
677
1072
|
for (const w of widgets) {
|
|
678
1073
|
if (w.widgets) {
|
|
679
1074
|
const innerColumns = innerColumnsOf(w);
|
|
@@ -704,23 +1099,7 @@ export function dashboard(options) {
|
|
|
704
1099
|
boardGroup.addMember(w.id);
|
|
705
1100
|
ctx.boardGroups.set(w.id, cg);
|
|
706
1101
|
mountBoard(w.id, viewId, w.widgets, cg);
|
|
707
|
-
|
|
708
|
-
columns: innerColumns,
|
|
709
|
-
gap,
|
|
710
|
-
padding: 0,
|
|
711
|
-
sizing: 'fit',
|
|
712
|
-
baseRowHeight: rowHeight,
|
|
713
|
-
designHeight: 0,
|
|
714
|
-
maxRows: innerRows,
|
|
715
|
-
float: false,
|
|
716
|
-
rtl: (_d = options.rtl) !== null && _d !== void 0 ? _d : false,
|
|
717
|
-
onGesture: (e) => {
|
|
718
|
-
var _a, _b;
|
|
719
|
-
if (e.type === 'commit')
|
|
720
|
-
reportLayoutChange(viewId);
|
|
721
|
-
(_b = (_a = options.binder) === null || _a === void 0 ? void 0 : _a.onGesture) === null || _b === void 0 ? void 0 : _b.call(_a, e);
|
|
722
|
-
},
|
|
723
|
-
}));
|
|
1102
|
+
bindContainer(cg, w, viewId);
|
|
724
1103
|
continue;
|
|
725
1104
|
}
|
|
726
1105
|
const n = model.getNode(w.id);
|
|
@@ -734,7 +1113,7 @@ export function dashboard(options) {
|
|
|
734
1113
|
// and made toJSON() → dashboard() NOT round-trip (a saved layout
|
|
735
1114
|
// rebuilt back into its declaration order rather than its cells).
|
|
736
1115
|
if (w.x !== undefined && w.y !== undefined) {
|
|
737
|
-
n.setGridItem(gridItemFromCell({ x: w.x, y: w.y, w: (
|
|
1116
|
+
n.setGridItem(gridItemFromCell({ x: w.x, y: w.y, w: (_d = w.span) !== null && _d !== void 0 ? _d : 3, h: (_e = w.rows) !== null && _e !== void 0 ? _e : 1 }));
|
|
738
1117
|
}
|
|
739
1118
|
if (w.pinned)
|
|
740
1119
|
n.setState({ locked: true });
|
|
@@ -751,22 +1130,54 @@ export function dashboard(options) {
|
|
|
751
1130
|
boardGroup.addMember(w.id);
|
|
752
1131
|
}
|
|
753
1132
|
}
|
|
1133
|
+
/** Bind a VIEW's board on its group, under the given layout. */
|
|
1134
|
+
function bindView(v, g, viewLayout) {
|
|
1135
|
+
var _a, _b, _c, _d, _e, _f;
|
|
1136
|
+
const common = Object.assign(Object.assign({ gap, padding: gap, rtl: (_a = options.rtl) !== null && _a !== void 0 ? _a : false, fluid: mode === 'fluid', static: (_b = options.static) !== null && _b !== void 0 ? _b : false }, ((_c = options.binder) !== null && _c !== void 0 ? _c : {})), { onGesture: (e) => {
|
|
1137
|
+
var _a, _b;
|
|
1138
|
+
if (e.type === 'commit')
|
|
1139
|
+
reportChanged();
|
|
1140
|
+
(_b = (_a = options.binder) === null || _a === void 0 ? void 0 : _a.onGesture) === null || _b === void 0 ? void 0 : _b.call(_a, e);
|
|
1141
|
+
} });
|
|
1142
|
+
if (viewLayout === 'split') {
|
|
1143
|
+
return bindDashboardSplit(a, g, Object.assign(Object.assign(Object.assign({}, common), { columns: (_d = v.columns) !== null && _d !== void 0 ? _d : columns, baseRowHeight: rowHeight, designHeight: viewH(v) }), (v.tree !== undefined ? { tree: v.tree } : {})));
|
|
1144
|
+
}
|
|
1145
|
+
return bindDashboardGrid(a, g, Object.assign(Object.assign(Object.assign({}, common), { columns: (_e = v.columns) !== null && _e !== void 0 ? _e : columns, sizing, baseRowHeight: rowHeight, designHeight: viewH(v), float: (_f = options.float) !== null && _f !== void 0 ? _f : false, overflow }), (options.responsive ? { responsive: options.responsive } : {})));
|
|
1146
|
+
}
|
|
1147
|
+
/** Bind (or re-bind) a container's inner grid on its group. */
|
|
1148
|
+
function bindContainer(cg, w, viewId) {
|
|
1149
|
+
var _a, _b, _c, _d;
|
|
1150
|
+
binders.set(w.id, bindDashboardGrid(a, cg, {
|
|
1151
|
+
columns: innerColumnsOf(w),
|
|
1152
|
+
gap,
|
|
1153
|
+
padding: 0,
|
|
1154
|
+
sizing: 'fit',
|
|
1155
|
+
baseRowHeight: rowHeight,
|
|
1156
|
+
designHeight: 0,
|
|
1157
|
+
maxRows: (_a = w.maxRows) !== null && _a !== void 0 ? _a : rowExtentOf((_b = w.widgets) !== null && _b !== void 0 ? _b : []),
|
|
1158
|
+
float: false,
|
|
1159
|
+
rtl: (_c = options.rtl) !== null && _c !== void 0 ? _c : false,
|
|
1160
|
+
static: (_d = options.static) !== null && _d !== void 0 ? _d : false,
|
|
1161
|
+
onGesture: (e) => {
|
|
1162
|
+
var _a, _b;
|
|
1163
|
+
if (e.type === 'commit')
|
|
1164
|
+
reportChanged();
|
|
1165
|
+
(_b = (_a = options.binder) === null || _a === void 0 ? void 0 : _a.onGesture) === null || _b === void 0 ? void 0 : _b.call(_a, e);
|
|
1166
|
+
},
|
|
1167
|
+
}));
|
|
1168
|
+
}
|
|
754
1169
|
/**
|
|
755
1170
|
* One reporter for every binder on a view — the view's own and each
|
|
756
|
-
* container's
|
|
757
|
-
*
|
|
758
|
-
*
|
|
759
|
-
*
|
|
760
|
-
*
|
|
1171
|
+
* container's — and for every API call, undo and redo (D3): the handle's
|
|
1172
|
+
* diff-based `reportChanged`. The payload is the view's FULL NESTED TREE
|
|
1173
|
+
* derived from live membership (handle.toJSON()), so an inner commit
|
|
1174
|
+
* reports the same truth an outer one does, and a tile that crossed a
|
|
1175
|
+
* boundary shows up under its NEW parent.
|
|
761
1176
|
*/
|
|
762
|
-
function
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
const snapshot = handle.toJSON().views.find((x) => x.id === viewId);
|
|
766
|
-
if (snapshot)
|
|
767
|
-
options.onLayoutChange(viewId, snapshot.widgets);
|
|
1177
|
+
function reportChanged() {
|
|
1178
|
+
var _a;
|
|
1179
|
+
(_a = ctx.reportChanged) === null || _a === void 0 ? void 0 : _a.call(ctx);
|
|
768
1180
|
}
|
|
769
|
-
}
|
|
770
|
-
};
|
|
1181
|
+
} });
|
|
771
1182
|
}
|
|
772
1183
|
//# sourceMappingURL=dashboard.js.map
|