@grafloria/element 0.3.23 → 0.4.1
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/README.md +9 -7
- package/package.json +7 -9
- package/src/index.d.ts +16 -16
- package/src/index.js +58 -339
- package/src/lib/dashboard-kit/dashboard.d.ts +1 -1
- package/src/lib/dashboard-kit/dashboard.js +22 -26
- package/src/lib/dashboard-kit/grid-binder.d.ts +1 -1
- package/src/lib/dashboard-kit/grid-binder.js +62 -65
- package/src/lib/dashboard-kit/grid-mapping.js +14 -26
- package/src/lib/dashboard-kit/index.d.ts +5 -5
- package/src/lib/dashboard-kit/index.js +5 -30
- package/src/lib/dashboard-kit/styles.js +4 -8
- package/src/lib/dashboard-kit/widgets.d.ts +1 -1
- package/src/lib/dashboard-kit/widgets.js +16 -26
- package/src/lib/diagram-kit/card.d.ts +2 -2
- package/src/lib/diagram-kit/card.js +22 -32
- package/src/lib/diagram-kit/editing.js +15 -20
- package/src/lib/diagram-kit/er.d.ts +1 -1
- package/src/lib/diagram-kit/er.js +15 -18
- package/src/lib/diagram-kit/handles.d.ts +4 -4
- package/src/lib/diagram-kit/handles.js +16 -29
- package/src/lib/diagram-kit/index.d.ts +10 -10
- package/src/lib/diagram-kit/index.js +10 -50
- package/src/lib/diagram-kit/join-guidance.js +9 -18
- package/src/lib/diagram-kit/rows.js +1 -4
- package/src/lib/diagram-kit/styles.js +4 -8
- package/src/lib/diagram-kit/uml.js +10 -13
- package/src/lib/diagram-kit/update.d.ts +1 -1
- package/src/lib/diagram-kit/update.js +19 -26
- package/src/lib/grafloria-flow-element.js +19 -24
- package/src/lib/grafloria.d.ts +4 -4
- package/src/lib/grafloria.js +12 -17
- package/src/lib/load.d.ts +3 -3
- package/src/lib/load.js +19 -22
- package/src/lib/node-type-registry.js +6 -14
- package/src/lib/stencil-kit/builders.js +4 -10
- package/src/lib/stencil-kit/card-builders.js +14 -17
- package/src/lib/stencil-kit/index.d.ts +7 -7
- package/src/lib/stencil-kit/index.js +6 -16
- package/src/lib/stencil-kit/palette.js +14 -17
- package/src/lib/stencil-kit/shape-data.js +25 -28
- package/src/lib/stencil-kit/styles.js +1 -4
- package/esm/src/index.js +0 -295
- package/esm/src/lib/dashboard-kit/dashboard.js +0 -590
- package/esm/src/lib/dashboard-kit/grid-binder.js +0 -1633
- package/esm/src/lib/dashboard-kit/grid-mapping.js +0 -164
- package/esm/src/lib/dashboard-kit/index.js +0 -8
- package/esm/src/lib/dashboard-kit/styles.js +0 -205
- package/esm/src/lib/dashboard-kit/widgets.js +0 -379
- package/esm/src/lib/diagram-kit/card.js +0 -199
- package/esm/src/lib/diagram-kit/editing.js +0 -448
- package/esm/src/lib/diagram-kit/er.js +0 -160
- package/esm/src/lib/diagram-kit/handles.js +0 -312
- package/esm/src/lib/diagram-kit/index.js +0 -15
- package/esm/src/lib/diagram-kit/join-guidance.js +0 -322
- package/esm/src/lib/diagram-kit/rows.js +0 -144
- package/esm/src/lib/diagram-kit/styles.js +0 -118
- package/esm/src/lib/diagram-kit/uml.js +0 -135
- package/esm/src/lib/diagram-kit/update.js +0 -244
- package/esm/src/lib/grafloria-flow-element.js +0 -266
- package/esm/src/lib/grafloria.js +0 -69
- package/esm/src/lib/load.js +0 -252
- package/esm/src/lib/node-type-registry.js +0 -42
- package/esm/src/lib/stencil-kit/builders.js +0 -18
- package/esm/src/lib/stencil-kit/card-builders.js +0 -105
- package/esm/src/lib/stencil-kit/index.js +0 -9
- package/esm/src/lib/stencil-kit/palette.js +0 -427
- package/esm/src/lib/stencil-kit/shape-data.js +0 -537
- package/esm/src/lib/stencil-kit/styles.js +0 -126
|
@@ -1,590 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* `dashboard({ views, widgets })` — the DATA-FIRST dashboard authoring API.
|
|
3
|
-
*
|
|
4
|
-
* The exact shape `erDiagram()` / `umlDiagram()` have for ER and UML: you
|
|
5
|
-
* describe WHAT the dashboard is, `render()` runs the returned `finalize(api)`
|
|
6
|
-
* automatically, and every interactive part (the pack grid, drag/resize with
|
|
7
|
-
* live push and a truthful placeholder, fit/grow, float, pin, undoable
|
|
8
|
-
* commands) wires itself.
|
|
9
|
-
*
|
|
10
|
-
* Before this, a developer got `bindDashboardGrid()` — a gesture BINDER one
|
|
11
|
-
* layer down — and had to hand-assemble everything above it: a GroupModel per
|
|
12
|
-
* view, a NodeModel per widget, `useHTMLLayer` / `widgetKind` metadata, grid
|
|
13
|
-
* cells, membership, then the bind. The demo page needed ~143 lines just to
|
|
14
|
-
* build its boards. That was the missing authoring layer; this is it:
|
|
15
|
-
*
|
|
16
|
-
* ```js
|
|
17
|
-
* const SPEC = dashboard({
|
|
18
|
-
* columns: 12,
|
|
19
|
-
* sizing: 'fit',
|
|
20
|
-
* views: [{
|
|
21
|
-
* id: 'overview', name: 'Overview',
|
|
22
|
-
* widgets: [
|
|
23
|
-
* { id: 'rev', kind: 'kpi', span: 3, rows: 1, data: {…} },
|
|
24
|
-
* { id: 'trend', kind: 'line', span: 8, rows: 2, data: {…} },
|
|
25
|
-
* { id: 'mix', kind: 'donut', span: 4, rows: 2, pinned: true },
|
|
26
|
-
* ],
|
|
27
|
-
* }],
|
|
28
|
-
* renderWidget: (widget, host) => { … }, // optional: your charts
|
|
29
|
-
* });
|
|
30
|
-
* render(SPEC, host);
|
|
31
|
-
* ```
|
|
32
|
-
*
|
|
33
|
-
* WHAT IT DELIBERATELY DOES NOT DO: pick a charting library. `renderWidget` is
|
|
34
|
-
* the seam — the kit hands you the widget and a raw HTML host (the renderer's
|
|
35
|
-
* custom-node path, which unlike `metadata.html` is not sanitised, so real
|
|
36
|
-
* `<svg>`/`<canvas>` is fine). Omit it and `defaultWidgetRenderer` (widgets.ts)
|
|
37
|
-
* draws the declared `kind` from your own `data` with hand-rolled inline SVG —
|
|
38
|
-
* kpi / line / bar / donut / funnel / table, no dependency, no sample dataset —
|
|
39
|
-
* falling back to a titled frame for kinds it does not know, so a layout is
|
|
40
|
-
* testable before any chart exists.
|
|
41
|
-
*
|
|
42
|
-
* Cells are the truth and live in the existing `GridItemConfig`, so save/load
|
|
43
|
-
* round-trips with no extra work — same as every other kit.
|
|
44
|
-
*/
|
|
45
|
-
import { __awaiter } from "tslib";
|
|
46
|
-
import { AddToGroupCommand, BatchCommand, BringNodeToFrontCommand, Command, GroupModel, NodeModel, RemoveFromGroupCommand, RemoveNodeCommand, SendNodeToBackCommand, } from '@grafloria/engine';
|
|
47
|
-
import { bindDashboardGrid, } from './grid-binder';
|
|
48
|
-
import { gridItemFromCell } from './grid-mapping';
|
|
49
|
-
import { ensureDashboardKitStyles } from './styles';
|
|
50
|
-
import { defaultWidgetRenderer } from './widgets';
|
|
51
|
-
/**
|
|
52
|
-
* Add a widget node AND its board membership as ONE undoable step.
|
|
53
|
-
*
|
|
54
|
-
* A `BatchCommand([AddNodeCommand, AddToGroupCommand])` cannot express this:
|
|
55
|
-
* the manager validates the whole batch up front, and
|
|
56
|
-
* `AddToGroupCommand.canExecute` pre-gates on the node ALREADY being in the
|
|
57
|
-
* diagram — which it is not until the first command runs. Sequencing two
|
|
58
|
-
* commands works but costs two undo steps, so an interactive "add widget"
|
|
59
|
-
* would need two Ctrl-Z. This composite does both in its own execute(), and
|
|
60
|
-
* unwinds both in undo().
|
|
61
|
-
*/
|
|
62
|
-
class AddWidgetCommand extends Command {
|
|
63
|
-
constructor(node, groupId) {
|
|
64
|
-
super('Add widget');
|
|
65
|
-
this.node = node;
|
|
66
|
-
this.groupId = groupId;
|
|
67
|
-
}
|
|
68
|
-
execute(context) {
|
|
69
|
-
var _a;
|
|
70
|
-
const diagram = context.diagram;
|
|
71
|
-
if (!diagram)
|
|
72
|
-
return;
|
|
73
|
-
diagram.addNode(this.node);
|
|
74
|
-
(_a = diagram.getGroup(this.groupId)) === null || _a === void 0 ? void 0 : _a.addMember(this.node.id);
|
|
75
|
-
}
|
|
76
|
-
undo(context) {
|
|
77
|
-
var _a;
|
|
78
|
-
const diagram = context.diagram;
|
|
79
|
-
if (!diagram)
|
|
80
|
-
return;
|
|
81
|
-
(_a = diagram.getGroup(this.groupId)) === null || _a === void 0 ? void 0 : _a.removeMember(this.node.id);
|
|
82
|
-
diagram.removeNode(this.node.id);
|
|
83
|
-
}
|
|
84
|
-
serialize() {
|
|
85
|
-
return {
|
|
86
|
-
id: this.id,
|
|
87
|
-
name: this.name,
|
|
88
|
-
timestamp: this.timestamp,
|
|
89
|
-
data: { nodeId: this.node.id, groupId: this.groupId },
|
|
90
|
-
};
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
const DEFAULTS = { columns: 12, gap: 8, rowHeight: 130, width: 1180, height: 660 };
|
|
94
|
-
const OFFSCREEN_X = -20000;
|
|
95
|
-
let autoId = 0;
|
|
96
|
-
/** The node a widget spec becomes — one place, so addWidget() and the initial
|
|
97
|
-
* spec build can never drift apart on metadata. */
|
|
98
|
-
function buildWidgetNode(w, rowHeight) {
|
|
99
|
-
var _a, _b, _c, _d, _e, _f;
|
|
100
|
-
const node = new NodeModel({
|
|
101
|
-
id: w.id,
|
|
102
|
-
type: 'widget',
|
|
103
|
-
position: { x: 0, y: 0 },
|
|
104
|
-
size: { width: 120, height: rowHeight, depth: 0 },
|
|
105
|
-
});
|
|
106
|
-
node.setMetadata('useHTMLLayer', true);
|
|
107
|
-
node.setMetadata('widgetKind', (_a = w.kind) !== null && _a !== void 0 ? _a : 'widget');
|
|
108
|
-
node.setMetadata('widgetSpec', (_b = w.data) !== null && _b !== void 0 ? _b : {});
|
|
109
|
-
// The TITLE was the one authored field that never reached the node — so a
|
|
110
|
-
// document-level reload rebuilt every card with `titleOf()` falling through to
|
|
111
|
-
// the kind, and a board of "Revenue vs target" / "Top reps by revenue" came
|
|
112
|
-
// back as "line" / "table". Only written when authored, so an untitled widget
|
|
113
|
-
// serialises exactly as it always did.
|
|
114
|
-
if (w.title !== undefined)
|
|
115
|
-
node.setMetadata('widgetTitle', w.title);
|
|
116
|
-
node.setMetadata('columnSpan', (_c = w.span) !== null && _c !== void 0 ? _c : 3);
|
|
117
|
-
node.setMetadata('rowSpan', (_d = w.rows) !== null && _d !== void 0 ? _d : 1);
|
|
118
|
-
if (w.x !== undefined && w.y !== undefined) {
|
|
119
|
-
node.setGridItem({
|
|
120
|
-
columnStart: w.x + 1,
|
|
121
|
-
columnEnd: w.x + 1 + ((_e = w.span) !== null && _e !== void 0 ? _e : 3),
|
|
122
|
-
rowStart: w.y + 1,
|
|
123
|
-
rowEnd: w.y + 1 + ((_f = w.rows) !== null && _f !== void 0 ? _f : 1),
|
|
124
|
-
});
|
|
125
|
-
}
|
|
126
|
-
// A dashboard widget is not a wiring endpoint: no ports, no hover glyphs.
|
|
127
|
-
node.setBehavior({ connectable: false });
|
|
128
|
-
for (const p of [...node.getPorts().values()])
|
|
129
|
-
node.removePort(p.id);
|
|
130
|
-
return node;
|
|
131
|
-
}
|
|
132
|
-
/** Flow widgets that declared no cell: left-to-right, wrapping at `columns`. */
|
|
133
|
-
function assignCells(widgets, columns) {
|
|
134
|
-
var _a, _b;
|
|
135
|
-
let x = 0;
|
|
136
|
-
let y = 0;
|
|
137
|
-
let rowMax = 0;
|
|
138
|
-
for (const w of widgets) {
|
|
139
|
-
const span = Math.max(1, Math.min(columns, (_a = w.span) !== null && _a !== void 0 ? _a : 3));
|
|
140
|
-
const rows = Math.max(1, (_b = w.rows) !== null && _b !== void 0 ? _b : 1);
|
|
141
|
-
if (w.x === undefined || w.y === undefined) {
|
|
142
|
-
if (x + span > columns) {
|
|
143
|
-
x = 0;
|
|
144
|
-
y += rowMax || 1;
|
|
145
|
-
rowMax = 0;
|
|
146
|
-
}
|
|
147
|
-
w.x = x;
|
|
148
|
-
w.y = y;
|
|
149
|
-
x += span;
|
|
150
|
-
rowMax = Math.max(rowMax, rows);
|
|
151
|
-
}
|
|
152
|
-
w.span = span;
|
|
153
|
-
w.rows = rows;
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
/**
|
|
157
|
-
* Build THE `DashboardHandle` — the one and only implementation, shared by
|
|
158
|
-
* `dashboard()` and `fromDocument()`. Reads/writes the mutable `ctx.active` /
|
|
159
|
-
* `ctx.apiRef` cells so the caller's finalize can wire the API in afterwards.
|
|
160
|
-
*/
|
|
161
|
-
export function createDashboardHandle(ctx) {
|
|
162
|
-
const { views, groups, binders, specById, viewOfWidget } = ctx;
|
|
163
|
-
const hostOf = (id) => ctx.hosts.get(id);
|
|
164
|
-
const execCommand = (cmd) => {
|
|
165
|
-
var _a, _b, _c, _d;
|
|
166
|
-
try {
|
|
167
|
-
const r = (_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.commandManager.execute(cmd);
|
|
168
|
-
// Fire-and-forget like the binder's own commits, but never leave an
|
|
169
|
-
// unhandled rejection: a refused command is a no-op, not a crash.
|
|
170
|
-
(_d = r === null || r === void 0 ? void 0 : r.catch) === null || _d === void 0 ? void 0 : _d.call(r, () => undefined);
|
|
171
|
-
}
|
|
172
|
-
catch (_e) {
|
|
173
|
-
/* a refused command must not break the caller */
|
|
174
|
-
}
|
|
175
|
-
};
|
|
176
|
-
const handle = {
|
|
177
|
-
get views() {
|
|
178
|
-
return views.map((v) => v.id);
|
|
179
|
-
},
|
|
180
|
-
get activeView() {
|
|
181
|
-
return ctx.active;
|
|
182
|
-
},
|
|
183
|
-
showView(id) {
|
|
184
|
-
var _a, _b, _c, _d, _e, _f;
|
|
185
|
-
if (!groups.has(id))
|
|
186
|
-
return;
|
|
187
|
-
ctx.active = id;
|
|
188
|
-
for (const [vid, g] of groups) {
|
|
189
|
-
const x = vid === id ? 0 : OFFSCREEN_X;
|
|
190
|
-
const s = (_a = g.size) !== null && _a !== void 0 ? _a : { width: ctx.boardW, height: ctx.boardH };
|
|
191
|
-
if (g.position.x !== x)
|
|
192
|
-
g.setFrame({ x, y: 0, width: s.width, height: s.height });
|
|
193
|
-
}
|
|
194
|
-
(_b = binders.get(id)) === null || _b === void 0 ? void 0 : _b.sync();
|
|
195
|
-
(_c = ctx.apiRef) === null || _c === void 0 ? void 0 : _c.renderNow();
|
|
196
|
-
const g = groups.get(id);
|
|
197
|
-
const gs = (_d = g.size) !== null && _d !== void 0 ? _d : { width: ctx.boardW, height: ctx.boardH };
|
|
198
|
-
(_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 });
|
|
199
|
-
},
|
|
200
|
-
widget(id) {
|
|
201
|
-
return makeWidgetHandle(id);
|
|
202
|
-
},
|
|
203
|
-
widgetsOf(viewId) {
|
|
204
|
-
var _a;
|
|
205
|
-
const v = views.find((x) => x.id === (viewId !== null && viewId !== void 0 ? viewId : ctx.active));
|
|
206
|
-
return ((_a = v === null || v === void 0 ? void 0 : v.widgets) !== null && _a !== void 0 ? _a : []).map((w) => makeWidgetHandle(w.id)).filter(Boolean);
|
|
207
|
-
},
|
|
208
|
-
setSizing(mode) {
|
|
209
|
-
var _a;
|
|
210
|
-
for (const b of binders.values())
|
|
211
|
-
b.setSizing(mode);
|
|
212
|
-
(_a = ctx.apiRef) === null || _a === void 0 ? void 0 : _a.renderNow();
|
|
213
|
-
},
|
|
214
|
-
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 : ((_c = ctx.optionsBase.sizing) !== null && _c !== void 0 ? _c : 'fit'); },
|
|
215
|
-
setFloat(on) {
|
|
216
|
-
var _a;
|
|
217
|
-
for (const b of binders.values())
|
|
218
|
-
b.setFloat(on);
|
|
219
|
-
(_a = ctx.apiRef) === null || _a === void 0 ? void 0 : _a.renderNow();
|
|
220
|
-
},
|
|
221
|
-
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); },
|
|
222
|
-
setColumns(n, layout, viewId) {
|
|
223
|
-
var _a;
|
|
224
|
-
const targets = viewId ? [binders.get(viewId)] : [...binders.values()];
|
|
225
|
-
for (const b of targets)
|
|
226
|
-
b === null || b === void 0 ? void 0 : b.setColumns(n, layout);
|
|
227
|
-
(_a = ctx.apiRef) === null || _a === void 0 ? void 0 : _a.renderNow();
|
|
228
|
-
},
|
|
229
|
-
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; },
|
|
230
|
-
setRtl(on) {
|
|
231
|
-
var _a;
|
|
232
|
-
for (const b of binders.values())
|
|
233
|
-
b.setRtl(on);
|
|
234
|
-
(_a = ctx.apiRef) === null || _a === void 0 ? void 0 : _a.renderNow();
|
|
235
|
-
},
|
|
236
|
-
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); },
|
|
237
|
-
addWidget(spec, viewId) {
|
|
238
|
-
var _a, _b, _c, _d, _e;
|
|
239
|
-
const vid = viewId !== null && viewId !== void 0 ? viewId : ctx.active;
|
|
240
|
-
const v = views.find((x) => x.id === vid);
|
|
241
|
-
const model = (_a = ctx.apiRef) === null || _a === void 0 ? void 0 : _a.getModel();
|
|
242
|
-
const group = groups.get(vid);
|
|
243
|
-
if (!v || !model || !group)
|
|
244
|
-
return undefined;
|
|
245
|
-
const w = Object.assign(Object.assign({}, spec), { id: spec.id || `w-${++autoId}`, span: (_b = spec.span) !== null && _b !== void 0 ? _b : 3, rows: (_c = spec.rows) !== null && _c !== void 0 ? _c : 1 });
|
|
246
|
-
// REGISTER FIRST: a custom node mounts exactly once, and the painter
|
|
247
|
-
// returns early for an id the spec does not know — so the widget must be
|
|
248
|
-
// known before the node reaches the model, or it paints blank forever.
|
|
249
|
-
v.widgets.push(w);
|
|
250
|
-
specById.set(w.id, w);
|
|
251
|
-
viewOfWidget.set(w.id, vid);
|
|
252
|
-
const existing = model.getNode(w.id);
|
|
253
|
-
const node = existing !== null && existing !== void 0 ? existing : buildWidgetNode(w, ctx.rowHeight);
|
|
254
|
-
if (w.pinned)
|
|
255
|
-
node.setState({ locked: true });
|
|
256
|
-
// ONE undoable step (see AddWidgetCommand for why this cannot be a batch).
|
|
257
|
-
execCommand(existing ? new AddToGroupCommand(group.id, w.id) : new AddWidgetCommand(node, group.id));
|
|
258
|
-
(_d = binders.get(vid)) === null || _d === void 0 ? void 0 : _d.sync();
|
|
259
|
-
(_e = ctx.apiRef) === null || _e === void 0 ? void 0 : _e.renderNow();
|
|
260
|
-
return makeWidgetHandle(w.id);
|
|
261
|
-
},
|
|
262
|
-
refresh() {
|
|
263
|
-
var _a;
|
|
264
|
-
for (const b of binders.values())
|
|
265
|
-
b.sync();
|
|
266
|
-
(_a = ctx.apiRef) === null || _a === void 0 ? void 0 : _a.renderNow();
|
|
267
|
-
},
|
|
268
|
-
fit(viewId) {
|
|
269
|
-
var _a, _b, _c;
|
|
270
|
-
const g = groups.get(viewId !== null && viewId !== void 0 ? viewId : ctx.active);
|
|
271
|
-
if (!g)
|
|
272
|
-
return;
|
|
273
|
-
const gs = (_a = g.size) !== null && _a !== void 0 ? _a : { width: ctx.boardW, height: ctx.boardH };
|
|
274
|
-
(_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 });
|
|
275
|
-
},
|
|
276
|
-
metrics(viewId) {
|
|
277
|
-
var _a;
|
|
278
|
-
return (_a = binders.get(viewId !== null && viewId !== void 0 ? viewId : ctx.active)) === null || _a === void 0 ? void 0 : _a.metrics();
|
|
279
|
-
},
|
|
280
|
-
binderOf(viewId) {
|
|
281
|
-
return binders.get(viewId !== null && viewId !== void 0 ? viewId : ctx.active);
|
|
282
|
-
},
|
|
283
|
-
exportIds(viewId) {
|
|
284
|
-
var _a, _b;
|
|
285
|
-
const id = viewId !== null && viewId !== void 0 ? viewId : ctx.active;
|
|
286
|
-
const ids = new Set();
|
|
287
|
-
// A view with no group is a view that was never finalized — an empty set
|
|
288
|
-
// is the honest answer, and scoping an export to nothing is a visible
|
|
289
|
-
// failure rather than a silently enormous document.
|
|
290
|
-
if (!groups.has(id))
|
|
291
|
-
return ids;
|
|
292
|
-
ids.add(id);
|
|
293
|
-
// Read the SPEC, not the group's member Set: membership is maintained by
|
|
294
|
-
// commands and an in-flight drag can have a widget momentarily reparented.
|
|
295
|
-
// The spec is what the view IS.
|
|
296
|
-
for (const w of (_b = (_a = views.find((v) => v.id === id)) === null || _a === void 0 ? void 0 : _a.widgets) !== null && _b !== void 0 ? _b : [])
|
|
297
|
-
ids.add(w.id);
|
|
298
|
-
return ids;
|
|
299
|
-
},
|
|
300
|
-
toJSON() {
|
|
301
|
-
// SAVING ON A PHONE SAVES THE DESKTOP LAYOUT. The binder serialises from
|
|
302
|
-
// the engine's LARGEST cached column count (gridstack's `save()`), so a
|
|
303
|
-
// board currently squeezed to 1 column still writes out the 12-column
|
|
304
|
-
// layout its user authored — and the view's `columns` is that count, so
|
|
305
|
-
// feeding this straight back into dashboard() rebuilds the wide board.
|
|
306
|
-
const savedViews = views.map((v) => {
|
|
307
|
-
var _a;
|
|
308
|
-
const saved = (_a = binders.get(v.id)) === null || _a === void 0 ? void 0 : _a.saveLayout();
|
|
309
|
-
return Object.assign(Object.assign(Object.assign({}, v), (saved ? { columns: saved.columns } : {})), { widgets: v.widgets.map((w) => {
|
|
310
|
-
var _a, _b;
|
|
311
|
-
const cell = (_a = saved === null || saved === void 0 ? void 0 : saved.cells.get(w.id)) !== null && _a !== void 0 ? _a : (_b = binders.get(v.id)) === null || _b === void 0 ? void 0 : _b.cellOf(w.id);
|
|
312
|
-
return cell ? Object.assign(Object.assign({}, w), { x: cell.x, y: cell.y, span: cell.w, rows: cell.h }) : Object.assign({}, w);
|
|
313
|
-
}) });
|
|
314
|
-
});
|
|
315
|
-
// Board options come off the LIVE board wherever the handle can see it —
|
|
316
|
-
// `sizing` and `float` are the two a user changes from the toolbar, and
|
|
317
|
-
// reading them from the authored literal would restore the board they
|
|
318
|
-
// started with rather than the one they are looking at.
|
|
319
|
-
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 });
|
|
320
|
-
},
|
|
321
|
-
dispose() {
|
|
322
|
-
var _a, _b;
|
|
323
|
-
for (const b of binders.values())
|
|
324
|
-
b.dispose();
|
|
325
|
-
binders.clear();
|
|
326
|
-
// The groups finalize() created are ours to clean up — leaving them
|
|
327
|
-
// behind made a rebuild stack a second set of boards on the first.
|
|
328
|
-
const model = (_a = ctx.apiRef) === null || _a === void 0 ? void 0 : _a.getModel();
|
|
329
|
-
for (const id of groups.keys())
|
|
330
|
-
(_b = model === null || model === void 0 ? void 0 : model.removeGroup) === null || _b === void 0 ? void 0 : _b.call(model, id);
|
|
331
|
-
groups.clear();
|
|
332
|
-
ctx.hosts.clear();
|
|
333
|
-
},
|
|
334
|
-
};
|
|
335
|
-
function makeWidgetHandle(id) {
|
|
336
|
-
const spec = specById.get(id);
|
|
337
|
-
const viewId = viewOfWidget.get(id);
|
|
338
|
-
if (!spec || !viewId)
|
|
339
|
-
return undefined;
|
|
340
|
-
const binder = () => binders.get(viewId);
|
|
341
|
-
const node = () => { var _a; return (_a = ctx.apiRef) === null || _a === void 0 ? void 0 : _a.getModel().getNode(id); };
|
|
342
|
-
return {
|
|
343
|
-
id,
|
|
344
|
-
viewId,
|
|
345
|
-
get node() {
|
|
346
|
-
return node();
|
|
347
|
-
},
|
|
348
|
-
get spec() {
|
|
349
|
-
return spec;
|
|
350
|
-
},
|
|
351
|
-
get cell() {
|
|
352
|
-
var _a;
|
|
353
|
-
return (_a = binder()) === null || _a === void 0 ? void 0 : _a.cellOf(id);
|
|
354
|
-
},
|
|
355
|
-
get rect() {
|
|
356
|
-
var _a;
|
|
357
|
-
return (_a = binder()) === null || _a === void 0 ? void 0 : _a.cellRectOf(id);
|
|
358
|
-
},
|
|
359
|
-
get pinned() {
|
|
360
|
-
var _a, _b;
|
|
361
|
-
return ((_b = (_a = node()) === null || _a === void 0 ? void 0 : _a.state) === null || _b === void 0 ? void 0 : _b.locked) === true;
|
|
362
|
-
},
|
|
363
|
-
resize(span, rows) {
|
|
364
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
365
|
-
var _a, _b;
|
|
366
|
-
return (_b = (yield ((_a = binder()) === null || _a === void 0 ? void 0 : _a.resizeTo(id, span, rows)))) !== null && _b !== void 0 ? _b : false;
|
|
367
|
-
});
|
|
368
|
-
},
|
|
369
|
-
moveTo(x, y) {
|
|
370
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
371
|
-
var _a, _b;
|
|
372
|
-
return (_b = (yield ((_a = binder()) === null || _a === void 0 ? void 0 : _a.moveTo(id, x, y)))) !== null && _b !== void 0 ? _b : false;
|
|
373
|
-
});
|
|
374
|
-
},
|
|
375
|
-
pin(on) {
|
|
376
|
-
var _a, _b, _c;
|
|
377
|
-
const n = node();
|
|
378
|
-
if (!n)
|
|
379
|
-
return;
|
|
380
|
-
n.setState({ locked: on !== null && on !== void 0 ? on : !(((_a = n.state) === null || _a === void 0 ? void 0 : _a.locked) === true) });
|
|
381
|
-
// Re-sync so the ENGINE's locked flag (never pushed, drags refused)
|
|
382
|
-
// and the hidden corner handle take effect on this frame, not the next
|
|
383
|
-
// gesture.
|
|
384
|
-
(_b = binder()) === null || _b === void 0 ? void 0 : _b.sync();
|
|
385
|
-
(_c = ctx.apiRef) === null || _c === void 0 ? void 0 : _c.renderNow();
|
|
386
|
-
},
|
|
387
|
-
bringToFront() {
|
|
388
|
-
var _a;
|
|
389
|
-
execCommand(new BringNodeToFrontCommand(id));
|
|
390
|
-
(_a = ctx.apiRef) === null || _a === void 0 ? void 0 : _a.renderNow();
|
|
391
|
-
},
|
|
392
|
-
sendToBack() {
|
|
393
|
-
var _a;
|
|
394
|
-
execCommand(new SendNodeToBackCommand(id));
|
|
395
|
-
(_a = ctx.apiRef) === null || _a === void 0 ? void 0 : _a.renderNow();
|
|
396
|
-
},
|
|
397
|
-
update(patch) {
|
|
398
|
-
if (patch.data !== undefined)
|
|
399
|
-
spec.data = patch.data;
|
|
400
|
-
if (patch.title !== undefined)
|
|
401
|
-
spec.title = patch.title;
|
|
402
|
-
if (patch.kind !== undefined)
|
|
403
|
-
spec.kind = patch.kind;
|
|
404
|
-
const host = hostOf(id);
|
|
405
|
-
if (host)
|
|
406
|
-
ctx.renderWidget(spec, host);
|
|
407
|
-
},
|
|
408
|
-
remove(displaced) {
|
|
409
|
-
var _a, _b;
|
|
410
|
-
const n = node();
|
|
411
|
-
const group = groups.get(viewId);
|
|
412
|
-
const b = binder();
|
|
413
|
-
if (!n || !group || !b)
|
|
414
|
-
return;
|
|
415
|
-
// ONE undoable step, survivors' re-pack folded in — the same atomic
|
|
416
|
-
// shape the kit's own drag-out-to-remove uses. A gesture that ALREADY
|
|
417
|
-
// computed the survivors passes them in: after a drag-out the tile is
|
|
418
|
-
// gone from the engine, so planRemoval() would return [] and the
|
|
419
|
-
// survivors' cells would never commit.
|
|
420
|
-
const survivors = (_a = displaced) !== null && _a !== void 0 ? _a : b.planRemoval(id);
|
|
421
|
-
const cmds = [...survivors, new RemoveFromGroupCommand(group.id, id), new RemoveNodeCommand(id)];
|
|
422
|
-
void execCommand(new BatchCommand('Remove widget', cmds));
|
|
423
|
-
const v = views.find((x) => x.id === viewId);
|
|
424
|
-
if (v)
|
|
425
|
-
v.widgets = v.widgets.filter((w) => w.id !== id);
|
|
426
|
-
specById.delete(id);
|
|
427
|
-
viewOfWidget.delete(id);
|
|
428
|
-
b.sync();
|
|
429
|
-
(_b = ctx.apiRef) === null || _b === void 0 ? void 0 : _b.renderNow();
|
|
430
|
-
},
|
|
431
|
-
repaint() {
|
|
432
|
-
const host = hostOf(id);
|
|
433
|
-
if (host)
|
|
434
|
-
ctx.renderWidget(spec, host);
|
|
435
|
-
},
|
|
436
|
-
// (hosts are captured in renderCustomNode, so repaint works for every
|
|
437
|
-
// widget the renderer has mounted — including after a rebuild.)
|
|
438
|
-
};
|
|
439
|
-
}
|
|
440
|
-
return handle;
|
|
441
|
-
}
|
|
442
|
-
export function dashboard(options) {
|
|
443
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m;
|
|
444
|
-
ensureDashboardKitStyles();
|
|
445
|
-
const columns = (_a = options.columns) !== null && _a !== void 0 ? _a : DEFAULTS.columns;
|
|
446
|
-
const gap = (_b = options.gap) !== null && _b !== void 0 ? _b : DEFAULTS.gap;
|
|
447
|
-
const rowHeight = (_c = options.rowHeight) !== null && _c !== void 0 ? _c : DEFAULTS.rowHeight;
|
|
448
|
-
const boardW = (_d = options.width) !== null && _d !== void 0 ? _d : DEFAULTS.width;
|
|
449
|
-
const boardH = (_e = options.height) !== null && _e !== void 0 ? _e : DEFAULTS.height;
|
|
450
|
-
const views = options.views
|
|
451
|
-
? options.views.map((v) => (Object.assign(Object.assign({}, v), { widgets: v.widgets.map((w) => (Object.assign({}, w))) })))
|
|
452
|
-
: [{ id: 'main', widgets: ((_f = options.widgets) !== null && _f !== void 0 ? _f : []).map((w) => (Object.assign({}, w))) }];
|
|
453
|
-
for (const v of views)
|
|
454
|
-
assignCells(v.widgets, (_g = v.columns) !== null && _g !== void 0 ? _g : columns);
|
|
455
|
-
// -- the render spec: one custom-HTML node per widget ----------------------
|
|
456
|
-
const nodes = [];
|
|
457
|
-
const specById = new Map();
|
|
458
|
-
const viewOfWidget = new Map();
|
|
459
|
-
for (const v of views) {
|
|
460
|
-
for (const w of v.widgets) {
|
|
461
|
-
specById.set(w.id, w);
|
|
462
|
-
viewOfWidget.set(w.id, v.id);
|
|
463
|
-
nodes.push({
|
|
464
|
-
id: w.id,
|
|
465
|
-
type: 'widget',
|
|
466
|
-
position: { x: 0, y: 0 },
|
|
467
|
-
size: { width: 100, height: rowHeight },
|
|
468
|
-
custom: true,
|
|
469
|
-
metadata: Object.assign(Object.assign({ useHTMLLayer: true, widgetKind: (_h = w.kind) !== null && _h !== void 0 ? _h : 'widget', widgetSpec: (_j = w.data) !== null && _j !== void 0 ? _j : {} }, (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 } }),
|
|
470
|
-
});
|
|
471
|
-
}
|
|
472
|
-
}
|
|
473
|
-
// No renderWidget → the built-in renderers draw the declared `kind` from the
|
|
474
|
-
// developer's own `data` (widgets.ts), unknown kinds landing on the titled
|
|
475
|
-
// frame they always did.
|
|
476
|
-
const renderWidget = (_k = options.renderWidget) !== null && _k !== void 0 ? _k : defaultWidgetRenderer;
|
|
477
|
-
// -- runtime: ONE shared handle over a boxed context -----------------------
|
|
478
|
-
// The two MUTABLE cells the handle used to close over as free `let`s —
|
|
479
|
-
// `active` (showView reassigns it) and `apiRef` (finalize sets it) — are boxed
|
|
480
|
-
// on `ctx`, so the SAME builder that fromDocument() calls reads and writes
|
|
481
|
-
// them. Everything else is the stable state; the handle is identical to what
|
|
482
|
-
// was inline here, moved verbatim into createDashboardHandle().
|
|
483
|
-
const ctx = {
|
|
484
|
-
views,
|
|
485
|
-
groups: new Map(),
|
|
486
|
-
binders: new Map(),
|
|
487
|
-
specById,
|
|
488
|
-
viewOfWidget,
|
|
489
|
-
hosts: new Map(),
|
|
490
|
-
renderWidget,
|
|
491
|
-
columns,
|
|
492
|
-
gap,
|
|
493
|
-
rowHeight,
|
|
494
|
-
boardW,
|
|
495
|
-
boardH,
|
|
496
|
-
optionsBase: options,
|
|
497
|
-
active: (_m = (_l = views[0]) === null || _l === void 0 ? void 0 : _l.id) !== null && _m !== void 0 ? _m : 'main',
|
|
498
|
-
apiRef: null,
|
|
499
|
-
};
|
|
500
|
-
const { binders, groups } = ctx;
|
|
501
|
-
const handle = createDashboardHandle(ctx);
|
|
502
|
-
return {
|
|
503
|
-
nodes,
|
|
504
|
-
edges: [],
|
|
505
|
-
renderCustomNode: (node, host) => {
|
|
506
|
-
const n = node;
|
|
507
|
-
const spec = specById.get(n.id);
|
|
508
|
-
if (!spec)
|
|
509
|
-
return;
|
|
510
|
-
ctx.hosts.set(n.id, host);
|
|
511
|
-
renderWidget(spec, host);
|
|
512
|
-
},
|
|
513
|
-
get handle() {
|
|
514
|
-
return handle;
|
|
515
|
-
},
|
|
516
|
-
finalize: (api) => {
|
|
517
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r;
|
|
518
|
-
const a = api;
|
|
519
|
-
if (!a)
|
|
520
|
-
return;
|
|
521
|
-
ctx.apiRef = a;
|
|
522
|
-
const model = a.getModel();
|
|
523
|
-
for (const v of views) {
|
|
524
|
-
// One board per view; the group is a pure LAYOUT CONTAINER, so its
|
|
525
|
-
// chrome is suppressed (frameChrome) exactly as a dashboard needs.
|
|
526
|
-
const g = new GroupModel({ id: v.id, name: (_a = v.name) !== null && _a !== void 0 ? _a : v.id });
|
|
527
|
-
model.addGroup(g);
|
|
528
|
-
g.setMetadata('frameChrome', 'none');
|
|
529
|
-
// THE BOARD'S GEOMETRY, PERSISTED. Cells alone do not describe a board:
|
|
530
|
-
// the column count, gap and sizing mode are what turn them into pixels,
|
|
531
|
-
// and they live only in the `dashboard()` call. Without them a reloaded
|
|
532
|
-
// document could paint the widgets but never rebind the grid — the
|
|
533
|
-
// reload would be a picture of a dashboard rather than a dashboard.
|
|
534
|
-
// Serializable values only: the binder's callbacks belong to the app.
|
|
535
|
-
g.setMetadata('dashboardBoard', {
|
|
536
|
-
columns: (_b = v.columns) !== null && _b !== void 0 ? _b : columns,
|
|
537
|
-
gap,
|
|
538
|
-
padding: gap,
|
|
539
|
-
sizing: (_c = options.sizing) !== null && _c !== void 0 ? _c : 'fit',
|
|
540
|
-
baseRowHeight: rowHeight,
|
|
541
|
-
designHeight: (_d = v.height) !== null && _d !== void 0 ? _d : boardH,
|
|
542
|
-
float: (_e = options.float) !== null && _e !== void 0 ? _e : false,
|
|
543
|
-
rtl: (_f = options.rtl) !== null && _f !== void 0 ? _f : false,
|
|
544
|
-
});
|
|
545
|
-
g.size = { width: (_g = v.width) !== null && _g !== void 0 ? _g : boardW, height: (_h = v.height) !== null && _h !== void 0 ? _h : boardH, depth: 0 };
|
|
546
|
-
g.position = { x: v.id === ctx.active ? 0 : OFFSCREEN_X, y: 0 };
|
|
547
|
-
groups.set(v.id, g);
|
|
548
|
-
for (const w of v.widgets) {
|
|
549
|
-
const n = model.getNode(w.id);
|
|
550
|
-
if (!n)
|
|
551
|
-
continue;
|
|
552
|
-
// DECLARED CELLS ARE AUTHORITATIVE. `metadata.gridItem` on the node
|
|
553
|
-
// SPEC is inert — the model's GridItemConfig is a real field
|
|
554
|
-
// (`setGridItem`), and it is the only thing the binder reads. Without
|
|
555
|
-
// this write the board silently auto-positioned instead, which
|
|
556
|
-
// matched the declaration only while flow order happened to agree,
|
|
557
|
-
// and made toJSON() → dashboard() NOT round-trip (a saved layout
|
|
558
|
-
// rebuilt back into its declaration order rather than its cells).
|
|
559
|
-
if (w.x !== undefined && w.y !== undefined) {
|
|
560
|
-
n.setGridItem(gridItemFromCell({ x: w.x, y: w.y, w: (_j = w.span) !== null && _j !== void 0 ? _j : 3, h: (_k = w.rows) !== null && _k !== void 0 ? _k : 1 }));
|
|
561
|
-
}
|
|
562
|
-
if (w.pinned)
|
|
563
|
-
n.setState({ locked: true });
|
|
564
|
-
// A dashboard widget is not a wiring endpoint. This MUST happen here
|
|
565
|
-
// rather than on the node spec: the render-input path IGNORES a
|
|
566
|
-
// spec-level `behavior` (the same trap erDiagram documents for
|
|
567
|
-
// `resizable`), so a DECLARED widget shipped connectable with four
|
|
568
|
-
// default ports and sprouted glyphs on hover — while addWidget()'s
|
|
569
|
-
// directly-built nodes were already correct. One path being right is
|
|
570
|
-
// exactly what hid it, and why the tooth now covers both.
|
|
571
|
-
n.setBehavior({ connectable: false });
|
|
572
|
-
for (const p of [...n.getPorts().values()])
|
|
573
|
-
n.removePort(p.id);
|
|
574
|
-
g.addMember(w.id);
|
|
575
|
-
}
|
|
576
|
-
binders.set(v.id, bindDashboardGrid(a, g, Object.assign(Object.assign(Object.assign({ columns: (_l = v.columns) !== null && _l !== void 0 ? _l : columns, gap, padding: gap, sizing: (_m = options.sizing) !== null && _m !== void 0 ? _m : 'fit', baseRowHeight: rowHeight, designHeight: (_o = v.height) !== null && _o !== void 0 ? _o : boardH, float: (_p = options.float) !== null && _p !== void 0 ? _p : false, rtl: (_q = options.rtl) !== null && _q !== void 0 ? _q : false }, (options.responsive ? { responsive: options.responsive } : {})), ((_r = options.binder) !== null && _r !== void 0 ? _r : {})), { onGesture: (e) => {
|
|
577
|
-
var _a, _b;
|
|
578
|
-
if (e.type === 'commit' && options.onLayoutChange) {
|
|
579
|
-
const snapshot = handle.toJSON().views.find((x) => x.id === v.id);
|
|
580
|
-
if (snapshot)
|
|
581
|
-
options.onLayoutChange(v.id, snapshot.widgets);
|
|
582
|
-
}
|
|
583
|
-
(_b = (_a = options.binder) === null || _a === void 0 ? void 0 : _a.onGesture) === null || _b === void 0 ? void 0 : _b.call(_a, e);
|
|
584
|
-
} })));
|
|
585
|
-
}
|
|
586
|
-
handle.showView(ctx.active);
|
|
587
|
-
},
|
|
588
|
-
};
|
|
589
|
-
}
|
|
590
|
-
//# sourceMappingURL=dashboard.js.map
|