@grafloria/element 0.4.42 → 0.4.44
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 +1 -1
- package/src/lib/dashboard-kit/dashboard.js +10 -4
- package/src/lib/dashboard-kit/grid-binder.js +184 -12
- package/src/lib/dashboard-kit/split-binder.js +68 -6
- package/src/lib/dashboard-kit/styles.d.ts +1 -1
- package/src/lib/dashboard-kit/styles.js +44 -0
- package/src/lib/dashboard-kit/tabs.d.ts +11 -0
- package/src/lib/dashboard-kit/tabs.js +6 -0
package/package.json
CHANGED
|
@@ -48,7 +48,7 @@ import { bindDashboardGrid, parentPeerOf, SequenceCommand, } from './grid-binder
|
|
|
48
48
|
import { bindDashboardSplit, SPLIT_TREE_KEY } from './split-binder.js';
|
|
49
49
|
import { gridItemFromCell } from './grid-mapping.js';
|
|
50
50
|
import { ensureDashboardKitStyles } from './styles.js';
|
|
51
|
-
import { paintTabStrip, tabStripKey, tabStripReserve } from './tabs.js';
|
|
51
|
+
import { paintTabStrip, tabStripKey, tabStripReserve, tabPageInset } from './tabs.js';
|
|
52
52
|
import { defaultWidgetRenderer } from './widgets.js';
|
|
53
53
|
/**
|
|
54
54
|
* Add a widget node AND its board membership as ONE undoable step.
|
|
@@ -457,8 +457,12 @@ export function attachTabsRuntime(ctx, model, container, handle) {
|
|
|
457
457
|
active = pages[0].id;
|
|
458
458
|
ctx.activeTab.set(id, active);
|
|
459
459
|
const strip = tabStripReserve(ctx.tabsOf.get(id), pages.length);
|
|
460
|
+
// The pages sit INSET in the container's frame (0.4.43): below the strip
|
|
461
|
+
// and its inset, `inset` px in from the sides and the bottom, so their
|
|
462
|
+
// cards read as inside the panel rather than as loose cards under a strip.
|
|
463
|
+
const inset = tabPageInset(ctx.tabsOf.get(id));
|
|
460
464
|
const f = { x: cg.position.x, y: cg.position.y, width: (_d = (_c = cg.size) === null || _c === void 0 ? void 0 : _c.width) !== null && _d !== void 0 ? _d : 0, height: (_f = (_e = cg.size) === null || _e === void 0 ? void 0 : _e.height) !== null && _f !== void 0 ? _f : 0 };
|
|
461
|
-
const inner = { width: f.width, height: Math.max(0, f.height - strip) };
|
|
465
|
+
const inner = { width: Math.max(0, f.width - 2 * inset), height: Math.max(0, f.height - strip - 2 * inset) };
|
|
462
466
|
const write = (fn) => (model.runSystemWrite ? model.runSystemWrite(fn) : fn());
|
|
463
467
|
write(() => {
|
|
464
468
|
for (const p of pages) {
|
|
@@ -467,7 +471,7 @@ export function attachTabsRuntime(ctx, model, container, handle) {
|
|
|
467
471
|
continue;
|
|
468
472
|
// A PARKED page keeps its size, so its own board keeps its layout and
|
|
469
473
|
// comes back exactly as it was; only its x leaves the canvas.
|
|
470
|
-
pg.setFrame({ x: p.id === active ? f.x : OFFSCREEN_X, y: f.y + strip, width: inner.width, height: inner.height });
|
|
474
|
+
pg.setFrame({ x: p.id === active ? f.x + inset : OFFSCREEN_X, y: f.y + strip + inset, width: inner.width, height: inner.height });
|
|
471
475
|
}
|
|
472
476
|
});
|
|
473
477
|
for (const p of pages)
|
|
@@ -1049,7 +1053,9 @@ export function createDashboardHandle(ctx) {
|
|
|
1049
1053
|
W = `${pageId}__group${n}`;
|
|
1050
1054
|
const tabsOpts = (_d = ctx.tabsOf.get(containerId)) !== null && _d !== void 0 ? _d : {};
|
|
1051
1055
|
const label = (_e = pageSpec.title) !== null && _e !== void 0 ? _e : pageId;
|
|
1052
|
-
|
|
1056
|
+
// The new group is sized for the page PLUS its frame: the strip above, the inset around.
|
|
1057
|
+
const inset = tabPageInset(tabsOpts);
|
|
1058
|
+
const size = { width: ((_g = (_f = pg.size) === null || _f === void 0 ? void 0 : _f.width) !== null && _g !== void 0 ? _g : 0) + 2 * inset, height: ((_j = (_h = pg.size) === null || _h === void 0 ? void 0 : _h.height) !== null && _j !== void 0 ? _j : 0) + tabStripReserve(tabsOpts, 1) + 2 * inset };
|
|
1053
1059
|
const remaining = [...((_k = from.members) !== null && _k !== void 0 ? _k : [])].filter((m) => m !== pageId && !!model.getGroup(m));
|
|
1054
1060
|
// The page that was showing when the gesture began. Undo puts the tab
|
|
1055
1061
|
// back — and shows THAT page again, not the one the container switched to
|
|
@@ -1196,10 +1196,10 @@ export function bindDashboardGrid(api, group, options = {}) {
|
|
|
1196
1196
|
return null;
|
|
1197
1197
|
};
|
|
1198
1198
|
/** Which of a section frame's edges a world point is within EDGE_GRIP of. */
|
|
1199
|
-
const slabEdgesNear = (grp, x, y) => {
|
|
1199
|
+
const slabEdgesNear = (grp, x, y, grip = edgeGripFor(grp)) => {
|
|
1200
1200
|
const p = grp.position;
|
|
1201
1201
|
const s = sizeOf(grp);
|
|
1202
|
-
return { n: y - p.y <=
|
|
1202
|
+
return { n: y - p.y <= grip, s: p.y + s.height - y <= grip, w: x - p.x <= grip, e: p.x + s.width - x <= grip };
|
|
1203
1203
|
};
|
|
1204
1204
|
/**
|
|
1205
1205
|
* SECTION CHROME. A section (member group) paints no card of its own, so it
|
|
@@ -1211,9 +1211,121 @@ export function bindDashboardGrid(api, group, options = {}) {
|
|
|
1211
1211
|
* corner handle; its frame edges answer the resize cursor.
|
|
1212
1212
|
*/
|
|
1213
1213
|
const slabEls = new Map();
|
|
1214
|
+
/**
|
|
1215
|
+
* GROUP FRAME (0.4.43): a TAB CONTAINER wears a frame by default — its slab
|
|
1216
|
+
* is bordered and a tinted surface lies under its pages, first in the layer
|
|
1217
|
+
* so the tiles paint over it. A page torn out with two widgets under its tab
|
|
1218
|
+
* read as a strip floating over two loose cards: nothing said the second
|
|
1219
|
+
* card was the tab's. A plain section keeps its invisible slab.
|
|
1220
|
+
*/
|
|
1221
|
+
const groupBgs = new Map();
|
|
1222
|
+
const syncGroupBg = (layer, id, on, x, y, w, h) => {
|
|
1223
|
+
var _a;
|
|
1224
|
+
let bg = (_a = groupBgs.get(id)) !== null && _a !== void 0 ? _a : null;
|
|
1225
|
+
if (!on) {
|
|
1226
|
+
bg === null || bg === void 0 ? void 0 : bg.remove();
|
|
1227
|
+
groupBgs.delete(id);
|
|
1228
|
+
return;
|
|
1229
|
+
}
|
|
1230
|
+
if (!bg || bg.parentElement !== layer) {
|
|
1231
|
+
bg === null || bg === void 0 ? void 0 : bg.remove();
|
|
1232
|
+
bg = document.createElement('div');
|
|
1233
|
+
bg.className = 'axdb-group-bg';
|
|
1234
|
+
bg.setAttribute('data-group-bg', id);
|
|
1235
|
+
layer.prepend(bg);
|
|
1236
|
+
groupBgs.set(id, bg);
|
|
1237
|
+
}
|
|
1238
|
+
bg.style.left = `${x}px`;
|
|
1239
|
+
bg.style.top = `${y}px`;
|
|
1240
|
+
bg.style.width = `${w}px`;
|
|
1241
|
+
bg.style.height = `${h}px`;
|
|
1242
|
+
};
|
|
1243
|
+
const isTabsGroup = (grp) => { var _a; return ((_a = grp.getMetadata('containerWidget')) === null || _a === void 0 ? void 0 : _a.layout) === 'tabs'; };
|
|
1244
|
+
/**
|
|
1245
|
+
* A TAB CONTAINER's frame is its drag handle (0.4.43): its 8-px margin —
|
|
1246
|
+
* under the strip, beside the pages — moves the group, so the edge-resize
|
|
1247
|
+
* zone shrinks to 3 px there (the corner handle still resizes). A section's
|
|
1248
|
+
* edges keep the full grip: its empty band is a drop target, not a handle.
|
|
1249
|
+
*/
|
|
1250
|
+
const TAB_FRAME_GRIP = 3;
|
|
1251
|
+
const edgeGripFor = (grp) => (isTabsGroup(grp) ? TAB_FRAME_GRIP : EDGE_GRIP);
|
|
1252
|
+
/**
|
|
1253
|
+
* CARRIED (0.4.43): a group dragged by its strip or band moves as ONE thing.
|
|
1254
|
+
* The held TILE is transition-exempt (the ghost), but a group has no host of
|
|
1255
|
+
* its own: its strip jumped to the pointer while its pages' tiles GLIDED
|
|
1256
|
+
* after it — on the live demo the content trailed the strip by up to 140 px
|
|
1257
|
+
* at every step. Everything in the group's subtree — tiles, strips, slabs,
|
|
1258
|
+
* the surface — is exempt for the gesture and, like the ghost, through the
|
|
1259
|
+
* drop write; then the glides resume.
|
|
1260
|
+
*/
|
|
1261
|
+
const carriedEls = new Set();
|
|
1262
|
+
let carriedTimer = null;
|
|
1263
|
+
const subtreeIds = (id) => {
|
|
1264
|
+
var _a;
|
|
1265
|
+
const groups = [];
|
|
1266
|
+
const nodes = [];
|
|
1267
|
+
const queue = [id];
|
|
1268
|
+
const seen = new Set();
|
|
1269
|
+
while (queue.length) {
|
|
1270
|
+
const cur = queue.shift();
|
|
1271
|
+
if (seen.has(cur))
|
|
1272
|
+
continue;
|
|
1273
|
+
seen.add(cur);
|
|
1274
|
+
const grp = diagram.getGroup(cur);
|
|
1275
|
+
if (grp) {
|
|
1276
|
+
groups.push(cur);
|
|
1277
|
+
for (const m of (_a = grp.members) !== null && _a !== void 0 ? _a : [])
|
|
1278
|
+
queue.push(m);
|
|
1279
|
+
}
|
|
1280
|
+
else if (diagram.getNode(cur))
|
|
1281
|
+
nodes.push(cur);
|
|
1282
|
+
}
|
|
1283
|
+
return { groups, nodes };
|
|
1284
|
+
};
|
|
1285
|
+
const cssId = (id) => (typeof CSS !== 'undefined' && CSS.escape ? CSS.escape(id) : id.replace(/"/g, '\\"'));
|
|
1286
|
+
const setCarried = (id, on) => {
|
|
1287
|
+
const layer = htmlLayer();
|
|
1288
|
+
if (!layer)
|
|
1289
|
+
return;
|
|
1290
|
+
if (carriedTimer) {
|
|
1291
|
+
clearTimeout(carriedTimer);
|
|
1292
|
+
carriedTimer = null;
|
|
1293
|
+
}
|
|
1294
|
+
if (on) {
|
|
1295
|
+
const { groups, nodes } = subtreeIds(id);
|
|
1296
|
+
const els = [];
|
|
1297
|
+
for (const n of nodes) {
|
|
1298
|
+
const h = hostOf(n);
|
|
1299
|
+
if (h)
|
|
1300
|
+
els.push(h);
|
|
1301
|
+
}
|
|
1302
|
+
for (const g of groups) {
|
|
1303
|
+
els.push(...Array.from(layer.querySelectorAll(`:scope > .axdb-tabs[data-tabs-id="${cssId(g)}"], :scope > .axdb-slab[data-slab-id="${cssId(g)}"], :scope > .axdb-group-bg[data-group-bg="${cssId(g)}"]`)));
|
|
1304
|
+
}
|
|
1305
|
+
for (const el of els) {
|
|
1306
|
+
el.classList.add('axdb-carried');
|
|
1307
|
+
carriedEls.add(el);
|
|
1308
|
+
}
|
|
1309
|
+
return;
|
|
1310
|
+
}
|
|
1311
|
+
carriedTimer = setTimeout(() => {
|
|
1312
|
+
for (const el of carriedEls)
|
|
1313
|
+
el.classList.remove('axdb-carried');
|
|
1314
|
+
carriedEls.clear();
|
|
1315
|
+
carriedTimer = null;
|
|
1316
|
+
}, 60);
|
|
1317
|
+
};
|
|
1318
|
+
const flushCarried = () => {
|
|
1319
|
+
if (carriedTimer)
|
|
1320
|
+
clearTimeout(carriedTimer);
|
|
1321
|
+
carriedTimer = null;
|
|
1322
|
+
for (const el of carriedEls)
|
|
1323
|
+
el.classList.remove('axdb-carried');
|
|
1324
|
+
carriedEls.clear();
|
|
1325
|
+
};
|
|
1214
1326
|
let slabLayer = null;
|
|
1215
1327
|
const syncSlabs = () => {
|
|
1216
|
-
var _a, _b;
|
|
1328
|
+
var _a, _b, _c;
|
|
1217
1329
|
if (disposed)
|
|
1218
1330
|
return;
|
|
1219
1331
|
const layer = (slabLayer === null || slabLayer === void 0 ? void 0 : slabLayer.isConnected) ? slabLayer : (slabLayer = htmlLayer());
|
|
@@ -1247,6 +1359,9 @@ export function bindDashboardGrid(api, group, options = {}) {
|
|
|
1247
1359
|
el.classList.toggle('axdb-slab--selected', selectedId === id);
|
|
1248
1360
|
el.classList.toggle('axdb-slab--static', isStatic);
|
|
1249
1361
|
(_b = el.querySelector(':scope > .axdb-rs')) === null || _b === void 0 ? void 0 : _b.classList.toggle('axdb-rs--rtl', rtl);
|
|
1362
|
+
const tabs = isTabsGroup(grp);
|
|
1363
|
+
el.classList.toggle('axdb-slab--tabs', tabs);
|
|
1364
|
+
syncGroupBg(layer, id, tabs, p.x, p.y, sz.width, sz.height);
|
|
1250
1365
|
syncCaption(el, id, grp, sz.height);
|
|
1251
1366
|
}
|
|
1252
1367
|
for (const [id, el] of slabEls) {
|
|
@@ -1254,6 +1369,8 @@ export function bindDashboardGrid(api, group, options = {}) {
|
|
|
1254
1369
|
el.remove();
|
|
1255
1370
|
hoverSlabs.delete(el);
|
|
1256
1371
|
slabEls.delete(id);
|
|
1372
|
+
(_c = groupBgs.get(id)) === null || _c === void 0 ? void 0 : _c.remove();
|
|
1373
|
+
groupBgs.delete(id);
|
|
1257
1374
|
}
|
|
1258
1375
|
}
|
|
1259
1376
|
};
|
|
@@ -1359,13 +1476,36 @@ export function bindDashboardGrid(api, group, options = {}) {
|
|
|
1359
1476
|
api.container.style.cursor = cursorFor(edges);
|
|
1360
1477
|
};
|
|
1361
1478
|
/**
|
|
1362
|
-
* MOVE a section by its caption band
|
|
1363
|
-
* is a LOCKED tile so that
|
|
1364
|
-
*
|
|
1365
|
-
*
|
|
1366
|
-
*
|
|
1367
|
-
*
|
|
1479
|
+
* MOVE a section by its caption band, its strip's empty space or its frame
|
|
1480
|
+
* margin. A section is a LOCKED tile so that no WIDGET pushes it; for the
|
|
1481
|
+
* gesture's duration the moving tile is unlocked, and so are the OTHER
|
|
1482
|
+
* member groups (0.4.44): a moved section or group pushes the sections in
|
|
1483
|
+
* its way, the way a dock does. They used to stay locked against it too
|
|
1484
|
+
* (E4b), so the fluid demo's eight-row side panel, with the Operations
|
|
1485
|
+
* section spanning the row beneath it, had no legal column to its left —
|
|
1486
|
+
* the refusal tone at every cell, and the release moved nothing ("I can't
|
|
1487
|
+
* drag the tab group"). Every unlocked tile gets pushed the way a widget
|
|
1488
|
+
* pushes it; a group's children ride along: the frame moves, the nested
|
|
1489
|
+
* board re-projects. The others relock on release.
|
|
1368
1490
|
*/
|
|
1491
|
+
let slabUnlocked = [];
|
|
1492
|
+
const unlockOthersForSlab = (id) => {
|
|
1493
|
+
slabUnlocked = [];
|
|
1494
|
+
for (const o of engine.getItems()) {
|
|
1495
|
+
if (o.id !== id && o.locked && isGroupMember(o.id)) {
|
|
1496
|
+
o.locked = false;
|
|
1497
|
+
slabUnlocked.push(o.id);
|
|
1498
|
+
}
|
|
1499
|
+
}
|
|
1500
|
+
};
|
|
1501
|
+
const relockOthersForSlab = () => {
|
|
1502
|
+
for (const oid of slabUnlocked) {
|
|
1503
|
+
const o = engine.getItem(oid);
|
|
1504
|
+
if (o)
|
|
1505
|
+
o.locked = true;
|
|
1506
|
+
}
|
|
1507
|
+
slabUnlocked = [];
|
|
1508
|
+
};
|
|
1369
1509
|
const beginSlabMove = (id, ev) => {
|
|
1370
1510
|
const grp = diagram.getGroup(id);
|
|
1371
1511
|
const it = engine.getItem(id);
|
|
@@ -1374,6 +1514,7 @@ export function bindDashboardGrid(api, group, options = {}) {
|
|
|
1374
1514
|
engine.beginGesture();
|
|
1375
1515
|
const snap = snapshotAll();
|
|
1376
1516
|
it.locked = false;
|
|
1517
|
+
unlockOthersForSlab(id);
|
|
1377
1518
|
slabGesture = {
|
|
1378
1519
|
id,
|
|
1379
1520
|
edges: NO_EDGES,
|
|
@@ -1427,6 +1568,8 @@ export function bindDashboardGrid(api, group, options = {}) {
|
|
|
1427
1568
|
return;
|
|
1428
1569
|
g.started = true;
|
|
1429
1570
|
armGlide();
|
|
1571
|
+
if (g.move)
|
|
1572
|
+
setCarried(g.id, true);
|
|
1430
1573
|
}
|
|
1431
1574
|
const it = engine.getItem(g.id);
|
|
1432
1575
|
if (!it)
|
|
@@ -1447,8 +1590,10 @@ export function bindDashboardGrid(api, group, options = {}) {
|
|
|
1447
1590
|
// column (the row search) read as the wrong tile moving.
|
|
1448
1591
|
const was = { x: it.x, y: it.y };
|
|
1449
1592
|
const moved = engine.moveCheck(g.id, cell.x, cell.y, { gate: false }).changed || placeOnRow(g.id, cell.x, cell.y, it.w);
|
|
1450
|
-
if (moved)
|
|
1593
|
+
if (moved) {
|
|
1451
1594
|
project();
|
|
1595
|
+
setCarried(g.id, true); // chrome repainted by the projection is carried too
|
|
1596
|
+
}
|
|
1452
1597
|
// STUCK: the pointer asks for another cell and the slab did not budge
|
|
1453
1598
|
// (placeOnRow counts "already on a legal cell" as placed) — paint what
|
|
1454
1599
|
// was asked for as refused.
|
|
@@ -1507,6 +1652,9 @@ export function bindDashboardGrid(api, group, options = {}) {
|
|
|
1507
1652
|
releasePointer(g.pointerId);
|
|
1508
1653
|
api.container.style.cursor = '';
|
|
1509
1654
|
relockSlab(g.id);
|
|
1655
|
+
relockOthersForSlab();
|
|
1656
|
+
if (g.move && g.started)
|
|
1657
|
+
setCarried(g.id, false); // exempt through the drop write, then the glides resume
|
|
1510
1658
|
if (!g.started) {
|
|
1511
1659
|
engine.endGesture();
|
|
1512
1660
|
return;
|
|
@@ -1515,7 +1663,20 @@ export function bindDashboardGrid(api, group, options = {}) {
|
|
|
1515
1663
|
project();
|
|
1516
1664
|
const it = engine.getItem(g.id);
|
|
1517
1665
|
const grp = diagram.getGroup(g.id);
|
|
1518
|
-
const
|
|
1666
|
+
const deltas = deltasSince(g.startCells, g.startGeom, g.id);
|
|
1667
|
+
const commands = buildCommitCommands(deltas);
|
|
1668
|
+
// The sections and groups the move PUSHED (0.4.44): the tile commit skips
|
|
1669
|
+
// groups by design (a group's cell is its own command), so they are
|
|
1670
|
+
// committed here the way a dock commits the groups it displaced — or the
|
|
1671
|
+
// 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
|
+
}
|
|
1519
1680
|
const b = g.cellBefore;
|
|
1520
1681
|
if (it && grp && (b.x !== it.x || b.y !== it.y || b.w !== it.w || b.h !== it.h)) {
|
|
1521
1682
|
commands.push(new SetGroupCellCommand(g.id, b, { x: it.x, y: it.y, w: it.w, h: it.h }, g.frameBefore, frameOfGroup(grp)));
|
|
@@ -1535,6 +1696,9 @@ export function bindDashboardGrid(api, group, options = {}) {
|
|
|
1535
1696
|
releasePointer(g.pointerId);
|
|
1536
1697
|
api.container.style.cursor = '';
|
|
1537
1698
|
relockSlab(g.id);
|
|
1699
|
+
relockOthersForSlab();
|
|
1700
|
+
if (g.move && g.started)
|
|
1701
|
+
setCarried(g.id, false);
|
|
1538
1702
|
if (g.started)
|
|
1539
1703
|
engine.cancelGesture();
|
|
1540
1704
|
else
|
|
@@ -2914,7 +3078,11 @@ export function bindDashboardGrid(api, group, options = {}) {
|
|
|
2914
3078
|
beginSlabResize(slabId, edges, ev);
|
|
2915
3079
|
// The caption band is the section's handle: pressed and travelled,
|
|
2916
3080
|
// it moves the whole section (its inner empty space only selects).
|
|
2917
|
-
|
|
3081
|
+
// A TAB CONTAINER's frame is its handle (0.4.43): the margin around
|
|
3082
|
+
// its pages moves the group — the strip's empty space, the only
|
|
3083
|
+
// handle before, was one nobody found ("I'm not able to drag an
|
|
3084
|
+
// entire tab group").
|
|
3085
|
+
else if (ownCaption || isTabsGroup(grp))
|
|
2918
3086
|
beginSlabMove(slabId, ev);
|
|
2919
3087
|
return;
|
|
2920
3088
|
}
|
|
@@ -4417,10 +4585,14 @@ export function bindDashboardGrid(api, group, options = {}) {
|
|
|
4417
4585
|
el.remove();
|
|
4418
4586
|
slabEls.clear();
|
|
4419
4587
|
hoverSlabs.clear();
|
|
4588
|
+
for (const bg of groupBgs.values())
|
|
4589
|
+
bg.remove();
|
|
4590
|
+
groupBgs.clear();
|
|
4420
4591
|
// A rebind inside the 60 ms window (a layout switch right after an undo)
|
|
4421
4592
|
// disposed this binder with the timer pending — the host kept its
|
|
4422
4593
|
// lifted ghost for good (visual gate, nested-containers ⑤).
|
|
4423
4594
|
flushGhost();
|
|
4595
|
+
flushCarried();
|
|
4424
4596
|
(_d = htmlLayer()) === null || _d === void 0 ? void 0 : _d.classList.remove('axdb-glide');
|
|
4425
4597
|
api.container.style.cursor = '';
|
|
4426
4598
|
},
|
|
@@ -264,6 +264,43 @@ export function bindDashboardSplit(api, group, options = {}) {
|
|
|
264
264
|
// resizes a pane); the band is the section's drag handle (beginMemberDrag).
|
|
265
265
|
const slabEls = new Map();
|
|
266
266
|
const hoverSlabs = new Set();
|
|
267
|
+
/** The member group (a pane's section or tab container) whose frame holds a world point. */
|
|
268
|
+
const memberGroupAt = (x, y) => {
|
|
269
|
+
var _a, _b;
|
|
270
|
+
for (const id of (_a = group.members) !== null && _a !== void 0 ? _a : new Set()) {
|
|
271
|
+
const grp = diagram.getGroup(id);
|
|
272
|
+
if (!grp || diagram.getNode(id))
|
|
273
|
+
continue;
|
|
274
|
+
const p = grp.position;
|
|
275
|
+
const sz = (_b = grp.size) !== null && _b !== void 0 ? _b : { width: 0, height: 0 };
|
|
276
|
+
if (x >= p.x && x <= p.x + sz.width && y >= p.y && y <= p.y + sz.height)
|
|
277
|
+
return id;
|
|
278
|
+
}
|
|
279
|
+
return null;
|
|
280
|
+
};
|
|
281
|
+
/** GROUP FRAME (0.4.43): a tab container's tinted surface, under its pages — see the grid binder. */
|
|
282
|
+
const groupBgs = new Map();
|
|
283
|
+
const syncGroupBg = (layer, id, on, x, y, w, h) => {
|
|
284
|
+
var _a;
|
|
285
|
+
let bg = (_a = groupBgs.get(id)) !== null && _a !== void 0 ? _a : null;
|
|
286
|
+
if (!on) {
|
|
287
|
+
bg === null || bg === void 0 ? void 0 : bg.remove();
|
|
288
|
+
groupBgs.delete(id);
|
|
289
|
+
return;
|
|
290
|
+
}
|
|
291
|
+
if (!bg || bg.parentElement !== layer) {
|
|
292
|
+
bg === null || bg === void 0 ? void 0 : bg.remove();
|
|
293
|
+
bg = document.createElement('div');
|
|
294
|
+
bg.className = 'axdb-group-bg';
|
|
295
|
+
bg.setAttribute('data-group-bg', id);
|
|
296
|
+
layer.prepend(bg);
|
|
297
|
+
groupBgs.set(id, bg);
|
|
298
|
+
}
|
|
299
|
+
bg.style.left = `${x}px`;
|
|
300
|
+
bg.style.top = `${y}px`;
|
|
301
|
+
bg.style.width = `${w}px`;
|
|
302
|
+
bg.style.height = `${h}px`;
|
|
303
|
+
};
|
|
267
304
|
const syncCaption = (el, id, grp, sectionH) => {
|
|
268
305
|
const cap = captionOfGroup(grp);
|
|
269
306
|
let band = el.querySelector(':scope > .axdb-slab-h');
|
|
@@ -303,7 +340,7 @@ export function bindDashboardSplit(api, group, options = {}) {
|
|
|
303
340
|
}
|
|
304
341
|
};
|
|
305
342
|
const syncSlabs = () => {
|
|
306
|
-
var _a, _b;
|
|
343
|
+
var _a, _b, _c, _d;
|
|
307
344
|
if (disposed)
|
|
308
345
|
return;
|
|
309
346
|
const layer = htmlLayer();
|
|
@@ -332,6 +369,9 @@ export function bindDashboardSplit(api, group, options = {}) {
|
|
|
332
369
|
el.style.height = `${sz.height}px`;
|
|
333
370
|
el.classList.toggle('axdb-slab--selected', selectedId === id);
|
|
334
371
|
el.classList.toggle('axdb-slab--static', isStatic);
|
|
372
|
+
const tabs = ((_c = grp.getMetadata('containerWidget')) === null || _c === void 0 ? void 0 : _c.layout) === 'tabs';
|
|
373
|
+
el.classList.toggle('axdb-slab--tabs', tabs);
|
|
374
|
+
syncGroupBg(layer, id, tabs, p.x, p.y, sz.width, sz.height);
|
|
335
375
|
syncCaption(el, id, grp, sz.height);
|
|
336
376
|
}
|
|
337
377
|
for (const [id, el] of slabEls) {
|
|
@@ -339,6 +379,8 @@ export function bindDashboardSplit(api, group, options = {}) {
|
|
|
339
379
|
el.remove();
|
|
340
380
|
hoverSlabs.delete(el);
|
|
341
381
|
slabEls.delete(id);
|
|
382
|
+
(_d = groupBgs.get(id)) === null || _d === void 0 ? void 0 : _d.remove();
|
|
383
|
+
groupBgs.delete(id);
|
|
342
384
|
}
|
|
343
385
|
}
|
|
344
386
|
};
|
|
@@ -767,7 +809,7 @@ export function bindDashboardSplit(api, group, options = {}) {
|
|
|
767
809
|
return worldInsideBoard(ev.world.x, ev.world.y);
|
|
768
810
|
},
|
|
769
811
|
onPointerDown(ev, hit) {
|
|
770
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r;
|
|
812
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s;
|
|
771
813
|
if (gesture)
|
|
772
814
|
return;
|
|
773
815
|
const target = ((_b = (_a = ev.source) === null || _a === void 0 ? void 0 : _a.target) !== null && _b !== void 0 ? _b : null);
|
|
@@ -817,13 +859,30 @@ export function bindDashboardSplit(api, group, options = {}) {
|
|
|
817
859
|
return;
|
|
818
860
|
}
|
|
819
861
|
if ((!hit.node && !onGrip) || sectionHandle) {
|
|
862
|
+
// A press inside a MEMBER's frame — a tab container's margin under
|
|
863
|
+
// its strip or beside its pages, a section's empty band — selects
|
|
864
|
+
// that member; a tab container's frame is its handle (0.4.43) and
|
|
865
|
+
// drags it the way its strip's empty space does.
|
|
866
|
+
if (!sectionHandle) {
|
|
867
|
+
const member = memberGroupAt(ev.world.x, ev.world.y);
|
|
868
|
+
const mg = member ? diagram.getGroup(member) : undefined;
|
|
869
|
+
if (member && mg) {
|
|
870
|
+
selectWidget(member);
|
|
871
|
+
api.render();
|
|
872
|
+
const srcM = ev.source;
|
|
873
|
+
const tabsM = ((_m = mg.getMetadata('containerWidget')) === null || _m === void 0 ? void 0 : _m.layout) === 'tabs';
|
|
874
|
+
if (!isStatic && tabsM && typeof (srcM === null || srcM === void 0 ? void 0 : srcM.clientX) === 'number' && typeof (srcM === null || srcM === void 0 ? void 0 : srcM.clientY) === 'number')
|
|
875
|
+
beginMemberDrag(member, srcM);
|
|
876
|
+
return;
|
|
877
|
+
}
|
|
878
|
+
}
|
|
820
879
|
// OUR OWN empty band or corner handle, and we are a section of a
|
|
821
880
|
// parent board: select the section there; an edge or the handle
|
|
822
881
|
// starts the section resize, which the parent runs while this tool
|
|
823
882
|
// forwards the pointer sequence (the grid binder does the same).
|
|
824
883
|
const parent = parentPeerOf(api.container, group.id);
|
|
825
884
|
if ((parent === null || parent === void 0 ? void 0 : parent.selectMember) && (sectionHandle || worldInsideBoard(ev.world.x, ev.world.y))) {
|
|
826
|
-
const own = ((
|
|
885
|
+
const own = ((_o = sectionHandle === null || sectionHandle === void 0 ? void 0 : sectionHandle.parentElement) === null || _o === void 0 ? void 0 : _o.getAttribute('data-slab-id')) === group.id;
|
|
827
886
|
parent.selectMember(group.id);
|
|
828
887
|
if (!isStatic && parent.beginSlabResize) {
|
|
829
888
|
const f = frame();
|
|
@@ -835,7 +894,7 @@ export function bindDashboardSplit(api, group, options = {}) {
|
|
|
835
894
|
}
|
|
836
895
|
return;
|
|
837
896
|
}
|
|
838
|
-
(
|
|
897
|
+
(_q = (_p = diagram).clearSelection) === null || _q === void 0 ? void 0 : _q.call(_p);
|
|
839
898
|
selectWidget(undefined);
|
|
840
899
|
api.render();
|
|
841
900
|
return;
|
|
@@ -844,9 +903,9 @@ export function bindDashboardSplit(api, group, options = {}) {
|
|
|
844
903
|
if (!node)
|
|
845
904
|
return;
|
|
846
905
|
selectWidget(node.id); // a press selects, whether or not it starts a gesture
|
|
847
|
-
if (((
|
|
906
|
+
if (((_r = node.state) === null || _r === void 0 ? void 0 : _r.locked) === true || isStatic)
|
|
848
907
|
return;
|
|
849
|
-
if (((
|
|
908
|
+
if (((_s = node.getMetadata) === null || _s === void 0 ? void 0 : _s.call(node, 'widgetMovable')) === false)
|
|
850
909
|
return;
|
|
851
910
|
// Drag-handle mode: only the handle (the caption strip, a custom element
|
|
852
911
|
// or the painted grip) lifts a widget out — see pressOnDragHandle.
|
|
@@ -1665,6 +1724,9 @@ export function bindDashboardSplit(api, group, options = {}) {
|
|
|
1665
1724
|
api.container.removeEventListener('pointerleave', onHoverLeave);
|
|
1666
1725
|
for (const el of slabEls.values())
|
|
1667
1726
|
el.remove();
|
|
1727
|
+
for (const bg of groupBgs.values())
|
|
1728
|
+
bg.remove();
|
|
1729
|
+
groupBgs.clear();
|
|
1668
1730
|
slabEls.clear();
|
|
1669
1731
|
hoverSlabs.clear();
|
|
1670
1732
|
insertion === null || insertion === void 0 ? void 0 : insertion.remove();
|
|
@@ -22,6 +22,6 @@
|
|
|
22
22
|
*/
|
|
23
23
|
export declare const DASHBOARD_KIT_STYLE_ID = "grafloria-dashboard-kit-styles";
|
|
24
24
|
/** The kit stylesheet as text — for a host that bundles its own CSS, and for the styling spec. */
|
|
25
|
-
export declare const DASHBOARD_KIT_CSS = "\n/* ===== reflow glide (armed on the html layer for the whole gesture) ===== */\n.grafloria-html-layer.axdb-glide > .grafloria-node-host {\n transition: left .28s cubic-bezier(.2, 0, .2, 1), top .28s cubic-bezier(.2, 0, .2, 1),\n width .28s cubic-bezier(.2, 0, .2, 1), height .28s cubic-bezier(.2, 0, .2, 1);\n}\n\n/* Motion is a preference (WCAG 2.3.3): no glide for those who asked for none. */\n@media (prefers-reduced-motion: reduce) {\n .grafloria-html-layer.axdb-glide > .grafloria-node-host { transition: none; }\n}\n\n/* ===== keyboard focus: the roving tab stop shows where it is (WCAG 2.4.7) ===== */\n.grafloria-html-layer > .grafloria-node-host:focus-visible {\n outline: 2px solid var(--axdb-accent, #3b52d9);\n outline-offset: 2px;\n border-radius: var(--axdb-rs-radius, 8px);\n}\n\n/* ===== the held tile: transition-exempt ghost, above everything ===== */\n.grafloria-html-layer > .grafloria-node-host.axdb-ghost,\n.grafloria-html-layer.axdb-glide > .grafloria-node-host.axdb-ghost {\n transition: none;\n z-index: 30;\n opacity: .85;\n cursor: grabbing;\n filter: drop-shadow(0 10px 16px rgba(16, 24, 40, .3));\n}\n/* Outside the board: release will REMOVE \u2014 dim the ghost to say so. */\n.grafloria-html-layer > .grafloria-node-host.axdb-ghost.axdb-out { opacity: .35; filter: grayscale(.6); }\n\n/* ===== the placeholder: dashed slab, truthful, never animated ===== */\n.grafloria-html-layer > .axdb-ph {\n position: absolute;\n border-radius: var(--axdb-rs-radius, 8px);\n background: rgba(30, 34, 45, .14);\n border: 2px dashed rgba(30, 34, 45, .28);\n box-sizing: border-box;\n pointer-events: none;\n z-index: 0;\n transition: none;\n}\n/* A slab move asked for a cell it cannot have (a locked section in the way): the wanted cell in the danger tone. */\n.grafloria-html-layer > .axdb-ph.axdb-ph--no { border-color: var(--axdb-danger, #b3123c); background: var(--axdb-danger-soft, rgba(179, 18, 60, .07)); }\n\n@media (prefers-color-scheme: dark) {\n .grafloria-html-layer > .axdb-ph { background: rgba(220, 225, 240, .12); border-color: rgba(220, 225, 240, .3); }\n}\n\n/* ===== corner resize handle (hover-revealed, radius-matched) ===== */\n:is(.grafloria-node-host, .axdb-slab) > .axdb-rs {\n position: absolute;\n right: 0;\n bottom: 0;\n width: 24px; /* WCAG 2.5.8: a 24-px minimum target (was 18) */\n height: 24px;\n cursor: nwse-resize;\n border-right: 3px solid rgba(120, 130, 148, .55);\n border-bottom: 3px solid rgba(120, 130, 148, .55);\n border-bottom-right-radius: var(--axdb-rs-radius, 8px);\n opacity: 0;\n transition: opacity .12s;\n z-index: 5;\n}\n.grafloria-node-host:hover > .axdb-rs { opacity: 1; }\n/* A finger never hovers: on touch devices the corner handle is always visible\n (review D8 \u2014 hover-only meant no resize at all on a tablet). */\n@media (hover: none) { .grafloria-node-host > .axdb-rs { opacity: .8; } }\n.grafloria-node-host > .axdb-rs:hover { border-color: #3b52d9; }\n/* RTL boards grow leftwards, so the grab corner mirrors with them. */\n:is(.grafloria-node-host, .axdb-slab) > .axdb-rs.axdb-rs--rtl {\n right: auto;\n left: 0;\n cursor: nesw-resize;\n border-right: none;\n border-left: 3px solid rgba(120, 130, 148, .55);\n border-bottom-right-radius: 0;\n border-bottom-left-radius: var(--axdb-rs-radius, 8px);\n}\n\n/* ===== palette drag-in chip (screen-space clone following the cursor) ===== */\n.axdb-drag-chip {\n position: fixed;\n z-index: 9999;\n pointer-events: none;\n opacity: .9;\n filter: drop-shadow(0 8px 14px rgba(16, 24, 40, .3));\n transition: opacity .12s, filter .12s;\n}\n/* A bounded board with no room refuses the entry: the chip dims to say so\n (the same signal a tile ghost gives outside the board). Without this rule\n the class was set and nothing showed \u2014 a refusal a user could not see. */\n.axdb-drag-chip.axdb-out { opacity: .35; filter: grayscale(.7) drop-shadow(0 8px 14px rgba(16, 24, 40, .2)); }\n/* A tab dragged off its strip: VS Code carries the TAB, not the editor, so\n what follows the pointer is a chip wearing the tab's own label. */\n.axdb-tab-chip {\n padding: 5px 12px;\n max-width: 220px;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n font: 500 12px/1.4 var(--axdb-font, system-ui, -apple-system, \"Segoe UI\", Roboto, sans-serif);\n color: var(--axdb-tabs-on-fg, #1f2430);\n background: var(--axdb-tabs-on-bg, #fff);\n border-radius: var(--axdb-tab-radius, 6px);\n box-shadow: 0 1px 2px rgba(16, 24, 40, .1), 0 0 0 1px rgba(31, 36, 48, .08);\n}\n@media (prefers-color-scheme: dark) {\n .axdb-tab-chip {\n color: var(--axdb-tabs-on-fg, #eceef4);\n background: var(--axdb-tabs-on-bg, #232836);\n box-shadow: 0 1px 2px rgba(0, 0, 0, .45), 0 0 0 1px rgba(236, 238, 244, .12);\n }\n}\n/* A tab held over ANOTHER group: it will join that group, not the board. The\n group's frame lights up, and its strip marks the slot the tab will take \u2014\n a bar before the tab it lands in front of, or after the last one. */\n.grafloria-html-layer > .axdb-join {\n position: absolute;\n z-index: 3;\n pointer-events: none;\n box-sizing: border-box;\n border: 2px dashed var(--axdb-accent, #3b52d9);\n background: var(--axdb-accent-soft, rgba(59, 82, 217, .08));\n border-radius: var(--axdb-rs-radius, 8px);\n}\n.axdb-tabs.axdb-tabs--drop { box-shadow: inset 0 -2px 0 var(--axdb-accent, #3b52d9); }\n.axdb-tab.axdb-tab--drop-before { position: relative; }\n.axdb-tab.axdb-tab--drop-before::before {\n content: \"\"; position: absolute; left: -3px; top: 4px; bottom: 4px; width: 3px;\n border-radius: 2px; background: var(--axdb-accent, #3b52d9);\n}\n.axdb-tabs.axdb-tabs--drop-end::after {\n content: \"\"; display: inline-block; width: 3px; height: 18px; margin: 0 4px;\n border-radius: 2px; background: var(--axdb-accent, #3b52d9); vertical-align: middle; align-self: center;\n}\n\n/* ===========================================================================\n BUILT-IN WIDGET CARDS \u2014 what widgets.ts paints when a page writes no\n renderWidget. Class-prefixed (never element selectors), so this can only\n ever style DOM the kit itself produced. Colours are CSS variables on the\n card, so a page re-skins every built-in widget by setting them once; the\n corner radius follows --axdb-rs-radius, which is also the placeholder's and\n the resize handle's, so the three can never drift apart.\n =========================================================================== */\n.axdb-widget {\n --axdb-ink: #1f2430;\n /* 5.95:1 on the card \u2014 WCAG 1.4.3 for the 9\u201311px captions this paints\n (the #7a8496 it replaced sat at 3.77:1, axe-core's first finding). */\n --axdb-muted: #5a6478;\n --axdb-grid: rgba(120, 130, 148, .22);\n --axdb-card: #fff;\n --axdb-line: #e5e8ef;\n --axdb-soft: rgba(120, 130, 148, .14);\n --axdb-up: #0f7a3d; /* 5.42:1 */\n --axdb-down: #be123c; /* 6.29:1 */\n /* The categorical palette, as tokens so the dark card can carry its own\n steps: every entry clears 3:1 against its card (WCAG 1.4.11). */\n --axdb-c1: #3b52d9; --axdb-c2: #0369a1; --axdb-c3: #0f766e;\n --axdb-c4: #b45309; --axdb-c5: #6d28d9; --axdb-c6: #475569;\n /* The header band: a panel wears its title in a band across its top, the\n way DevExpress, Grafana and Metabase panels do \u2014 tinted ground, hairline\n below, sentence case (0.4.36; the tracked uppercase micro-label before). */\n --axdb-head-bg: #f7f8fb;\n --axdb-head-fg: #2a3140;\n box-sizing: border-box;\n width: 100%;\n height: 100%;\n display: flex;\n flex-direction: column;\n overflow: hidden;\n padding: 0;\n background: var(--axdb-card);\n border: 1px solid var(--axdb-line);\n border-radius: var(--axdb-rs-radius, 8px);\n box-shadow: 0 1px 2px rgba(16, 24, 40, .04);\n font-family: system-ui, -apple-system, \"Segoe UI\", sans-serif;\n color: var(--axdb-ink);\n}\n/* The host is the card's size container (named, so a widget's inner parts can\n ask the TILE's size as well as their own body's), so the card can shed its\n padding as the tile gets short instead of clipping its own content. */\n.grafloria-html-layer > .grafloria-node-host { container: axdb-tile / size; }\n/* Two class hops on the header: these blocks precede the header's own rule\n below and would lose the cascade at equal specificity \u2014 at 54 px the margin\n stayed 8 px, the body shrank to 14 px and the KPI figure hid. */\n@container axdb-tile (max-height: 90px) {\n .axdb-widget > .axdb-widget-h { padding: 5px 12px; font-size: 11.5px; }\n .axdb-widget > .axdb-widget-b { padding: 6px 12px; }\n}\n/* The row floor: the band gives way to an inline label beside the figure. */\n@container axdb-tile (max-height: 46px) {\n .axdb-widget > .axdb-widget-h { padding: 0 0 0 12px; background: none; border-bottom: 0; }\n .axdb-widget > .axdb-widget-b { padding: 3px 12px; }\n}\n@container axdb-tile (max-height: 26px) {\n .axdb-widget > .axdb-widget-h { display: none; }\n .axdb-widget > .axdb-widget-b { padding: 1px 12px; }\n}\n.axdb-widget-h {\n display: flex;\n align-items: center;\n gap: 8px;\n flex: none;\n min-width: 0;\n padding: 9px 14px;\n background: var(--axdb-head-bg);\n border-bottom: 1px solid var(--axdb-line);\n font: 600 12.5px/1.3 system-ui, -apple-system, \"Segoe UI\", sans-serif;\n letter-spacing: -.005em;\n color: var(--axdb-head-fg);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n/* Drag-handle mode (DevExpress drags an item by its caption): the header is\n the grip and says so with a dot pattern and a grab cursor; the body keeps\n its own cursor because a press there starts nothing. */\n.axdb-drag-handle .axdb-widget > .axdb-widget-h { cursor: grab; }\n.axdb-drag-handle .axdb-widget > .axdb-widget-h::before {\n content: ''; flex: none; width: 8px; height: 12px; opacity: .55;\n background: radial-gradient(circle, currentColor 1.1px, transparent 1.5px) 0 0 / 4px 4px;\n}\n/* A PAINTED GRIP (dragHandle: { grip: true }): the only drag zone, a small\n dotted tab along the card's top edge. Inside sits in the header band and\n the header makes room for it; outside is a tab above the card, the\n DevExpress item bar. The host must not clip it. */\n.grafloria-node-host > .axdb-grip {\n position: absolute; z-index: 4; box-sizing: border-box; width: 24px; height: 12px;\n border: 1px solid var(--axdb-line, #e7eaf1); border-radius: 3px; background: var(--axdb-card, #fff);\n color: var(--axdb-muted, #5a6478); cursor: grab;\n /* Shown on the SELECTED widget only (the DevExpress designer shows an item's\n bar on the selected item): a press or keyboard focus selects, a void\n click clears. Hidden, it takes no pointer either. */\n opacity: 0; pointer-events: none; transition: opacity .12s ease;\n}\n.grafloria-node-host.axdb-selected > .axdb-grip, .grafloria-node-host:focus-within > .axdb-grip { opacity: 1; pointer-events: auto; }\n.grafloria-node-host > .axdb-grip::before {\n content: ''; position: absolute; left: 5px; top: 2px; width: 12px; height: 6px;\n background: radial-gradient(circle, currentColor 1px, transparent 1.4px) 0 0 / 4px 3px;\n}\n.grafloria-node-host > .axdb-grip:hover { border-color: var(--axdb-accent, #3b52d9); color: var(--axdb-accent, #3b52d9); }\n/* The selected card says so, quietly. --axdb-accent themes the grip's hover, the\n ring and the focus outline together; --axdb-accent-ring the ring alone. */\n.grafloria-node-host.axdb-selected > .axdb-widget { box-shadow: 0 0 0 1.5px var(--axdb-accent-ring, rgba(59, 82, 217, .55)), 0 1px 2px rgba(16, 24, 40, .05), 0 1px 3px rgba(16, 24, 40, .05); }\n/* While a resize edge is near, the host and everything in it (a chart canvas\n with its own cursor included) show the edge's cursor. */\n.grafloria-node-host[data-axdb-edge=\"ns-resize\"], .grafloria-node-host[data-axdb-edge=\"ns-resize\"] * { cursor: ns-resize !important; }\n.grafloria-node-host[data-axdb-edge=\"ew-resize\"], .grafloria-node-host[data-axdb-edge=\"ew-resize\"] * { cursor: ew-resize !important; }\n.grafloria-node-host[data-axdb-edge=\"nwse-resize\"], .grafloria-node-host[data-axdb-edge=\"nwse-resize\"] * { cursor: nwse-resize !important; }\n.grafloria-node-host[data-axdb-edge=\"nesw-resize\"], .grafloria-node-host[data-axdb-edge=\"nesw-resize\"] * { cursor: nesw-resize !important; }\n/* INSIDE: centred on the header's text line and flush with the card padding,\n per size tier (padding 13/15, then 8/14, 4/12, 2/12). */\n.grafloria-node-host > .axdb-grip--inside { top: 12px; }\n.grafloria-node-host > .axdb-grip--inside.axdb-grip--left { left: 15px; }\n.grafloria-node-host > .axdb-grip--inside.axdb-grip--right { right: 15px; }\n.grafloria-node-host > .axdb-grip--inside.axdb-grip--center { top: 3px; }\n@container axdb-tile (max-height: 90px) {\n .grafloria-node-host > .axdb-grip--inside { top: 9px; }\n .grafloria-node-host > .axdb-grip--inside.axdb-grip--left { left: 14px; }\n .grafloria-node-host > .axdb-grip--inside.axdb-grip--right { right: 14px; }\n .grafloria-node-host > .axdb-grip--inside.axdb-grip--center { top: 2px; }\n .grafloria-node-host.axdb-gp-inside.axdb-gp-left .axdb-widget > .axdb-widget-h { padding-left: 46px; }\n .grafloria-node-host.axdb-gp-inside.axdb-gp-right .axdb-widget > .axdb-widget-h { padding-right: 46px; }\n .grafloria-node-host.axdb-gp-inside.axdb-gp-center .axdb-widget > .axdb-widget-h { padding-top: 15px; }\n}\n@container axdb-tile (max-height: 46px) {\n .grafloria-node-host > .axdb-grip--inside { top: 5px; }\n .grafloria-node-host > .axdb-grip--inside.axdb-grip--left { left: 12px; }\n .grafloria-node-host > .axdb-grip--inside.axdb-grip--right { right: 12px; }\n .grafloria-node-host > .axdb-grip--inside.axdb-grip--center { top: 1px; }\n .grafloria-node-host.axdb-gp-inside.axdb-gp-left .axdb-widget > .axdb-widget-h { padding-left: 44px; }\n .grafloria-node-host.axdb-gp-inside.axdb-gp-right .axdb-widget > .axdb-widget-h { padding-right: 44px; }\n .grafloria-node-host.axdb-gp-inside.axdb-gp-center .axdb-widget > .axdb-widget-h { padding-top: 13px; }\n}\n@container axdb-tile (max-height: 26px) {\n .grafloria-node-host > .axdb-grip--inside { top: 2px; }\n}\n/* OUTSIDE: a tab on the card's top edge, its corners in line with the card's. */\n/* The OUTSIDE tab lives in the gap between tiles: never taller than the gap\n less a pixel (an 11-px tab in a 10-px gap sat on the tile above), never\n shorter than 6 px so the dots still read. */\n.grafloria-node-host > .axdb-grip--outside { top: calc(-1 * clamp(6px, var(--axdb-gap, 11px) - 1px, 11px)); height: clamp(6px, calc(var(--axdb-gap, 11px) - 1px), 11px); border-bottom-left-radius: 0; border-bottom-right-radius: 0; border-bottom: 0; box-shadow: 0 -1px 2px rgba(16, 24, 40, .08); }\n.grafloria-node-host > .axdb-grip--outside.axdb-grip--left { left: 10px; }\n.grafloria-node-host > .axdb-grip--outside.axdb-grip--right { right: 10px; }\n.grafloria-node-host > .axdb-grip--center { left: 50%; transform: translateX(-50%); }\n/* The header makes room for an inside grip on its side \u2014 inset + the 24-px\n grip + an 8-px gap, per tier (32 px under the band's 14-px inset hid the\n first letter of the title) \u2014 and a centred one sits above the text. */\n.grafloria-node-host.axdb-gp-inside.axdb-gp-left .axdb-widget > .axdb-widget-h { padding-left: 47px; }\n.grafloria-node-host.axdb-gp-inside.axdb-gp-right .axdb-widget > .axdb-widget-h { padding-right: 47px; }\n.grafloria-node-host.axdb-gp-inside.axdb-gp-center .axdb-widget > .axdb-widget-h { padding-top: 17px; }\n.axdb-widget-b { flex: 1; min-height: 0; position: relative; padding: 12px 14px; }\n/* A drag across a STATIC board (nothing prevents the press's default there)\n used to select every label on it; kit cards are not prose. Tables stay\n copyable \u2014 a figure in a grid is the one thing a viewer selects. */\n.axdb-widget { user-select: none; -webkit-user-select: none; }\n.axdb-widget .axdb-table { user-select: text; -webkit-user-select: text; }\n.axdb-widget-b > svg { display: block; width: 100%; height: 100%; }\n.axdb-widget-b.axdb-scroll { overflow: auto; }\n/* A chart WITH a legend under it: the plot yields height, the legend keeps its\n own (without this the 100%-tall svg pushes the legend out of the card). */\n.axdb-widget-b.axdb-has-lg { display: flex; flex-direction: column; }\n.axdb-widget-b.axdb-has-lg > svg { flex: 1; height: auto; min-height: 0; }\n/* The data behind a chart, for readers that cannot see the chart: present in\n the accessibility tree, absent from the picture (WCAG 1.1.1). */\n.axdb-sr {\n position: absolute; top: 0; left: 0; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden;\n clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap; border: 0;\n}\n.axdb-widget-empty {\n display: flex; align-items: center; justify-content: center; height: 100%;\n font: 500 11.5px/1.3 system-ui, sans-serif; color: var(--axdb-muted);\n}\n\n/* kpi: value + delta stack, the spark yields its height before they do */\n/* A SIZE CONTAINER, so the card's type scales with the tile instead of\n clipping. Fit-mode shrinks tiles when rows are added (dropping a chart above\n the KPI row took these from 122px to 85px) and fixed 30px/12px lines were\n sliced mid-glyph by the card's overflow \u2014 the audited \"trimmed\" widgets.\n cqh = 1% of the body's own height, clamped so full-size tiles look exactly\n as before and short tiles compress instead of cutting. */\n.axdb-widget-b.axdb-kpi { display: flex; flex-direction: column; container: axdb-kpi / size; }\n/* STEP DOWN, NEVER CLIP. As the body gets shorter the sparkline goes first, then the\n delta, then the value \u2014 the header alone at the row floor. Before this a 99-px row\n drew the sparkline as a 13-px sliver and a 28-px row cut the value mid-glyph (the\n fluid board, after a drag pushed the KPI row down). Thresholds are body heights. */\n/* Two class hops, so these outrank \".axdb-widget-b > svg { display: block }\" \u2014\n a bare .axdb-kpi-s lost that cascade and the sparkline stayed. */\n@container (max-height: 78px) { .axdb-kpi > .axdb-kpi-s { display: none; } }\n@container (max-height: 40px) { .axdb-kpi > .axdb-kpi-d { display: none; } }\n@container (max-height: 16px) { .axdb-kpi > .axdb-kpi-v { display: none; } }\n.axdb-kpi-v { font: 700 clamp(15px, 44cqh, 30px)/1.05 system-ui, sans-serif; letter-spacing: -.02em; color: var(--axdb-ink); white-space: nowrap; }\n.axdb-kpi-d { margin-top: clamp(1px, 5cqh, 6px); font: 600 clamp(9px, 19cqh, 12px)/1.2 system-ui, sans-serif; white-space: nowrap; }\n.axdb-kpi-d span { color: var(--axdb-muted); font-weight: 500; }\n.axdb-kpi-d.up { color: var(--axdb-up); }\n.axdb-kpi-d.down { color: var(--axdb-down); }\n/* Grows into a tall tile (a 3-row KPI is not a number over a void) and never\n takes more than two fifths of the body. */\n.axdb-kpi-s { margin-top: auto; height: auto; min-height: 0; flex: 1 1 34px; max-height: 40%; }\n/* SHORT AND WIDE \u2014 a one-row KPI on a fluid board. Stacked, a short tile dropped\n its sparkline and left a small figure in the corner of an empty card. Here the\n figure, its delta and the sparkline sit in a row and the spark fills the width.\n The outer query asks the TILE (host), the inner one the body, so a strip too\n thin for a readable spark (under 24 px) keeps the figure alone. 340 px is what\n a figure, its delta, the gaps and a spark worth reading need \u2014 the builder's\n 236-px tiles got a 40-px wedge of spark beside the delta. */\n@container axdb-tile (max-height: 125px) and (min-width: 340px) {\n .axdb-widget--kpi > .axdb-kpi { flex-direction: row; align-items: center; gap: 14px; }\n .axdb-widget--kpi .axdb-kpi-d { margin-top: 0; }\n @container axdb-kpi (min-height: 24px) {\n .axdb-kpi > .axdb-kpi-s { display: block; flex: 1 1 40%; min-width: 80px; height: 100%; max-height: 100%; margin-top: 0; align-self: stretch; }\n }\n}\n/* THE STRIP (row floor): header and figure on one line, the spark beside them\n when the tile is wide enough. */\n@container axdb-tile (max-height: 46px) {\n .axdb-widget--kpi { flex-direction: row; align-items: center; gap: 12px; }\n .axdb-widget--kpi > .axdb-widget-h { flex: none; padding: 0 0 0 12px; background: none; border-bottom: 0; }\n /* The body is a SIZE container: in a centred row it would collapse to zero\n and every height query would fire. It takes the card's full inner height. */\n .axdb-widget--kpi > .axdb-kpi { flex: 1; align-self: stretch; }\n}\n\n/* SPLIT LAYOUT chrome: a divider sits in the gap between two siblings (a\n 24-px hit zone around a 10-px gap) and shows its line on hover or while\n dragged; the insertion line marks the edge a dragged widget will land on. */\n.grafloria-html-layer > .axdb-div { position: absolute; z-index: 2; pointer-events: auto; background: transparent; }\n.grafloria-html-layer > .axdb-div--row { cursor: col-resize; }\n.grafloria-html-layer > .axdb-div--column { cursor: row-resize; }\n.grafloria-html-layer > .axdb-div::after {\n content: \"\"; position: absolute; border-radius: 2px; background: #3b52d9; opacity: 0; transition: opacity .12s ease;\n}\n.grafloria-html-layer > .axdb-div--row::after { left: 50%; top: 8px; bottom: 8px; width: 4px; margin-left: -2px; }\n.grafloria-html-layer > .axdb-div--column::after { top: 50%; left: 8px; right: 8px; height: 4px; margin-top: -2px; }\n.grafloria-html-layer > .axdb-div:hover::after, .grafloria-html-layer > .axdb-div.axdb-active::after { opacity: .9; }\n.grafloria-html-layer > .axdb-ins {\n position: absolute; z-index: 3; pointer-events: none; border-radius: 2px;\n background: #3b52d9; box-shadow: 0 0 0 3px rgba(59, 82, 217, .22);\n}\n@media (prefers-reduced-motion: reduce) { .grafloria-html-layer > .axdb-div::after { transition: none; } }\n\n/* donut: ring beside its legend. The ring takes the body's height (a tall\n tile gets a bigger ring, not dead card), square, capped so its centre figure\n stays a figure and not a headline. */\n.axdb-widget-b.axdb-donut { display: flex; align-items: center; gap: 10px; }\n/* A legend taller than a SHORT body (a 2-row donut on a squeezed board) is\n clipped at its own foot, not centred over the title above it. */\n.axdb-widget-b.axdb-donut > .axdb-lg--col { max-height: 100%; min-height: 0; overflow: hidden; }\n.axdb-widget-b.axdb-donut > svg {\n flex: 0 0 auto; width: auto; height: 100%; max-height: 260px; max-width: 60%; aspect-ratio: 1 / 1;\n}\n\n/* READABILITY TIERS (widgets.ts chartTier): the text is always in the DOM;\n what a short body cannot afford is hidden, not dropped. */\n.axdb-tier-1 .axdb-yt--q, .axdb-tier-1 .axdb-yl--q,\n.axdb-tier-2 .axdb-yt--q, .axdb-tier-2 .axdb-yl--q,\n.axdb-tier-2 .axdb-yt--h, .axdb-tier-2 .axdb-yl--h,\n.axdb-tier-2 .axdb-xt, .axdb-tier-2 .axdb-vt,\n.axdb-lg--off { display: none; }\n\n/* SECTION CHROME: a pointer-transparent overlay on every member group. It\n wears the selection ring and, while selected, the corner handle. */\n.grafloria-html-layer > .axdb-slab { position: absolute; pointer-events: none; border-radius: var(--axdb-rs-radius, 8px); z-index: 4; }\n/* Selected, the overlay rises above the tiles so ITS corner handle wins a\n corner it shares with a child's; unselected, its handle takes no presses. */\n.grafloria-html-layer > .axdb-slab.axdb-slab--selected { z-index: 6; box-shadow: 0 0 0 1.5px var(--axdb-accent-ring, rgba(59, 82, 217, .55)); }\n.grafloria-html-layer > .axdb-slab > .axdb-rs { pointer-events: none; opacity: 0; }\n.grafloria-html-layer > .axdb-slab.axdb-slab--selected > .axdb-rs { pointer-events: auto; opacity: 1; }\n.grafloria-html-layer > .axdb-slab.axdb-slab--static > .axdb-rs { display: none; }\n\n/* SECTION CAPTION (0.4.22): the band on the slab. Geometry is inline (the\n reserve and the pixels come from one function); everything visual is a\n variable the options write and a theme may override. The band takes the\n pointer \u2014 the slab does not \u2014 so a press on it is the section's. */\n.grafloria-html-layer > .axdb-slab > .axdb-slab-h {\n position: absolute; box-sizing: border-box; pointer-events: auto; z-index: 1;\n display: flex; align-items: center; gap: 6px; min-width: 0;\n padding: var(--axdb-caption-pad, 0 12px);\n background: var(--axdb-caption-bg, rgba(31, 36, 48, .045));\n /* A hairline by default: two bands meeting (a captioned section inside a\n captioned section) read as two headers, not one grey block. */\n border-bottom: var(--axdb-caption-border, 1px solid rgba(31, 36, 48, .1));\n color: var(--axdb-caption-fg, #1f2430);\n font: var(--axdb-caption-font-weight, 600) var(--axdb-caption-font-size, 13px)/1.2 var(--axdb-caption-font-family, system-ui, -apple-system, \"Segoe UI\", sans-serif);\n text-transform: var(--axdb-caption-transform, none);\n letter-spacing: -.005em;\n border-radius: var(--axdb-rs-radius, 8px) var(--axdb-rs-radius, 8px) 0 0;\n cursor: default; user-select: none; -webkit-user-select: none; overflow: hidden;\n transition: opacity .12s;\n}\n/* The modifiers carry the band's own selector so they outrank its defaults. */\n.grafloria-html-layer > .axdb-slab > .axdb-slab-h.axdb-slab-h--center { justify-content: center; }\n.grafloria-html-layer > .axdb-slab > .axdb-slab-h.axdb-slab-h--end { justify-content: flex-end; }\n.grafloria-html-layer > .axdb-slab > .axdb-slab-h.axdb-slab-h--vtop { align-items: flex-start; }\n.grafloria-html-layer > .axdb-slab > .axdb-slab-h.axdb-slab-h--vbottom { align-items: flex-end; }\n/* 'tab': the same band sized to its text, a chip at the leading corner. It\n reserves its height like 'inside' \u2014 see captionReserve. */\n.grafloria-html-layer > .axdb-slab > .axdb-slab-h.axdb-slab-h--tab {\n max-width: 100%; border-radius: var(--axdb-rs-radius, 8px);\n border-bottom: var(--axdb-caption-border-tab, none);\n box-shadow: inset 0 0 0 1px rgba(31, 36, 48, .08);\n}\n/* 'hover': an overlay. It reserves nothing, so while hidden it must not take\n the pointer (an invisible band swallowed clicks on the content beneath),\n and while shown it paints OPAQUE \u2014 a 5% tint over a chart is a smear. */\n.grafloria-html-layer > .axdb-slab > .axdb-slab-h.axdb-slab-h--hover {\n opacity: 0; pointer-events: none;\n background: var(--axdb-caption-bg, #fff);\n box-shadow: 0 1px 4px rgba(31, 36, 48, .16);\n}\n/* .axdb-slab--hot is set by the binder from the live pointer (the overlay\n takes no pointer of its own, so CSS :hover can never fire on it). */\n.grafloria-html-layer > .axdb-slab.axdb-slab--hot > .axdb-slab-h.axdb-slab-h--hover,\n.grafloria-html-layer > .axdb-slab.axdb-slab--selected > .axdb-slab-h.axdb-slab-h--hover { pointer-events: auto; opacity: 1; }\n/* the actions of a hovered or selected section */\n.grafloria-html-layer > .axdb-slab.axdb-slab--hot > .axdb-slab-h > .axdb-slab-h-actions { opacity: 1; }\n/* the tight tier: a section under 90 px */\n.grafloria-html-layer > .axdb-slab > .axdb-slab-h.axdb-slab-h--tight { font-size: min(var(--axdb-caption-font-size, 12px), 12px); gap: 4px; }\n.axdb-slab-h--tight .axdb-slab-h-sub, .axdb-slab-h--tight .axdb-slab-h-actions { display: none; }\n.axdb-slab-h-icon { flex: none; }\n.axdb-slab-h-body { display: flex; flex-direction: column; justify-content: center; min-width: 0; flex: 0 1 auto; }\n.axdb-slab-h-text, .axdb-slab-h-sub { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }\n.axdb-slab-h-sub { font-size: .88em; font-weight: 500; opacity: .7; }\n.axdb-slab-h-info { flex: none; opacity: .6; font-size: .9em; cursor: help; }\n.axdb-slab-h-actions { flex: none; display: flex; gap: 2px; margin-inline-start: auto; opacity: 0; transition: opacity .12s; }\n.axdb-slab-h--center .axdb-slab-h-actions, .axdb-slab-h--end .axdb-slab-h-actions { margin-inline-start: 0; }\n.axdb-slab-h:hover > .axdb-slab-h-actions, .axdb-slab--selected > .axdb-slab-h > .axdb-slab-h-actions, .axdb-slab-h:focus-within > .axdb-slab-h-actions { opacity: 1; }\n.axdb-slab-h-action {\n all: unset; box-sizing: border-box; width: 26px; height: 26px; display: inline-flex; align-items: center; justify-content: center;\n border-radius: 6px; cursor: pointer; font-size: 14px; line-height: 1; color: inherit; transition: background-color .12s;\n}\n.axdb-slab-h-action:hover { background: rgba(31, 36, 48, .08); }\n.axdb-slab-h-action:focus-visible { outline: 2px solid var(--axdb-accent-ring, rgba(59, 82, 217, .55)); outline-offset: -2px; }\n.axdb-slab-h-action[disabled] { opacity: .4; cursor: default; }\n.axdb-slab-h-action[disabled]:hover { background: none; }\n.grafloria-html-layer > .axdb-slab.axdb-slab--static > .axdb-slab-h { cursor: default; }\n@media (prefers-color-scheme: dark) {\n /* The band sits on the PAGE, whose ground the kit does not own (a host that\n stays light while the kit goes dark is common): a solid ink, not a tint,\n so the light caption always reads. */\n .grafloria-html-layer > .axdb-slab > .axdb-slab-h {\n background: var(--axdb-caption-bg, #1b2029);\n color: var(--axdb-caption-fg, #eceef4);\n border-bottom: var(--axdb-caption-border, 1px solid rgba(236, 238, 244, .12));\n }\n .grafloria-html-layer > .axdb-slab > .axdb-slab-h.axdb-slab-h--tab { box-shadow: inset 0 0 0 1px rgba(236, 238, 244, .12); }\n .grafloria-html-layer > .axdb-slab > .axdb-slab-h.axdb-slab-h--hover { background: var(--axdb-caption-bg, #1a1d25); box-shadow: 0 1px 4px rgba(0, 0, 0, .5); }\n .axdb-slab-h-action:hover { background: rgba(236, 238, 244, .1); }\n}\n\n/* TAB CONTAINER (0.4.27): a strip of pages across the container's top. The\n strip takes the pointer (the tabs are real buttons); the pages below it are\n ordinary boards. */\n.grafloria-html-layer > .axdb-tabs {\n position: absolute; box-sizing: border-box; pointer-events: auto; z-index: 5;\n display: flex; align-items: center; gap: 2px; padding: 3px 6px; overflow-x: auto; overflow-y: hidden;\n background: var(--axdb-tabs-bg, #f1f3f8);\n border-bottom: 1px solid var(--axdb-tabs-line, #e5e8ef);\n border-radius: var(--axdb-rs-radius, 8px) var(--axdb-rs-radius, 8px) 0 0;\n scrollbar-width: thin;\n}\n.axdb-tabs--center { justify-content: center; }\n.axdb-tabs--end { justify-content: flex-end; }\n.axdb-tabs--stretch > .axdb-tab { flex: 1 1 0; }\n/* A tab is a PILL on the track (a segmented control): quiet until hovered,\n the active one lifted in the card's own ground. */\n.axdb-tab {\n all: unset; box-sizing: border-box; flex: 0 0 auto; max-width: 200px;\n padding: 0 12px; height: 100%; display: inline-flex; align-items: center;\n font: 500 12.5px/1 system-ui, -apple-system, \"Segoe UI\", sans-serif;\n letter-spacing: -.005em;\n color: var(--axdb-tabs-fg, #5a6478); cursor: pointer;\n border-radius: var(--axdb-tab-radius, 6px);\n white-space: nowrap; overflow: hidden; text-overflow: ellipsis;\n transition: background-color .12s, color .12s, box-shadow .12s;\n}\n.axdb-tab:hover { background: rgba(31, 36, 48, .06); color: var(--axdb-tabs-on-fg, #1f2430); }\n.axdb-tab:focus-visible { outline: 2px solid var(--axdb-accent-ring, rgba(59, 82, 217, .55)); outline-offset: -2px; }\n.axdb-tab.axdb-tab--on {\n background: var(--axdb-tabs-on-bg, #fff);\n color: var(--axdb-tabs-on-fg, #1f2430);\n font-weight: 600;\n box-shadow: 0 1px 2px rgba(16, 24, 40, .1), 0 0 0 1px rgba(31, 36, 48, .06);\n}\n@media (prefers-reduced-motion: reduce) { .axdb-tab { transition: none; } }\n@media (prefers-color-scheme: dark) {\n /* Solid for the same reason as the caption band: the track sits on the page. */\n .grafloria-html-layer > .axdb-tabs { background: var(--axdb-tabs-bg, #1b2029); border-bottom-color: var(--axdb-tabs-line, rgba(236, 238, 244, .12)); }\n .axdb-tab { color: var(--axdb-tabs-fg, #98a1b4); }\n .axdb-tab:hover { background: rgba(236, 238, 244, .08); color: var(--axdb-tabs-on-fg, #eceef4); }\n .axdb-tab.axdb-tab--on { background: var(--axdb-tabs-on-bg, #232836); color: var(--axdb-tabs-on-fg, #eceef4); box-shadow: 0 1px 2px rgba(0, 0, 0, .45), 0 0 0 1px rgba(236, 238, 244, .1); }\n}\n\n/* legend chips, shared by line and donut */\n.axdb-lg { display: flex; flex-wrap: wrap; gap: 4px 12px; margin-top: 9px; }\n.axdb-lg--col { flex-direction: column; flex-wrap: nowrap; gap: 6px; margin-top: 0; }\n.axdb-lg i {\n display: inline-flex; align-items: center; gap: 5px; font-style: normal;\n font: 500 11px/1.3 system-ui, sans-serif; color: var(--axdb-muted);\n}\n.axdb-lg i b { width: 9px; height: 9px; border-radius: 3px; flex: none; }\n\n/* table */\n.axdb-table { width: 100%; border-collapse: collapse; font-size: 12px; color: var(--axdb-ink); }\n.axdb-table th {\n text-align: left; padding: 4px 8px 7px; border-bottom: 1px solid var(--axdb-line);\n font: 600 10px/1.3 system-ui, sans-serif; letter-spacing: .4px; text-transform: uppercase;\n color: var(--axdb-muted);\n}\n.axdb-table td { padding: 6px 8px; border-bottom: 1px solid var(--axdb-line); white-space: nowrap; }\n.axdb-table td.num { text-align: right; font-variant-numeric: tabular-nums; }\n.axdb-table tr:last-child td { border-bottom: none; }\n\n@media (prefers-color-scheme: dark) {\n .axdb-widget {\n --axdb-ink: #eceef4;\n --axdb-muted: #98a1b4;\n --axdb-grid: rgba(150, 160, 182, .2);\n --axdb-card: #161a22;\n --axdb-line: #2a3040;\n --axdb-head-bg: rgba(255, 255, 255, .035);\n --axdb-head-fg: #e6e9f0;\n --axdb-soft: rgba(150, 160, 182, .16);\n --axdb-up: #4ade80;\n --axdb-down: #fb7185;\n --axdb-c1: #8b9cff; --axdb-c2: #38bdf8; --axdb-c3: #2dd4bf;\n --axdb-c4: #fbbf24; --axdb-c5: #a78bfa; --axdb-c6: #94a3b8;\n }\n}\n";
|
|
25
|
+
export declare const DASHBOARD_KIT_CSS = "\n/* ===== reflow glide (armed on the html layer for the whole gesture) ===== */\n.grafloria-html-layer.axdb-glide > .grafloria-node-host {\n transition: left .28s cubic-bezier(.2, 0, .2, 1), top .28s cubic-bezier(.2, 0, .2, 1),\n width .28s cubic-bezier(.2, 0, .2, 1), height .28s cubic-bezier(.2, 0, .2, 1);\n}\n\n/* Motion is a preference (WCAG 2.3.3): no glide for those who asked for none. */\n@media (prefers-reduced-motion: reduce) {\n .grafloria-html-layer.axdb-glide > .grafloria-node-host { transition: none; }\n}\n\n/* ===== keyboard focus: the roving tab stop shows where it is (WCAG 2.4.7) ===== */\n.grafloria-html-layer > .grafloria-node-host:focus-visible {\n outline: 2px solid var(--axdb-accent, #3b52d9);\n outline-offset: 2px;\n border-radius: var(--axdb-rs-radius, 8px);\n}\n\n/* ===== the held tile: transition-exempt ghost, above everything ===== */\n.grafloria-html-layer > .grafloria-node-host.axdb-ghost,\n.grafloria-html-layer.axdb-glide > .grafloria-node-host.axdb-ghost {\n transition: none;\n z-index: 30;\n opacity: .85;\n cursor: grabbing;\n filter: drop-shadow(0 10px 16px rgba(16, 24, 40, .3));\n}\n/* Outside the board: release will REMOVE \u2014 dim the ghost to say so. */\n.grafloria-html-layer > .grafloria-node-host.axdb-ghost.axdb-out { opacity: .35; filter: grayscale(.6); }\n\n/* ===== the kit's chrome glides WITH the tiles it belongs to (0.4.43) =====\n A pushed group's strip, slab and surface used to jump to the new slot while\n its tiles slid there: one group, two motions. */\n.grafloria-html-layer.axdb-glide > .axdb-tabs,\n.grafloria-html-layer.axdb-glide > .axdb-slab,\n.grafloria-html-layer.axdb-glide > .axdb-group-bg {\n transition: left .28s cubic-bezier(.2, 0, .2, 1), top .28s cubic-bezier(.2, 0, .2, 1),\n width .28s cubic-bezier(.2, 0, .2, 1), height .28s cubic-bezier(.2, 0, .2, 1);\n}\n@media (prefers-reduced-motion: reduce) {\n .grafloria-html-layer.axdb-glide > .axdb-tabs,\n .grafloria-html-layer.axdb-glide > .axdb-slab,\n .grafloria-html-layer.axdb-glide > .axdb-group-bg { transition: none; }\n}\n\n/* ===== CARRIED (0.4.43): a group dragged by its strip or band moves as ONE thing =====\n Its whole subtree \u2014 tiles, strips, slabs, surface \u2014 is transition-exempt like\n the ghost and floats with it; the surface casts the shadow. */\n.grafloria-html-layer > .axdb-carried,\n.grafloria-html-layer.axdb-glide > .axdb-carried { transition: none; cursor: grabbing; }\n.grafloria-html-layer > .grafloria-node-host.axdb-carried { z-index: 30; opacity: .92; }\n.grafloria-html-layer > .axdb-group-bg.axdb-carried { z-index: 29; filter: drop-shadow(0 10px 16px rgba(16, 24, 40, .3)); }\n.grafloria-html-layer > .axdb-slab.axdb-carried { z-index: 31; }\n.grafloria-html-layer > .axdb-tabs.axdb-carried { z-index: 32; }\n\n/* ===== the placeholder: dashed slab, truthful, never animated ===== */\n.grafloria-html-layer > .axdb-ph {\n position: absolute;\n border-radius: var(--axdb-rs-radius, 8px);\n background: rgba(30, 34, 45, .14);\n border: 2px dashed rgba(30, 34, 45, .28);\n box-sizing: border-box;\n pointer-events: none;\n z-index: 0;\n transition: none;\n}\n/* A slab move asked for a cell it cannot have (a locked section in the way): the wanted cell in the danger tone. */\n.grafloria-html-layer > .axdb-ph.axdb-ph--no { border-color: var(--axdb-danger, #b3123c); background: var(--axdb-danger-soft, rgba(179, 18, 60, .07)); }\n\n@media (prefers-color-scheme: dark) {\n .grafloria-html-layer > .axdb-ph { background: rgba(220, 225, 240, .12); border-color: rgba(220, 225, 240, .3); }\n}\n\n/* ===== corner resize handle (hover-revealed, radius-matched) ===== */\n:is(.grafloria-node-host, .axdb-slab) > .axdb-rs {\n position: absolute;\n right: 0;\n bottom: 0;\n width: 24px; /* WCAG 2.5.8: a 24-px minimum target (was 18) */\n height: 24px;\n cursor: nwse-resize;\n border-right: 3px solid rgba(120, 130, 148, .55);\n border-bottom: 3px solid rgba(120, 130, 148, .55);\n border-bottom-right-radius: var(--axdb-rs-radius, 8px);\n opacity: 0;\n transition: opacity .12s;\n z-index: 5;\n}\n.grafloria-node-host:hover > .axdb-rs { opacity: 1; }\n/* A finger never hovers: on touch devices the corner handle is always visible\n (review D8 \u2014 hover-only meant no resize at all on a tablet). */\n@media (hover: none) { .grafloria-node-host > .axdb-rs { opacity: .8; } }\n.grafloria-node-host > .axdb-rs:hover { border-color: #3b52d9; }\n/* RTL boards grow leftwards, so the grab corner mirrors with them. */\n:is(.grafloria-node-host, .axdb-slab) > .axdb-rs.axdb-rs--rtl {\n right: auto;\n left: 0;\n cursor: nesw-resize;\n border-right: none;\n border-left: 3px solid rgba(120, 130, 148, .55);\n border-bottom-right-radius: 0;\n border-bottom-left-radius: var(--axdb-rs-radius, 8px);\n}\n\n/* ===== palette drag-in chip (screen-space clone following the cursor) ===== */\n.axdb-drag-chip {\n position: fixed;\n z-index: 9999;\n pointer-events: none;\n opacity: .9;\n filter: drop-shadow(0 8px 14px rgba(16, 24, 40, .3));\n transition: opacity .12s, filter .12s;\n}\n/* A bounded board with no room refuses the entry: the chip dims to say so\n (the same signal a tile ghost gives outside the board). Without this rule\n the class was set and nothing showed \u2014 a refusal a user could not see. */\n.axdb-drag-chip.axdb-out { opacity: .35; filter: grayscale(.7) drop-shadow(0 8px 14px rgba(16, 24, 40, .2)); }\n/* A tab dragged off its strip: VS Code carries the TAB, not the editor, so\n what follows the pointer is a chip wearing the tab's own label. */\n.axdb-tab-chip {\n padding: 5px 12px;\n max-width: 220px;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n font: 500 12px/1.4 var(--axdb-font, system-ui, -apple-system, \"Segoe UI\", Roboto, sans-serif);\n color: var(--axdb-tabs-on-fg, #1f2430);\n background: var(--axdb-tabs-on-bg, #fff);\n border-radius: var(--axdb-tab-radius, 6px);\n box-shadow: 0 1px 2px rgba(16, 24, 40, .1), 0 0 0 1px rgba(31, 36, 48, .08);\n}\n@media (prefers-color-scheme: dark) {\n .axdb-tab-chip {\n color: var(--axdb-tabs-on-fg, #eceef4);\n background: var(--axdb-tabs-on-bg, #232836);\n box-shadow: 0 1px 2px rgba(0, 0, 0, .45), 0 0 0 1px rgba(236, 238, 244, .12);\n }\n}\n/* A tab held over ANOTHER group: it will join that group, not the board. The\n group's frame lights up, and its strip marks the slot the tab will take \u2014\n a bar before the tab it lands in front of, or after the last one. */\n.grafloria-html-layer > .axdb-join {\n position: absolute;\n z-index: 3;\n pointer-events: none;\n box-sizing: border-box;\n border: 2px dashed var(--axdb-accent, #3b52d9);\n background: var(--axdb-accent-soft, rgba(59, 82, 217, .08));\n border-radius: var(--axdb-rs-radius, 8px);\n}\n.axdb-tabs.axdb-tabs--drop { box-shadow: inset 0 -2px 0 var(--axdb-accent, #3b52d9); }\n.axdb-tab.axdb-tab--drop-before { position: relative; }\n.axdb-tab.axdb-tab--drop-before::before {\n content: \"\"; position: absolute; left: -3px; top: 4px; bottom: 4px; width: 3px;\n border-radius: 2px; background: var(--axdb-accent, #3b52d9);\n}\n.axdb-tabs.axdb-tabs--drop-end::after {\n content: \"\"; display: inline-block; width: 3px; height: 18px; margin: 0 4px;\n border-radius: 2px; background: var(--axdb-accent, #3b52d9); vertical-align: middle; align-self: center;\n}\n\n/* ===========================================================================\n BUILT-IN WIDGET CARDS \u2014 what widgets.ts paints when a page writes no\n renderWidget. Class-prefixed (never element selectors), so this can only\n ever style DOM the kit itself produced. Colours are CSS variables on the\n card, so a page re-skins every built-in widget by setting them once; the\n corner radius follows --axdb-rs-radius, which is also the placeholder's and\n the resize handle's, so the three can never drift apart.\n =========================================================================== */\n.axdb-widget {\n --axdb-ink: #1f2430;\n /* 5.95:1 on the card \u2014 WCAG 1.4.3 for the 9\u201311px captions this paints\n (the #7a8496 it replaced sat at 3.77:1, axe-core's first finding). */\n --axdb-muted: #5a6478;\n --axdb-grid: rgba(120, 130, 148, .22);\n --axdb-card: #fff;\n --axdb-line: #e5e8ef;\n --axdb-soft: rgba(120, 130, 148, .14);\n --axdb-up: #0f7a3d; /* 5.42:1 */\n --axdb-down: #be123c; /* 6.29:1 */\n /* The categorical palette, as tokens so the dark card can carry its own\n steps: every entry clears 3:1 against its card (WCAG 1.4.11). */\n --axdb-c1: #3b52d9; --axdb-c2: #0369a1; --axdb-c3: #0f766e;\n --axdb-c4: #b45309; --axdb-c5: #6d28d9; --axdb-c6: #475569;\n /* The header band: a panel wears its title in a band across its top, the\n way DevExpress, Grafana and Metabase panels do \u2014 tinted ground, hairline\n below, sentence case (0.4.36; the tracked uppercase micro-label before). */\n --axdb-head-bg: #f7f8fb;\n --axdb-head-fg: #2a3140;\n box-sizing: border-box;\n width: 100%;\n height: 100%;\n display: flex;\n flex-direction: column;\n overflow: hidden;\n padding: 0;\n background: var(--axdb-card);\n border: 1px solid var(--axdb-line);\n border-radius: var(--axdb-rs-radius, 8px);\n box-shadow: 0 1px 2px rgba(16, 24, 40, .04);\n font-family: system-ui, -apple-system, \"Segoe UI\", sans-serif;\n color: var(--axdb-ink);\n}\n/* The host is the card's size container (named, so a widget's inner parts can\n ask the TILE's size as well as their own body's), so the card can shed its\n padding as the tile gets short instead of clipping its own content. */\n.grafloria-html-layer > .grafloria-node-host { container: axdb-tile / size; }\n/* Two class hops on the header: these blocks precede the header's own rule\n below and would lose the cascade at equal specificity \u2014 at 54 px the margin\n stayed 8 px, the body shrank to 14 px and the KPI figure hid. */\n@container axdb-tile (max-height: 90px) {\n .axdb-widget > .axdb-widget-h { padding: 5px 12px; font-size: 11.5px; }\n .axdb-widget > .axdb-widget-b { padding: 6px 12px; }\n}\n/* The row floor: the band gives way to an inline label beside the figure. */\n@container axdb-tile (max-height: 46px) {\n .axdb-widget > .axdb-widget-h { padding: 0 0 0 12px; background: none; border-bottom: 0; }\n .axdb-widget > .axdb-widget-b { padding: 3px 12px; }\n}\n@container axdb-tile (max-height: 26px) {\n .axdb-widget > .axdb-widget-h { display: none; }\n .axdb-widget > .axdb-widget-b { padding: 1px 12px; }\n}\n.axdb-widget-h {\n display: flex;\n align-items: center;\n gap: 8px;\n flex: none;\n min-width: 0;\n padding: 9px 14px;\n background: var(--axdb-head-bg);\n border-bottom: 1px solid var(--axdb-line);\n font: 600 12.5px/1.3 system-ui, -apple-system, \"Segoe UI\", sans-serif;\n letter-spacing: -.005em;\n color: var(--axdb-head-fg);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n/* Drag-handle mode (DevExpress drags an item by its caption): the header is\n the grip and says so with a dot pattern and a grab cursor; the body keeps\n its own cursor because a press there starts nothing. */\n.axdb-drag-handle .axdb-widget > .axdb-widget-h { cursor: grab; }\n.axdb-drag-handle .axdb-widget > .axdb-widget-h::before {\n content: ''; flex: none; width: 8px; height: 12px; opacity: .55;\n background: radial-gradient(circle, currentColor 1.1px, transparent 1.5px) 0 0 / 4px 4px;\n}\n/* A PAINTED GRIP (dragHandle: { grip: true }): the only drag zone, a small\n dotted tab along the card's top edge. Inside sits in the header band and\n the header makes room for it; outside is a tab above the card, the\n DevExpress item bar. The host must not clip it. */\n.grafloria-node-host > .axdb-grip {\n position: absolute; z-index: 4; box-sizing: border-box; width: 24px; height: 12px;\n border: 1px solid var(--axdb-line, #e7eaf1); border-radius: 3px; background: var(--axdb-card, #fff);\n color: var(--axdb-muted, #5a6478); cursor: grab;\n /* Shown on the SELECTED widget only (the DevExpress designer shows an item's\n bar on the selected item): a press or keyboard focus selects, a void\n click clears. Hidden, it takes no pointer either. */\n opacity: 0; pointer-events: none; transition: opacity .12s ease;\n}\n.grafloria-node-host.axdb-selected > .axdb-grip, .grafloria-node-host:focus-within > .axdb-grip { opacity: 1; pointer-events: auto; }\n.grafloria-node-host > .axdb-grip::before {\n content: ''; position: absolute; left: 5px; top: 2px; width: 12px; height: 6px;\n background: radial-gradient(circle, currentColor 1px, transparent 1.4px) 0 0 / 4px 3px;\n}\n.grafloria-node-host > .axdb-grip:hover { border-color: var(--axdb-accent, #3b52d9); color: var(--axdb-accent, #3b52d9); }\n/* The selected card says so, quietly. --axdb-accent themes the grip's hover, the\n ring and the focus outline together; --axdb-accent-ring the ring alone. */\n.grafloria-node-host.axdb-selected > .axdb-widget { box-shadow: 0 0 0 1.5px var(--axdb-accent-ring, rgba(59, 82, 217, .55)), 0 1px 2px rgba(16, 24, 40, .05), 0 1px 3px rgba(16, 24, 40, .05); }\n/* While a resize edge is near, the host and everything in it (a chart canvas\n with its own cursor included) show the edge's cursor. */\n.grafloria-node-host[data-axdb-edge=\"ns-resize\"], .grafloria-node-host[data-axdb-edge=\"ns-resize\"] * { cursor: ns-resize !important; }\n.grafloria-node-host[data-axdb-edge=\"ew-resize\"], .grafloria-node-host[data-axdb-edge=\"ew-resize\"] * { cursor: ew-resize !important; }\n.grafloria-node-host[data-axdb-edge=\"nwse-resize\"], .grafloria-node-host[data-axdb-edge=\"nwse-resize\"] * { cursor: nwse-resize !important; }\n.grafloria-node-host[data-axdb-edge=\"nesw-resize\"], .grafloria-node-host[data-axdb-edge=\"nesw-resize\"] * { cursor: nesw-resize !important; }\n/* INSIDE: centred on the header's text line and flush with the card padding,\n per size tier (padding 13/15, then 8/14, 4/12, 2/12). */\n.grafloria-node-host > .axdb-grip--inside { top: 12px; }\n.grafloria-node-host > .axdb-grip--inside.axdb-grip--left { left: 15px; }\n.grafloria-node-host > .axdb-grip--inside.axdb-grip--right { right: 15px; }\n.grafloria-node-host > .axdb-grip--inside.axdb-grip--center { top: 3px; }\n@container axdb-tile (max-height: 90px) {\n .grafloria-node-host > .axdb-grip--inside { top: 9px; }\n .grafloria-node-host > .axdb-grip--inside.axdb-grip--left { left: 14px; }\n .grafloria-node-host > .axdb-grip--inside.axdb-grip--right { right: 14px; }\n .grafloria-node-host > .axdb-grip--inside.axdb-grip--center { top: 2px; }\n .grafloria-node-host.axdb-gp-inside.axdb-gp-left .axdb-widget > .axdb-widget-h { padding-left: 46px; }\n .grafloria-node-host.axdb-gp-inside.axdb-gp-right .axdb-widget > .axdb-widget-h { padding-right: 46px; }\n .grafloria-node-host.axdb-gp-inside.axdb-gp-center .axdb-widget > .axdb-widget-h { padding-top: 15px; }\n}\n@container axdb-tile (max-height: 46px) {\n .grafloria-node-host > .axdb-grip--inside { top: 5px; }\n .grafloria-node-host > .axdb-grip--inside.axdb-grip--left { left: 12px; }\n .grafloria-node-host > .axdb-grip--inside.axdb-grip--right { right: 12px; }\n .grafloria-node-host > .axdb-grip--inside.axdb-grip--center { top: 1px; }\n .grafloria-node-host.axdb-gp-inside.axdb-gp-left .axdb-widget > .axdb-widget-h { padding-left: 44px; }\n .grafloria-node-host.axdb-gp-inside.axdb-gp-right .axdb-widget > .axdb-widget-h { padding-right: 44px; }\n .grafloria-node-host.axdb-gp-inside.axdb-gp-center .axdb-widget > .axdb-widget-h { padding-top: 13px; }\n}\n@container axdb-tile (max-height: 26px) {\n .grafloria-node-host > .axdb-grip--inside { top: 2px; }\n}\n/* OUTSIDE: a tab on the card's top edge, its corners in line with the card's. */\n/* The OUTSIDE tab lives in the gap between tiles: never taller than the gap\n less a pixel (an 11-px tab in a 10-px gap sat on the tile above), never\n shorter than 6 px so the dots still read. */\n.grafloria-node-host > .axdb-grip--outside { top: calc(-1 * clamp(6px, var(--axdb-gap, 11px) - 1px, 11px)); height: clamp(6px, calc(var(--axdb-gap, 11px) - 1px), 11px); border-bottom-left-radius: 0; border-bottom-right-radius: 0; border-bottom: 0; box-shadow: 0 -1px 2px rgba(16, 24, 40, .08); }\n.grafloria-node-host > .axdb-grip--outside.axdb-grip--left { left: 10px; }\n.grafloria-node-host > .axdb-grip--outside.axdb-grip--right { right: 10px; }\n.grafloria-node-host > .axdb-grip--center { left: 50%; transform: translateX(-50%); }\n/* The header makes room for an inside grip on its side \u2014 inset + the 24-px\n grip + an 8-px gap, per tier (32 px under the band's 14-px inset hid the\n first letter of the title) \u2014 and a centred one sits above the text. */\n.grafloria-node-host.axdb-gp-inside.axdb-gp-left .axdb-widget > .axdb-widget-h { padding-left: 47px; }\n.grafloria-node-host.axdb-gp-inside.axdb-gp-right .axdb-widget > .axdb-widget-h { padding-right: 47px; }\n.grafloria-node-host.axdb-gp-inside.axdb-gp-center .axdb-widget > .axdb-widget-h { padding-top: 17px; }\n.axdb-widget-b { flex: 1; min-height: 0; position: relative; padding: 12px 14px; }\n/* A drag across a STATIC board (nothing prevents the press's default there)\n used to select every label on it; kit cards are not prose. Tables stay\n copyable \u2014 a figure in a grid is the one thing a viewer selects. */\n.axdb-widget { user-select: none; -webkit-user-select: none; }\n.axdb-widget .axdb-table { user-select: text; -webkit-user-select: text; }\n.axdb-widget-b > svg { display: block; width: 100%; height: 100%; }\n.axdb-widget-b.axdb-scroll { overflow: auto; }\n/* A chart WITH a legend under it: the plot yields height, the legend keeps its\n own (without this the 100%-tall svg pushes the legend out of the card). */\n.axdb-widget-b.axdb-has-lg { display: flex; flex-direction: column; }\n.axdb-widget-b.axdb-has-lg > svg { flex: 1; height: auto; min-height: 0; }\n/* The data behind a chart, for readers that cannot see the chart: present in\n the accessibility tree, absent from the picture (WCAG 1.1.1). */\n.axdb-sr {\n position: absolute; top: 0; left: 0; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden;\n clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap; border: 0;\n}\n.axdb-widget-empty {\n display: flex; align-items: center; justify-content: center; height: 100%;\n font: 500 11.5px/1.3 system-ui, sans-serif; color: var(--axdb-muted);\n}\n\n/* kpi: value + delta stack, the spark yields its height before they do */\n/* A SIZE CONTAINER, so the card's type scales with the tile instead of\n clipping. Fit-mode shrinks tiles when rows are added (dropping a chart above\n the KPI row took these from 122px to 85px) and fixed 30px/12px lines were\n sliced mid-glyph by the card's overflow \u2014 the audited \"trimmed\" widgets.\n cqh = 1% of the body's own height, clamped so full-size tiles look exactly\n as before and short tiles compress instead of cutting. */\n.axdb-widget-b.axdb-kpi { display: flex; flex-direction: column; container: axdb-kpi / size; }\n/* STEP DOWN, NEVER CLIP. As the body gets shorter the sparkline goes first, then the\n delta, then the value \u2014 the header alone at the row floor. Before this a 99-px row\n drew the sparkline as a 13-px sliver and a 28-px row cut the value mid-glyph (the\n fluid board, after a drag pushed the KPI row down). Thresholds are body heights. */\n/* Two class hops, so these outrank \".axdb-widget-b > svg { display: block }\" \u2014\n a bare .axdb-kpi-s lost that cascade and the sparkline stayed. */\n@container (max-height: 78px) { .axdb-kpi > .axdb-kpi-s { display: none; } }\n@container (max-height: 40px) { .axdb-kpi > .axdb-kpi-d { display: none; } }\n@container (max-height: 16px) { .axdb-kpi > .axdb-kpi-v { display: none; } }\n.axdb-kpi-v { font: 700 clamp(15px, 44cqh, 30px)/1.05 system-ui, sans-serif; letter-spacing: -.02em; color: var(--axdb-ink); white-space: nowrap; }\n.axdb-kpi-d { margin-top: clamp(1px, 5cqh, 6px); font: 600 clamp(9px, 19cqh, 12px)/1.2 system-ui, sans-serif; white-space: nowrap; }\n.axdb-kpi-d span { color: var(--axdb-muted); font-weight: 500; }\n.axdb-kpi-d.up { color: var(--axdb-up); }\n.axdb-kpi-d.down { color: var(--axdb-down); }\n/* Grows into a tall tile (a 3-row KPI is not a number over a void) and never\n takes more than two fifths of the body. */\n.axdb-kpi-s { margin-top: auto; height: auto; min-height: 0; flex: 1 1 34px; max-height: 40%; }\n/* SHORT AND WIDE \u2014 a one-row KPI on a fluid board. Stacked, a short tile dropped\n its sparkline and left a small figure in the corner of an empty card. Here the\n figure, its delta and the sparkline sit in a row and the spark fills the width.\n The outer query asks the TILE (host), the inner one the body, so a strip too\n thin for a readable spark (under 24 px) keeps the figure alone. 340 px is what\n a figure, its delta, the gaps and a spark worth reading need \u2014 the builder's\n 236-px tiles got a 40-px wedge of spark beside the delta. */\n@container axdb-tile (max-height: 125px) and (min-width: 340px) {\n .axdb-widget--kpi > .axdb-kpi { flex-direction: row; align-items: center; gap: 14px; }\n .axdb-widget--kpi .axdb-kpi-d { margin-top: 0; }\n @container axdb-kpi (min-height: 24px) {\n .axdb-kpi > .axdb-kpi-s { display: block; flex: 1 1 40%; min-width: 80px; height: 100%; max-height: 100%; margin-top: 0; align-self: stretch; }\n }\n}\n/* THE STRIP (row floor): header and figure on one line, the spark beside them\n when the tile is wide enough. */\n@container axdb-tile (max-height: 46px) {\n .axdb-widget--kpi { flex-direction: row; align-items: center; gap: 12px; }\n .axdb-widget--kpi > .axdb-widget-h { flex: none; padding: 0 0 0 12px; background: none; border-bottom: 0; }\n /* The body is a SIZE container: in a centred row it would collapse to zero\n and every height query would fire. It takes the card's full inner height. */\n .axdb-widget--kpi > .axdb-kpi { flex: 1; align-self: stretch; }\n}\n\n/* SPLIT LAYOUT chrome: a divider sits in the gap between two siblings (a\n 24-px hit zone around a 10-px gap) and shows its line on hover or while\n dragged; the insertion line marks the edge a dragged widget will land on. */\n.grafloria-html-layer > .axdb-div { position: absolute; z-index: 2; pointer-events: auto; background: transparent; }\n.grafloria-html-layer > .axdb-div--row { cursor: col-resize; }\n.grafloria-html-layer > .axdb-div--column { cursor: row-resize; }\n.grafloria-html-layer > .axdb-div::after {\n content: \"\"; position: absolute; border-radius: 2px; background: #3b52d9; opacity: 0; transition: opacity .12s ease;\n}\n.grafloria-html-layer > .axdb-div--row::after { left: 50%; top: 8px; bottom: 8px; width: 4px; margin-left: -2px; }\n.grafloria-html-layer > .axdb-div--column::after { top: 50%; left: 8px; right: 8px; height: 4px; margin-top: -2px; }\n.grafloria-html-layer > .axdb-div:hover::after, .grafloria-html-layer > .axdb-div.axdb-active::after { opacity: .9; }\n.grafloria-html-layer > .axdb-ins {\n position: absolute; z-index: 3; pointer-events: none; border-radius: 2px;\n background: #3b52d9; box-shadow: 0 0 0 3px rgba(59, 82, 217, .22);\n}\n@media (prefers-reduced-motion: reduce) { .grafloria-html-layer > .axdb-div::after { transition: none; } }\n\n/* donut: ring beside its legend. The ring takes the body's height (a tall\n tile gets a bigger ring, not dead card), square, capped so its centre figure\n stays a figure and not a headline. */\n.axdb-widget-b.axdb-donut { display: flex; align-items: center; gap: 10px; }\n/* A legend taller than a SHORT body (a 2-row donut on a squeezed board) is\n clipped at its own foot, not centred over the title above it. */\n.axdb-widget-b.axdb-donut > .axdb-lg--col { max-height: 100%; min-height: 0; overflow: hidden; }\n.axdb-widget-b.axdb-donut > svg {\n flex: 0 0 auto; width: auto; height: 100%; max-height: 260px; max-width: 60%; aspect-ratio: 1 / 1;\n}\n\n/* READABILITY TIERS (widgets.ts chartTier): the text is always in the DOM;\n what a short body cannot afford is hidden, not dropped. */\n.axdb-tier-1 .axdb-yt--q, .axdb-tier-1 .axdb-yl--q,\n.axdb-tier-2 .axdb-yt--q, .axdb-tier-2 .axdb-yl--q,\n.axdb-tier-2 .axdb-yt--h, .axdb-tier-2 .axdb-yl--h,\n.axdb-tier-2 .axdb-xt, .axdb-tier-2 .axdb-vt,\n.axdb-lg--off { display: none; }\n\n/* SECTION CHROME: a pointer-transparent overlay on every member group. It\n wears the selection ring and, while selected, the corner handle. */\n.grafloria-html-layer > .axdb-slab { position: absolute; pointer-events: none; border-radius: var(--axdb-rs-radius, 8px); z-index: 4; }\n/* Selected, the overlay rises above the tiles so ITS corner handle wins a\n corner it shares with a child's; unselected, its handle takes no presses. */\n.grafloria-html-layer > .axdb-slab.axdb-slab--selected { z-index: 6; box-shadow: 0 0 0 1.5px var(--axdb-accent-ring, rgba(59, 82, 217, .55)); }\n.grafloria-html-layer > .axdb-slab > .axdb-rs { pointer-events: none; opacity: 0; }\n.grafloria-html-layer > .axdb-slab.axdb-slab--selected > .axdb-rs { pointer-events: auto; opacity: 1; }\n.grafloria-html-layer > .axdb-slab.axdb-slab--static > .axdb-rs { display: none; }\n\n/* GROUP FRAME (0.4.43): a TAB CONTAINER wears a frame by default \u2014 a bordered\n slab, and a tinted surface UNDER its pages (the strip's own track colour, so\n strip and body read as one panel). The layer is isolated so the surface can\n sit below every tile at z -1 and still paint above the canvas. */\n.grafloria-html-layer { isolation: isolate; }\n.grafloria-html-layer > .axdb-group-bg {\n position: absolute; pointer-events: none; z-index: -1; box-sizing: border-box;\n border-radius: var(--axdb-rs-radius, 8px);\n background: var(--axdb-group-bg, #f1f3f8);\n}\n.grafloria-html-layer > .axdb-slab.axdb-slab--tabs {\n box-sizing: border-box;\n border: 1px solid var(--axdb-group-border, var(--axdb-line, #e5e8ef));\n}\n@media (prefers-color-scheme: dark) {\n .grafloria-html-layer > .axdb-group-bg { background: var(--axdb-group-bg, #1b2029); }\n .grafloria-html-layer > .axdb-slab.axdb-slab--tabs { border-color: var(--axdb-group-border, rgba(236, 238, 244, .12)); }\n}\n\n/* SECTION CAPTION (0.4.22): the band on the slab. Geometry is inline (the\n reserve and the pixels come from one function); everything visual is a\n variable the options write and a theme may override. The band takes the\n pointer \u2014 the slab does not \u2014 so a press on it is the section's. */\n.grafloria-html-layer > .axdb-slab > .axdb-slab-h {\n position: absolute; box-sizing: border-box; pointer-events: auto; z-index: 1;\n display: flex; align-items: center; gap: 6px; min-width: 0;\n padding: var(--axdb-caption-pad, 0 12px);\n background: var(--axdb-caption-bg, rgba(31, 36, 48, .045));\n /* A hairline by default: two bands meeting (a captioned section inside a\n captioned section) read as two headers, not one grey block. */\n border-bottom: var(--axdb-caption-border, 1px solid rgba(31, 36, 48, .1));\n color: var(--axdb-caption-fg, #1f2430);\n font: var(--axdb-caption-font-weight, 600) var(--axdb-caption-font-size, 13px)/1.2 var(--axdb-caption-font-family, system-ui, -apple-system, \"Segoe UI\", sans-serif);\n text-transform: var(--axdb-caption-transform, none);\n letter-spacing: -.005em;\n border-radius: var(--axdb-rs-radius, 8px) var(--axdb-rs-radius, 8px) 0 0;\n cursor: default; user-select: none; -webkit-user-select: none; overflow: hidden;\n transition: opacity .12s;\n}\n/* The modifiers carry the band's own selector so they outrank its defaults. */\n.grafloria-html-layer > .axdb-slab > .axdb-slab-h.axdb-slab-h--center { justify-content: center; }\n.grafloria-html-layer > .axdb-slab > .axdb-slab-h.axdb-slab-h--end { justify-content: flex-end; }\n.grafloria-html-layer > .axdb-slab > .axdb-slab-h.axdb-slab-h--vtop { align-items: flex-start; }\n.grafloria-html-layer > .axdb-slab > .axdb-slab-h.axdb-slab-h--vbottom { align-items: flex-end; }\n/* 'tab': the same band sized to its text, a chip at the leading corner. It\n reserves its height like 'inside' \u2014 see captionReserve. */\n.grafloria-html-layer > .axdb-slab > .axdb-slab-h.axdb-slab-h--tab {\n max-width: 100%; border-radius: var(--axdb-rs-radius, 8px);\n border-bottom: var(--axdb-caption-border-tab, none);\n box-shadow: inset 0 0 0 1px rgba(31, 36, 48, .08);\n}\n/* 'hover': an overlay. It reserves nothing, so while hidden it must not take\n the pointer (an invisible band swallowed clicks on the content beneath),\n and while shown it paints OPAQUE \u2014 a 5% tint over a chart is a smear. */\n.grafloria-html-layer > .axdb-slab > .axdb-slab-h.axdb-slab-h--hover {\n opacity: 0; pointer-events: none;\n background: var(--axdb-caption-bg, #fff);\n box-shadow: 0 1px 4px rgba(31, 36, 48, .16);\n}\n/* .axdb-slab--hot is set by the binder from the live pointer (the overlay\n takes no pointer of its own, so CSS :hover can never fire on it). */\n.grafloria-html-layer > .axdb-slab.axdb-slab--hot > .axdb-slab-h.axdb-slab-h--hover,\n.grafloria-html-layer > .axdb-slab.axdb-slab--selected > .axdb-slab-h.axdb-slab-h--hover { pointer-events: auto; opacity: 1; }\n/* the actions of a hovered or selected section */\n.grafloria-html-layer > .axdb-slab.axdb-slab--hot > .axdb-slab-h > .axdb-slab-h-actions { opacity: 1; }\n/* the tight tier: a section under 90 px */\n.grafloria-html-layer > .axdb-slab > .axdb-slab-h.axdb-slab-h--tight { font-size: min(var(--axdb-caption-font-size, 12px), 12px); gap: 4px; }\n.axdb-slab-h--tight .axdb-slab-h-sub, .axdb-slab-h--tight .axdb-slab-h-actions { display: none; }\n.axdb-slab-h-icon { flex: none; }\n.axdb-slab-h-body { display: flex; flex-direction: column; justify-content: center; min-width: 0; flex: 0 1 auto; }\n.axdb-slab-h-text, .axdb-slab-h-sub { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }\n.axdb-slab-h-sub { font-size: .88em; font-weight: 500; opacity: .7; }\n.axdb-slab-h-info { flex: none; opacity: .6; font-size: .9em; cursor: help; }\n.axdb-slab-h-actions { flex: none; display: flex; gap: 2px; margin-inline-start: auto; opacity: 0; transition: opacity .12s; }\n.axdb-slab-h--center .axdb-slab-h-actions, .axdb-slab-h--end .axdb-slab-h-actions { margin-inline-start: 0; }\n.axdb-slab-h:hover > .axdb-slab-h-actions, .axdb-slab--selected > .axdb-slab-h > .axdb-slab-h-actions, .axdb-slab-h:focus-within > .axdb-slab-h-actions { opacity: 1; }\n.axdb-slab-h-action {\n all: unset; box-sizing: border-box; width: 26px; height: 26px; display: inline-flex; align-items: center; justify-content: center;\n border-radius: 6px; cursor: pointer; font-size: 14px; line-height: 1; color: inherit; transition: background-color .12s;\n}\n.axdb-slab-h-action:hover { background: rgba(31, 36, 48, .08); }\n.axdb-slab-h-action:focus-visible { outline: 2px solid var(--axdb-accent-ring, rgba(59, 82, 217, .55)); outline-offset: -2px; }\n.axdb-slab-h-action[disabled] { opacity: .4; cursor: default; }\n.axdb-slab-h-action[disabled]:hover { background: none; }\n.grafloria-html-layer > .axdb-slab.axdb-slab--static > .axdb-slab-h { cursor: default; }\n@media (prefers-color-scheme: dark) {\n /* The band sits on the PAGE, whose ground the kit does not own (a host that\n stays light while the kit goes dark is common): a solid ink, not a tint,\n so the light caption always reads. */\n .grafloria-html-layer > .axdb-slab > .axdb-slab-h {\n background: var(--axdb-caption-bg, #1b2029);\n color: var(--axdb-caption-fg, #eceef4);\n border-bottom: var(--axdb-caption-border, 1px solid rgba(236, 238, 244, .12));\n }\n .grafloria-html-layer > .axdb-slab > .axdb-slab-h.axdb-slab-h--tab { box-shadow: inset 0 0 0 1px rgba(236, 238, 244, .12); }\n .grafloria-html-layer > .axdb-slab > .axdb-slab-h.axdb-slab-h--hover { background: var(--axdb-caption-bg, #1a1d25); box-shadow: 0 1px 4px rgba(0, 0, 0, .5); }\n .axdb-slab-h-action:hover { background: rgba(236, 238, 244, .1); }\n}\n\n/* TAB CONTAINER (0.4.27): a strip of pages across the container's top. The\n strip takes the pointer (the tabs are real buttons); the pages below it are\n ordinary boards. */\n.grafloria-html-layer > .axdb-tabs {\n position: absolute; box-sizing: border-box; pointer-events: auto; z-index: 5;\n display: flex; align-items: center; gap: 2px; padding: 3px 6px; overflow-x: auto; overflow-y: hidden;\n background: var(--axdb-tabs-bg, #f1f3f8);\n border-bottom: 1px solid var(--axdb-tabs-line, #e5e8ef);\n border-radius: var(--axdb-rs-radius, 8px) var(--axdb-rs-radius, 8px) 0 0;\n scrollbar-width: thin;\n}\n.axdb-tabs--center { justify-content: center; }\n.axdb-tabs--end { justify-content: flex-end; }\n.axdb-tabs--stretch > .axdb-tab { flex: 1 1 0; }\n/* A tab is a PILL on the track (a segmented control): quiet until hovered,\n the active one lifted in the card's own ground. */\n.axdb-tab {\n all: unset; box-sizing: border-box; flex: 0 0 auto; max-width: 200px;\n padding: 0 12px; height: 100%; display: inline-flex; align-items: center;\n font: 500 12.5px/1 system-ui, -apple-system, \"Segoe UI\", sans-serif;\n letter-spacing: -.005em;\n color: var(--axdb-tabs-fg, #5a6478); cursor: pointer;\n border-radius: var(--axdb-tab-radius, 6px);\n white-space: nowrap; overflow: hidden; text-overflow: ellipsis;\n transition: background-color .12s, color .12s, box-shadow .12s;\n}\n.axdb-tab:hover { background: rgba(31, 36, 48, .06); color: var(--axdb-tabs-on-fg, #1f2430); }\n.axdb-tab:focus-visible { outline: 2px solid var(--axdb-accent-ring, rgba(59, 82, 217, .55)); outline-offset: -2px; }\n.axdb-tab.axdb-tab--on {\n background: var(--axdb-tabs-on-bg, #fff);\n color: var(--axdb-tabs-on-fg, #1f2430);\n font-weight: 600;\n box-shadow: 0 1px 2px rgba(16, 24, 40, .1), 0 0 0 1px rgba(31, 36, 48, .06);\n}\n@media (prefers-reduced-motion: reduce) { .axdb-tab { transition: none; } }\n@media (prefers-color-scheme: dark) {\n /* Solid for the same reason as the caption band: the track sits on the page. */\n .grafloria-html-layer > .axdb-tabs { background: var(--axdb-tabs-bg, #1b2029); border-bottom-color: var(--axdb-tabs-line, rgba(236, 238, 244, .12)); }\n .axdb-tab { color: var(--axdb-tabs-fg, #98a1b4); }\n .axdb-tab:hover { background: rgba(236, 238, 244, .08); color: var(--axdb-tabs-on-fg, #eceef4); }\n .axdb-tab.axdb-tab--on { background: var(--axdb-tabs-on-bg, #232836); color: var(--axdb-tabs-on-fg, #eceef4); box-shadow: 0 1px 2px rgba(0, 0, 0, .45), 0 0 0 1px rgba(236, 238, 244, .1); }\n}\n\n/* legend chips, shared by line and donut */\n.axdb-lg { display: flex; flex-wrap: wrap; gap: 4px 12px; margin-top: 9px; }\n.axdb-lg--col { flex-direction: column; flex-wrap: nowrap; gap: 6px; margin-top: 0; }\n.axdb-lg i {\n display: inline-flex; align-items: center; gap: 5px; font-style: normal;\n font: 500 11px/1.3 system-ui, sans-serif; color: var(--axdb-muted);\n}\n.axdb-lg i b { width: 9px; height: 9px; border-radius: 3px; flex: none; }\n\n/* table */\n.axdb-table { width: 100%; border-collapse: collapse; font-size: 12px; color: var(--axdb-ink); }\n.axdb-table th {\n text-align: left; padding: 4px 8px 7px; border-bottom: 1px solid var(--axdb-line);\n font: 600 10px/1.3 system-ui, sans-serif; letter-spacing: .4px; text-transform: uppercase;\n color: var(--axdb-muted);\n}\n.axdb-table td { padding: 6px 8px; border-bottom: 1px solid var(--axdb-line); white-space: nowrap; }\n.axdb-table td.num { text-align: right; font-variant-numeric: tabular-nums; }\n.axdb-table tr:last-child td { border-bottom: none; }\n\n@media (prefers-color-scheme: dark) {\n .axdb-widget {\n --axdb-ink: #eceef4;\n --axdb-muted: #98a1b4;\n --axdb-grid: rgba(150, 160, 182, .2);\n --axdb-card: #161a22;\n --axdb-line: #2a3040;\n --axdb-head-bg: rgba(255, 255, 255, .035);\n --axdb-head-fg: #e6e9f0;\n --axdb-soft: rgba(150, 160, 182, .16);\n --axdb-up: #4ade80;\n --axdb-down: #fb7185;\n --axdb-c1: #8b9cff; --axdb-c2: #38bdf8; --axdb-c3: #2dd4bf;\n --axdb-c4: #fbbf24; --axdb-c5: #a78bfa; --axdb-c6: #94a3b8;\n }\n}\n";
|
|
26
26
|
/** Idempotently inject the kit stylesheet (safe to call per binder). */
|
|
27
27
|
export declare function ensureDashboardKitStyles(doc?: Document): void;
|
|
@@ -52,6 +52,31 @@ const CSS = `
|
|
|
52
52
|
/* Outside the board: release will REMOVE — dim the ghost to say so. */
|
|
53
53
|
.grafloria-html-layer > .grafloria-node-host.axdb-ghost.axdb-out { opacity: .35; filter: grayscale(.6); }
|
|
54
54
|
|
|
55
|
+
/* ===== the kit's chrome glides WITH the tiles it belongs to (0.4.43) =====
|
|
56
|
+
A pushed group's strip, slab and surface used to jump to the new slot while
|
|
57
|
+
its tiles slid there: one group, two motions. */
|
|
58
|
+
.grafloria-html-layer.axdb-glide > .axdb-tabs,
|
|
59
|
+
.grafloria-html-layer.axdb-glide > .axdb-slab,
|
|
60
|
+
.grafloria-html-layer.axdb-glide > .axdb-group-bg {
|
|
61
|
+
transition: left .28s cubic-bezier(.2, 0, .2, 1), top .28s cubic-bezier(.2, 0, .2, 1),
|
|
62
|
+
width .28s cubic-bezier(.2, 0, .2, 1), height .28s cubic-bezier(.2, 0, .2, 1);
|
|
63
|
+
}
|
|
64
|
+
@media (prefers-reduced-motion: reduce) {
|
|
65
|
+
.grafloria-html-layer.axdb-glide > .axdb-tabs,
|
|
66
|
+
.grafloria-html-layer.axdb-glide > .axdb-slab,
|
|
67
|
+
.grafloria-html-layer.axdb-glide > .axdb-group-bg { transition: none; }
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/* ===== CARRIED (0.4.43): a group dragged by its strip or band moves as ONE thing =====
|
|
71
|
+
Its whole subtree — tiles, strips, slabs, surface — is transition-exempt like
|
|
72
|
+
the ghost and floats with it; the surface casts the shadow. */
|
|
73
|
+
.grafloria-html-layer > .axdb-carried,
|
|
74
|
+
.grafloria-html-layer.axdb-glide > .axdb-carried { transition: none; cursor: grabbing; }
|
|
75
|
+
.grafloria-html-layer > .grafloria-node-host.axdb-carried { z-index: 30; opacity: .92; }
|
|
76
|
+
.grafloria-html-layer > .axdb-group-bg.axdb-carried { z-index: 29; filter: drop-shadow(0 10px 16px rgba(16, 24, 40, .3)); }
|
|
77
|
+
.grafloria-html-layer > .axdb-slab.axdb-carried { z-index: 31; }
|
|
78
|
+
.grafloria-html-layer > .axdb-tabs.axdb-carried { z-index: 32; }
|
|
79
|
+
|
|
55
80
|
/* ===== the placeholder: dashed slab, truthful, never animated ===== */
|
|
56
81
|
.grafloria-html-layer > .axdb-ph {
|
|
57
82
|
position: absolute;
|
|
@@ -432,6 +457,25 @@ const CSS = `
|
|
|
432
457
|
.grafloria-html-layer > .axdb-slab.axdb-slab--selected > .axdb-rs { pointer-events: auto; opacity: 1; }
|
|
433
458
|
.grafloria-html-layer > .axdb-slab.axdb-slab--static > .axdb-rs { display: none; }
|
|
434
459
|
|
|
460
|
+
/* GROUP FRAME (0.4.43): a TAB CONTAINER wears a frame by default — a bordered
|
|
461
|
+
slab, and a tinted surface UNDER its pages (the strip's own track colour, so
|
|
462
|
+
strip and body read as one panel). The layer is isolated so the surface can
|
|
463
|
+
sit below every tile at z -1 and still paint above the canvas. */
|
|
464
|
+
.grafloria-html-layer { isolation: isolate; }
|
|
465
|
+
.grafloria-html-layer > .axdb-group-bg {
|
|
466
|
+
position: absolute; pointer-events: none; z-index: -1; box-sizing: border-box;
|
|
467
|
+
border-radius: var(--axdb-rs-radius, 8px);
|
|
468
|
+
background: var(--axdb-group-bg, #f1f3f8);
|
|
469
|
+
}
|
|
470
|
+
.grafloria-html-layer > .axdb-slab.axdb-slab--tabs {
|
|
471
|
+
box-sizing: border-box;
|
|
472
|
+
border: 1px solid var(--axdb-group-border, var(--axdb-line, #e5e8ef));
|
|
473
|
+
}
|
|
474
|
+
@media (prefers-color-scheme: dark) {
|
|
475
|
+
.grafloria-html-layer > .axdb-group-bg { background: var(--axdb-group-bg, #1b2029); }
|
|
476
|
+
.grafloria-html-layer > .axdb-slab.axdb-slab--tabs { border-color: var(--axdb-group-border, rgba(236, 238, 244, .12)); }
|
|
477
|
+
}
|
|
478
|
+
|
|
435
479
|
/* SECTION CAPTION (0.4.22): the band on the slab. Geometry is inline (the
|
|
436
480
|
reserve and the pixels come from one function); everything visual is a
|
|
437
481
|
variable the options write and a theme may override. The band takes the
|
|
@@ -9,6 +9,14 @@
|
|
|
9
9
|
* binder's.
|
|
10
10
|
*/
|
|
11
11
|
export interface TabsOptions {
|
|
12
|
+
/**
|
|
13
|
+
* Pixels between the frame and the pages — left, right, bottom, and below
|
|
14
|
+
* the strip (0.4.43). The cards of a page sit INSIDE the container's tinted
|
|
15
|
+
* frame, so two cards under one tab read as one panel; a border on the
|
|
16
|
+
* cell alone ran exactly where the cards' own borders were. Default 8;
|
|
17
|
+
* 0 puts the pages edge to edge, the look before 0.4.43.
|
|
18
|
+
*/
|
|
19
|
+
inset?: number;
|
|
12
20
|
/** Where the strip sits. Only 'top' today; 'bottom' | 'start' | 'end' follow. */
|
|
13
21
|
position?: 'top';
|
|
14
22
|
/** Along the strip (default 'start', mirrored on RTL). */
|
|
@@ -33,6 +41,9 @@ export declare const TAB_STRIP_HEIGHT = 30;
|
|
|
33
41
|
export declare const TAB_DRAG_THRESHOLD = 4;
|
|
34
42
|
/** Pixels the pages give up at the top of the container. */
|
|
35
43
|
export declare function tabStripReserve(o: TabsOptions | undefined, pageCount: number): number;
|
|
44
|
+
export declare const TAB_PAGE_INSET = 8;
|
|
45
|
+
/** Pixels between the container's frame and its pages: left, right, bottom, and below the strip. */
|
|
46
|
+
export declare function tabPageInset(o: TabsOptions | undefined): number;
|
|
36
47
|
/**
|
|
37
48
|
* Paint the strip. Repainted only when its identity changes (the pages, the
|
|
38
49
|
* active one, the options, RTL) so a page switch is a class toggle, not a
|
|
@@ -22,6 +22,12 @@ export function tabStripReserve(o, pageCount) {
|
|
|
22
22
|
return 0;
|
|
23
23
|
return Math.max(18, (_a = o === null || o === void 0 ? void 0 : o.height) !== null && _a !== void 0 ? _a : TAB_STRIP_HEIGHT);
|
|
24
24
|
}
|
|
25
|
+
export const TAB_PAGE_INSET = 8;
|
|
26
|
+
/** Pixels between the container's frame and its pages: left, right, bottom, and below the strip. */
|
|
27
|
+
export function tabPageInset(o) {
|
|
28
|
+
var _a;
|
|
29
|
+
return Math.max(0, (_a = o === null || o === void 0 ? void 0 : o.inset) !== null && _a !== void 0 ? _a : TAB_PAGE_INSET);
|
|
30
|
+
}
|
|
25
31
|
/**
|
|
26
32
|
* Paint the strip. Repainted only when its identity changes (the pages, the
|
|
27
33
|
* active one, the options, RTL) so a page switch is a class toggle, not a
|