@grafloria/element 0.4.47 → 0.4.49

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,11 +1,11 @@
1
1
  {
2
2
  "name": "@grafloria/element",
3
- "version": "0.4.47",
3
+ "version": "0.4.49",
4
4
  "type": "module",
5
5
  "main": "./src/index.js",
6
6
  "types": "./src/index.d.ts",
7
7
  "peerDependencies": {
8
- "@grafloria/engine": "^0.3.0",
8
+ "@grafloria/engine": "^0.3.8",
9
9
  "@grafloria/renderer": "^0.4.0"
10
10
  },
11
11
  "dependencies": {
@@ -1298,124 +1298,109 @@ export function bindDashboardGrid(api, group, options = {}) {
1298
1298
  }
1299
1299
  return null;
1300
1300
  };
1301
- /** The BESIDE promise the overlay shows: the cell the widget takes on release, and where the container shifts to make it (null: it is pushed, or has room). */
1301
+ /** The container a BESIDE drop unlocked (and maybe shifted): relocked, and put back if asked, when the zone or the gesture ends. */
1302
1302
  let beside = null;
1303
- let besideEl = null;
1304
- const showBesideOverlay = (r) => {
1305
- const layer = htmlLayer();
1306
- if (!layer)
1307
- return;
1308
- if (!besideEl || besideEl.parentElement !== layer) {
1309
- besideEl === null || besideEl === void 0 ? void 0 : besideEl.remove();
1310
- besideEl = document.createElement('div');
1311
- besideEl.className = 'axdb-join';
1312
- layer.prepend(besideEl);
1313
- }
1314
- besideEl.style.left = `${r.x}px`;
1315
- besideEl.style.top = `${r.y}px`;
1316
- besideEl.style.width = `${r.width}px`;
1317
- besideEl.style.height = `${r.height}px`;
1318
- };
1319
- const hideBesideOverlay = () => {
1320
- besideEl === null || besideEl === void 0 ? void 0 : besideEl.remove();
1321
- besideEl = null;
1322
- };
1323
- /** The promise withdrawn (the pointer left, the gesture ended) or kept (realized and committed): the overlay goes, the container and the others relock. */
1324
- const endBeside = () => {
1303
+ const endBeside = (restore) => {
1325
1304
  if (!beside)
1326
1305
  return;
1327
1306
  const it = engine.getItem(beside.id);
1307
+ let moved = false;
1308
+ if (it && restore && (it.x !== beside.from.x || it.y !== beside.from.y))
1309
+ moved = engine.moveCheck(beside.id, beside.from.x, beside.from.y, { gate: false }).changed || moved;
1310
+ if (restore) {
1311
+ // The sections the shift pushed come back with it.
1312
+ for (const [oid, c] of beside.others) {
1313
+ const o = engine.getItem(oid);
1314
+ if (o && (o.x !== c.x || o.y !== c.y))
1315
+ moved = engine.moveCheck(oid, c.x, c.y, { gate: false }).changed || moved;
1316
+ }
1317
+ }
1328
1318
  if (it)
1329
1319
  it.locked = true;
1330
1320
  relockOthersForSlab();
1331
- hideBesideOverlay();
1332
1321
  beside = null;
1322
+ // The frames the zone test reads are the MODEL's: a restore that moved
1323
+ // the container without projecting left its frame where the push had
1324
+ // put it, so the next test read the pointer as OUTSIDE it (the user's
1325
+ // 3440-px corner on 0.4.48: along the top band the panel was pushed
1326
+ // down, and the corner never turned into "right").
1327
+ if (moved)
1328
+ project();
1333
1329
  };
1334
- /** Where a widget of this span lands beside the container, and where the container goes to make room — from the layout AT REST, since nothing moves while held. */
1335
- const besidePlan = (tc, side, w) => {
1330
+ const applyBeside = (g, z) => {
1331
+ var _a;
1332
+ const tc = engine.getItem(z.id);
1333
+ if (!tc)
1334
+ return;
1335
+ if (g.leg) {
1336
+ g.leg.adopted.abort();
1337
+ g.leg = null;
1338
+ }
1339
+ const w = g.spans.w;
1340
+ const h = g.spans.h;
1341
+ if (g.removedFromBoard) {
1342
+ g.removedFromBoard = false;
1343
+ (_a = hostOf(g.id)) === null || _a === void 0 ? void 0 : _a.classList.remove('axdb-out');
1344
+ engine.add({ id: g.id, x: 0, y: engine.rows(), w, h });
1345
+ }
1346
+ if (beside && (beside.id !== z.id || beside.side !== z.side))
1347
+ endBeside(true); // another container, or another side of it: start over from the rest layout
1348
+ const from = beside ? beside.from : { x: tc.x, y: tc.y };
1336
1349
  let cell;
1337
1350
  let shiftTo = null;
1338
- switch (side) {
1351
+ switch (z.side) {
1339
1352
  case 'right':
1340
- cell = { x: tc.x + tc.w, y: tc.y };
1353
+ cell = { x: from.x + tc.w, y: from.y };
1341
1354
  if (cell.x + w > columns) {
1342
- shiftTo = { x: columns - w - tc.w, y: tc.y };
1343
- cell = { x: columns - w, y: tc.y };
1355
+ shiftTo = { x: columns - w - tc.w, y: from.y };
1356
+ cell = { x: columns - w, y: from.y };
1344
1357
  }
1345
1358
  break;
1346
1359
  case 'left':
1347
- cell = { x: tc.x - w, y: tc.y };
1360
+ cell = { x: from.x - w, y: from.y };
1348
1361
  if (cell.x < 0) {
1349
- shiftTo = { x: w, y: tc.y };
1350
- cell = { x: 0, y: tc.y };
1362
+ shiftTo = { x: w, y: from.y };
1363
+ cell = { x: 0, y: from.y };
1351
1364
  }
1352
1365
  break;
1353
1366
  case 'top':
1354
- cell = { x: Math.max(0, Math.min(tc.x, columns - w)), y: tc.y }; // the widget takes the container's rows; the container is pushed down under it
1367
+ cell = { x: Math.max(0, Math.min(from.x, columns - w)), y: from.y }; // the ghost takes the container's rows; the container is pushed down under it
1355
1368
  break;
1356
1369
  default:
1357
- cell = { x: Math.max(0, Math.min(tc.x, columns - w)), y: tc.y + tc.h };
1370
+ cell = { x: Math.max(0, Math.min(from.x, columns - w)), y: from.y + tc.h };
1358
1371
  }
1359
1372
  if (shiftTo && shiftTo.x < 0)
1360
1373
  shiftTo = null; // no room even shifted: the widget goes where it can
1361
- return { cell, shiftTo };
1362
- };
1363
- const applyBeside = (g, z) => {
1364
- var _a;
1365
- const tc = engine.getItem(z.id);
1366
- if (!tc)
1367
- return;
1368
- if (g.leg) {
1369
- g.leg.adopted.abort();
1370
- g.leg = null;
1371
- }
1372
- // The ghost leaves the board, as it does over a strip: the survivors
1373
- // settle home and nothing under the hand moves until the release.
1374
- if (!g.removedFromBoard) {
1375
- g.removedFromBoard = true;
1376
- engine.remove(g.id);
1377
- project();
1374
+ if (!beside) {
1375
+ // The other sections give way to the shift the way they give way to a
1376
+ // moved group (0.4.44): on the demo the Operations section spans the
1377
+ // row under the panel and a locked tile refused the shift outright.
1378
+ // Their cells are kept so they come back if the pointer leaves.
1379
+ const others = new Map();
1380
+ for (const o of engine.getItems())
1381
+ if (o.id !== z.id && o.id !== g.id && isGroupMember(o.id))
1382
+ others.set(o.id, { x: o.x, y: o.y });
1383
+ const grp0 = diagram.getGroup(z.id);
1384
+ beside = { id: z.id, side: z.side, from, frame0: grp0 ? frameOfGroup(grp0) : cellToRect({ x: from.x, y: from.y, w: tc.w, h: tc.h }, frame(), geom(), rows()), vacated: cell, others };
1385
+ unlockOthersForSlab(z.id);
1378
1386
  }
1379
- (_a = hostOf(g.id)) === null || _a === void 0 ? void 0 : _a.classList.remove('axdb-out');
1380
- const plan = besidePlan(tc, z.side, g.spans.w);
1381
- if (!beside || beside.id !== z.id || beside.side !== z.side || beside.cell.x !== plan.cell.x || beside.cell.y !== plan.cell.y) {
1382
- beside = { id: z.id, side: z.side, cell: plan.cell, shiftTo: plan.shiftTo };
1383
- showBesideOverlay(cellToRect({ x: plan.cell.x, y: plan.cell.y, w: g.spans.w, h: g.spans.h }, frame(), geom(), rows()));
1384
- }
1385
- };
1386
- /**
1387
- * The release: the container shifts (or is pushed) and the ghost takes the
1388
- * promised cell — for real now, so the commit's deltas carry both. The
1389
- * ghost steps back on at the bottom edge first: the engine will not push
1390
- * the dragged tile, so a chart as wide as the panel had its own cell as the
1391
- * panel's target and the shift was refused (lab L94).
1392
- */
1393
- const realizeBeside = (g) => {
1394
- var _a;
1395
- if (!beside)
1396
- return;
1397
- const tc = engine.getItem(beside.id);
1398
- if (!tc)
1399
- return;
1400
- const w = g.spans.w;
1401
- const h = g.spans.h;
1402
- // The other sections give way to the shift the way they give way to a
1403
- // moved group (0.4.44): on the demo the Operations section spans the
1404
- // row under the panel and a locked tile refused the shift outright.
1405
- unlockOthersForSlab(beside.id);
1406
- tc.locked = false; // it shifts, or the ghost pushes it
1407
- if (beside.shiftTo && (tc.x !== beside.shiftTo.x || tc.y !== beside.shiftTo.y))
1408
- engine.moveCheck(beside.id, beside.shiftTo.x, beside.shiftTo.y, { gate: false });
1409
- if (g.removedFromBoard) {
1410
- g.removedFromBoard = false;
1411
- (_a = hostOf(g.id)) === null || _a === void 0 ? void 0 : _a.classList.remove('axdb-out');
1387
+ tc.locked = false; // for the gesture: it shifts, or the ghost pushes it
1388
+ // The ghost steps off the board while the container shifts: the engine
1389
+ // will not push the dragged tile, so a chart as wide as the panel had its
1390
+ // own cell as the panel's target and the shift was refused (lab L94).
1391
+ // Then it takes the cell the shift vacated.
1392
+ if (shiftTo && (tc.x !== shiftTo.x || tc.y !== shiftTo.y)) {
1393
+ if (engine.getItem(g.id))
1394
+ engine.remove(g.id);
1395
+ engine.moveCheck(z.id, shiftTo.x, shiftTo.y, { gate: false });
1412
1396
  }
1397
+ beside.vacated = cell;
1413
1398
  if (!engine.getItem(g.id))
1414
1399
  engine.add({ id: g.id, x: 0, y: engine.rows(), w, h });
1415
- if (!engine.moveCheck(g.id, beside.cell.x, beside.cell.y, { gate: false }).changed) {
1400
+ if (!engine.moveCheck(g.id, cell.x, cell.y, { gate: false }).changed) {
1416
1401
  const at = engine.getItem(g.id);
1417
- if (!at || at.x !== beside.cell.x || at.y !== beside.cell.y)
1418
- placeNear(g.id, beside.cell.x, beside.cell.y, w);
1402
+ if (!at || at.x !== cell.x || at.y !== cell.y)
1403
+ placeNear(g.id, cell.x, cell.y, w);
1419
1404
  }
1420
1405
  project();
1421
1406
  };
@@ -1962,7 +1947,7 @@ export function bindDashboardGrid(api, group, options = {}) {
1962
1947
  const deltas = deltasSince(g.startCells, g.startGeom);
1963
1948
  const commands = buildCommitCommands(deltas);
1964
1949
  commands.push(...groupCellCommands(deltas)); // a container a BESIDE drop shifted (0.4.45)
1965
- endBeside();
1950
+ endBeside(false);
1966
1951
  if (g.esc && g.esc.rowsAdded !== 0) {
1967
1952
  commands.push(new SetGroupCellCommand(group.id, g.esc.cellBefore, g.esc.cellAfter, g.esc.frameBefore, g.esc.frameAfter));
1968
1953
  }
@@ -2001,7 +1986,7 @@ export function bindDashboardGrid(api, group, options = {}) {
2001
1986
  g.esc.peer.resizeMemberBy(group.id, -g.esc.rowsAdded); // slab back down
2002
1987
  g.esc = null;
2003
1988
  }
2004
- endBeside();
1989
+ endBeside(true);
2005
1990
  if (g.started) {
2006
1991
  if (g.removedFromBoard || g.kind === 'palette') {
2007
1992
  // The engine cannot resurrect a removed item — rebuild from the
@@ -2101,7 +2086,7 @@ export function bindDashboardGrid(api, group, options = {}) {
2101
2086
  g.leg.adopted.abort();
2102
2087
  g.leg = null;
2103
2088
  }
2104
- endBeside(); // a band's promise gives way to the strip's
2089
+ endBeside(true); // a band's shift gives way to the strip: the container comes back
2105
2090
  if (!g.removedFromBoard) {
2106
2091
  g.removedFromBoard = true;
2107
2092
  engine.remove(g.id);
@@ -2122,8 +2107,8 @@ export function bindDashboardGrid(api, group, options = {}) {
2122
2107
  }
2123
2108
  }
2124
2109
  // BESIDE a tab container (0.4.45): its outer band puts the widget next
2125
- // to it — at the board's edge the container shifts over to make room.
2126
- // Nothing moves while held (0.4.47): an overlay marks the cell.
2110
+ // to it — at the board's edge the container shifts over to make room,
2111
+ // live and gliding, like any widget gives way (0.4.48).
2127
2112
  if (!isStatic) {
2128
2113
  const z = besideZoneAt(ev.world.x, ev.world.y);
2129
2114
  if (z) {
@@ -2131,17 +2116,50 @@ export function bindDashboardGrid(api, group, options = {}) {
2131
2116
  syncPlaceholder();
2132
2117
  return;
2133
2118
  }
2119
+ let z2 = null;
2134
2120
  if (beside) {
2135
- // Off the band — unless the hand is resting on its boundary: the
2136
- // band is a little wider for a pointer already in it, so the
2137
- // promise does not flicker against "into the page" at the line.
2138
- const grp = diagram.getGroup(beside.id);
2139
- const stay = !!grp && bandOf(frameOfGroup(grp), ev.world.x, ev.world.y, BESIDE_BAND + BESIDE_STAY) === beside.side;
2140
- if (stay) {
2121
+ // Off the band: the container comes back unless the POINTER is
2122
+ // still over the cell it vacated the widget's own landing, which
2123
+ // is what "beside" leaves under the pointer once the container has
2124
+ // shifted away (the ghost's wanted cell carries the grab offset and
2125
+ // read as "elsewhere" a move later, and the shift undid itself).
2126
+ // "Still here" is the pointer in the SAME band of the container's
2127
+ // ORIGINAL frame (it asked for beside from there, and the container
2128
+ // is what moved; the band is a little wider for a hand already in
2129
+ // it, so it does not flicker against "into the page" at the line)
2130
+ // or over the cell the widget took: a one-row widget's cell is not
2131
+ // where a hand hovering the band sits, and testing only that undid
2132
+ // the shift on the next move, which pushed the widget to the bottom
2133
+ // as the panel came back (live walk N). "Anywhere inside the
2134
+ // original frame" is too much: a hand crossing the bottom band on
2135
+ // its way to the middle never reached the page (lab L73) — the
2136
+ // middle of the original frame is a zone change.
2137
+ const r = cellToRect({ x: beside.vacated.x, y: beside.vacated.y, w: g.spans.w, h: g.spans.h }, frame(), geom(), rows());
2138
+ const inR = (q) => ev.world.x >= q.x - gap && ev.world.x <= q.x + q.width + gap && ev.world.y >= q.y - gap && ev.world.y <= q.y + q.height + gap;
2139
+ // A DIFFERENT band of the original frame wins over the vacated
2140
+ // cell: a widget as wide as the container took the container's
2141
+ // whole frame as its cell on a top-band push, so "over the cell"
2142
+ // held "above" all the way into the right corner (lab L95).
2143
+ const stay = bandOf(beside.frame0, ev.world.x, ev.world.y, BESIDE_BAND + BESIDE_STAY);
2144
+ const other = bandOf(beside.frame0, ev.world.x, ev.world.y);
2145
+ const over = stay === beside.side || (!(other !== null && other !== beside.side) && inR(r));
2146
+ if (!over) {
2147
+ endBeside(true);
2148
+ // The container is back where it was: the pointer may be in
2149
+ // ANOTHER of its bands now (the top band's push had carried the
2150
+ // frame away from under a hand heading for the corner) — resolve
2151
+ // again in this same move, or the hand rests on a stale preview.
2152
+ z2 = besideZoneAt(ev.world.x, ev.world.y);
2153
+ }
2154
+ else {
2141
2155
  syncPlaceholder();
2142
2156
  return;
2143
2157
  }
2144
- endBeside(); // the overlay goes; the handoff below puts the ghost back on a board
2158
+ }
2159
+ if (z2) {
2160
+ applyBeside(g, z2);
2161
+ syncPlaceholder();
2162
+ return;
2145
2163
  }
2146
2164
  }
2147
2165
  // Deepest board under the pointer wins: the nested KPI strip beats the
@@ -2578,13 +2596,6 @@ export function bindDashboardGrid(api, group, options = {}) {
2578
2596
  (_b = options.onGesture) === null || _b === void 0 ? void 0 : _b.call(options, { type: 'commit', kind: g.kind, nodeId: g.id, changed: true });
2579
2597
  return;
2580
2598
  }
2581
- if (beside) {
2582
- // -- BESIDE A TAB CONTAINER (0.4.47): the shift or push the overlay
2583
- // promised happens now, and the commit below records both.
2584
- realizeBeside(g);
2585
- commitGesture(g);
2586
- return;
2587
- }
2588
2599
  if (g.leg) {
2589
2600
  // -- CROSS-CONTAINER COMMIT: one batch across both boards -----------
2590
2601
  const fin = g.leg.adopted.finalize();