@grafloria/element 0.4.52 → 0.4.54
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/commit.d.ts +82 -0
- package/src/lib/dashboard-kit/commit.js +121 -0
- package/src/lib/dashboard-kit/dashboard.d.ts +11 -3
- package/src/lib/dashboard-kit/dashboard.js +11 -6
- package/src/lib/dashboard-kit/grid-binder.d.ts +33 -40
- package/src/lib/dashboard-kit/grid-binder.js +328 -351
- package/src/lib/dashboard-kit/split-binder.js +1 -1
package/package.json
CHANGED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* THE COMMIT (tile first, step 4a) — what a gesture writes into history.
|
|
3
|
+
*
|
|
4
|
+
* Every gesture ends the same way: each board it touched reports the tiles
|
|
5
|
+
* whose cells or pixels differ from the snapshot taken when the gesture
|
|
6
|
+
* entered it, and those deltas become commands. A widget's cell is on its
|
|
7
|
+
* node (`SetGridItemCommand`, then the pixel move and resize the projection
|
|
8
|
+
* derived); a container's cell is on its GROUP, together with the frame the
|
|
9
|
+
* projection wrote. Both come out of ONE function here. Before this the node
|
|
10
|
+
* path skipped groups "by design" and every site that could have displaced
|
|
11
|
+
* a section rebuilt the group command by hand — six of them — and the one
|
|
12
|
+
* that did not (the cross-board drop) dropped the target board's pushed
|
|
13
|
+
* sections on the floor: the drop drew the push and never persisted it.
|
|
14
|
+
*/
|
|
15
|
+
import { Command } from '@grafloria/engine';
|
|
16
|
+
import { type CellRect, type TileDelta, type WorldRect } from './grid-mapping.js';
|
|
17
|
+
/**
|
|
18
|
+
* A CONTAINER's cell lives in its group's metadata and its frame is what the
|
|
19
|
+
* projection wrote for that cell: one command sets both, and undo puts both
|
|
20
|
+
* back. (A node's cell rides on the node itself — see `buildCommitCommands`.)
|
|
21
|
+
*/
|
|
22
|
+
export declare class SetGroupCellCommand extends Command {
|
|
23
|
+
private groupId;
|
|
24
|
+
private cellBefore;
|
|
25
|
+
private cellAfter;
|
|
26
|
+
private frameBefore;
|
|
27
|
+
private frameAfter;
|
|
28
|
+
constructor(groupId: string, cellBefore: CellRect, cellAfter: CellRect, frameBefore: WorldRect, frameAfter: WorldRect);
|
|
29
|
+
private apply;
|
|
30
|
+
execute(context: {
|
|
31
|
+
diagram?: unknown;
|
|
32
|
+
}): void;
|
|
33
|
+
undo(context: {
|
|
34
|
+
diagram?: unknown;
|
|
35
|
+
}): void;
|
|
36
|
+
serialize(): {
|
|
37
|
+
id: string;
|
|
38
|
+
name: string;
|
|
39
|
+
timestamp: number;
|
|
40
|
+
data: {
|
|
41
|
+
groupId: string;
|
|
42
|
+
cellBefore: CellRect;
|
|
43
|
+
cellAfter: CellRect;
|
|
44
|
+
frameBefore: WorldRect;
|
|
45
|
+
frameAfter: WorldRect;
|
|
46
|
+
};
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Steps that depend on each other's RESULT — create a group, then move a page
|
|
51
|
+
* into it, then place the group on the board; or remove a member and then the
|
|
52
|
+
* group it emptied — run as one history step. A batch checks every member's
|
|
53
|
+
* `canExecute` before running any of them and every member's `canUndo` before
|
|
54
|
+
* undoing any, and a membership command's precondition (its group exists) is
|
|
55
|
+
* exactly what a neighbouring step creates or removes. This runs the chain in
|
|
56
|
+
* order and reverses it on undo, judging nothing up front.
|
|
57
|
+
*/
|
|
58
|
+
export declare class SequenceCommand extends Command {
|
|
59
|
+
private steps;
|
|
60
|
+
constructor(name: string, steps: Command[]);
|
|
61
|
+
execute(context: Parameters<Command['execute']>[0]): void;
|
|
62
|
+
undo(context: Parameters<Command['undo']>[0]): void;
|
|
63
|
+
canExecute(): boolean;
|
|
64
|
+
canUndo(): boolean;
|
|
65
|
+
serialize(): {
|
|
66
|
+
id: string;
|
|
67
|
+
name: string;
|
|
68
|
+
timestamp: number;
|
|
69
|
+
data: {
|
|
70
|
+
steps: import("@grafloria/engine").SerializedCommand[];
|
|
71
|
+
};
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* The commands for every tile a board's deltas say moved — nodes AND groups,
|
|
76
|
+
* in the deltas' order. A node's commands are its cell then its pixels; a
|
|
77
|
+
* group's is one cell-and-frame command. A delta whose cell did not change
|
|
78
|
+
* contributes nothing for a group (its frame follows its cell), and only the
|
|
79
|
+
* pixel commands for a node (a fit board re-projects every tile when a row
|
|
80
|
+
* comes or goes: the cells stand, the pixels move).
|
|
81
|
+
*/
|
|
82
|
+
export declare function tileCommands(deltas: TileDelta[]): Command[];
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* THE COMMIT (tile first, step 4a) — what a gesture writes into history.
|
|
3
|
+
*
|
|
4
|
+
* Every gesture ends the same way: each board it touched reports the tiles
|
|
5
|
+
* whose cells or pixels differ from the snapshot taken when the gesture
|
|
6
|
+
* entered it, and those deltas become commands. A widget's cell is on its
|
|
7
|
+
* node (`SetGridItemCommand`, then the pixel move and resize the projection
|
|
8
|
+
* derived); a container's cell is on its GROUP, together with the frame the
|
|
9
|
+
* projection wrote. Both come out of ONE function here. Before this the node
|
|
10
|
+
* path skipped groups "by design" and every site that could have displaced
|
|
11
|
+
* a section rebuilt the group command by hand — six of them — and the one
|
|
12
|
+
* that did not (the cross-board drop) dropped the target board's pushed
|
|
13
|
+
* sections on the floor: the drop drew the push and never persisted it.
|
|
14
|
+
*/
|
|
15
|
+
import { Command } from '@grafloria/engine';
|
|
16
|
+
import { buildCommitCommands, gridItemFromCell } from './grid-mapping.js';
|
|
17
|
+
/**
|
|
18
|
+
* A CONTAINER's cell lives in its group's metadata and its frame is what the
|
|
19
|
+
* projection wrote for that cell: one command sets both, and undo puts both
|
|
20
|
+
* back. (A node's cell rides on the node itself — see `buildCommitCommands`.)
|
|
21
|
+
*/
|
|
22
|
+
export class SetGroupCellCommand extends Command {
|
|
23
|
+
constructor(groupId, cellBefore, cellAfter, frameBefore, frameAfter) {
|
|
24
|
+
super('Resize section');
|
|
25
|
+
this.groupId = groupId;
|
|
26
|
+
this.cellBefore = cellBefore;
|
|
27
|
+
this.cellAfter = cellAfter;
|
|
28
|
+
this.frameBefore = frameBefore;
|
|
29
|
+
this.frameAfter = frameAfter;
|
|
30
|
+
}
|
|
31
|
+
apply(context, cell, frame) {
|
|
32
|
+
const diagram = context.diagram;
|
|
33
|
+
const grp = diagram === null || diagram === void 0 ? void 0 : diagram.getGroup(this.groupId);
|
|
34
|
+
if (!grp)
|
|
35
|
+
return;
|
|
36
|
+
grp.setMetadata('gridItem', gridItemFromCell(cell));
|
|
37
|
+
grp.setFrame(Object.assign({}, frame));
|
|
38
|
+
}
|
|
39
|
+
execute(context) {
|
|
40
|
+
this.apply(context, this.cellAfter, this.frameAfter);
|
|
41
|
+
}
|
|
42
|
+
undo(context) {
|
|
43
|
+
this.apply(context, this.cellBefore, this.frameBefore);
|
|
44
|
+
}
|
|
45
|
+
serialize() {
|
|
46
|
+
return {
|
|
47
|
+
id: this.id,
|
|
48
|
+
name: this.name,
|
|
49
|
+
timestamp: this.timestamp,
|
|
50
|
+
data: {
|
|
51
|
+
groupId: this.groupId,
|
|
52
|
+
cellBefore: this.cellBefore,
|
|
53
|
+
cellAfter: this.cellAfter,
|
|
54
|
+
frameBefore: this.frameBefore,
|
|
55
|
+
frameAfter: this.frameAfter,
|
|
56
|
+
},
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Steps that depend on each other's RESULT — create a group, then move a page
|
|
62
|
+
* into it, then place the group on the board; or remove a member and then the
|
|
63
|
+
* group it emptied — run as one history step. A batch checks every member's
|
|
64
|
+
* `canExecute` before running any of them and every member's `canUndo` before
|
|
65
|
+
* undoing any, and a membership command's precondition (its group exists) is
|
|
66
|
+
* exactly what a neighbouring step creates or removes. This runs the chain in
|
|
67
|
+
* order and reverses it on undo, judging nothing up front.
|
|
68
|
+
*/
|
|
69
|
+
export class SequenceCommand extends Command {
|
|
70
|
+
constructor(name, steps) {
|
|
71
|
+
super(name);
|
|
72
|
+
this.steps = steps;
|
|
73
|
+
}
|
|
74
|
+
execute(context) {
|
|
75
|
+
for (const c of this.steps)
|
|
76
|
+
c.execute(context);
|
|
77
|
+
}
|
|
78
|
+
undo(context) {
|
|
79
|
+
for (let i = this.steps.length - 1; i >= 0; i--)
|
|
80
|
+
this.steps[i].undo(context);
|
|
81
|
+
}
|
|
82
|
+
canExecute() {
|
|
83
|
+
return true;
|
|
84
|
+
}
|
|
85
|
+
canUndo() {
|
|
86
|
+
return true;
|
|
87
|
+
}
|
|
88
|
+
serialize() {
|
|
89
|
+
return { id: this.id, name: this.name, timestamp: this.timestamp, data: { steps: this.steps.map((c) => c.serialize()) } };
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
const sameCell = (a, b) => a.x === b.x && a.y === b.y && a.w === b.w && a.h === b.h;
|
|
93
|
+
/** The frame a delta's pixels describe — a group's frame is its position and size in the model. */
|
|
94
|
+
const frameOf = (pos, size) => ({
|
|
95
|
+
x: pos.x,
|
|
96
|
+
y: pos.y,
|
|
97
|
+
width: size.width,
|
|
98
|
+
height: size.height,
|
|
99
|
+
});
|
|
100
|
+
/**
|
|
101
|
+
* The commands for every tile a board's deltas say moved — nodes AND groups,
|
|
102
|
+
* in the deltas' order. A node's commands are its cell then its pixels; a
|
|
103
|
+
* group's is one cell-and-frame command. A delta whose cell did not change
|
|
104
|
+
* contributes nothing for a group (its frame follows its cell), and only the
|
|
105
|
+
* pixel commands for a node (a fit board re-projects every tile when a row
|
|
106
|
+
* comes or goes: the cells stand, the pixels move).
|
|
107
|
+
*/
|
|
108
|
+
export function tileCommands(deltas) {
|
|
109
|
+
const out = [];
|
|
110
|
+
for (const d of deltas) {
|
|
111
|
+
if (d.isGroup) {
|
|
112
|
+
if (sameCell(d.cellBefore, d.cellAfter))
|
|
113
|
+
continue;
|
|
114
|
+
out.push(new SetGroupCellCommand(d.id, d.cellBefore, d.cellAfter, frameOf(d.posBefore, d.sizeBefore), frameOf(d.posAfter, d.sizeAfter)));
|
|
115
|
+
continue;
|
|
116
|
+
}
|
|
117
|
+
out.push(...buildCommitCommands([d]));
|
|
118
|
+
}
|
|
119
|
+
return out;
|
|
120
|
+
}
|
|
121
|
+
//# sourceMappingURL=commit.js.map
|
|
@@ -411,9 +411,17 @@ export interface DashboardHandle {
|
|
|
411
411
|
/**
|
|
412
412
|
* Add a widget to a view. CREATES the node (you do not pre-build one), wires
|
|
413
413
|
* its metadata, and commits node + membership as ONE undoable step.
|
|
414
|
-
* Auto-positions when the spec names no cell.
|
|
415
|
-
|
|
416
|
-
|
|
414
|
+
* Auto-positions when the spec names no cell. `opts.displaced`: the
|
|
415
|
+
* commands a palette drop handed `onDropIn` for the tiles the placeholder
|
|
416
|
+
* pushed aside — folded into the same step, so the widget lands on the cell
|
|
417
|
+
* the drop showed and undo puts the pushed tiles back with it. Left out,
|
|
418
|
+
* the board is re-read from the model and the push is forgotten: the new
|
|
419
|
+
* widget then auto-positions into whatever hole is left (Quantia's "lands
|
|
420
|
+
* on the cell it was aimed at", element 0.4.54).
|
|
421
|
+
*/
|
|
422
|
+
addWidget(spec: DashboardWidgetSpec, viewId?: string, opts?: {
|
|
423
|
+
displaced?: Command[];
|
|
424
|
+
}): WidgetHandle | undefined;
|
|
417
425
|
/**
|
|
418
426
|
* Re-read every board from the model — call after undo/redo, or any
|
|
419
427
|
* out-of-band mutation, so the grid and the projection agree again.
|
|
@@ -1575,8 +1575,8 @@ export function createDashboardHandle(ctx) {
|
|
|
1575
1575
|
(_a = ctx.apiRef) === null || _a === void 0 ? void 0 : _a.renderNow();
|
|
1576
1576
|
},
|
|
1577
1577
|
getDragHandle: () => { var _a, _b, _c; return (_b = (_a = binders.get(ctx.active)) === null || _a === void 0 ? void 0 : _a.getDragHandle()) !== null && _b !== void 0 ? _b : ((_c = ctx.optionsBase.dragHandle) !== null && _c !== void 0 ? _c : false); },
|
|
1578
|
-
addWidget(spec, viewId) {
|
|
1579
|
-
var _a, _b, _c, _d, _e, _f;
|
|
1578
|
+
addWidget(spec, viewId, opts) {
|
|
1579
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
1580
1580
|
const vid = viewId !== null && viewId !== void 0 ? viewId : ctx.active;
|
|
1581
1581
|
// `vid` may name a view OR a container — both are boards with a group,
|
|
1582
1582
|
// a binder and an authored array.
|
|
@@ -1601,10 +1601,15 @@ export function createDashboardHandle(ctx) {
|
|
|
1601
1601
|
const node = existing !== null && existing !== void 0 ? existing : buildWidgetNode(w, ctx.rowHeight);
|
|
1602
1602
|
if (w.pinned)
|
|
1603
1603
|
node.setState({ locked: true });
|
|
1604
|
-
// ONE undoable step, registration included (see AddWidgetCommand).
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1604
|
+
// ONE undoable step, registration included (see AddWidgetCommand). The
|
|
1605
|
+
// tiles a drop pushed aside ride in front of it, in a SEQUENCE: a batch
|
|
1606
|
+
// runs its members across awaits, and the board is re-read right after
|
|
1607
|
+
// this call — the pushed cells must be in the model by then.
|
|
1608
|
+
const add = new AddWidgetCommand(node, group.id, registry, !!existing);
|
|
1609
|
+
const displaced = (_e = opts === null || opts === void 0 ? void 0 : opts.displaced) !== null && _e !== void 0 ? _e : [];
|
|
1610
|
+
execCommand(displaced.length > 0 ? new SequenceCommand('Add widget', [...displaced, add]) : add);
|
|
1611
|
+
(_f = binders.get(vid)) === null || _f === void 0 ? void 0 : _f.sync();
|
|
1612
|
+
(_g = ctx.apiRef) === null || _g === void 0 ? void 0 : _g.renderNow();
|
|
1608
1613
|
return makeWidgetHandle(w.id);
|
|
1609
1614
|
},
|
|
1610
1615
|
refresh() {
|
|
@@ -52,6 +52,8 @@
|
|
|
52
52
|
import { Command, type DiagramModel, type GridColumnLayout, type GroupModel, type NodeModel } from '@grafloria/engine';
|
|
53
53
|
import { type ToolPointerEvent } from '@grafloria/renderer';
|
|
54
54
|
import { type CellRect, type WorldRect } from './grid-mapping.js';
|
|
55
|
+
import { type BesideSide } from './zones.js';
|
|
56
|
+
export { SequenceCommand, SetGroupCellCommand } from './commit.js';
|
|
55
57
|
/** The slice of a DiagramInstance the binder needs (structural, test-friendly). */
|
|
56
58
|
export interface DashboardGridApi {
|
|
57
59
|
getModel(): DiagramModel;
|
|
@@ -184,10 +186,13 @@ export interface DashboardGridOptions {
|
|
|
184
186
|
onRemoveRequest?: (nodeId: string, displaced: Command[]) => void | Promise<void>;
|
|
185
187
|
/**
|
|
186
188
|
* Page hook for palette drag-in release: add `node` (already carrying
|
|
187
|
-
* `cell` in its gridItem
|
|
188
|
-
*
|
|
189
|
+
* `cell` in its gridItem) to `target.boardId` — this view, or a page or
|
|
190
|
+
* section the chip was dropped into (tile first, step 4a) — through the
|
|
191
|
+
* page's command path, folding `displaced` into the same batch.
|
|
189
192
|
*/
|
|
190
|
-
onDropIn?: (node: NodeModel, cell: CellRect, displaced: Command[]
|
|
193
|
+
onDropIn?: (node: NodeModel, cell: CellRect, displaced: Command[], target: {
|
|
194
|
+
boardId: string;
|
|
195
|
+
}) => void | Promise<void>;
|
|
191
196
|
/**
|
|
192
197
|
* A member is about to LEAVE this board through a gesture (moved into
|
|
193
198
|
* another board, made a tab of its own). Answers the commands that follow
|
|
@@ -595,31 +600,6 @@ export interface TearOutPlan {
|
|
|
595
600
|
}
|
|
596
601
|
/** A torn-out page never arrives shorter than this: a strip with no room under it is not a group. */
|
|
597
602
|
export declare const TEAR_OUT_MIN_ROWS = 2;
|
|
598
|
-
/**
|
|
599
|
-
* Steps that depend on each other's RESULT — create a group, then move a page
|
|
600
|
-
* into it, then place the group on the board; or remove a member and then the
|
|
601
|
-
* group it emptied — run as one history step. A batch checks every member's
|
|
602
|
-
* `canExecute` before running any of them and every member's `canUndo` before
|
|
603
|
-
* undoing any, and a membership command's precondition (its group exists) is
|
|
604
|
-
* exactly what a neighbouring step creates or removes. This runs the chain in
|
|
605
|
-
* order and reverses it on undo, judging nothing up front.
|
|
606
|
-
*/
|
|
607
|
-
export declare class SequenceCommand extends Command {
|
|
608
|
-
private steps;
|
|
609
|
-
constructor(name: string, steps: Command[]);
|
|
610
|
-
execute(context: Parameters<Command['execute']>[0]): void;
|
|
611
|
-
undo(context: Parameters<Command['undo']>[0]): void;
|
|
612
|
-
canExecute(): boolean;
|
|
613
|
-
canUndo(): boolean;
|
|
614
|
-
serialize(): {
|
|
615
|
-
id: string;
|
|
616
|
-
name: string;
|
|
617
|
-
timestamp: number;
|
|
618
|
-
data: {
|
|
619
|
-
steps: import("@grafloria/engine").SerializedCommand[];
|
|
620
|
-
};
|
|
621
|
-
};
|
|
622
|
-
}
|
|
623
603
|
/**
|
|
624
604
|
* A WIDGET dropped on a tab strip becomes a new tab there (a tab dropped on
|
|
625
605
|
* one joins; a widget wraps into a page of its own first). The dashboard
|
|
@@ -646,14 +626,35 @@ interface AdoptOptions {
|
|
|
646
626
|
fit?: 'shrink';
|
|
647
627
|
/** Anchor the tile's TOP edge at the pointer instead of centring it (the pointer holds a tab, the page hangs below it). */
|
|
648
628
|
anchor?: 'top';
|
|
629
|
+
/** Enter WITH INTENT: the tile takes the cell under the hand and pushes the solid tiles there (D2 — a section this board holds refused it). */
|
|
630
|
+
push?: boolean;
|
|
631
|
+
/** Enter BESIDE a container of this board: the container gives way, the tile takes the band's side at the pointer's row. */
|
|
632
|
+
beside?: {
|
|
633
|
+
containerId: string;
|
|
634
|
+
side: BesideSide;
|
|
635
|
+
};
|
|
649
636
|
}
|
|
650
637
|
interface AdoptedLeg {
|
|
651
638
|
groupId: string;
|
|
652
|
-
/** Drive the target engine from the source binder's pointer stream. */
|
|
639
|
+
/** Drive the target engine from the source binder's pointer stream. `push`: the tile means it — solid tiles under the wanted cell are pushed (D2). */
|
|
653
640
|
move(world: {
|
|
654
641
|
x: number;
|
|
655
642
|
y: number;
|
|
643
|
+
}, opts?: {
|
|
644
|
+
push?: boolean;
|
|
645
|
+
}): void;
|
|
646
|
+
/** Put the tile BESIDE `containerId` on this board, at the row under `world` — the container shifts or the tiles behind it are pushed; a repeat at the same row is a no-op. */
|
|
647
|
+
beside(containerId: string, side: BesideSide, world: {
|
|
648
|
+
x: number;
|
|
649
|
+
y: number;
|
|
656
650
|
}): void;
|
|
651
|
+
/** The beside this board holds for the tile, for the zone resolve's stickiness: the container's frame at rest and the cell the tile took — null when none. */
|
|
652
|
+
besideState(): {
|
|
653
|
+
containerId: string;
|
|
654
|
+
side: BesideSide;
|
|
655
|
+
frame0: WorldRect;
|
|
656
|
+
vacated: WorldRect;
|
|
657
|
+
} | null;
|
|
657
658
|
/**
|
|
658
659
|
* Take the tile OFF the board while the pointer is somewhere the board is
|
|
659
660
|
* not the target (over a group the page will join instead); the tiles it
|
|
@@ -680,22 +681,14 @@ interface AdoptedLeg {
|
|
|
680
681
|
/** Undo the adoption: target board back to its pre-entry layout. */
|
|
681
682
|
abort(): void;
|
|
682
683
|
/**
|
|
683
|
-
* Close the leg for commit:
|
|
684
|
-
* tile's final cell and its projected rect
|
|
685
|
-
*
|
|
686
|
-
* commits them through these). Null when the tile is somehow gone.
|
|
684
|
+
* Close the leg for commit: the target board's displaced commands (widgets
|
|
685
|
+
* and sections alike), the tile's final cell and its projected rect. Null
|
|
686
|
+
* when the tile is somehow gone.
|
|
687
687
|
*/
|
|
688
688
|
finalize(): {
|
|
689
689
|
commands: Command[];
|
|
690
690
|
cell: CellRect;
|
|
691
691
|
rect: WorldRect;
|
|
692
|
-
groups: Array<{
|
|
693
|
-
id: string;
|
|
694
|
-
cellBefore: CellRect;
|
|
695
|
-
cellAfter: CellRect;
|
|
696
|
-
frameBefore: WorldRect;
|
|
697
|
-
frameAfter: WorldRect;
|
|
698
|
-
}>;
|
|
699
692
|
} | null;
|
|
700
693
|
}
|
|
701
694
|
/**
|