@grafloria/element 0.4.43 → 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
|
@@ -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
|
|
@@ -1476,13 +1593,36 @@ export function bindDashboardGrid(api, group, options = {}) {
|
|
|
1476
1593
|
api.container.style.cursor = cursorFor(edges);
|
|
1477
1594
|
};
|
|
1478
1595
|
/**
|
|
1479
|
-
* MOVE a section by its caption band
|
|
1480
|
-
* is a LOCKED tile so that
|
|
1481
|
-
*
|
|
1482
|
-
*
|
|
1483
|
-
*
|
|
1484
|
-
*
|
|
1596
|
+
* MOVE a section by its caption band, its strip's empty space or its frame
|
|
1597
|
+
* margin. A section is a LOCKED tile so that no WIDGET pushes it; for the
|
|
1598
|
+
* gesture's duration the moving tile is unlocked, and so are the OTHER
|
|
1599
|
+
* member groups (0.4.44): a moved section or group pushes the sections in
|
|
1600
|
+
* its way, the way a dock does. They used to stay locked against it too
|
|
1601
|
+
* (E4b), so the fluid demo's eight-row side panel, with the Operations
|
|
1602
|
+
* section spanning the row beneath it, had no legal column to its left —
|
|
1603
|
+
* the refusal tone at every cell, and the release moved nothing ("I can't
|
|
1604
|
+
* drag the tab group"). Every unlocked tile gets pushed the way a widget
|
|
1605
|
+
* pushes it; a group's children ride along: the frame moves, the nested
|
|
1606
|
+
* board re-projects. The others relock on release.
|
|
1485
1607
|
*/
|
|
1608
|
+
let slabUnlocked = [];
|
|
1609
|
+
const unlockOthersForSlab = (id) => {
|
|
1610
|
+
slabUnlocked = [];
|
|
1611
|
+
for (const o of engine.getItems()) {
|
|
1612
|
+
if (o.id !== id && o.locked && isGroupMember(o.id)) {
|
|
1613
|
+
o.locked = false;
|
|
1614
|
+
slabUnlocked.push(o.id);
|
|
1615
|
+
}
|
|
1616
|
+
}
|
|
1617
|
+
};
|
|
1618
|
+
const relockOthersForSlab = () => {
|
|
1619
|
+
for (const oid of slabUnlocked) {
|
|
1620
|
+
const o = engine.getItem(oid);
|
|
1621
|
+
if (o)
|
|
1622
|
+
o.locked = true;
|
|
1623
|
+
}
|
|
1624
|
+
slabUnlocked = [];
|
|
1625
|
+
};
|
|
1486
1626
|
const beginSlabMove = (id, ev) => {
|
|
1487
1627
|
const grp = diagram.getGroup(id);
|
|
1488
1628
|
const it = engine.getItem(id);
|
|
@@ -1491,6 +1631,7 @@ export function bindDashboardGrid(api, group, options = {}) {
|
|
|
1491
1631
|
engine.beginGesture();
|
|
1492
1632
|
const snap = snapshotAll();
|
|
1493
1633
|
it.locked = false;
|
|
1634
|
+
unlockOthersForSlab(id);
|
|
1494
1635
|
slabGesture = {
|
|
1495
1636
|
id,
|
|
1496
1637
|
edges: NO_EDGES,
|
|
@@ -1628,6 +1769,7 @@ export function bindDashboardGrid(api, group, options = {}) {
|
|
|
1628
1769
|
releasePointer(g.pointerId);
|
|
1629
1770
|
api.container.style.cursor = '';
|
|
1630
1771
|
relockSlab(g.id);
|
|
1772
|
+
relockOthersForSlab();
|
|
1631
1773
|
if (g.move && g.started)
|
|
1632
1774
|
setCarried(g.id, false); // exempt through the drop write, then the glides resume
|
|
1633
1775
|
if (!g.started) {
|
|
@@ -1638,7 +1780,13 @@ export function bindDashboardGrid(api, group, options = {}) {
|
|
|
1638
1780
|
project();
|
|
1639
1781
|
const it = engine.getItem(g.id);
|
|
1640
1782
|
const grp = diagram.getGroup(g.id);
|
|
1641
|
-
const
|
|
1783
|
+
const deltas = deltasSince(g.startCells, g.startGeom, g.id);
|
|
1784
|
+
const commands = buildCommitCommands(deltas);
|
|
1785
|
+
// The sections and groups the move PUSHED (0.4.44): the tile commit skips
|
|
1786
|
+
// groups by design (a group's cell is its own command), so they are
|
|
1787
|
+
// committed here the way a dock commits the groups it displaced — or the
|
|
1788
|
+
// model snapped every one of them, and the mover, straight back.
|
|
1789
|
+
commands.push(...groupCellCommands(deltas));
|
|
1642
1790
|
const b = g.cellBefore;
|
|
1643
1791
|
if (it && grp && (b.x !== it.x || b.y !== it.y || b.w !== it.w || b.h !== it.h)) {
|
|
1644
1792
|
commands.push(new SetGroupCellCommand(g.id, b, { x: it.x, y: it.y, w: it.w, h: it.h }, g.frameBefore, frameOfGroup(grp)));
|
|
@@ -1658,6 +1806,7 @@ export function bindDashboardGrid(api, group, options = {}) {
|
|
|
1658
1806
|
releasePointer(g.pointerId);
|
|
1659
1807
|
api.container.style.cursor = '';
|
|
1660
1808
|
relockSlab(g.id);
|
|
1809
|
+
relockOthersForSlab();
|
|
1661
1810
|
if (g.move && g.started)
|
|
1662
1811
|
setCarried(g.id, false);
|
|
1663
1812
|
if (g.started)
|
|
@@ -1759,6 +1908,8 @@ export function bindDashboardGrid(api, group, options = {}) {
|
|
|
1759
1908
|
project();
|
|
1760
1909
|
const deltas = deltasSince(g.startCells, g.startGeom);
|
|
1761
1910
|
const commands = buildCommitCommands(deltas);
|
|
1911
|
+
commands.push(...groupCellCommands(deltas)); // a container a BESIDE drop shifted (0.4.45)
|
|
1912
|
+
endBeside(false);
|
|
1762
1913
|
if (g.esc && g.esc.rowsAdded !== 0) {
|
|
1763
1914
|
commands.push(new SetGroupCellCommand(group.id, g.esc.cellBefore, g.esc.cellAfter, g.esc.frameBefore, g.esc.frameAfter));
|
|
1764
1915
|
}
|
|
@@ -1797,6 +1948,7 @@ export function bindDashboardGrid(api, group, options = {}) {
|
|
|
1797
1948
|
g.esc.peer.resizeMemberBy(group.id, -g.esc.rowsAdded); // slab back down
|
|
1798
1949
|
g.esc = null;
|
|
1799
1950
|
}
|
|
1951
|
+
endBeside(true);
|
|
1800
1952
|
if (g.started) {
|
|
1801
1953
|
if (g.removedFromBoard || g.kind === 'palette') {
|
|
1802
1954
|
// The engine cannot resurrect a removed item — rebuild from the
|
|
@@ -1915,6 +2067,31 @@ export function bindDashboardGrid(api, group, options = {}) {
|
|
|
1915
2067
|
g.strip = null;
|
|
1916
2068
|
}
|
|
1917
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
|
+
}
|
|
1918
2095
|
// Deepest board under the pointer wins: the nested KPI strip beats the
|
|
1919
2096
|
// tab that contains it; a foreign board beats "outside". Strict frames
|
|
1920
2097
|
// first; the one-row grace band below each board (gridstack's extra
|