@grafloria/element 0.3.23 → 0.4.0
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 +7 -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,1633 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* `bindDashboardGrid(api, group, options)` — the dashboard-grid gesture binder
|
|
3
|
-
* (Phase 2 of the dashboard-grid plan; the plan page's Section-1 prototype is
|
|
4
|
-
* the executable spec of the feel this reproduces).
|
|
5
|
-
*
|
|
6
|
-
* One binder owns ONE `GridPackEngine` mirroring the group's members — the
|
|
7
|
-
* engine is the single source of truth for cell math (three swap shapes, the
|
|
8
|
-
* >50% anti-jitter gate, locked refusal, push-down + skipDown, teleport-home
|
|
9
|
-
* settle). This module only converts: pointer → cells in, cells → pixels out,
|
|
10
|
-
* gesture → ONE undoable BatchCommand.
|
|
11
|
-
*
|
|
12
|
-
* HOW THE RENDERER'S OWN NODE-DRAG IS BYPASSED — a registered `CanvasTool`
|
|
13
|
-
* (the renderer's wave-6 tool registry, `registerTool`). The tool makes a
|
|
14
|
-
* POINT-SPECIFIC claim (priority 2, like the whiteboard ink tool): any
|
|
15
|
-
* pointerdown whose hit node is a member of the bound group, or that lands
|
|
16
|
-
* inside a member group's frame with no node under it (the KPI-slab dead
|
|
17
|
-
* zone — otherwise the built-in group-drag would fight the pack layout
|
|
18
|
-
* there). A claimed gesture bypasses the ENTIRE built-in ladder — node-drag,
|
|
19
|
-
* selection, marquee, wave-12 resize handles — and the DomEventBinder
|
|
20
|
-
* forwards move/up/cancel with world coordinates already computed. This beats
|
|
21
|
-
* `behavior.draggable = false` (which still runs selection and shows the
|
|
22
|
-
* built-in resize handles) and beats capture-phase DOM listeners (which fight
|
|
23
|
-
* the binder's own preventDefault bookkeeping). `click` is NOT a
|
|
24
|
-
* compatibility mouse event, so the page's click-to-focus keeps firing for
|
|
25
|
-
* sub-threshold presses.
|
|
26
|
-
*
|
|
27
|
-
* DURING a gesture, positions are written through the MODEL, not commands:
|
|
28
|
-
* - the held tile is a GHOST — transition-exempt, tracking the cursor in
|
|
29
|
-
* WORLD coordinates (no `position: fixed` hack; the html layer carries the
|
|
30
|
-
* camera transform, and the tool events arrive already converted);
|
|
31
|
-
* - every cell crossing runs `engine.moveCheck` / `resizeCheck`; every item
|
|
32
|
-
* the engine displaced is re-projected cells→pixels. Those writes are
|
|
33
|
-
* DERIVED state, so they run inside `diagram.runSystemWrite` — which is
|
|
34
|
-
* also what lets a PINNED tile's pixels follow a fit-mode row-height
|
|
35
|
-
* change without violating the authoritative geometry lock (its CELLS
|
|
36
|
-
* never change; the pin protects the document fact, not the projection);
|
|
37
|
-
* - a dashed PLACEHOLDER (`.axdb-ph`) always shows the engine's current cell
|
|
38
|
-
* for the held tile — the truthful drop preview.
|
|
39
|
-
*
|
|
40
|
-
* The gesture ends in ONE `BatchCommand`: `SetGridItemCommand` per tile whose
|
|
41
|
-
* cells changed (cells are the truth) + `MoveNodeCommand`/`ResizeNodeCommand`
|
|
42
|
-
* (merge-opted-OUT) per unlocked node whose geometry changed, so a bare undo
|
|
43
|
-
* restores geometry AND cells with no binder help. Escape restores the
|
|
44
|
-
* engine snapshot (`cancelGesture`) and every pixel — nothing is committed.
|
|
45
|
-
*
|
|
46
|
-
* MEMBER GROUPS (e.g. a KPI section) ride as LOCKED slab items — never
|
|
47
|
-
* pushed, never packed, drags onto them refused (the prototype's pinned
|
|
48
|
-
* full-width row). Their cells persist in group metadata `gridItem` (groups
|
|
49
|
-
* carry no GridItemConfig); their INNER layout is their own concern — bind a
|
|
50
|
-
* second `bindDashboardGrid` on the section for a nested pack grid.
|
|
51
|
-
*/
|
|
52
|
-
import { __awaiter } from "tslib";
|
|
53
|
-
import { AddToGroupCommand, BatchCommand, Command, GridPackEngine, RemoveFromGroupCommand, } from '@grafloria/engine';
|
|
54
|
-
import { registerTool } from '@grafloria/renderer';
|
|
55
|
-
import { buildCommitCommands, cellFromGridItem, cellToRect, columnUnitFor, gridItemFromCell, pointToCell, rowHeightFor, sizeToSpan, } from './grid-mapping';
|
|
56
|
-
import { ensureDashboardKitStyles } from './styles';
|
|
57
|
-
const BOARD_REGISTRY = new Map();
|
|
58
|
-
/**
|
|
59
|
-
* Undoable cell+frame write for a GROUP member (the strip's slab). The engine
|
|
60
|
-
* has Move/Resize commands for nodes but none for a group's frame, and slab
|
|
61
|
-
* cells live in group metadata — this closes nested height escalation into
|
|
62
|
-
* the gesture's single BatchCommand so one undo restores the strip too.
|
|
63
|
-
*/
|
|
64
|
-
class SetGroupCellCommand extends Command {
|
|
65
|
-
constructor(groupId, cellBefore, cellAfter, frameBefore, frameAfter) {
|
|
66
|
-
super('Resize section');
|
|
67
|
-
this.groupId = groupId;
|
|
68
|
-
this.cellBefore = cellBefore;
|
|
69
|
-
this.cellAfter = cellAfter;
|
|
70
|
-
this.frameBefore = frameBefore;
|
|
71
|
-
this.frameAfter = frameAfter;
|
|
72
|
-
}
|
|
73
|
-
apply(context, cell, frame) {
|
|
74
|
-
const diagram = context.diagram;
|
|
75
|
-
const grp = diagram === null || diagram === void 0 ? void 0 : diagram.getGroup(this.groupId);
|
|
76
|
-
if (!grp)
|
|
77
|
-
return;
|
|
78
|
-
grp.setMetadata('gridItem', gridItemFromCell(cell));
|
|
79
|
-
grp.setFrame(Object.assign({}, frame));
|
|
80
|
-
}
|
|
81
|
-
execute(context) {
|
|
82
|
-
this.apply(context, this.cellAfter, this.frameAfter);
|
|
83
|
-
}
|
|
84
|
-
undo(context) {
|
|
85
|
-
this.apply(context, this.cellBefore, this.frameBefore);
|
|
86
|
-
}
|
|
87
|
-
serialize() {
|
|
88
|
-
return {
|
|
89
|
-
id: this.id,
|
|
90
|
-
name: this.name,
|
|
91
|
-
timestamp: this.timestamp,
|
|
92
|
-
data: {
|
|
93
|
-
groupId: this.groupId,
|
|
94
|
-
cellBefore: this.cellBefore,
|
|
95
|
-
cellAfter: this.cellAfter,
|
|
96
|
-
frameBefore: this.frameBefore,
|
|
97
|
-
frameAfter: this.frameAfter,
|
|
98
|
-
},
|
|
99
|
-
};
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
const DRAG_THRESHOLD = 4;
|
|
103
|
-
const GLIDE_OFF_DELAY = 400;
|
|
104
|
-
let binderSeq = 0;
|
|
105
|
-
export function bindDashboardGrid(api, group, options = {}) {
|
|
106
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
|
|
107
|
-
ensureDashboardKitStyles();
|
|
108
|
-
const diagram = api.getModel();
|
|
109
|
-
/** The DECLARED count — the board's authored width, and the responsive cap. */
|
|
110
|
-
const maxColumns = (_a = options.columns) !== null && _a !== void 0 ? _a : 12;
|
|
111
|
-
/** The LIVE count. Responsive width evaluation and `setColumns` move it. */
|
|
112
|
-
let columns = maxColumns;
|
|
113
|
-
let rtl = options.rtl === true;
|
|
114
|
-
const responsive = options.responsive;
|
|
115
|
-
/** An explicit `setColumns` pins the count and stops width evaluation. */
|
|
116
|
-
let responsivePinned = false;
|
|
117
|
-
const gap = (_b = options.gap) !== null && _b !== void 0 ? _b : 12;
|
|
118
|
-
const padding = (_c = options.padding) !== null && _c !== void 0 ? _c : gap;
|
|
119
|
-
const baseRowHeight = (_d = options.baseRowHeight) !== null && _d !== void 0 ? _d : 110;
|
|
120
|
-
const minRowHeight = (_e = options.minRowHeight) !== null && _e !== void 0 ? _e : 28;
|
|
121
|
-
let float = (_f = options.float) !== null && _f !== void 0 ? _f : false;
|
|
122
|
-
const maxRows = options.maxRows;
|
|
123
|
-
const dragOut = (_g = options.dragOut) !== null && _g !== void 0 ? _g : 'cancel';
|
|
124
|
-
const wantHandles = options.resizeHandles !== false;
|
|
125
|
-
const designH = (_k = (_h = options.designHeight) !== null && _h !== void 0 ? _h : (_j = group.size) === null || _j === void 0 ? void 0 : _j.height) !== null && _k !== void 0 ? _k : 0;
|
|
126
|
-
let sizing = (_l = options.sizing) !== null && _l !== void 0 ? _l : 'fit';
|
|
127
|
-
let engine = new GridPackEngine([], { columns, float, maxRows });
|
|
128
|
-
let gesture = null;
|
|
129
|
-
let disposed = false;
|
|
130
|
-
/** Reentrancy guard: our own derived frame writes must not re-project. */
|
|
131
|
-
let writing = false;
|
|
132
|
-
let placeholder = null;
|
|
133
|
-
/** Foreign tile currently adopted from another binder's gesture. */
|
|
134
|
-
let adoptedGhostId = null;
|
|
135
|
-
let glideTimer = null;
|
|
136
|
-
let ghostTimer = null;
|
|
137
|
-
const frame = () => {
|
|
138
|
-
var _a, _b, _c, _d;
|
|
139
|
-
return ({
|
|
140
|
-
x: group.position.x,
|
|
141
|
-
y: group.position.y,
|
|
142
|
-
width: (_b = (_a = group.size) === null || _a === void 0 ? void 0 : _a.width) !== null && _b !== void 0 ? _b : 0,
|
|
143
|
-
height: (_d = (_c = group.size) === null || _c === void 0 ? void 0 : _c.height) !== null && _d !== void 0 ? _d : 0,
|
|
144
|
-
});
|
|
145
|
-
};
|
|
146
|
-
/** Entity size with GroupModel's optionality flattened away. */
|
|
147
|
-
const sizeOf = (e) => { var _a; return (_a = e.size) !== null && _a !== void 0 ? _a : { width: 0, height: 0 }; };
|
|
148
|
-
/** Mapping geometry. 'fit' derives row height from the LIVE frame height
|
|
149
|
-
* (which `enforceBoardHeight` pins to the design height), so an externally
|
|
150
|
-
* resized board still fits itself. */
|
|
151
|
-
const geom = () => ({
|
|
152
|
-
columns,
|
|
153
|
-
gap,
|
|
154
|
-
padding,
|
|
155
|
-
sizing,
|
|
156
|
-
baseRowHeight,
|
|
157
|
-
minRowHeight,
|
|
158
|
-
designHeight: sizing === 'fit' ? frame().height : designH,
|
|
159
|
-
rtl,
|
|
160
|
-
});
|
|
161
|
-
const rows = () => Math.max(1, engine.rows());
|
|
162
|
-
const htmlLayer = () => api.container.querySelector('.grafloria-html-layer');
|
|
163
|
-
const hostOf = (id) => {
|
|
164
|
-
const esc = typeof CSS !== 'undefined' && CSS.escape ? CSS.escape(id) : id.replace(/"/g, '\\"');
|
|
165
|
-
return api.container.querySelector(`.grafloria-node-host[data-node-id="${esc}"]`);
|
|
166
|
-
};
|
|
167
|
-
const memberEntity = (id) => { var _a; return (_a = diagram.getNode(id)) !== null && _a !== void 0 ? _a : diagram.getGroup(id); };
|
|
168
|
-
const isGroupMember = (id) => !diagram.getNode(id) && !!diagram.getGroup(id);
|
|
169
|
-
// -- cells <-> members -----------------------------------------------------
|
|
170
|
-
/** The engine item a member should enter as (cells from GridItemConfig /
|
|
171
|
-
* group metadata; spans falling back to metadata columnSpan/rowSpan; last
|
|
172
|
-
* resort: adopt from the current pixel geometry). */
|
|
173
|
-
const itemFor = (id) => {
|
|
174
|
-
var _a, _b, _c, _d, _e;
|
|
175
|
-
const node = diagram.getNode(id);
|
|
176
|
-
if (node) {
|
|
177
|
-
const spanMeta = Number((_a = node.getMetadata) === null || _a === void 0 ? void 0 : _a.call(node, 'columnSpan')) || 0;
|
|
178
|
-
const rowsMeta = Number((_b = node.getMetadata) === null || _b === void 0 ? void 0 : _b.call(node, 'rowSpan')) || 0;
|
|
179
|
-
const locked = ((_c = node.state) === null || _c === void 0 ? void 0 : _c.locked) === true;
|
|
180
|
-
const cell = cellFromGridItem((_d = node.getGridItem) === null || _d === void 0 ? void 0 : _d.call(node), {
|
|
181
|
-
w: spanMeta || 1,
|
|
182
|
-
h: rowsMeta || 1,
|
|
183
|
-
});
|
|
184
|
-
if (cell)
|
|
185
|
-
return Object.assign(Object.assign({ id }, cell), { locked });
|
|
186
|
-
const f = frame();
|
|
187
|
-
const g = geom();
|
|
188
|
-
if (node.position && (node.position.x !== 0 || node.position.y !== 0)) {
|
|
189
|
-
// First adoption from pixels: where the tile already sits. The SPAN is
|
|
190
|
-
// resolved first because the mirrored (RTL) mapping needs it to turn a
|
|
191
|
-
// left edge into a cell.
|
|
192
|
-
const s = sizeToSpan(node.size.width, node.size.height, f, g, rows());
|
|
193
|
-
const p = pointToCell(node.position.x, node.position.y, f, g, rows(), spanMeta || s.w);
|
|
194
|
-
return {
|
|
195
|
-
id,
|
|
196
|
-
x: Math.max(0, p.x),
|
|
197
|
-
y: Math.max(0, p.y),
|
|
198
|
-
w: spanMeta || s.w,
|
|
199
|
-
h: rowsMeta || s.h,
|
|
200
|
-
locked,
|
|
201
|
-
};
|
|
202
|
-
}
|
|
203
|
-
return { id, x: 0, y: 0, w: spanMeta || 1, h: rowsMeta || 1, locked, autoPosition: true };
|
|
204
|
-
}
|
|
205
|
-
const grp = diagram.getGroup(id);
|
|
206
|
-
const cell = grp
|
|
207
|
-
? cellFromGridItem((_e = grp.getMetadata) === null || _e === void 0 ? void 0 : _e.call(grp, 'gridItem'))
|
|
208
|
-
: null;
|
|
209
|
-
// Member groups are LOCKED slabs (see the module doc).
|
|
210
|
-
if (cell)
|
|
211
|
-
return Object.assign(Object.assign({ id }, cell), { locked: true });
|
|
212
|
-
return { id, x: 0, y: 0, w: columns, h: 1, locked: true, autoPosition: true };
|
|
213
|
-
};
|
|
214
|
-
/** Persist adopted cells so save/undo round-trips them. */
|
|
215
|
-
const persistAdoptedCell = (id, item) => {
|
|
216
|
-
var _a, _b;
|
|
217
|
-
const cell = { x: item.x, y: item.y, w: item.w, h: item.h };
|
|
218
|
-
const node = diagram.getNode(id);
|
|
219
|
-
if (node) {
|
|
220
|
-
if (!cellFromGridItem((_a = node.getGridItem) === null || _a === void 0 ? void 0 : _a.call(node)))
|
|
221
|
-
node.setGridItem(gridItemFromCell(cell));
|
|
222
|
-
return;
|
|
223
|
-
}
|
|
224
|
-
const grp = diagram.getGroup(id);
|
|
225
|
-
if (grp && !cellFromGridItem((_b = grp.getMetadata) === null || _b === void 0 ? void 0 : _b.call(grp, 'gridItem'))) {
|
|
226
|
-
grp.setMetadata('gridItem', gridItemFromCell(cell));
|
|
227
|
-
}
|
|
228
|
-
};
|
|
229
|
-
// -- projection: cells -> pixels -------------------------------------------
|
|
230
|
-
/** Write one member's projected rect (derived state → system write). */
|
|
231
|
-
const writeRect = (id, r) => {
|
|
232
|
-
const node = diagram.getNode(id);
|
|
233
|
-
if (node) {
|
|
234
|
-
if (Math.abs(node.position.x - r.x) > 0.25 ||
|
|
235
|
-
Math.abs(node.position.y - r.y) > 0.25 ||
|
|
236
|
-
Math.abs(node.size.width - r.width) > 0.25 ||
|
|
237
|
-
Math.abs(node.size.height - r.height) > 0.25) {
|
|
238
|
-
diagram.runSystemWrite(() => {
|
|
239
|
-
var _a;
|
|
240
|
-
node.setPosition(r.x, r.y);
|
|
241
|
-
node.setSize(r.width, r.height, (_a = node.size.depth) !== null && _a !== void 0 ? _a : 0);
|
|
242
|
-
});
|
|
243
|
-
}
|
|
244
|
-
return;
|
|
245
|
-
}
|
|
246
|
-
const grp = diagram.getGroup(id);
|
|
247
|
-
if (grp) {
|
|
248
|
-
const p = grp.position;
|
|
249
|
-
const s = sizeOf(grp);
|
|
250
|
-
if (Math.abs(p.x - r.x) > 0.25 ||
|
|
251
|
-
Math.abs(p.y - r.y) > 0.25 ||
|
|
252
|
-
Math.abs(s.width - r.width) > 0.25 ||
|
|
253
|
-
Math.abs(s.height - r.height) > 0.25) {
|
|
254
|
-
diagram.runSystemWrite(() => grp.setFrame(Object.assign({}, r)));
|
|
255
|
-
}
|
|
256
|
-
}
|
|
257
|
-
};
|
|
258
|
-
/** Enforce the board-frame height the sizing mode implies. */
|
|
259
|
-
const enforceBoardHeight = () => {
|
|
260
|
-
if (designH <= 0)
|
|
261
|
-
return;
|
|
262
|
-
const r = rows();
|
|
263
|
-
const target = sizing === 'fit'
|
|
264
|
-
? designH
|
|
265
|
-
: Math.max(designH, 2 * padding + r * baseRowHeight + (r - 1) * gap);
|
|
266
|
-
const f = frame();
|
|
267
|
-
if (Math.abs(f.height - target) > 0.5) {
|
|
268
|
-
writing = true;
|
|
269
|
-
try {
|
|
270
|
-
diagram.runSystemWrite(() => group.setFrame({ x: f.x, y: f.y, width: f.width, height: target }));
|
|
271
|
-
}
|
|
272
|
-
finally {
|
|
273
|
-
writing = false;
|
|
274
|
-
}
|
|
275
|
-
}
|
|
276
|
-
};
|
|
277
|
-
/** Project every member from its engine cells (the ghost is exempt). */
|
|
278
|
-
const project = () => {
|
|
279
|
-
enforceBoardHeight();
|
|
280
|
-
writing = true;
|
|
281
|
-
try {
|
|
282
|
-
const f = frame();
|
|
283
|
-
const g = geom();
|
|
284
|
-
const r = rows();
|
|
285
|
-
for (const item of engine.getItems()) {
|
|
286
|
-
if ((gesture === null || gesture === void 0 ? void 0 : gesture.started) && item.id === gesture.id)
|
|
287
|
-
continue; // the ghost
|
|
288
|
-
if (item.id === adoptedGhostId)
|
|
289
|
-
continue; // a ghost adopted from another binder
|
|
290
|
-
writeRect(item.id, cellToRect(item, f, g, r));
|
|
291
|
-
}
|
|
292
|
-
}
|
|
293
|
-
finally {
|
|
294
|
-
writing = false;
|
|
295
|
-
}
|
|
296
|
-
syncPlaceholder();
|
|
297
|
-
};
|
|
298
|
-
// -- placeholder / ghost chrome --------------------------------------------
|
|
299
|
-
/** The placeholder exists ONLY while a gesture is live — so at any moment
|
|
300
|
-
* the DOM holds at most one `.axdb-ph` per active gesture, not one idle
|
|
301
|
-
* div per bound board. */
|
|
302
|
-
const syncPlaceholder = () => {
|
|
303
|
-
const ghostId = adoptedGhostId !== null && adoptedGhostId !== void 0 ? adoptedGhostId : ((gesture === null || gesture === void 0 ? void 0 : gesture.started) && !gesture.removedFromBoard ? gesture.id : null);
|
|
304
|
-
const item = ghostId ? engine.getItem(ghostId) : undefined;
|
|
305
|
-
const live = !!item;
|
|
306
|
-
if (!live || !item) {
|
|
307
|
-
placeholder === null || placeholder === void 0 ? void 0 : placeholder.remove();
|
|
308
|
-
placeholder = null;
|
|
309
|
-
return;
|
|
310
|
-
}
|
|
311
|
-
const layer = htmlLayer();
|
|
312
|
-
if (!layer)
|
|
313
|
-
return;
|
|
314
|
-
if (!placeholder || placeholder.parentElement !== layer) {
|
|
315
|
-
placeholder === null || placeholder === void 0 ? void 0 : placeholder.remove();
|
|
316
|
-
placeholder = document.createElement('div');
|
|
317
|
-
placeholder.className = 'axdb-ph';
|
|
318
|
-
layer.prepend(placeholder);
|
|
319
|
-
}
|
|
320
|
-
const r = cellToRect(item, frame(), geom(), rows());
|
|
321
|
-
placeholder.style.display = 'block';
|
|
322
|
-
placeholder.style.left = `${r.x}px`;
|
|
323
|
-
placeholder.style.top = `${r.y}px`;
|
|
324
|
-
placeholder.style.width = `${r.width}px`;
|
|
325
|
-
placeholder.style.height = `${r.height}px`;
|
|
326
|
-
};
|
|
327
|
-
const armGlide = () => {
|
|
328
|
-
var _a;
|
|
329
|
-
(_a = htmlLayer()) === null || _a === void 0 ? void 0 : _a.classList.add('axdb-glide');
|
|
330
|
-
if (glideTimer)
|
|
331
|
-
clearTimeout(glideTimer);
|
|
332
|
-
};
|
|
333
|
-
const disarmGlideSoon = () => {
|
|
334
|
-
if (glideTimer)
|
|
335
|
-
clearTimeout(glideTimer);
|
|
336
|
-
glideTimer = setTimeout(() => { var _a; return (_a = htmlLayer()) === null || _a === void 0 ? void 0 : _a.classList.remove('axdb-glide'); }, GLIDE_OFF_DELAY);
|
|
337
|
-
};
|
|
338
|
-
const setGhost = (id, on) => {
|
|
339
|
-
const host = hostOf(id);
|
|
340
|
-
if (!host)
|
|
341
|
-
return;
|
|
342
|
-
if (on) {
|
|
343
|
-
if (ghostTimer)
|
|
344
|
-
clearTimeout(ghostTimer);
|
|
345
|
-
host.classList.add('axdb-ghost');
|
|
346
|
-
host.classList.remove('axdb-out');
|
|
347
|
-
}
|
|
348
|
-
else {
|
|
349
|
-
host.classList.remove('axdb-out');
|
|
350
|
-
// Keep transition-exemption through the drop write so the snap into the
|
|
351
|
-
// placeholder is INSTANT (gridstack-style), then let glides resume.
|
|
352
|
-
if (ghostTimer)
|
|
353
|
-
clearTimeout(ghostTimer);
|
|
354
|
-
ghostTimer = setTimeout(() => host.classList.remove('axdb-ghost'), 60);
|
|
355
|
-
}
|
|
356
|
-
};
|
|
357
|
-
// -- resize handles ---------------------------------------------------------
|
|
358
|
-
const syncHandles = () => {
|
|
359
|
-
var _a, _b;
|
|
360
|
-
if (!wantHandles || disposed)
|
|
361
|
-
return;
|
|
362
|
-
for (const id of (_a = group.members) !== null && _a !== void 0 ? _a : []) {
|
|
363
|
-
const node = diagram.getNode(id);
|
|
364
|
-
if (!node)
|
|
365
|
-
continue;
|
|
366
|
-
const host = hostOf(id);
|
|
367
|
-
if (!host)
|
|
368
|
-
continue;
|
|
369
|
-
const existing = host.querySelector(':scope > .axdb-rs');
|
|
370
|
-
if (((_b = node.state) === null || _b === void 0 ? void 0 : _b.locked) === true) {
|
|
371
|
-
existing === null || existing === void 0 ? void 0 : existing.remove();
|
|
372
|
-
continue;
|
|
373
|
-
}
|
|
374
|
-
const rs = existing !== null && existing !== void 0 ? existing : document.createElement('div');
|
|
375
|
-
if (!existing) {
|
|
376
|
-
rs.className = 'axdb-rs';
|
|
377
|
-
rs.setAttribute('title', 'Resize');
|
|
378
|
-
host.appendChild(rs);
|
|
379
|
-
}
|
|
380
|
-
// The grab corner mirrors with the board: bottom-right LTR, bottom-left
|
|
381
|
-
// RTL — the same corner the tile actually grows from in each direction.
|
|
382
|
-
rs.classList.toggle('axdb-rs--rtl', rtl);
|
|
383
|
-
}
|
|
384
|
-
};
|
|
385
|
-
const hostObserver = new MutationObserver(() => syncHandles());
|
|
386
|
-
// -- gesture snapshot / commit ---------------------------------------------
|
|
387
|
-
const snapshotAll = () => {
|
|
388
|
-
const cells = new Map();
|
|
389
|
-
const geoms = new Map();
|
|
390
|
-
for (const item of engine.getItems()) {
|
|
391
|
-
cells.set(item.id, { x: item.x, y: item.y, w: item.w, h: item.h });
|
|
392
|
-
const e = memberEntity(item.id);
|
|
393
|
-
if (e) {
|
|
394
|
-
const es = sizeOf(e);
|
|
395
|
-
geoms.set(item.id, {
|
|
396
|
-
pos: { x: e.position.x, y: e.position.y },
|
|
397
|
-
size: { width: es.width, height: es.height, depth: es.depth },
|
|
398
|
-
});
|
|
399
|
-
}
|
|
400
|
-
}
|
|
401
|
-
return { cells, geoms };
|
|
402
|
-
};
|
|
403
|
-
const deltasSince = (startCells, startGeom, excludeId) => {
|
|
404
|
-
const out = [];
|
|
405
|
-
for (const item of engine.getItems()) {
|
|
406
|
-
if (item.id === excludeId)
|
|
407
|
-
continue;
|
|
408
|
-
const before = startCells.get(item.id);
|
|
409
|
-
const geomBefore = startGeom.get(item.id);
|
|
410
|
-
const e = memberEntity(item.id);
|
|
411
|
-
if (!before || !geomBefore || !e)
|
|
412
|
-
continue; // items added mid-gesture commit separately
|
|
413
|
-
out.push({
|
|
414
|
-
id: item.id,
|
|
415
|
-
locked: !!item.locked,
|
|
416
|
-
isGroup: isGroupMember(item.id),
|
|
417
|
-
cellBefore: before,
|
|
418
|
-
cellAfter: { x: item.x, y: item.y, w: item.w, h: item.h },
|
|
419
|
-
posBefore: geomBefore.pos,
|
|
420
|
-
posAfter: { x: e.position.x, y: e.position.y },
|
|
421
|
-
sizeBefore: geomBefore.size,
|
|
422
|
-
sizeAfter: (({ width, height, depth }) => ({ width, height, depth }))(sizeOf(e)),
|
|
423
|
-
});
|
|
424
|
-
}
|
|
425
|
-
return out;
|
|
426
|
-
};
|
|
427
|
-
const execute = (name, commands) => {
|
|
428
|
-
if (commands.length === 0)
|
|
429
|
-
return false;
|
|
430
|
-
void api.getEngine().commandManager.execute(new BatchCommand(name, commands));
|
|
431
|
-
return true;
|
|
432
|
-
};
|
|
433
|
-
// -- membership + bounds sync ----------------------------------------------
|
|
434
|
-
const onMemberAdded = (id) => {
|
|
435
|
-
if (disposed)
|
|
436
|
-
return;
|
|
437
|
-
if (!engine.getItem(id)) {
|
|
438
|
-
const item = itemFor(id);
|
|
439
|
-
const placed = engine.add(item);
|
|
440
|
-
if (placed)
|
|
441
|
-
persistAdoptedCell(id, placed);
|
|
442
|
-
}
|
|
443
|
-
project();
|
|
444
|
-
syncHandles();
|
|
445
|
-
api.render();
|
|
446
|
-
};
|
|
447
|
-
const onMemberRemoved = (id) => {
|
|
448
|
-
if (disposed)
|
|
449
|
-
return;
|
|
450
|
-
if (gesture && gesture.id === id)
|
|
451
|
-
cancelActiveGesture(false);
|
|
452
|
-
if (!engine.getItem(id))
|
|
453
|
-
return;
|
|
454
|
-
engine.remove(id);
|
|
455
|
-
project();
|
|
456
|
-
api.render();
|
|
457
|
-
};
|
|
458
|
-
const onBoundsChanged = () => {
|
|
459
|
-
if (disposed || writing)
|
|
460
|
-
return;
|
|
461
|
-
if (evaluateResponsive())
|
|
462
|
-
return; // a column change already re-projected
|
|
463
|
-
project();
|
|
464
|
-
api.render();
|
|
465
|
-
};
|
|
466
|
-
// -- responsive column count -----------------------------------------------
|
|
467
|
-
/**
|
|
468
|
-
* The column count this board's CURRENT WIDTH asks for. Breakpoints win when
|
|
469
|
-
* given (first step, ascending, whose `w` is at least the board width);
|
|
470
|
-
* otherwise `columnWidth` divides the width. Both clamp to `[1, columnMax]`.
|
|
471
|
-
*/
|
|
472
|
-
const columnsForWidth = (width) => {
|
|
473
|
-
var _a, _b, _c;
|
|
474
|
-
if (!responsive)
|
|
475
|
-
return null;
|
|
476
|
-
const max = Math.max(1, (_a = responsive.columnMax) !== null && _a !== void 0 ? _a : maxColumns);
|
|
477
|
-
if ((_b = responsive.breakpoints) === null || _b === void 0 ? void 0 : _b.length) {
|
|
478
|
-
const steps = [...responsive.breakpoints].sort((a, b) => a.w - b.w);
|
|
479
|
-
const hit = steps.find((s) => width <= s.w);
|
|
480
|
-
const c = hit ? hit.c : max;
|
|
481
|
-
return { c: Math.max(1, Math.min(max, c)), layout: (_c = hit === null || hit === void 0 ? void 0 : hit.layout) !== null && _c !== void 0 ? _c : responsive.layout };
|
|
482
|
-
}
|
|
483
|
-
if (responsive.columnWidth && responsive.columnWidth > 0) {
|
|
484
|
-
const c = Math.round(width / responsive.columnWidth);
|
|
485
|
-
return { c: Math.max(1, Math.min(max, c)), layout: responsive.layout };
|
|
486
|
-
}
|
|
487
|
-
return null;
|
|
488
|
-
};
|
|
489
|
-
/**
|
|
490
|
-
* Re-derive the column count from the board width and apply it. Returns true
|
|
491
|
-
* when the count actually changed (the caller then skips its own project(),
|
|
492
|
-
* because applyColumns already re-projected everything).
|
|
493
|
-
*/
|
|
494
|
-
const evaluateResponsive = () => {
|
|
495
|
-
var _a, _b;
|
|
496
|
-
if (!responsive || responsivePinned || disposed || gesture)
|
|
497
|
-
return false;
|
|
498
|
-
const want = columnsForWidth(frame().width);
|
|
499
|
-
if (!want || want.c === columns)
|
|
500
|
-
return false;
|
|
501
|
-
return applyColumns(want.c, (_b = (_a = want.layout) !== null && _a !== void 0 ? _a : responsive.layout) !== null && _b !== void 0 ? _b : 'moveScale');
|
|
502
|
-
};
|
|
503
|
-
/**
|
|
504
|
-
* Cells written by a column change are DERIVED STATE, not an edit: they go
|
|
505
|
-
* through `runSystemWrite` exactly like the pixel projection does, never
|
|
506
|
-
* through the command stack. A browser resize must not be undoable, and the
|
|
507
|
-
* authored layout is safe regardless — the engine's cache still holds it,
|
|
508
|
-
* and `saveLayout()` serialises from the widest cached count.
|
|
509
|
-
*/
|
|
510
|
-
const persistLiveCells = () => {
|
|
511
|
-
writing = true;
|
|
512
|
-
try {
|
|
513
|
-
diagram.runSystemWrite(() => {
|
|
514
|
-
var _a;
|
|
515
|
-
for (const item of engine.getItems()) {
|
|
516
|
-
const cell = { x: item.x, y: item.y, w: item.w, h: item.h };
|
|
517
|
-
const node = diagram.getNode(item.id);
|
|
518
|
-
if (node) {
|
|
519
|
-
node.setGridItem(gridItemFromCell(cell));
|
|
520
|
-
continue;
|
|
521
|
-
}
|
|
522
|
-
(_a = diagram.getGroup(item.id)) === null || _a === void 0 ? void 0 : _a.setMetadata('gridItem', gridItemFromCell(cell));
|
|
523
|
-
}
|
|
524
|
-
});
|
|
525
|
-
}
|
|
526
|
-
finally {
|
|
527
|
-
writing = false;
|
|
528
|
-
}
|
|
529
|
-
};
|
|
530
|
-
const applyColumns = (n, layout) => {
|
|
531
|
-
var _a;
|
|
532
|
-
const prev = columns;
|
|
533
|
-
if (!engine.setColumns(n, layout))
|
|
534
|
-
return false;
|
|
535
|
-
columns = engine.columns;
|
|
536
|
-
persistLiveCells();
|
|
537
|
-
armGlide();
|
|
538
|
-
project();
|
|
539
|
-
syncPlaceholder();
|
|
540
|
-
disarmGlideSoon();
|
|
541
|
-
api.renderNow();
|
|
542
|
-
(_a = options.onColumnsChange) === null || _a === void 0 ? void 0 : _a.call(options, columns, prev);
|
|
543
|
-
return true;
|
|
544
|
-
};
|
|
545
|
-
/**
|
|
546
|
-
* The canvas container resizing is the OTHER trigger for re-evaluation: a
|
|
547
|
-
* page that sizes its boards from the viewport changes the frame in the same
|
|
548
|
-
* turn, and a page that does not still wants the check to run. The binder
|
|
549
|
-
* owns this — pages should never have to wire a ResizeObserver for it.
|
|
550
|
-
*/
|
|
551
|
-
const containerObserver = responsive && typeof ResizeObserver !== 'undefined'
|
|
552
|
-
? new ResizeObserver(() => {
|
|
553
|
-
if (disposed)
|
|
554
|
-
return;
|
|
555
|
-
evaluateResponsive();
|
|
556
|
-
})
|
|
557
|
-
: null;
|
|
558
|
-
// -- board hit-testing ------------------------------------------------------
|
|
559
|
-
const boardVisualHeight = () => {
|
|
560
|
-
const f = frame();
|
|
561
|
-
const g = geom();
|
|
562
|
-
return Math.max(f.height, 2 * padding + rows() * (rowHeightFor(g, rows()) + gap) - gap);
|
|
563
|
-
};
|
|
564
|
-
const worldInsideBoard = (x, y) => {
|
|
565
|
-
const f = frame();
|
|
566
|
-
return x >= f.x && x <= f.x + f.width && y >= f.y && y <= f.y + boardVisualHeight();
|
|
567
|
-
};
|
|
568
|
-
/** Small overshoots CLAMP onto the board instead of counting as off-board —
|
|
569
|
-
* the plan prototype cannot leave its board at all (cells clamp at the
|
|
570
|
-
* edges), so a 60px slip past the frame must not dim or delete. */
|
|
571
|
-
const EDGE_GRACE = 60;
|
|
572
|
-
const worldInsideBoardGrace = (x, y) => {
|
|
573
|
-
if (worldInsideBoardExtended(x, y))
|
|
574
|
-
return true;
|
|
575
|
-
const f = frame();
|
|
576
|
-
const band = rowHeightFor(geom(), rows()) + gap;
|
|
577
|
-
return (x >= f.x - EDGE_GRACE &&
|
|
578
|
-
x <= f.x + f.width + EDGE_GRACE &&
|
|
579
|
-
y >= f.y - EDGE_GRACE &&
|
|
580
|
-
y <= f.y + boardVisualHeight() + band + EDGE_GRACE);
|
|
581
|
-
};
|
|
582
|
-
/** One extra row of grace below the frame (gridstack's extra drag row). */
|
|
583
|
-
const worldInsideBoardExtended = (x, y) => {
|
|
584
|
-
if (worldInsideBoard(x, y))
|
|
585
|
-
return true;
|
|
586
|
-
const f = frame();
|
|
587
|
-
const band = rowHeightFor(geom(), rows()) + gap;
|
|
588
|
-
return x >= f.x && x <= f.x + f.width && y >= f.y && y <= f.y + boardVisualHeight() + band;
|
|
589
|
-
};
|
|
590
|
-
const insideMemberGroupFrame = (x, y) => {
|
|
591
|
-
var _a;
|
|
592
|
-
for (const id of (_a = group.members) !== null && _a !== void 0 ? _a : []) {
|
|
593
|
-
const grp = diagram.getGroup(id);
|
|
594
|
-
if (!grp)
|
|
595
|
-
continue;
|
|
596
|
-
const p = grp.position;
|
|
597
|
-
const s = sizeOf(grp);
|
|
598
|
-
if (x >= p.x && x <= p.x + s.width && y >= p.y && y <= p.y + s.height)
|
|
599
|
-
return true;
|
|
600
|
-
}
|
|
601
|
-
return false;
|
|
602
|
-
};
|
|
603
|
-
const capturePointer = (pointerId) => {
|
|
604
|
-
var _a, _b;
|
|
605
|
-
if (pointerId === null)
|
|
606
|
-
return;
|
|
607
|
-
try {
|
|
608
|
-
(_b = (_a = api.container).setPointerCapture) === null || _b === void 0 ? void 0 : _b.call(_a, pointerId);
|
|
609
|
-
}
|
|
610
|
-
catch (_c) {
|
|
611
|
-
/* capture is best-effort */
|
|
612
|
-
}
|
|
613
|
-
};
|
|
614
|
-
const releasePointer = (pointerId) => {
|
|
615
|
-
var _a, _b;
|
|
616
|
-
if (pointerId === null)
|
|
617
|
-
return;
|
|
618
|
-
try {
|
|
619
|
-
(_b = (_a = api.container).releasePointerCapture) === null || _b === void 0 ? void 0 : _b.call(_a, pointerId);
|
|
620
|
-
}
|
|
621
|
-
catch (_c) {
|
|
622
|
-
/* nothing to release */
|
|
623
|
-
}
|
|
624
|
-
};
|
|
625
|
-
// -- the gesture machine ----------------------------------------------------
|
|
626
|
-
const beginGestureVisuals = (g) => {
|
|
627
|
-
engine.beginGesture();
|
|
628
|
-
const snap = snapshotAll();
|
|
629
|
-
g.startCells = snap.cells;
|
|
630
|
-
g.startGeom = snap.geoms;
|
|
631
|
-
g.started = true;
|
|
632
|
-
armGlide();
|
|
633
|
-
if (g.kind !== 'palette') {
|
|
634
|
-
setGhost(g.id, true);
|
|
635
|
-
g.hostEl = hostOf(g.id);
|
|
636
|
-
capturePointer(g.pointerId);
|
|
637
|
-
api.container.style.cursor = g.kind === 'resize' ? 'nwse-resize' : 'grabbing';
|
|
638
|
-
}
|
|
639
|
-
syncPlaceholder();
|
|
640
|
-
};
|
|
641
|
-
/**
|
|
642
|
-
* SAME-EVENT ghost fast-path — the vanilla-parity fix. The prototype writes
|
|
643
|
-
* the tile's style inside the pointermove handler, so the ghost sits at 0px
|
|
644
|
-
* behind the cursor; writing only the MODEL leaves the host to the next
|
|
645
|
-
* render frame (~7.5px measured trailing at hand speed). Hosts live in the
|
|
646
|
-
* HTML layer whose transform carries the camera, so world px == style px;
|
|
647
|
-
* the renderer's next pass writes the identical values from the model — no
|
|
648
|
-
* fight, just no wait.
|
|
649
|
-
*/
|
|
650
|
-
const ghostStyleFastPath = (g, rect) => {
|
|
651
|
-
const el = g.hostEl;
|
|
652
|
-
if (!el)
|
|
653
|
-
return;
|
|
654
|
-
if (rect.x !== undefined)
|
|
655
|
-
el.style.left = `${rect.x}px`;
|
|
656
|
-
if (rect.y !== undefined)
|
|
657
|
-
el.style.top = `${rect.y}px`;
|
|
658
|
-
if (rect.width !== undefined)
|
|
659
|
-
el.style.width = `${rect.width}px`;
|
|
660
|
-
if (rect.height !== undefined)
|
|
661
|
-
el.style.height = `${rect.height}px`;
|
|
662
|
-
};
|
|
663
|
-
const cleanupGestureVisuals = (g) => {
|
|
664
|
-
var _a;
|
|
665
|
-
if (g.kind !== 'palette')
|
|
666
|
-
setGhost(g.id, false);
|
|
667
|
-
disarmGlideSoon();
|
|
668
|
-
releasePointer(g.pointerId);
|
|
669
|
-
api.container.style.cursor = '';
|
|
670
|
-
(_a = g.chip) === null || _a === void 0 ? void 0 : _a.remove();
|
|
671
|
-
placeholder === null || placeholder === void 0 ? void 0 : placeholder.remove();
|
|
672
|
-
placeholder = null;
|
|
673
|
-
};
|
|
674
|
-
const commitGesture = (g) => {
|
|
675
|
-
var _a;
|
|
676
|
-
// SETTLE FIRST, then measure. Mid-drag the board can transiently hold more
|
|
677
|
-
// rows than the drop keeps (hover above a locked slab pushes everything
|
|
678
|
-
// down), and fit-mode row height shrinks with it — every displaced tile is
|
|
679
|
-
// re-projected at that TRANSIENT height. The old order computed the commit
|
|
680
|
-
// deltas from those pixels and, when the engine settled back with no cell
|
|
681
|
-
// changes, nothing ever wrote the pixels back: a refused drop left the
|
|
682
|
-
// whole board at the mid-drag height, KPI value lines clipped to nothing
|
|
683
|
-
// (live audit repro: drag the trend chart above the KPI row → every widget
|
|
684
|
-
// 122px→85px with identical cells). endGesture() keeps the settled cells;
|
|
685
|
-
// project() then derives every member's pixels — the ghost included — from
|
|
686
|
-
// that truth, so the deltas below record settled geometry and undo/redo
|
|
687
|
-
// both replay it faithfully.
|
|
688
|
-
engine.endGesture();
|
|
689
|
-
cleanupGestureVisuals(g);
|
|
690
|
-
gesture = null;
|
|
691
|
-
project();
|
|
692
|
-
const deltas = deltasSince(g.startCells, g.startGeom);
|
|
693
|
-
const commands = buildCommitCommands(deltas);
|
|
694
|
-
if (g.esc && g.esc.rowsAdded !== 0) {
|
|
695
|
-
commands.push(new SetGroupCellCommand(group.id, g.esc.cellBefore, g.esc.cellAfter, g.esc.frameBefore, g.esc.frameAfter));
|
|
696
|
-
}
|
|
697
|
-
const changed = execute(g.kind === 'resize' ? 'Resize widget' : 'Move widget', commands);
|
|
698
|
-
api.renderNow();
|
|
699
|
-
(_a = options.onGesture) === null || _a === void 0 ? void 0 : _a.call(options, { type: 'commit', kind: g.kind, nodeId: g.id, changed });
|
|
700
|
-
};
|
|
701
|
-
const cancelActiveGesture = (notify = true) => {
|
|
702
|
-
var _a, _b, _c;
|
|
703
|
-
const g = gesture;
|
|
704
|
-
if (!g)
|
|
705
|
-
return;
|
|
706
|
-
gesture = null;
|
|
707
|
-
if (g.kind !== 'palette' && g.leg) {
|
|
708
|
-
g.leg.adopted.abort(); // target board back to its pre-entry layout
|
|
709
|
-
g.leg = null;
|
|
710
|
-
}
|
|
711
|
-
if (g.kind !== 'palette' && g.esc && g.esc.rowsAdded !== 0) {
|
|
712
|
-
g.esc.peer.resizeMemberBy(group.id, -g.esc.rowsAdded); // slab back down
|
|
713
|
-
g.esc = null;
|
|
714
|
-
}
|
|
715
|
-
if (g.started) {
|
|
716
|
-
if (g.removedFromBoard || g.kind === 'palette') {
|
|
717
|
-
// The engine cannot resurrect a removed item — rebuild from the
|
|
718
|
-
// gesture-start snapshot (cells are pure data; the constructor
|
|
719
|
-
// honours legal layouts verbatim).
|
|
720
|
-
engine.endGesture();
|
|
721
|
-
const items = [];
|
|
722
|
-
for (const [id, c] of g.startCells) {
|
|
723
|
-
const lockedNode = ((_b = (_a = diagram.getNode(id)) === null || _a === void 0 ? void 0 : _a.state) === null || _b === void 0 ? void 0 : _b.locked) === true;
|
|
724
|
-
items.push(Object.assign(Object.assign({ id }, c), { locked: lockedNode || isGroupMember(id) }));
|
|
725
|
-
}
|
|
726
|
-
engine = new GridPackEngine(items, { columns, float, maxRows });
|
|
727
|
-
}
|
|
728
|
-
else {
|
|
729
|
-
engine.cancelGesture();
|
|
730
|
-
}
|
|
731
|
-
// Restore every pixel to its gesture-start state.
|
|
732
|
-
writing = true;
|
|
733
|
-
try {
|
|
734
|
-
for (const [id, snap] of g.startGeom) {
|
|
735
|
-
const e = memberEntity(id);
|
|
736
|
-
if (!e)
|
|
737
|
-
continue;
|
|
738
|
-
const node = diagram.getNode(id);
|
|
739
|
-
diagram.runSystemWrite(() => {
|
|
740
|
-
var _a;
|
|
741
|
-
if (node) {
|
|
742
|
-
node.setPosition(snap.pos.x, snap.pos.y);
|
|
743
|
-
node.setSize(snap.size.width, snap.size.height, (_a = snap.size.depth) !== null && _a !== void 0 ? _a : 0);
|
|
744
|
-
}
|
|
745
|
-
else {
|
|
746
|
-
e.setFrame({
|
|
747
|
-
x: snap.pos.x,
|
|
748
|
-
y: snap.pos.y,
|
|
749
|
-
width: snap.size.width,
|
|
750
|
-
height: snap.size.height,
|
|
751
|
-
});
|
|
752
|
-
}
|
|
753
|
-
});
|
|
754
|
-
}
|
|
755
|
-
}
|
|
756
|
-
finally {
|
|
757
|
-
writing = false;
|
|
758
|
-
}
|
|
759
|
-
}
|
|
760
|
-
else {
|
|
761
|
-
engine.endGesture();
|
|
762
|
-
}
|
|
763
|
-
cleanupGestureVisuals(g);
|
|
764
|
-
enforceBoardHeight();
|
|
765
|
-
api.renderNow();
|
|
766
|
-
if (notify) {
|
|
767
|
-
(_c = options.onGesture) === null || _c === void 0 ? void 0 : _c.call(options, { type: 'cancel', kind: g.kind, nodeId: g.id, changed: false });
|
|
768
|
-
}
|
|
769
|
-
};
|
|
770
|
-
/** Centre a w×h-span tile's top-left under the cursor, in world px. */
|
|
771
|
-
const centredTopLeft = (worldX, worldY, spans) => {
|
|
772
|
-
const f = frame();
|
|
773
|
-
const g = geom();
|
|
774
|
-
const cu = columnUnitFor(g, f.width);
|
|
775
|
-
const rh = rowHeightFor(g, rows());
|
|
776
|
-
return {
|
|
777
|
-
x: worldX - (spans.w * (cu + gap) - gap) / 2,
|
|
778
|
-
y: worldY - (spans.h * (rh + gap) - gap) / 2,
|
|
779
|
-
};
|
|
780
|
-
};
|
|
781
|
-
const onToolMove = (ev) => {
|
|
782
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _j;
|
|
783
|
-
const g = gesture;
|
|
784
|
-
if (!g || g.kind === 'palette')
|
|
785
|
-
return;
|
|
786
|
-
if (!g.started) {
|
|
787
|
-
const dx = ev.screen.x - g.downClient.x;
|
|
788
|
-
const dy = ev.screen.y - g.downClient.y;
|
|
789
|
-
if (Math.abs(dx) + Math.abs(dy) < DRAG_THRESHOLD)
|
|
790
|
-
return;
|
|
791
|
-
beginGestureVisuals(g);
|
|
792
|
-
}
|
|
793
|
-
if (g.kind === 'move') {
|
|
794
|
-
const desired = { x: ev.world.x - g.grab.dx, y: ev.world.y - g.grab.dy };
|
|
795
|
-
g.node.setPosition(desired.x, desired.y);
|
|
796
|
-
ghostStyleFastPath(g, desired);
|
|
797
|
-
g.lastWorld = { x: ev.world.x, y: ev.world.y };
|
|
798
|
-
g.lastScreen = { x: ev.screen.x, y: ev.screen.y };
|
|
799
|
-
// Deepest board under the pointer wins: the nested KPI strip beats the
|
|
800
|
-
// tab that contains it; a foreign board beats "outside". Strict frames
|
|
801
|
-
// first; the one-row grace band below each board (gridstack's extra
|
|
802
|
-
// drag row) is consulted only when NO strict frame matched — so "under
|
|
803
|
-
// the last row" appends instead of reading as off-board.
|
|
804
|
-
const strictSelf = worldInsideBoard(ev.world.x, ev.world.y);
|
|
805
|
-
let peer = peerAt(ev.world.x, ev.world.y);
|
|
806
|
-
let inside = strictSelf;
|
|
807
|
-
if (!strictSelf && !peer) {
|
|
808
|
-
if (worldInsideBoardExtended(ev.world.x, ev.world.y))
|
|
809
|
-
inside = true;
|
|
810
|
-
else
|
|
811
|
-
peer = peerAt(ev.world.x, ev.world.y, true);
|
|
812
|
-
// Last resort: the grace band — a small slip past the edge stays ON
|
|
813
|
-
// this board (the engine clamps the cell; prototype parity).
|
|
814
|
-
if (!inside && !peer && worldInsideBoardGrace(ev.world.x, ev.world.y))
|
|
815
|
-
inside = true;
|
|
816
|
-
}
|
|
817
|
-
const selfWins = inside && (!peer || boardArea() <= peer.frameArea());
|
|
818
|
-
if (peer && !selfWins) {
|
|
819
|
-
// -- HANDOFF: the pointer is over another board -------------------
|
|
820
|
-
if (g.leg && g.leg.peer === peer) {
|
|
821
|
-
g.leg.adopted.move(ev.world);
|
|
822
|
-
}
|
|
823
|
-
else {
|
|
824
|
-
if (g.leg) {
|
|
825
|
-
g.leg.adopted.abort();
|
|
826
|
-
g.leg = null;
|
|
827
|
-
}
|
|
828
|
-
if (!g.removedFromBoard) {
|
|
829
|
-
g.removedFromBoard = true;
|
|
830
|
-
engine.remove(g.id); // survivors settle home (gesture memory intact)
|
|
831
|
-
project();
|
|
832
|
-
}
|
|
833
|
-
const adopted = peer.adopt(g.node, ev.world, {
|
|
834
|
-
width: g.node.size.width,
|
|
835
|
-
height: g.node.size.height,
|
|
836
|
-
});
|
|
837
|
-
if (adopted) {
|
|
838
|
-
(_a = hostOf(g.id)) === null || _a === void 0 ? void 0 : _a.classList.remove('axdb-out');
|
|
839
|
-
g.leg = { peer, adopted };
|
|
840
|
-
g.leg.adopted.move(ev.world);
|
|
841
|
-
}
|
|
842
|
-
else {
|
|
843
|
-
// Bounded/full board refused the adoption: dim = will snap home.
|
|
844
|
-
(_b = hostOf(g.id)) === null || _b === void 0 ? void 0 : _b.classList.add('axdb-out');
|
|
845
|
-
}
|
|
846
|
-
}
|
|
847
|
-
}
|
|
848
|
-
else if (inside) {
|
|
849
|
-
// -- back on (or still on) our own board --------------------------
|
|
850
|
-
if (g.leg) {
|
|
851
|
-
g.leg.adopted.abort();
|
|
852
|
-
g.leg = null;
|
|
853
|
-
}
|
|
854
|
-
if (g.removedFromBoard) {
|
|
855
|
-
g.removedFromBoard = false;
|
|
856
|
-
(_c = hostOf(g.id)) === null || _c === void 0 ? void 0 : _c.classList.remove('axdb-out');
|
|
857
|
-
const cell = pointToCell(desired.x, desired.y, frame(), geom(), rows(), g.spans.w);
|
|
858
|
-
// Re-enter at the bottom edge (collision-free), then take the cursor
|
|
859
|
-
// cell GATELESSLY — a first placement skips the anti-jitter gate.
|
|
860
|
-
engine.add({ id: g.id, x: 0, y: engine.rows(), w: g.spans.w, h: g.spans.h });
|
|
861
|
-
engine.moveCheck(g.id, cell.x, cell.y, { gate: false });
|
|
862
|
-
project();
|
|
863
|
-
}
|
|
864
|
-
else {
|
|
865
|
-
const spanW = (_e = (_d = engine.getItem(g.id)) === null || _d === void 0 ? void 0 : _d.w) !== null && _e !== void 0 ? _e : g.spans.w;
|
|
866
|
-
const cell = pointToCell(desired.x, desired.y, frame(), geom(), rows(), spanW);
|
|
867
|
-
if (engine.moveCheck(g.id, cell.x, cell.y).changed)
|
|
868
|
-
project();
|
|
869
|
-
}
|
|
870
|
-
}
|
|
871
|
-
else {
|
|
872
|
-
// -- outside every board ------------------------------------------
|
|
873
|
-
if (g.leg) {
|
|
874
|
-
g.leg.adopted.abort();
|
|
875
|
-
g.leg = null;
|
|
876
|
-
}
|
|
877
|
-
if (!g.removedFromBoard) {
|
|
878
|
-
g.removedFromBoard = true;
|
|
879
|
-
engine.remove(g.id); // survivors settle home; cells minted nowhere
|
|
880
|
-
(_f = hostOf(g.id)) === null || _f === void 0 ? void 0 : _f.classList.add('axdb-out');
|
|
881
|
-
project();
|
|
882
|
-
}
|
|
883
|
-
}
|
|
884
|
-
syncPlaceholder();
|
|
885
|
-
return;
|
|
886
|
-
}
|
|
887
|
-
// resize: fluid pixel preview on the ghost, cell-stepped live push.
|
|
888
|
-
//
|
|
889
|
-
// RTL: the corner handle is on the BOTTOM-LEFT and the tile grows
|
|
890
|
-
// leftwards, so the horizontal delta inverts and the tile's RIGHT edge is
|
|
891
|
-
// what stays pinned. Everything below this line — the span rounding, the
|
|
892
|
-
// engine's resizeCheck, the maxRows escalation — is in CELLS and needs no
|
|
893
|
-
// mirroring at all.
|
|
894
|
-
const dw = rtl ? g.downWorld.x - ev.world.x : ev.world.x - g.downWorld.x;
|
|
895
|
-
const dh = ev.world.y - g.downWorld.y;
|
|
896
|
-
const f = frame();
|
|
897
|
-
const gg = geom();
|
|
898
|
-
const minW = Math.max(8, columnUnitFor(gg, f.width));
|
|
899
|
-
let w = Math.max(minW, g.startSize.width + dw);
|
|
900
|
-
// The height min-clamp is applied AFTER the escalation logic below: on a
|
|
901
|
-
// grown strip "one current row" IS the whole strip, and clamping the
|
|
902
|
-
// fluid pull to it made the de-escalation threshold unreachable — the
|
|
903
|
-
// ratchet's second disguise (grow committed; a fresh shrink gesture could
|
|
904
|
-
// never pull low enough to ask the parent for a row back).
|
|
905
|
-
let h = g.startSize.height + dh;
|
|
906
|
-
// NESTED HEIGHT ESCALATION (live report: "i cant increase height"). A
|
|
907
|
-
// bounded strip cannot grow a tile taller than itself — so pulling
|
|
908
|
-
// clearly past its bottom GROWS THE STRIP: the slab gains a row in the
|
|
909
|
-
// parent board (all tiles inside get taller together); pulling clearly
|
|
910
|
-
// back up REMOVES a row again. SYMMETRIC AND STATELESS ACROSS GESTURES:
|
|
911
|
-
// the first version only de-escalated inside the ledger of the gesture
|
|
912
|
-
// that grew ("rowsAdded > 0"), so grow → release → try to shrink was a
|
|
913
|
-
// RATCHET — the strip could only ever get taller (live report: "Total
|
|
914
|
-
// Revenue widget size can only increase"). Direction is now decided from
|
|
915
|
-
// the strip's CURRENT slab rows, whatever gesture created them; the
|
|
916
|
-
// ledger just accumulates this gesture's net change for the one-batch
|
|
917
|
-
// commit and for Escape.
|
|
918
|
-
if (maxRows !== undefined && g.kind === 'resize') {
|
|
919
|
-
const parent = parentPeer();
|
|
920
|
-
if (parent) {
|
|
921
|
-
const visual = boardVisualHeight();
|
|
922
|
-
const slabRows = (_h = (_g = parent.memberCell(group.id)) === null || _g === void 0 ? void 0 : _g.h) !== null && _h !== void 0 ? _h : 1;
|
|
923
|
-
const rowPx = visual / Math.max(1, slabRows);
|
|
924
|
-
const record = (res, d) => {
|
|
925
|
-
if (!res.changed || !res.cellBefore || !res.cellAfter || !res.frameBefore || !res.frameAfter)
|
|
926
|
-
return;
|
|
927
|
-
if (!g.esc) {
|
|
928
|
-
g.esc = {
|
|
929
|
-
peer: parent,
|
|
930
|
-
rowsAdded: 0,
|
|
931
|
-
cellBefore: res.cellBefore,
|
|
932
|
-
frameBefore: res.frameBefore,
|
|
933
|
-
cellAfter: res.cellAfter,
|
|
934
|
-
frameAfter: res.frameAfter,
|
|
935
|
-
};
|
|
936
|
-
}
|
|
937
|
-
g.esc.rowsAdded += d;
|
|
938
|
-
g.esc.cellAfter = res.cellAfter;
|
|
939
|
-
g.esc.frameAfter = res.frameAfter;
|
|
940
|
-
project();
|
|
941
|
-
};
|
|
942
|
-
if (h > visual + 24) {
|
|
943
|
-
record(parent.resizeMemberBy(group.id, +1), +1);
|
|
944
|
-
}
|
|
945
|
-
else if (slabRows > 1 && h < visual - rowPx * 0.7) {
|
|
946
|
-
record(parent.resizeMemberBy(group.id, -1), -1);
|
|
947
|
-
}
|
|
948
|
-
}
|
|
949
|
-
}
|
|
950
|
-
// The fluid preview must not outrun what the board can accept: on a
|
|
951
|
-
// bounded strip an unclamped ghost ballooned to 273px while the engine
|
|
952
|
-
// (rightly) refused every cell — visually indistinguishable from the
|
|
953
|
-
// squeeze bug it replaced. Clamp to the tile's maximum legal rect.
|
|
954
|
-
// (Escalation above may have just grown OR shrunk the board — re-read.)
|
|
955
|
-
const fNow = frame();
|
|
956
|
-
const ggNow = geom();
|
|
957
|
-
h = Math.max(Math.max(8, rowHeightFor(ggNow, rows())), h);
|
|
958
|
-
const itemNow = engine.getItem(g.id);
|
|
959
|
-
if (itemNow) {
|
|
960
|
-
const cuNow = columnUnitFor(ggNow, fNow.width);
|
|
961
|
-
const rhNow = rowHeightFor(ggNow, rows());
|
|
962
|
-
w = Math.min(w, (columns - itemNow.x) * (cuNow + gap) - gap);
|
|
963
|
-
if (maxRows !== undefined) {
|
|
964
|
-
h = Math.min(h, Math.max(1, maxRows - itemNow.y) * (rhNow + gap) - gap);
|
|
965
|
-
}
|
|
966
|
-
}
|
|
967
|
-
g.node.setSize(w, h, (_j = g.node.size.depth) !== null && _j !== void 0 ? _j : 0);
|
|
968
|
-
if (rtl) {
|
|
969
|
-
// Keep the RIGHT edge pinned: mirrored, that is the tile's origin. The
|
|
970
|
-
// start position is recoverable from the grab offset, so this needs no
|
|
971
|
-
// extra gesture state.
|
|
972
|
-
const startLeft = g.downWorld.x - g.grab.dx;
|
|
973
|
-
const right = startLeft + g.startSize.width;
|
|
974
|
-
g.node.setPosition(right - w, g.node.position.y);
|
|
975
|
-
ghostStyleFastPath(g, { x: right - w, width: w, height: h });
|
|
976
|
-
}
|
|
977
|
-
else {
|
|
978
|
-
ghostStyleFastPath(g, { width: w, height: h });
|
|
979
|
-
}
|
|
980
|
-
const spanF = maxRows !== undefined ? frame() : f;
|
|
981
|
-
const spanG = maxRows !== undefined ? geom() : gg;
|
|
982
|
-
const span = sizeToSpan(w, h, spanF, spanG, rows());
|
|
983
|
-
if (engine.resizeCheck(g.id, span.w, span.h).changed)
|
|
984
|
-
project();
|
|
985
|
-
syncPlaceholder();
|
|
986
|
-
};
|
|
987
|
-
const onToolUp = () => {
|
|
988
|
-
var _a, _b, _c, _d, _e, _f;
|
|
989
|
-
const g = gesture;
|
|
990
|
-
if (!g || g.kind === 'palette')
|
|
991
|
-
return;
|
|
992
|
-
if (!g.started) {
|
|
993
|
-
gesture = null; // a plain click — the page's own click-to-focus handles it
|
|
994
|
-
return;
|
|
995
|
-
}
|
|
996
|
-
if (g.leg) {
|
|
997
|
-
// -- CROSS-CONTAINER COMMIT: one batch across both boards -----------
|
|
998
|
-
const fin = g.leg.adopted.finalize();
|
|
999
|
-
if (!fin) {
|
|
1000
|
-
cancelActiveGesture();
|
|
1001
|
-
return;
|
|
1002
|
-
}
|
|
1003
|
-
const targetGroupId = g.leg.adopted.groupId;
|
|
1004
|
-
// Land the ghost on its target rect before the geometry deltas read it.
|
|
1005
|
-
writing = true;
|
|
1006
|
-
try {
|
|
1007
|
-
diagram.runSystemWrite(() => {
|
|
1008
|
-
var _a;
|
|
1009
|
-
g.node.setPosition(fin.rect.x, fin.rect.y);
|
|
1010
|
-
g.node.setSize(fin.rect.width, fin.rect.height, (_a = g.node.size.depth) !== null && _a !== void 0 ? _a : 0);
|
|
1011
|
-
});
|
|
1012
|
-
}
|
|
1013
|
-
finally {
|
|
1014
|
-
writing = false;
|
|
1015
|
-
}
|
|
1016
|
-
const sourceDisplaced = buildCommitCommands(deltasSince(g.startCells, g.startGeom, g.id));
|
|
1017
|
-
const before = g.startCells.get(g.id);
|
|
1018
|
-
const geomBefore = g.startGeom.get(g.id);
|
|
1019
|
-
const crossing = [
|
|
1020
|
-
...sourceDisplaced,
|
|
1021
|
-
new RemoveFromGroupCommand(group.id, g.id),
|
|
1022
|
-
new AddToGroupCommand(targetGroupId, g.id),
|
|
1023
|
-
...buildCommitCommands([
|
|
1024
|
-
{
|
|
1025
|
-
id: g.id,
|
|
1026
|
-
locked: false,
|
|
1027
|
-
isGroup: false,
|
|
1028
|
-
cellBefore: before !== null && before !== void 0 ? before : fin.cell,
|
|
1029
|
-
cellAfter: fin.cell,
|
|
1030
|
-
posBefore: (_a = geomBefore === null || geomBefore === void 0 ? void 0 : geomBefore.pos) !== null && _a !== void 0 ? _a : { x: fin.rect.x, y: fin.rect.y },
|
|
1031
|
-
posAfter: { x: fin.rect.x, y: fin.rect.y },
|
|
1032
|
-
sizeBefore: (_b = geomBefore === null || geomBefore === void 0 ? void 0 : geomBefore.size) !== null && _b !== void 0 ? _b : { width: fin.rect.width, height: fin.rect.height },
|
|
1033
|
-
sizeAfter: { width: fin.rect.width, height: fin.rect.height },
|
|
1034
|
-
},
|
|
1035
|
-
]),
|
|
1036
|
-
];
|
|
1037
|
-
execute('Move widget', crossing);
|
|
1038
|
-
engine.endGesture();
|
|
1039
|
-
cleanupGestureVisuals(g);
|
|
1040
|
-
gesture = null;
|
|
1041
|
-
enforceBoardHeight();
|
|
1042
|
-
api.renderNow();
|
|
1043
|
-
(_c = options.onGesture) === null || _c === void 0 ? void 0 : _c.call(options, { type: 'commit', kind: g.kind, nodeId: g.id, changed: true });
|
|
1044
|
-
return;
|
|
1045
|
-
}
|
|
1046
|
-
if (g.removedFromBoard && dragOut === 'cancel') {
|
|
1047
|
-
// Released outside every board on a snap-home board: full restore,
|
|
1048
|
-
// nothing committed (the parked-outside release).
|
|
1049
|
-
cancelActiveGesture();
|
|
1050
|
-
return;
|
|
1051
|
-
}
|
|
1052
|
-
// 'remove' fires ONLY for a release genuinely outside every board. A
|
|
1053
|
-
// refused adoption (full strip) leaves removedFromBoard=true while the
|
|
1054
|
-
// pointer is still over a board — releasing there must snap home, not
|
|
1055
|
-
// delete the tile (the battery's S5 caught exactly that deletion).
|
|
1056
|
-
const releasedOutsideAll = !g.lastWorld ||
|
|
1057
|
-
(!worldInsideBoardGrace(g.lastWorld.x, g.lastWorld.y) &&
|
|
1058
|
-
!peerAt(g.lastWorld.x, g.lastWorld.y, true));
|
|
1059
|
-
const inRemoveZone = !options.removeZone ||
|
|
1060
|
-
(g.lastScreen && g.lastWorld && options.removeZone(g.lastScreen, g.lastWorld));
|
|
1061
|
-
if (g.removedFromBoard && dragOut === 'remove' && (!releasedOutsideAll || !inRemoveZone)) {
|
|
1062
|
-
// Outside-but-not-over-the-trash (or a mere overshoot): snap home.
|
|
1063
|
-
cancelActiveGesture();
|
|
1064
|
-
return;
|
|
1065
|
-
}
|
|
1066
|
-
if (g.removedFromBoard && dragOut === 'remove') {
|
|
1067
|
-
// Release OUTSIDE the board → remove via the page's atomic command path.
|
|
1068
|
-
const displaced = buildCommitCommands(deltasSince(g.startCells, g.startGeom, g.id));
|
|
1069
|
-
const snap = g.startGeom.get(g.id);
|
|
1070
|
-
if (snap) {
|
|
1071
|
-
// Park the node on its start rect so the page's RemoveNodeCommand
|
|
1072
|
-
// captures sane geometry for undo.
|
|
1073
|
-
g.node.setPosition(snap.pos.x, snap.pos.y);
|
|
1074
|
-
g.node.setSize(snap.size.width, snap.size.height, (_d = snap.size.depth) !== null && _d !== void 0 ? _d : 0);
|
|
1075
|
-
}
|
|
1076
|
-
engine.endGesture();
|
|
1077
|
-
cleanupGestureVisuals(g);
|
|
1078
|
-
gesture = null;
|
|
1079
|
-
enforceBoardHeight();
|
|
1080
|
-
api.renderNow();
|
|
1081
|
-
void ((_e = options.onRemoveRequest) === null || _e === void 0 ? void 0 : _e.call(options, g.id, displaced));
|
|
1082
|
-
(_f = options.onGesture) === null || _f === void 0 ? void 0 : _f.call(options, { type: 'remove', kind: g.kind, nodeId: g.id, changed: true });
|
|
1083
|
-
return;
|
|
1084
|
-
}
|
|
1085
|
-
commitGesture(g);
|
|
1086
|
-
};
|
|
1087
|
-
// -- cross-container peers -------------------------------------------------
|
|
1088
|
-
const boardArea = () => {
|
|
1089
|
-
const f = frame();
|
|
1090
|
-
return f.width * boardVisualHeight();
|
|
1091
|
-
};
|
|
1092
|
-
const peersOnCanvas = () => {
|
|
1093
|
-
let set = BOARD_REGISTRY.get(api.container);
|
|
1094
|
-
if (!set) {
|
|
1095
|
-
set = new Set();
|
|
1096
|
-
BOARD_REGISTRY.set(api.container, set);
|
|
1097
|
-
}
|
|
1098
|
-
return set;
|
|
1099
|
-
};
|
|
1100
|
-
/** The board whose engine holds OUR group as an item (nesting parent). */
|
|
1101
|
-
const parentPeer = () => {
|
|
1102
|
-
for (const p of peersOnCanvas()) {
|
|
1103
|
-
if (p !== selfPeer && p.hasItem(group.id))
|
|
1104
|
-
return p;
|
|
1105
|
-
}
|
|
1106
|
-
return null;
|
|
1107
|
-
};
|
|
1108
|
-
/** Deepest OTHER registered board containing the world point. */
|
|
1109
|
-
const peerAt = (x, y, extended = false) => {
|
|
1110
|
-
let best = null;
|
|
1111
|
-
for (const p of peersOnCanvas()) {
|
|
1112
|
-
if (p === selfPeer)
|
|
1113
|
-
continue;
|
|
1114
|
-
if (!(extended ? p.containsWorldExtended(x, y) : p.containsWorld(x, y)))
|
|
1115
|
-
continue;
|
|
1116
|
-
if (!best || p.frameArea() < best.frameArea())
|
|
1117
|
-
best = p;
|
|
1118
|
-
}
|
|
1119
|
-
return best;
|
|
1120
|
-
};
|
|
1121
|
-
/** This binder's side of an adoption: enter gateless, then live-push. */
|
|
1122
|
-
const adopt = (node, world, pxSize) => {
|
|
1123
|
-
if (disposed)
|
|
1124
|
-
return null;
|
|
1125
|
-
const f = frame();
|
|
1126
|
-
const gg = geom();
|
|
1127
|
-
const span = sizeToSpan(pxSize.width, pxSize.height, f, gg, rows());
|
|
1128
|
-
// Clamp to the TARGET board's shape: a tall tile entering a one-row strip
|
|
1129
|
-
// arrives as a strip-height tile, not a refusal.
|
|
1130
|
-
span.w = Math.max(1, Math.min(columns, span.w));
|
|
1131
|
-
if (maxRows !== undefined)
|
|
1132
|
-
span.h = Math.max(1, Math.min(maxRows, span.h));
|
|
1133
|
-
engine.beginGesture(); // pre-entry snapshot — abort() restores it
|
|
1134
|
-
const entered = engine.add({ id: node.id, x: 0, y: engine.rows(), w: span.w, h: span.h });
|
|
1135
|
-
if (!entered) {
|
|
1136
|
-
engine.endGesture();
|
|
1137
|
-
return null; // a bounded, full board refuses the adoption
|
|
1138
|
-
}
|
|
1139
|
-
// Pre-entry baselines for THIS board's displaced-tile commit.
|
|
1140
|
-
const startCells = new Map();
|
|
1141
|
-
const startGeom = new Map();
|
|
1142
|
-
for (const item of engine.getItems()) {
|
|
1143
|
-
if (item.id === node.id)
|
|
1144
|
-
continue;
|
|
1145
|
-
startCells.set(item.id, { x: item.x, y: item.y, w: item.w, h: item.h });
|
|
1146
|
-
const e = memberEntity(item.id);
|
|
1147
|
-
if (e) {
|
|
1148
|
-
const sz = sizeOf(e);
|
|
1149
|
-
startGeom.set(item.id, {
|
|
1150
|
-
pos: { x: e.position.x, y: e.position.y },
|
|
1151
|
-
size: { width: sz.width, height: sz.height, depth: sz.depth },
|
|
1152
|
-
});
|
|
1153
|
-
}
|
|
1154
|
-
}
|
|
1155
|
-
adoptedGhostId = node.id;
|
|
1156
|
-
const tl = centredTopLeft(world.x, world.y, span);
|
|
1157
|
-
const cell0 = pointToCell(tl.x, tl.y, f, gg, rows(), span.w);
|
|
1158
|
-
engine.moveCheck(node.id, cell0.x, cell0.y, { gate: false });
|
|
1159
|
-
armGlide();
|
|
1160
|
-
project();
|
|
1161
|
-
syncPlaceholder();
|
|
1162
|
-
return {
|
|
1163
|
-
groupId: group.id,
|
|
1164
|
-
move: (w) => {
|
|
1165
|
-
const item = engine.getItem(node.id);
|
|
1166
|
-
if (!item)
|
|
1167
|
-
return;
|
|
1168
|
-
const tlm = centredTopLeft(w.x, w.y, { w: item.w, h: item.h });
|
|
1169
|
-
const cell = pointToCell(tlm.x, tlm.y, frame(), geom(), rows(), item.w);
|
|
1170
|
-
if (engine.moveCheck(node.id, cell.x, cell.y).changed)
|
|
1171
|
-
project();
|
|
1172
|
-
syncPlaceholder();
|
|
1173
|
-
},
|
|
1174
|
-
abort: () => {
|
|
1175
|
-
engine.remove(node.id);
|
|
1176
|
-
engine.cancelGesture(); // pre-entry layout, memory cleared
|
|
1177
|
-
adoptedGhostId = null;
|
|
1178
|
-
disarmGlideSoon();
|
|
1179
|
-
project();
|
|
1180
|
-
syncPlaceholder();
|
|
1181
|
-
},
|
|
1182
|
-
finalize: () => {
|
|
1183
|
-
const item = engine.getItem(node.id);
|
|
1184
|
-
if (!item) {
|
|
1185
|
-
engine.endGesture();
|
|
1186
|
-
adoptedGhostId = null;
|
|
1187
|
-
syncPlaceholder();
|
|
1188
|
-
return null;
|
|
1189
|
-
}
|
|
1190
|
-
const cell = { x: item.x, y: item.y, w: item.w, h: item.h };
|
|
1191
|
-
const rect = cellToRect(item, frame(), geom(), rows());
|
|
1192
|
-
const commands = buildCommitCommands(deltasSince(startCells, startGeom, node.id));
|
|
1193
|
-
engine.endGesture();
|
|
1194
|
-
adoptedGhostId = null;
|
|
1195
|
-
disarmGlideSoon();
|
|
1196
|
-
syncPlaceholder();
|
|
1197
|
-
return { commands, cell, rect };
|
|
1198
|
-
},
|
|
1199
|
-
};
|
|
1200
|
-
};
|
|
1201
|
-
const selfPeer = {
|
|
1202
|
-
group,
|
|
1203
|
-
hasItem: (id) => !!engine.getItem(id),
|
|
1204
|
-
memberCell: (id) => {
|
|
1205
|
-
const it = engine.getItem(id);
|
|
1206
|
-
return it ? { x: it.x, y: it.y, w: it.w, h: it.h } : undefined;
|
|
1207
|
-
},
|
|
1208
|
-
resizeMemberBy: (id, dRows) => {
|
|
1209
|
-
const item = engine.getItem(id);
|
|
1210
|
-
if (!item || disposed)
|
|
1211
|
-
return { changed: false };
|
|
1212
|
-
const grp = diagram.getGroup(id);
|
|
1213
|
-
const cellBefore = { x: item.x, y: item.y, w: item.w, h: item.h };
|
|
1214
|
-
const fb = grp
|
|
1215
|
-
? { x: grp.position.x, y: grp.position.y, width: sizeOf(grp).width, height: sizeOf(grp).height }
|
|
1216
|
-
: undefined;
|
|
1217
|
-
const r = engine.resizeCheck(id, item.w, item.h + dRows);
|
|
1218
|
-
if (!r.changed)
|
|
1219
|
-
return { changed: false };
|
|
1220
|
-
project();
|
|
1221
|
-
const after = engine.getItem(id);
|
|
1222
|
-
const fa = grp
|
|
1223
|
-
? { x: grp.position.x, y: grp.position.y, width: sizeOf(grp).width, height: sizeOf(grp).height }
|
|
1224
|
-
: undefined;
|
|
1225
|
-
return {
|
|
1226
|
-
changed: true,
|
|
1227
|
-
cellBefore,
|
|
1228
|
-
cellAfter: { x: after.x, y: after.y, w: after.w, h: after.h },
|
|
1229
|
-
frameBefore: fb,
|
|
1230
|
-
frameAfter: fa,
|
|
1231
|
-
};
|
|
1232
|
-
},
|
|
1233
|
-
containsWorld: worldInsideBoard,
|
|
1234
|
-
containsWorldExtended: worldInsideBoardExtended,
|
|
1235
|
-
frameArea: boardArea,
|
|
1236
|
-
adopt,
|
|
1237
|
-
};
|
|
1238
|
-
peersOnCanvas().add(selfPeer);
|
|
1239
|
-
const tool = {
|
|
1240
|
-
id: `dashboard-grid:${group.id}:${++binderSeq}`,
|
|
1241
|
-
priority: 2, // point-specific claim — outranks mode-style tools (see ext/tools.ts)
|
|
1242
|
-
hitTest(ev, hit) {
|
|
1243
|
-
var _a;
|
|
1244
|
-
if (disposed)
|
|
1245
|
-
return false;
|
|
1246
|
-
if (gesture)
|
|
1247
|
-
return true; // own the rest of an in-flight gesture
|
|
1248
|
-
if (hit.node)
|
|
1249
|
-
return ((_a = group.members) !== null && _a !== void 0 ? _a : new Set()).has(hit.node.id);
|
|
1250
|
-
// Claim (and deaden) empty presses inside a member group's frame so the
|
|
1251
|
-
// built-in group-drag cannot fight the pack layout for the KPI slab.
|
|
1252
|
-
return insideMemberGroupFrame(ev.world.x, ev.world.y);
|
|
1253
|
-
},
|
|
1254
|
-
onPointerDown(ev, hit) {
|
|
1255
|
-
var _a, _b, _c, _d, _e, _f;
|
|
1256
|
-
if (gesture || !hit.node)
|
|
1257
|
-
return; // dead zone (slab area), or mid-palette
|
|
1258
|
-
const node = diagram.getNode(hit.node.id);
|
|
1259
|
-
if (!node || ((_a = node.state) === null || _a === void 0 ? void 0 : _a.locked) === true)
|
|
1260
|
-
return; // pinned: refuse; click still focuses
|
|
1261
|
-
// Arm the glide class NOW, a full task before any displacement can
|
|
1262
|
-
// happen: a transition defined in the same style recalc as the first
|
|
1263
|
-
// left/top write does not run (CSS transitions fire only when the
|
|
1264
|
-
// property changes while the transition exists in the BEFORE-change
|
|
1265
|
-
// style) — the first pushed neighbour of every gesture TELEPORTED
|
|
1266
|
-
// (measured 109px in one frame) while later ones glided.
|
|
1267
|
-
armGlide();
|
|
1268
|
-
const target = ((_c = (_b = ev.source) === null || _b === void 0 ? void 0 : _b.target) !== null && _c !== void 0 ? _c : null);
|
|
1269
|
-
const isResize = !!((_d = target === null || target === void 0 ? void 0 : target.closest) === null || _d === void 0 ? void 0 : _d.call(target, '.axdb-rs'));
|
|
1270
|
-
const it = engine.getItem(node.id);
|
|
1271
|
-
gesture = {
|
|
1272
|
-
kind: isResize ? 'resize' : 'move',
|
|
1273
|
-
id: node.id,
|
|
1274
|
-
node,
|
|
1275
|
-
pointerId: typeof PointerEvent !== 'undefined' && ev.source instanceof PointerEvent
|
|
1276
|
-
? ev.source.pointerId
|
|
1277
|
-
: null,
|
|
1278
|
-
started: false,
|
|
1279
|
-
downClient: { x: ev.screen.x, y: ev.screen.y },
|
|
1280
|
-
downWorld: { x: ev.world.x, y: ev.world.y },
|
|
1281
|
-
grab: { dx: ev.world.x - node.position.x, dy: ev.world.y - node.position.y },
|
|
1282
|
-
startCells: new Map(),
|
|
1283
|
-
startGeom: new Map(),
|
|
1284
|
-
startSize: { width: node.size.width, height: node.size.height },
|
|
1285
|
-
spans: { w: (_e = it === null || it === void 0 ? void 0 : it.w) !== null && _e !== void 0 ? _e : 1, h: (_f = it === null || it === void 0 ? void 0 : it.h) !== null && _f !== void 0 ? _f : 1 },
|
|
1286
|
-
removedFromBoard: false,
|
|
1287
|
-
leg: null,
|
|
1288
|
-
lastWorld: null,
|
|
1289
|
-
lastScreen: null,
|
|
1290
|
-
hostEl: null,
|
|
1291
|
-
esc: null,
|
|
1292
|
-
chip: null,
|
|
1293
|
-
};
|
|
1294
|
-
},
|
|
1295
|
-
onPointerMove(ev) {
|
|
1296
|
-
onToolMove(ev);
|
|
1297
|
-
},
|
|
1298
|
-
onPointerUp() {
|
|
1299
|
-
onToolUp();
|
|
1300
|
-
},
|
|
1301
|
-
onCancel() {
|
|
1302
|
-
cancelActiveGesture();
|
|
1303
|
-
},
|
|
1304
|
-
};
|
|
1305
|
-
const unregisterTool = registerTool(tool);
|
|
1306
|
-
// -- palette drag-in --------------------------------------------------------
|
|
1307
|
-
const beginPaletteDrag = (node, spec, event) => {
|
|
1308
|
-
var _a, _b;
|
|
1309
|
-
if (disposed || gesture)
|
|
1310
|
-
return;
|
|
1311
|
-
const chip = (_a = spec.chip) !== null && _a !== void 0 ? _a : null;
|
|
1312
|
-
if (chip) {
|
|
1313
|
-
chip.classList.add('axdb-drag-chip');
|
|
1314
|
-
document.body.appendChild(chip);
|
|
1315
|
-
chip.style.left = `${event.clientX + 6}px`;
|
|
1316
|
-
chip.style.top = `${event.clientY + 6}px`;
|
|
1317
|
-
}
|
|
1318
|
-
const g = {
|
|
1319
|
-
kind: 'palette',
|
|
1320
|
-
id: node.id,
|
|
1321
|
-
node,
|
|
1322
|
-
pointerId: (_b = event.pointerId) !== null && _b !== void 0 ? _b : null,
|
|
1323
|
-
started: false,
|
|
1324
|
-
downClient: { x: event.clientX, y: event.clientY },
|
|
1325
|
-
downWorld: { x: 0, y: 0 },
|
|
1326
|
-
grab: { dx: 0, dy: 0 },
|
|
1327
|
-
startCells: new Map(),
|
|
1328
|
-
startGeom: new Map(),
|
|
1329
|
-
startSize: { width: 0, height: 0 },
|
|
1330
|
-
spans: { w: Math.max(1, spec.w), h: Math.max(1, spec.h) },
|
|
1331
|
-
removedFromBoard: true,
|
|
1332
|
-
leg: null,
|
|
1333
|
-
lastWorld: null,
|
|
1334
|
-
lastScreen: null,
|
|
1335
|
-
hostEl: null,
|
|
1336
|
-
esc: null,
|
|
1337
|
-
chip,
|
|
1338
|
-
};
|
|
1339
|
-
gesture = g;
|
|
1340
|
-
const toWorld = (cx, cy) => {
|
|
1341
|
-
var _a;
|
|
1342
|
-
const rect = api.container.getBoundingClientRect();
|
|
1343
|
-
return ((_a = api.viewport) === null || _a === void 0 ? void 0 : _a.clientToWorld)
|
|
1344
|
-
? api.viewport.clientToWorld(cx, cy, rect)
|
|
1345
|
-
: { x: cx - rect.left, y: cy - rect.top };
|
|
1346
|
-
};
|
|
1347
|
-
const detach = () => {
|
|
1348
|
-
window.removeEventListener('pointermove', onMove, true);
|
|
1349
|
-
window.removeEventListener('pointerup', onUp, true);
|
|
1350
|
-
window.removeEventListener('keydown', onKey, true);
|
|
1351
|
-
};
|
|
1352
|
-
const onMove = (e) => {
|
|
1353
|
-
if (gesture !== g)
|
|
1354
|
-
return detach();
|
|
1355
|
-
if (!g.started) {
|
|
1356
|
-
if (Math.abs(e.clientX - g.downClient.x) + Math.abs(e.clientY - g.downClient.y) <
|
|
1357
|
-
DRAG_THRESHOLD) {
|
|
1358
|
-
return;
|
|
1359
|
-
}
|
|
1360
|
-
beginGestureVisuals(g);
|
|
1361
|
-
}
|
|
1362
|
-
if (chip) {
|
|
1363
|
-
chip.style.left = `${e.clientX + 6}px`;
|
|
1364
|
-
chip.style.top = `${e.clientY + 6}px`;
|
|
1365
|
-
}
|
|
1366
|
-
const world = toWorld(e.clientX, e.clientY);
|
|
1367
|
-
const inside = worldInsideBoard(world.x, world.y);
|
|
1368
|
-
if (inside) {
|
|
1369
|
-
const tl = centredTopLeft(world.x, world.y, g.spans);
|
|
1370
|
-
const cell = pointToCell(tl.x, tl.y, frame(), geom(), rows(), g.spans.w);
|
|
1371
|
-
if (g.removedFromBoard) {
|
|
1372
|
-
g.removedFromBoard = false;
|
|
1373
|
-
// Enter at the bottom edge (collision-free), then take the cursor
|
|
1374
|
-
// cell GATELESSLY — gridstack's drag-in skips the gate on entry.
|
|
1375
|
-
engine.add({ id: g.id, x: 0, y: engine.rows(), w: g.spans.w, h: g.spans.h });
|
|
1376
|
-
engine.moveCheck(g.id, cell.x, cell.y, { gate: false });
|
|
1377
|
-
project();
|
|
1378
|
-
}
|
|
1379
|
-
else if (engine.moveCheck(g.id, cell.x, cell.y).changed) {
|
|
1380
|
-
project();
|
|
1381
|
-
}
|
|
1382
|
-
}
|
|
1383
|
-
else if (!g.removedFromBoard) {
|
|
1384
|
-
g.removedFromBoard = true;
|
|
1385
|
-
engine.remove(g.id); // displaced tiles come home (gesture memory)
|
|
1386
|
-
project();
|
|
1387
|
-
}
|
|
1388
|
-
syncPlaceholder();
|
|
1389
|
-
api.render();
|
|
1390
|
-
};
|
|
1391
|
-
const finish = (commit) => {
|
|
1392
|
-
var _a, _b;
|
|
1393
|
-
detach();
|
|
1394
|
-
if (gesture !== g)
|
|
1395
|
-
return;
|
|
1396
|
-
if (!g.started) {
|
|
1397
|
-
// Never crossed the threshold: a plain palette CLICK — the page's
|
|
1398
|
-
// click-to-add handler owns it.
|
|
1399
|
-
gesture = null;
|
|
1400
|
-
chip === null || chip === void 0 ? void 0 : chip.remove();
|
|
1401
|
-
return;
|
|
1402
|
-
}
|
|
1403
|
-
if (commit && !g.removedFromBoard && engine.getItem(g.id)) {
|
|
1404
|
-
const item = engine.getItem(g.id);
|
|
1405
|
-
const cell = { x: item.x, y: item.y, w: item.w, h: item.h };
|
|
1406
|
-
node.setGridItem(gridItemFromCell(cell));
|
|
1407
|
-
const displaced = buildCommitCommands(deltasSince(g.startCells, g.startGeom, g.id));
|
|
1408
|
-
engine.endGesture();
|
|
1409
|
-
cleanupGestureVisuals(g);
|
|
1410
|
-
gesture = null;
|
|
1411
|
-
void ((_a = options.onDropIn) === null || _a === void 0 ? void 0 : _a.call(options, node, cell, displaced));
|
|
1412
|
-
(_b = options.onGesture) === null || _b === void 0 ? void 0 : _b.call(options, { type: 'drop-in', kind: 'palette', nodeId: g.id, changed: true });
|
|
1413
|
-
api.renderNow();
|
|
1414
|
-
return;
|
|
1415
|
-
}
|
|
1416
|
-
// Abort (released outside, or Escape): restore the board.
|
|
1417
|
-
cancelActiveGesture();
|
|
1418
|
-
};
|
|
1419
|
-
const onUp = () => finish(true);
|
|
1420
|
-
const onKey = (e) => {
|
|
1421
|
-
if (e.key === 'Escape')
|
|
1422
|
-
finish(false);
|
|
1423
|
-
};
|
|
1424
|
-
window.addEventListener('pointermove', onMove, true);
|
|
1425
|
-
window.addEventListener('pointerup', onUp, true);
|
|
1426
|
-
window.addEventListener('keydown', onKey, true);
|
|
1427
|
-
};
|
|
1428
|
-
// -- handle -----------------------------------------------------------------
|
|
1429
|
-
const subs = [
|
|
1430
|
-
group.on('member:added', ((id) => onMemberAdded(id))),
|
|
1431
|
-
group.on('member:removed', ((id) => onMemberRemoved(id))),
|
|
1432
|
-
group.on('bounds:changed', (() => onBoundsChanged())),
|
|
1433
|
-
];
|
|
1434
|
-
/** Run one programmatic gesture through the same snapshot→op→commit pipeline. */
|
|
1435
|
-
const programmatic = (name, id, op) => __awaiter(this, void 0, void 0, function* () {
|
|
1436
|
-
if (disposed || gesture || !engine.getItem(id))
|
|
1437
|
-
return false;
|
|
1438
|
-
engine.beginGesture();
|
|
1439
|
-
const snap = snapshotAll();
|
|
1440
|
-
if (!op()) {
|
|
1441
|
-
engine.endGesture();
|
|
1442
|
-
return false;
|
|
1443
|
-
}
|
|
1444
|
-
armGlide();
|
|
1445
|
-
project();
|
|
1446
|
-
writing = true;
|
|
1447
|
-
try {
|
|
1448
|
-
const it = engine.getItem(id);
|
|
1449
|
-
if (it)
|
|
1450
|
-
writeRect(id, cellToRect(it, frame(), geom(), rows()));
|
|
1451
|
-
}
|
|
1452
|
-
finally {
|
|
1453
|
-
writing = false;
|
|
1454
|
-
}
|
|
1455
|
-
const commands = buildCommitCommands(deltasSince(snap.cells, snap.geoms));
|
|
1456
|
-
engine.endGesture();
|
|
1457
|
-
disarmGlideSoon();
|
|
1458
|
-
enforceBoardHeight();
|
|
1459
|
-
if (commands.length > 0) {
|
|
1460
|
-
yield api.getEngine().commandManager.execute(new BatchCommand(name, commands));
|
|
1461
|
-
}
|
|
1462
|
-
api.renderNow();
|
|
1463
|
-
return true;
|
|
1464
|
-
});
|
|
1465
|
-
const handle = {
|
|
1466
|
-
sync() {
|
|
1467
|
-
var _a;
|
|
1468
|
-
if (disposed)
|
|
1469
|
-
return;
|
|
1470
|
-
if (gesture)
|
|
1471
|
-
cancelActiveGesture(false);
|
|
1472
|
-
const items = [];
|
|
1473
|
-
for (const id of (_a = group.members) !== null && _a !== void 0 ? _a : []) {
|
|
1474
|
-
if (!memberEntity(id))
|
|
1475
|
-
continue;
|
|
1476
|
-
items.push(itemFor(id));
|
|
1477
|
-
}
|
|
1478
|
-
// CARRY THE PER-COLUMN CACHE ACROSS THE REBUILD. sync() runs on every
|
|
1479
|
-
// undo, member add and refresh; without this handoff a responsive board
|
|
1480
|
-
// would silently lose its wide layouts the first time anything else
|
|
1481
|
-
// happened, and growing back would re-derive instead of restoring.
|
|
1482
|
-
const carried = engine.getLayouts();
|
|
1483
|
-
engine = new GridPackEngine(items, { columns, float, maxRows });
|
|
1484
|
-
engine.setLayouts(carried);
|
|
1485
|
-
for (const item of engine.getItems())
|
|
1486
|
-
persistAdoptedCell(item.id, item);
|
|
1487
|
-
project();
|
|
1488
|
-
syncHandles();
|
|
1489
|
-
api.renderNow();
|
|
1490
|
-
evaluateResponsive();
|
|
1491
|
-
},
|
|
1492
|
-
setColumns(n, layout, opts) {
|
|
1493
|
-
var _a;
|
|
1494
|
-
if (disposed)
|
|
1495
|
-
return false;
|
|
1496
|
-
if (!(opts === null || opts === void 0 ? void 0 : opts.responsive))
|
|
1497
|
-
responsivePinned = true;
|
|
1498
|
-
return applyColumns(n, (_a = layout !== null && layout !== void 0 ? layout : responsive === null || responsive === void 0 ? void 0 : responsive.layout) !== null && _a !== void 0 ? _a : 'moveScale');
|
|
1499
|
-
},
|
|
1500
|
-
getColumns: () => columns,
|
|
1501
|
-
setRtl(on) {
|
|
1502
|
-
if (on === rtl)
|
|
1503
|
-
return;
|
|
1504
|
-
rtl = on;
|
|
1505
|
-
// Pixels only — the cells are already correct in both directions.
|
|
1506
|
-
project();
|
|
1507
|
-
syncHandles();
|
|
1508
|
-
api.renderNow();
|
|
1509
|
-
},
|
|
1510
|
-
getRtl: () => rtl,
|
|
1511
|
-
saveLayout() {
|
|
1512
|
-
const saved = engine.saveLayout();
|
|
1513
|
-
return {
|
|
1514
|
-
columns: saved.columns,
|
|
1515
|
-
cells: new Map(saved.items.map((i) => [i.id, { x: i.x, y: i.y, w: i.w, h: i.h }])),
|
|
1516
|
-
};
|
|
1517
|
-
},
|
|
1518
|
-
setSizing(mode) {
|
|
1519
|
-
if (mode === sizing)
|
|
1520
|
-
return;
|
|
1521
|
-
sizing = mode;
|
|
1522
|
-
project();
|
|
1523
|
-
api.renderNow();
|
|
1524
|
-
},
|
|
1525
|
-
getSizing: () => sizing,
|
|
1526
|
-
setFloat(on) {
|
|
1527
|
-
if (on === float)
|
|
1528
|
-
return;
|
|
1529
|
-
float = on;
|
|
1530
|
-
// Rebuild from persisted cells under the new mode: sync() constructs a
|
|
1531
|
-
// fresh engine with the captured `float`, and gravity (when turning
|
|
1532
|
-
// OFF) packs immediately through its settle.
|
|
1533
|
-
this.sync();
|
|
1534
|
-
api.renderNow();
|
|
1535
|
-
},
|
|
1536
|
-
getFloat: () => float,
|
|
1537
|
-
metrics() {
|
|
1538
|
-
const f = frame();
|
|
1539
|
-
const g = geom();
|
|
1540
|
-
const r = rows();
|
|
1541
|
-
return {
|
|
1542
|
-
columns,
|
|
1543
|
-
maxColumns,
|
|
1544
|
-
rtl,
|
|
1545
|
-
responsive: !!responsive && !responsivePinned,
|
|
1546
|
-
gap,
|
|
1547
|
-
padding,
|
|
1548
|
-
sizing,
|
|
1549
|
-
rows: r,
|
|
1550
|
-
rowHeight: rowHeightFor(g, r),
|
|
1551
|
-
columnUnit: columnUnitFor(g, f.width),
|
|
1552
|
-
boardHeight: f.height,
|
|
1553
|
-
frame: f,
|
|
1554
|
-
};
|
|
1555
|
-
},
|
|
1556
|
-
cellOf(id) {
|
|
1557
|
-
const it = engine.getItem(id);
|
|
1558
|
-
return it ? { x: it.x, y: it.y, w: it.w, h: it.h } : undefined;
|
|
1559
|
-
},
|
|
1560
|
-
cellRectOf(id) {
|
|
1561
|
-
const it = engine.getItem(id);
|
|
1562
|
-
return it ? cellToRect(it, frame(), geom(), rows()) : undefined;
|
|
1563
|
-
},
|
|
1564
|
-
planRemoval(id) {
|
|
1565
|
-
const it = engine.getItem(id);
|
|
1566
|
-
if (!it)
|
|
1567
|
-
return [];
|
|
1568
|
-
const clone = new GridPackEngine(engine.getItems().map((i) => (Object.assign({}, i))), { columns, float });
|
|
1569
|
-
clone.remove(id);
|
|
1570
|
-
const f = frame();
|
|
1571
|
-
const g = geom();
|
|
1572
|
-
const rAfter = Math.max(1, clone.rows());
|
|
1573
|
-
const deltas = [];
|
|
1574
|
-
for (const item of clone.getItems()) {
|
|
1575
|
-
const before = engine.getItem(item.id);
|
|
1576
|
-
const e = memberEntity(item.id);
|
|
1577
|
-
if (!before || !e)
|
|
1578
|
-
continue;
|
|
1579
|
-
const target = cellToRect(item, f, g, rAfter);
|
|
1580
|
-
deltas.push({
|
|
1581
|
-
id: item.id,
|
|
1582
|
-
locked: !!item.locked,
|
|
1583
|
-
isGroup: isGroupMember(item.id),
|
|
1584
|
-
cellBefore: { x: before.x, y: before.y, w: before.w, h: before.h },
|
|
1585
|
-
cellAfter: { x: item.x, y: item.y, w: item.w, h: item.h },
|
|
1586
|
-
posBefore: { x: e.position.x, y: e.position.y },
|
|
1587
|
-
posAfter: { x: target.x, y: target.y },
|
|
1588
|
-
sizeBefore: (({ width, height, depth }) => ({ width, height, depth }))(sizeOf(e)),
|
|
1589
|
-
sizeAfter: { width: target.width, height: target.height },
|
|
1590
|
-
});
|
|
1591
|
-
}
|
|
1592
|
-
return buildCommitCommands(deltas);
|
|
1593
|
-
},
|
|
1594
|
-
moveTo(id, x, y) {
|
|
1595
|
-
return programmatic('Move widget', id, () => engine.moveCheck(id, x, y).changed);
|
|
1596
|
-
},
|
|
1597
|
-
resizeTo(id, w, h) {
|
|
1598
|
-
return programmatic('Resize widget', id, () => engine.resizeCheck(id, w, h).changed);
|
|
1599
|
-
},
|
|
1600
|
-
beginPaletteDrag,
|
|
1601
|
-
dispose() {
|
|
1602
|
-
var _a;
|
|
1603
|
-
if (disposed)
|
|
1604
|
-
return;
|
|
1605
|
-
cancelActiveGesture(false);
|
|
1606
|
-
disposed = true;
|
|
1607
|
-
peersOnCanvas().delete(selfPeer);
|
|
1608
|
-
unregisterTool();
|
|
1609
|
-
hostObserver.disconnect();
|
|
1610
|
-
containerObserver === null || containerObserver === void 0 ? void 0 : containerObserver.disconnect();
|
|
1611
|
-
for (const off of subs)
|
|
1612
|
-
off();
|
|
1613
|
-
placeholder === null || placeholder === void 0 ? void 0 : placeholder.remove();
|
|
1614
|
-
placeholder = null;
|
|
1615
|
-
if (glideTimer)
|
|
1616
|
-
clearTimeout(glideTimer);
|
|
1617
|
-
if (ghostTimer)
|
|
1618
|
-
clearTimeout(ghostTimer);
|
|
1619
|
-
(_a = htmlLayer()) === null || _a === void 0 ? void 0 : _a.classList.remove('axdb-glide');
|
|
1620
|
-
api.container.style.cursor = '';
|
|
1621
|
-
},
|
|
1622
|
-
};
|
|
1623
|
-
// Boot: adopt the current members, observe host churn for handle re-injection,
|
|
1624
|
-
// and let a responsive board settle on the count its width asks for before the
|
|
1625
|
-
// first frame (a 400px board declared at 12 columns must not flash at 12).
|
|
1626
|
-
handle.sync();
|
|
1627
|
-
const layer = htmlLayer();
|
|
1628
|
-
if (layer)
|
|
1629
|
-
hostObserver.observe(layer, { childList: true, subtree: true });
|
|
1630
|
-
containerObserver === null || containerObserver === void 0 ? void 0 : containerObserver.observe(api.container);
|
|
1631
|
-
return handle;
|
|
1632
|
-
}
|
|
1633
|
-
//# sourceMappingURL=grid-binder.js.map
|