@grafloria/element 0.4.52 → 0.4.53

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@grafloria/element",
3
- "version": "0.4.52",
3
+ "version": "0.4.53",
4
4
  "type": "module",
5
5
  "main": "./src/index.js",
6
6
  "types": "./src/index.d.ts",
@@ -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
@@ -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, already placed in the engine) through the page's
188
- * command path, folding `displaced` into the same batch.
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[]) => void | Promise<void>;
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: returns the target-side displaced commands, the
684
- * tile's final cell and its projected rect — and the member GROUPS the
685
- * adoption moved (the node commands skip groups; a dock that pushed sections
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
  /**