@grafloria/element 0.4.44 → 0.4.45

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.44",
3
+ "version": "0.4.45",
4
4
  "type": "module",
5
5
  "main": "./src/index.js",
6
6
  "types": "./src/index.d.ts",
@@ -55,6 +55,7 @@ import { LiveRegionController, registerTool } from '@grafloria/renderer';
55
55
  import { buildCommitCommands, cellFromGridItem, cellToRect, columnUnitFor, gridItemFromCell, pointToCell, rowHeightFor, sizeToSpan, } from './grid-mapping.js';
56
56
  import { ensureDashboardKitStyles } from './styles.js';
57
57
  import { captionOfGroup, captionPainted, captionPassThrough, captionKey, paintCaptionBand, sectionCaptionReserve, sizeCaptionBand } from './caption.js';
58
+ import { TAB_STRIP_HEIGHT } from './tabs.js';
58
59
  /** The class of the painted grip element (a child of the node host). */
59
60
  export const GRIP_CLASS = 'axdb-grip';
60
61
  /** The container class that turns the caption strip's grip dots on. */
@@ -994,6 +995,19 @@ export function bindDashboardGrid(api, group, options = {}) {
994
995
  }
995
996
  return out;
996
997
  };
998
+ /** The member groups a gesture displaced, as their own cell commands — `buildCommitCommands` skips groups by design. */
999
+ const groupCellCommands = (deltas) => {
1000
+ const out = [];
1001
+ for (const d of deltas) {
1002
+ if (!d.isGroup || (d.cellBefore.x === d.cellAfter.x && d.cellBefore.y === d.cellAfter.y && d.cellBefore.w === d.cellAfter.w && d.cellBefore.h === d.cellAfter.h))
1003
+ continue;
1004
+ const og = diagram.getGroup(d.id);
1005
+ if (!og)
1006
+ continue;
1007
+ out.push(new SetGroupCellCommand(d.id, d.cellBefore, d.cellAfter, { x: d.posBefore.x, y: d.posBefore.y, width: d.sizeBefore.width, height: d.sizeBefore.height }, frameOfGroup(og)));
1008
+ }
1009
+ return out;
1010
+ };
997
1011
  const execute = (name, commands) => {
998
1012
  if (commands.length === 0)
999
1013
  return false;
@@ -1249,6 +1263,109 @@ export function bindDashboardGrid(api, group, options = {}) {
1249
1263
  */
1250
1264
  const TAB_FRAME_GRIP = 3;
1251
1265
  const edgeGripFor = (grp) => (isTabsGroup(grp) ? TAB_FRAME_GRIP : EDGE_GRIP);
1266
+ const BESIDE_BAND = 0.2;
1267
+ const besideZoneAt = (wx, wy) => {
1268
+ var _a;
1269
+ for (const id of (_a = group.members) !== null && _a !== void 0 ? _a : []) {
1270
+ const grp = diagram.getGroup(id);
1271
+ if (!grp || diagram.getNode(id) || !isTabsGroup(grp))
1272
+ continue;
1273
+ const p = grp.position;
1274
+ const sz = sizeOf(grp);
1275
+ if (wx < p.x || wx > p.x + sz.width || wy < p.y || wy > p.y + sz.height)
1276
+ continue;
1277
+ const bodyY = p.y + TAB_STRIP_HEIGHT;
1278
+ const bodyH = Math.max(1, sz.height - TAB_STRIP_HEIGHT);
1279
+ const rx = (wx - p.x) / Math.max(1, sz.width);
1280
+ const ry = (wy - bodyY) / bodyH;
1281
+ if (ry < 0)
1282
+ return null; // the strip: a new tab, the strip's own business
1283
+ if (rx >= BESIDE_BAND && rx <= 1 - BESIDE_BAND && ry >= BESIDE_BAND && ry <= 1 - BESIDE_BAND)
1284
+ return null; // the middle: into the page
1285
+ const d = [['left', rx], ['right', 1 - rx], ['top', ry], ['bottom', 1 - ry]];
1286
+ d.sort((a, b) => a[1] - b[1]);
1287
+ return { id, side: d[0][0] };
1288
+ }
1289
+ return null;
1290
+ };
1291
+ /** The container a BESIDE drop unlocked (and maybe shifted): relocked, and put back if asked, when the zone or the gesture ends. */
1292
+ let beside = null;
1293
+ const endBeside = (restore) => {
1294
+ if (!beside)
1295
+ return;
1296
+ const it = engine.getItem(beside.id);
1297
+ if (it && restore && (it.x !== beside.from.x || it.y !== beside.from.y))
1298
+ engine.moveCheck(beside.id, beside.from.x, beside.from.y, { gate: false });
1299
+ if (it)
1300
+ it.locked = true;
1301
+ beside = null;
1302
+ };
1303
+ const applyBeside = (g, z) => {
1304
+ var _a;
1305
+ const tc = engine.getItem(z.id);
1306
+ if (!tc)
1307
+ return;
1308
+ if (g.leg) {
1309
+ g.leg.adopted.abort();
1310
+ g.leg = null;
1311
+ }
1312
+ const w = g.spans.w;
1313
+ const h = g.spans.h;
1314
+ if (g.removedFromBoard) {
1315
+ g.removedFromBoard = false;
1316
+ (_a = hostOf(g.id)) === null || _a === void 0 ? void 0 : _a.classList.remove('axdb-out');
1317
+ engine.add({ id: g.id, x: 0, y: engine.rows(), w, h });
1318
+ }
1319
+ if (beside && beside.id !== z.id)
1320
+ endBeside(true);
1321
+ const from = beside ? beside.from : { x: tc.x, y: tc.y };
1322
+ let cell;
1323
+ let shiftTo = null;
1324
+ switch (z.side) {
1325
+ case 'right':
1326
+ cell = { x: from.x + tc.w, y: from.y };
1327
+ if (cell.x + w > columns) {
1328
+ shiftTo = { x: columns - w - tc.w, y: from.y };
1329
+ cell = { x: columns - w, y: from.y };
1330
+ }
1331
+ break;
1332
+ case 'left':
1333
+ cell = { x: from.x - w, y: from.y };
1334
+ if (cell.x < 0) {
1335
+ shiftTo = { x: w, y: from.y };
1336
+ cell = { x: 0, y: from.y };
1337
+ }
1338
+ break;
1339
+ case 'top':
1340
+ 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
1341
+ break;
1342
+ default:
1343
+ cell = { x: Math.max(0, Math.min(from.x, columns - w)), y: from.y + tc.h };
1344
+ }
1345
+ if (shiftTo && shiftTo.x < 0)
1346
+ shiftTo = null; // no room even shifted: the widget goes where it can
1347
+ if (!beside)
1348
+ beside = { id: z.id, from, vacated: cell };
1349
+ tc.locked = false; // for the gesture: it shifts, or the ghost pushes it
1350
+ // The ghost steps off the board while the container shifts: on a bounded
1351
+ // board a chart as wide as the panel had its own cell as the panel's
1352
+ // target, could not be pushed down, and the shift was refused (lab L94).
1353
+ // Then it takes the cell the shift vacated.
1354
+ if (shiftTo && (tc.x !== shiftTo.x || tc.y !== shiftTo.y)) {
1355
+ if (engine.getItem(g.id))
1356
+ engine.remove(g.id);
1357
+ engine.moveCheck(z.id, shiftTo.x, shiftTo.y, { gate: false });
1358
+ }
1359
+ beside.vacated = cell;
1360
+ if (!engine.getItem(g.id))
1361
+ engine.add({ id: g.id, x: 0, y: engine.rows(), w, h });
1362
+ if (!engine.moveCheck(g.id, cell.x, cell.y, { gate: false }).changed) {
1363
+ const at = engine.getItem(g.id);
1364
+ if (!at || at.x !== cell.x || at.y !== cell.y)
1365
+ placeNear(g.id, cell.x, cell.y, w);
1366
+ }
1367
+ project();
1368
+ };
1252
1369
  /**
1253
1370
  * CARRIED (0.4.43): a group dragged by its strip or band moves as ONE thing.
1254
1371
  * The held TILE is transition-exempt (the ghost), but a group has no host of
@@ -1669,14 +1786,7 @@ export function bindDashboardGrid(api, group, options = {}) {
1669
1786
  // groups by design (a group's cell is its own command), so they are
1670
1787
  // committed here the way a dock commits the groups it displaced — or the
1671
1788
  // model snapped every one of them, and the mover, straight back.
1672
- for (const d of deltas) {
1673
- if (!d.isGroup || (d.cellBefore.x === d.cellAfter.x && d.cellBefore.y === d.cellAfter.y && d.cellBefore.w === d.cellAfter.w && d.cellBefore.h === d.cellAfter.h))
1674
- continue;
1675
- const og = diagram.getGroup(d.id);
1676
- if (!og)
1677
- continue;
1678
- commands.push(new SetGroupCellCommand(d.id, d.cellBefore, d.cellAfter, { x: d.posBefore.x, y: d.posBefore.y, width: d.sizeBefore.width, height: d.sizeBefore.height }, frameOfGroup(og)));
1679
- }
1789
+ commands.push(...groupCellCommands(deltas));
1680
1790
  const b = g.cellBefore;
1681
1791
  if (it && grp && (b.x !== it.x || b.y !== it.y || b.w !== it.w || b.h !== it.h)) {
1682
1792
  commands.push(new SetGroupCellCommand(g.id, b, { x: it.x, y: it.y, w: it.w, h: it.h }, g.frameBefore, frameOfGroup(grp)));
@@ -1798,6 +1908,8 @@ export function bindDashboardGrid(api, group, options = {}) {
1798
1908
  project();
1799
1909
  const deltas = deltasSince(g.startCells, g.startGeom);
1800
1910
  const commands = buildCommitCommands(deltas);
1911
+ commands.push(...groupCellCommands(deltas)); // a container a BESIDE drop shifted (0.4.45)
1912
+ endBeside(false);
1801
1913
  if (g.esc && g.esc.rowsAdded !== 0) {
1802
1914
  commands.push(new SetGroupCellCommand(group.id, g.esc.cellBefore, g.esc.cellAfter, g.esc.frameBefore, g.esc.frameAfter));
1803
1915
  }
@@ -1836,6 +1948,7 @@ export function bindDashboardGrid(api, group, options = {}) {
1836
1948
  g.esc.peer.resizeMemberBy(group.id, -g.esc.rowsAdded); // slab back down
1837
1949
  g.esc = null;
1838
1950
  }
1951
+ endBeside(true);
1839
1952
  if (g.started) {
1840
1953
  if (g.removedFromBoard || g.kind === 'palette') {
1841
1954
  // The engine cannot resurrect a removed item — rebuild from the
@@ -1954,6 +2067,31 @@ export function bindDashboardGrid(api, group, options = {}) {
1954
2067
  g.strip = null;
1955
2068
  }
1956
2069
  }
2070
+ // BESIDE a tab container (0.4.45): its outer band puts the widget next
2071
+ // to it — at the board's edge the container shifts over to make room.
2072
+ if (!isStatic) {
2073
+ const z = besideZoneAt(ev.world.x, ev.world.y);
2074
+ if (z) {
2075
+ applyBeside(g, z);
2076
+ syncPlaceholder();
2077
+ return;
2078
+ }
2079
+ if (beside) {
2080
+ // Off the band: the container comes back unless the POINTER is
2081
+ // still over the cell it vacated — the widget's own landing, which
2082
+ // is what "beside" leaves under the pointer once the container has
2083
+ // shifted away (the ghost's wanted cell carries the grab offset and
2084
+ // read as "elsewhere" a move later, and the shift undid itself).
2085
+ const r = cellToRect({ x: beside.vacated.x, y: beside.vacated.y, w: g.spans.w, h: g.spans.h }, frame(), geom(), rows());
2086
+ const over = ev.world.x >= r.x - gap && ev.world.x <= r.x + r.width + gap && ev.world.y >= r.y - gap && ev.world.y <= r.y + r.height + gap;
2087
+ if (!over)
2088
+ endBeside(true);
2089
+ else {
2090
+ syncPlaceholder();
2091
+ return;
2092
+ }
2093
+ }
2094
+ }
1957
2095
  // Deepest board under the pointer wins: the nested KPI strip beats the
1958
2096
  // tab that contains it; a foreign board beats "outside". Strict frames
1959
2097
  // first; the one-row grace band below each board (gridstack's extra