@grafloria/element 0.4.60 → 0.4.62

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.60",
3
+ "version": "0.4.62",
4
4
  "type": "module",
5
5
  "main": "./src/index.js",
6
6
  "types": "./src/index.d.ts",
@@ -910,6 +910,52 @@ export function bindDashboardGrid(api, group, options = {}) {
910
910
  * come back by themselves when the widget leaves (S2).
911
911
  */
912
912
  let beside = null;
913
+ /**
914
+ * A VERTICAL BESIDE, HELD BUT NOT APPLIED (0.4.61). A container's top or
915
+ * bottom band pushes it a whole row along its own column — and the zone walk
916
+ * reads the container where it RESTS (0.4.60, which is what stopped the
917
+ * strip flickering), so the moment the push lands, the painted tabs sit a
918
+ * row below the only place that accepts them, and a hand following them down
919
+ * pushes them again. The user met it as "it pushes the entire tab group down
920
+ * and then I cannot go up to the tab header".
921
+ *
922
+ * So a vertical band moves NOTHING while the hand is held: an overlay marks
923
+ * the cell the widget will take and the container gives way on release — the
924
+ * rule a tab's dock and split previews have followed since 0.4.42. The LEFT
925
+ * and RIGHT bands keep their live slide (0.4.48): sideways, a full-width
926
+ * strip never leaves the hand, and the user missed that slide the one time
927
+ * it was frozen.
928
+ */
929
+ let pendingBeside = null;
930
+ let joinEl = null;
931
+ const showJoin = (r) => {
932
+ const layer = htmlLayer();
933
+ if (!layer)
934
+ return;
935
+ if (!joinEl || joinEl.parentElement !== layer) {
936
+ joinEl === null || joinEl === void 0 ? void 0 : joinEl.remove();
937
+ joinEl = document.createElement('div');
938
+ joinEl.className = 'axdb-join';
939
+ layer.prepend(joinEl);
940
+ }
941
+ joinEl.style.left = `${r.x}px`;
942
+ joinEl.style.top = `${r.y}px`;
943
+ joinEl.style.width = `${r.width}px`;
944
+ joinEl.style.height = `${r.height}px`;
945
+ };
946
+ const endPendingBeside = () => {
947
+ pendingBeside = null;
948
+ joinEl === null || joinEl === void 0 ? void 0 : joinEl.remove();
949
+ joinEl = null;
950
+ };
951
+ /** The cell a vertical beside would take: the container's own, or the row under it. Never the pointer's row — the container is not moving. */
952
+ const pendingCellOf = (containerId, side, spans) => {
953
+ const it = engine.getItem(containerId);
954
+ if (!it)
955
+ return null;
956
+ const x = Math.max(0, Math.min(engine.columns - spans.w, it.x));
957
+ return { x, y: side === 'top' ? it.y : it.y + it.h };
958
+ };
913
959
  const endBeside = (restore) => {
914
960
  if (!beside)
915
961
  return;
@@ -1292,6 +1338,7 @@ export function bindDashboardGrid(api, group, options = {}) {
1292
1338
  showRefusal(null, 0, 0);
1293
1339
  }
1294
1340
  }
1341
+ endPendingBeside();
1295
1342
  disarmGlideSoon();
1296
1343
  releasePointer(g.pointerId);
1297
1344
  api.container.style.cursor = '';
@@ -1335,6 +1382,7 @@ export function bindDashboardGrid(api, group, options = {}) {
1335
1382
  };
1336
1383
  const cancelActiveGesture = (notify = true) => {
1337
1384
  var _a, _b, _c;
1385
+ endPendingBeside();
1338
1386
  if (gesture === null || gesture === void 0 ? void 0 : gesture.strip) {
1339
1387
  (_a = options.tabDrop) === null || _a === void 0 ? void 0 : _a.markDrop(null, null);
1340
1388
  gesture.strip = null;
@@ -1483,6 +1531,7 @@ export function bindDashboardGrid(api, group, options = {}) {
1483
1531
  // -- INTO A STRIP: the widget becomes a new tab there, so it leaves
1484
1532
  // this board (survivors settle home) and the strip marks the slot.
1485
1533
  endBeside(true); // a band's shift gives way to the strip: the container comes back
1534
+ endPendingBeside();
1486
1535
  leaveSelf();
1487
1536
  setDim(g, false);
1488
1537
  if (!g.strip || g.strip.containerId !== z.containerId || g.strip.index !== z.index) {
@@ -1501,6 +1550,22 @@ export function bindDashboardGrid(api, group, options = {}) {
1501
1550
  // — at the board's edge the container shifts over to make room, live
1502
1551
  // and gliding, like any widget gives way (0.4.48). On THIS board the
1503
1552
  // engine is driven directly; on another board its leg drives it (4a).
1553
+ if (z.kind === 'beside' && !isStatic && z.board.ref === selfPeer && g.subject === 'node' && (z.side === 'top' || z.side === 'bottom')) {
1554
+ // -- A VERTICAL BAND: marked, not applied. Nothing moves until release.
1555
+ if (beside)
1556
+ endBeside(true);
1557
+ const cell = pendingCellOf(z.containerId, z.side, g.spans);
1558
+ if (cell) {
1559
+ leaveSelf();
1560
+ setDim(g, false);
1561
+ pendingBeside = { id: z.containerId, side: z.side, cell, spans: Object.assign({}, g.spans) };
1562
+ showJoin(cellToRect({ x: cell.x, y: cell.y, w: g.spans.w, h: g.spans.h }, frame(), geom(), rows()));
1563
+ syncPlaceholder();
1564
+ return;
1565
+ }
1566
+ }
1567
+ if (pendingBeside)
1568
+ endPendingBeside(); // the hand left the band: the mark goes
1504
1569
  if (z.kind === 'beside' && !isStatic && z.board.ref === selfPeer) {
1505
1570
  const row = rowOfPoint(ev.world.y);
1506
1571
  if (!z.kept || !beside || beside.row !== row)
@@ -1955,6 +2020,13 @@ export function bindDashboardGrid(api, group, options = {}) {
1955
2020
  (_b = options.onGesture) === null || _b === void 0 ? void 0 : _b.call(options, { type: 'commit', kind: g.kind, nodeId: g.id, changed: true });
1956
2021
  return;
1957
2022
  }
2023
+ if (pendingBeside) {
2024
+ // -- REALIZED ON RELEASE: the container held still while the hand chose;
2025
+ // now it gives way, and the commit below reads the settled layout.
2026
+ const p = pendingBeside;
2027
+ endPendingBeside();
2028
+ applyBeside(g, { id: p.id, side: p.side }, p.cell.y);
2029
+ }
1958
2030
  if (g.leg) {
1959
2031
  // -- CROSS-CONTAINER COMMIT: one batch across both boards -----------
1960
2032
  const fin = g.leg.adopted.finalize();
@@ -2125,6 +2197,11 @@ export function bindDashboardGrid(api, group, options = {}) {
2125
2197
  frame: frameOfGroup(grp),
2126
2198
  stripHeight: layout === 'tabs' ? TAB_STRIP_HEIGHT : 0,
2127
2199
  band: layout === 'tabs' ? BESIDE_BAND : 0, // a section's whole body is "into" (Quantia's Groups page)
2200
+ // The top and bottom are a FIXED depth — one strip's worth, under
2201
+ // the strip — not a fifth of the body, which grew with the panel
2202
+ // until 216 px of the fluid demo's page meant "above the whole
2203
+ // panel" (0.4.62). The sides keep the fifth.
2204
+ bandY: layout === 'tabs' ? TAB_STRIP_HEIGHT : 0,
2128
2205
  inner: innerPeer ? boardRef(innerPeer, depth + 1) : null,
2129
2206
  });
2130
2207
  }
@@ -2209,7 +2286,15 @@ export function bindDashboardGrid(api, group, options = {}) {
2209
2286
  strip,
2210
2287
  prev: beside
2211
2288
  ? { containerId: beside.id, side: beside.side, frame0: beside.frame0, vacated: cellToRect({ x: beside.vacated.x, y: beside.vacated.y, w: g.spans.w, h: g.spans.h }, frame(), geom(), rows()) }
2212
- : ((_e = (_d = g.leg) === null || _d === void 0 ? void 0 : _d.adopted.besideState()) !== null && _e !== void 0 ? _e : null), // a beside another board holds for the ghost, through its leg
2289
+ : pendingBeside
2290
+ ? // a VERTICAL band the hand holds: the container never moved, so its own frame is the sticky one
2291
+ {
2292
+ containerId: pendingBeside.id,
2293
+ side: pendingBeside.side,
2294
+ frame0: (() => { const grp = diagram.getGroup(pendingBeside.id); return grp ? frameOfGroup(grp) : cellToRect({ x: pendingBeside.cell.x, y: pendingBeside.cell.y, w: pendingBeside.spans.w, h: pendingBeside.spans.h }, frame(), geom(), rows()); })(),
2295
+ // no vacated cell: nothing has moved, so the BAND alone holds the mark
2296
+ }
2297
+ : ((_e = (_d = g.leg) === null || _d === void 0 ? void 0 : _d.adopted.besideState()) !== null && _e !== void 0 ? _e : null), // a beside another board holds for the ghost, through its leg
2213
2298
  maxDepth: nesting,
2214
2299
  ghostDepth: g.subject === 'group' ? 1 + levelsInside(g.id) : 0, // a group's widgets sit one board deeper than wherever it lands
2215
2300
  restFrames: rests,
@@ -3206,6 +3291,13 @@ export function bindDashboardGrid(api, group, options = {}) {
3206
3291
  chip === null || chip === void 0 ? void 0 : chip.remove();
3207
3292
  return;
3208
3293
  }
3294
+ if (commit && pendingBeside) {
3295
+ const p = pendingBeside;
3296
+ endPendingBeside();
3297
+ applyBeside(g, { id: p.id, side: p.side }, p.cell.y);
3298
+ }
3299
+ else
3300
+ endPendingBeside();
3209
3301
  if (commit && g.leg) {
3210
3302
  // Dropped into another board (a page, a section): its leg closes with
3211
3303
  // that board's displaced tiles; this board displaced nothing.
@@ -57,6 +57,8 @@ export interface ZoneContainer {
57
57
  stripHeight: number;
58
58
  /** The outer fraction of the body that means "beside"; 0 for a container whose whole body is "into" (a section). */
59
59
  band: number;
60
+ /** The TOP and BOTTOM depth in world units, when it is a fixed depth rather than `band` of the body. */
61
+ bandY?: number;
60
62
  /** The board a descent enters: a tab container's ACTIVE page, a section's own board. Null: nothing to enter. */
61
63
  inner: ZoneBoard | null;
62
64
  }
@@ -69,7 +71,14 @@ export interface BesideMemory {
69
71
  containerId: string;
70
72
  side: BesideSide;
71
73
  frame0: ZoneRect;
72
- vacated: ZoneRect;
74
+ /**
75
+ * The cell the widget TOOK, when it took one. A beside that is only MARKED
76
+ * (a vertical band since 0.4.61 — nothing moves until release) has none, and
77
+ * must not borrow one: the cell a mark would take is the container's own, so
78
+ * for a widget the size of its container that rect covers the whole panel
79
+ * and the band would then hold the hand everywhere inside it (lab L105).
80
+ */
81
+ vacated?: ZoneRect;
73
82
  }
74
83
  export interface ResolveInput {
75
84
  x: number;
@@ -129,8 +138,16 @@ export declare const BESIDE_STAY = 0.05;
129
138
  * the middle, or outside. The left and right bands take the corners (VS
130
139
  * Code's precedence): a one-row widget carried along the top of a tall panel
131
140
  * to its far right means "after it", not "above it".
141
+ *
142
+ * `bandY` is the TOP and BOTTOM depth in world units. A fraction is the wrong
143
+ * unit there: it grows with the container, so the taller and more useful the
144
+ * panel, the more of its page means "above the whole panel" — 216 px of the
145
+ * fluid demo's panel, starting right under its tabs, which is exactly where a
146
+ * hand aiming into the page lands (0.4.62). The SIDES keep the fraction: that
147
+ * is how a widget gets beside a full-height panel, and a tab's split bands
148
+ * are a fifth on all four edges by design.
132
149
  */
133
- export declare function bandOf(f: ZoneRect, stripHeight: number, x: number, y: number, band?: number): BesideSide | null;
150
+ export declare function bandOf(f: ZoneRect, stripHeight: number, x: number, y: number, band?: number, bandY?: number): BesideSide | null;
134
151
  export interface StripProbe {
135
152
  x: number;
136
153
  y: number;
@@ -39,8 +39,16 @@ const inRect = (r, x, y, tol = 0) => x >= r.x - tol && x <= r.x + r.width + tol
39
39
  * the middle, or outside. The left and right bands take the corners (VS
40
40
  * Code's precedence): a one-row widget carried along the top of a tall panel
41
41
  * to its far right means "after it", not "above it".
42
+ *
43
+ * `bandY` is the TOP and BOTTOM depth in world units. A fraction is the wrong
44
+ * unit there: it grows with the container, so the taller and more useful the
45
+ * panel, the more of its page means "above the whole panel" — 216 px of the
46
+ * fluid demo's panel, starting right under its tabs, which is exactly where a
47
+ * hand aiming into the page lands (0.4.62). The SIDES keep the fraction: that
48
+ * is how a widget gets beside a full-height panel, and a tab's split bands
49
+ * are a fifth on all four edges by design.
42
50
  */
43
- export function bandOf(f, stripHeight, x, y, band = BESIDE_BAND) {
51
+ export function bandOf(f, stripHeight, x, y, band = BESIDE_BAND, bandY) {
44
52
  if (band <= 0 || !inRect(f, x, y))
45
53
  return null;
46
54
  const bodyY = f.y + stripHeight;
@@ -53,9 +61,10 @@ export function bandOf(f, stripHeight, x, y, band = BESIDE_BAND) {
53
61
  return 'left';
54
62
  if (rx > 1 - band)
55
63
  return 'right';
56
- if (ry < band)
64
+ const depth = bandY !== undefined && bandY > 0 ? Math.min(bandY, bodyH / 2) : band * bodyH;
65
+ if (y - bodyY < depth)
57
66
  return 'top';
58
- if (ry > 1 - band)
67
+ if (bodyY + bodyH - y < depth)
59
68
  return 'bottom';
60
69
  return null;
61
70
  }
@@ -128,6 +137,7 @@ function boardOf(roots, containerId) {
128
137
  return null;
129
138
  }
130
139
  export function resolve(input) {
140
+ var _a;
131
141
  const { x, y } = input;
132
142
  if (input.strip)
133
143
  return { kind: 'strip', containerId: input.strip.containerId, index: input.strip.index };
@@ -138,10 +148,13 @@ export function resolve(input) {
138
148
  // middle of it is a zone change too (into the page).
139
149
  if (input.prev) {
140
150
  const p = input.prev;
141
- const stripH = stripHeightOf(input.roots, p.containerId);
142
- const stay = bandOf(p.frame0, stripH, x, y, BESIDE_BAND + BESIDE_STAY);
143
- const other = bandOf(p.frame0, stripH, x, y);
144
- const held = stay === p.side || (!(other !== null && other !== p.side) && inRect(p.vacated, x, y, input.gap));
151
+ const c0 = containerOf(input.roots, p.containerId);
152
+ const stripH = (_a = c0 === null || c0 === void 0 ? void 0 : c0.stripHeight) !== null && _a !== void 0 ? _a : 0;
153
+ const depth = c0 === null || c0 === void 0 ? void 0 : c0.bandY;
154
+ const stay = bandOf(p.frame0, stripH, x, y, BESIDE_BAND + BESIDE_STAY, depth === undefined ? undefined : depth * (1 + BESIDE_STAY / BESIDE_BAND));
155
+ const other = bandOf(p.frame0, stripH, x, y, BESIDE_BAND, depth);
156
+ const onVacated = !!p.vacated && inRect(p.vacated, x, y, input.gap);
157
+ const held = stay === p.side || (!(other !== null && other !== p.side) && onVacated);
145
158
  if (held) {
146
159
  const board = boardOf(input.roots, p.containerId);
147
160
  if (board)
@@ -183,7 +196,7 @@ export function resolve(input) {
183
196
  const ndx0 = atRest ? c.frame.x - atRest.x : dx;
184
197
  const ndy0 = atRest ? c.frame.y - atRest.y : dy;
185
198
  const overNested = !!c.inner && c.inner.children().some((cc) => inRect(cc.frame, x + ndx0, y + ndy0));
186
- const side = overNested || input.homeChain.has(c.id) ? null : bandOf(frame, c.stripHeight, tx, ty, c.band);
199
+ const side = overNested || input.homeChain.has(c.id) ? null : bandOf(frame, c.stripHeight, tx, ty, c.band, c.bandY);
187
200
  if (side)
188
201
  return { kind: 'beside', board, containerId: c.id, side, kept: false };
189
202
  if (opaque(board, c) || !c.inner)
@@ -210,23 +223,24 @@ export function resolve(input) {
210
223
  visit(root);
211
224
  return deepest ? { kind: 'plain', board: deepest, grace: true } : { kind: 'off' };
212
225
  }
213
- function stripHeightOf(roots, containerId) {
226
+ /** The container with this id, anywhere in the tree — its strip rows and its band depth. */
227
+ function containerOf(roots, containerId) {
214
228
  const visit = (b) => {
215
229
  for (const c of b.children()) {
216
230
  if (c.id === containerId)
217
- return c.stripHeight;
231
+ return c;
218
232
  const deeper = c.inner ? visit(c.inner) : null;
219
- if (deeper !== null)
233
+ if (deeper)
220
234
  return deeper;
221
235
  }
222
236
  return null;
223
237
  };
224
238
  for (const r of roots) {
225
- const h = visit(r);
226
- if (h !== null)
227
- return h;
239
+ const c = visit(r);
240
+ if (c)
241
+ return c;
228
242
  }
229
- return 0;
243
+ return null;
230
244
  }
231
245
  /**
232
246
  * What a pointer means for a TORN-OUT PAGE, in the one order both binders