@grafloria/element 0.4.17 → 0.4.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json
CHANGED
|
@@ -429,6 +429,13 @@ interface BinderPeer {
|
|
|
429
429
|
group: GroupModel;
|
|
430
430
|
/** True when this board's engine holds `id` as an item (member lookup). */
|
|
431
431
|
hasItem(id: string): boolean;
|
|
432
|
+
/**
|
|
433
|
+
* Drop this board's selection: a press that selects a tile on ONE board of
|
|
434
|
+
* a canvas clears the others, so a section and its parent never show two
|
|
435
|
+
* rings at once (Quantia Groups page: three tiles selected across three
|
|
436
|
+
* boards after three clicks).
|
|
437
|
+
*/
|
|
438
|
+
clearSelection?(): void;
|
|
432
439
|
/** The member's current cell in this board (undefined when absent). */
|
|
433
440
|
memberCell(id: string): CellRect | undefined;
|
|
434
441
|
/**
|
|
@@ -490,6 +497,8 @@ interface AdoptedLeg {
|
|
|
490
497
|
* unregister. A split board adopts nothing and grows no slab: its `adopt`
|
|
491
498
|
* answers null and `resizeMemberBy` answers unchanged.
|
|
492
499
|
*/
|
|
500
|
+
/** Clear the selection on every OTHER board of the canvas — one selection per canvas. */
|
|
501
|
+
export declare function clearOtherSelections(container: HTMLElement, self: BinderPeer | null): void;
|
|
493
502
|
export declare function registerBoardPeer(container: HTMLElement, peer: BinderPeer): () => void;
|
|
494
503
|
export type { BinderPeer };
|
|
495
504
|
export declare function bindDashboardGrid(api: DashboardGridApi, group: GroupModel, options?: DashboardGridOptions): DashboardGridHandle;
|
|
@@ -153,6 +153,13 @@ const BOARD_REGISTRY = new WeakMap();
|
|
|
153
153
|
* unregister. A split board adopts nothing and grows no slab: its `adopt`
|
|
154
154
|
* answers null and `resizeMemberBy` answers unchanged.
|
|
155
155
|
*/
|
|
156
|
+
/** Clear the selection on every OTHER board of the canvas — one selection per canvas. */
|
|
157
|
+
export function clearOtherSelections(container, self) {
|
|
158
|
+
var _a, _b;
|
|
159
|
+
for (const p of (_a = BOARD_REGISTRY.get(container)) !== null && _a !== void 0 ? _a : [])
|
|
160
|
+
if (p !== self)
|
|
161
|
+
(_b = p.clearSelection) === null || _b === void 0 ? void 0 : _b.call(p);
|
|
162
|
+
}
|
|
156
163
|
export function registerBoardPeer(container, peer) {
|
|
157
164
|
let set = BOARD_REGISTRY.get(container);
|
|
158
165
|
if (!set) {
|
|
@@ -701,11 +708,15 @@ export function bindDashboardGrid(api, group, options = {}) {
|
|
|
701
708
|
*/
|
|
702
709
|
let selectedId;
|
|
703
710
|
const selectWidget = (id) => {
|
|
711
|
+
if (id !== undefined)
|
|
712
|
+
clearOtherSelections(api.container, selfPeerRef);
|
|
704
713
|
if (id === selectedId)
|
|
705
714
|
return;
|
|
706
715
|
selectedId = id;
|
|
707
716
|
syncA11y();
|
|
708
717
|
};
|
|
718
|
+
/** Set once the peer object exists (below); `selectWidget` runs before that only on boot. */
|
|
719
|
+
let selfPeerRef = null;
|
|
709
720
|
const syncA11y = (only) => {
|
|
710
721
|
var _a, _b;
|
|
711
722
|
if (disposed)
|
|
@@ -1790,6 +1801,12 @@ export function bindDashboardGrid(api, group, options = {}) {
|
|
|
1790
1801
|
};
|
|
1791
1802
|
const selfPeer = {
|
|
1792
1803
|
group,
|
|
1804
|
+
clearSelection: () => {
|
|
1805
|
+
if (selectedId === undefined)
|
|
1806
|
+
return;
|
|
1807
|
+
selectedId = undefined;
|
|
1808
|
+
syncA11y();
|
|
1809
|
+
},
|
|
1793
1810
|
hasItem: (id) => !!engine.getItem(id),
|
|
1794
1811
|
memberCell: (id) => {
|
|
1795
1812
|
const it = engine.getItem(id);
|
|
@@ -1826,6 +1843,9 @@ export function bindDashboardGrid(api, group, options = {}) {
|
|
|
1826
1843
|
adopt,
|
|
1827
1844
|
};
|
|
1828
1845
|
peersOnCanvas().add(selfPeer);
|
|
1846
|
+
selfPeerRef = selfPeer;
|
|
1847
|
+
// The board's gap, for chrome that must fit BETWEEN tiles (the outside grip tab).
|
|
1848
|
+
api.container.style.setProperty('--axdb-gap', `${gap}px`);
|
|
1829
1849
|
const tool = {
|
|
1830
1850
|
id: `dashboard-grid:${group.id}:${++binderSeq}`,
|
|
1831
1851
|
priority: 2, // point-specific claim — outranks mode-style tools (see ext/tools.ts)
|
|
@@ -2070,7 +2090,7 @@ export function bindDashboardGrid(api, group, options = {}) {
|
|
|
2070
2090
|
return;
|
|
2071
2091
|
if (focusedId !== hit.id || selectedId !== hit.id) {
|
|
2072
2092
|
focusedId = hit.id;
|
|
2073
|
-
|
|
2093
|
+
selectWidget(hit.id);
|
|
2074
2094
|
syncA11y();
|
|
2075
2095
|
}
|
|
2076
2096
|
};
|
|
@@ -2422,7 +2442,7 @@ export function bindDashboardGrid(api, group, options = {}) {
|
|
|
2422
2442
|
if (disposed || !((_a = group.members) !== null && _a !== void 0 ? _a : new Set()).has(id) || !diagram.getNode(id))
|
|
2423
2443
|
return false;
|
|
2424
2444
|
focusedId = id;
|
|
2425
|
-
|
|
2445
|
+
selectWidget(id);
|
|
2426
2446
|
syncA11y();
|
|
2427
2447
|
(_c = (_b = hostOf(id)) === null || _b === void 0 ? void 0 : _b.focus) === null || _c === void 0 ? void 0 : _c.call(_b, { preventScroll: true });
|
|
2428
2448
|
return true;
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
import { __awaiter } from "tslib";
|
|
26
26
|
import { Command } from '@grafloria/engine';
|
|
27
27
|
import { LiveRegionController, registerTool } from '@grafloria/renderer';
|
|
28
|
-
import { dragHandleSelector, gripHostOf, gripOf, normalizeDragHandle, ownsPress, pressOnDragHandle, registerBoardPeer, syncGrip, DRAG_HANDLE_CLASS } from './grid-binder.js';
|
|
28
|
+
import { clearOtherSelections, dragHandleSelector, gripHostOf, gripOf, normalizeDragHandle, ownsPress, pressOnDragHandle, registerBoardPeer, syncGrip, DRAG_HANDLE_CLASS } from './grid-binder.js';
|
|
29
29
|
import { cellFromGridItem } from './grid-mapping.js';
|
|
30
30
|
import { addSplitLeaf, cellsFromSplit, cloneSplit, dividersOf, groupRectsOf, insertSplitLeaf, moveSplitDivider, normalizeSplit, pathToLeaf, projectSplit, removeSplitLeaf, splitFromCells, splitLeaves, } from './split-layout.js';
|
|
31
31
|
import { ensureDashboardKitStyles } from './styles.js';
|
|
@@ -372,11 +372,14 @@ export function bindDashboardSplit(api, group, options = {}) {
|
|
|
372
372
|
/** The selected widget (see grid-binder): stamped `axdb-selected`, shows the grip. */
|
|
373
373
|
let selectedId;
|
|
374
374
|
const selectWidget = (id) => {
|
|
375
|
+
if (id !== undefined)
|
|
376
|
+
clearOtherSelections(api.container, selfPeerRef);
|
|
375
377
|
if (id === selectedId)
|
|
376
378
|
return;
|
|
377
379
|
selectedId = id;
|
|
378
380
|
syncA11y();
|
|
379
381
|
};
|
|
382
|
+
let selfPeerRef = null;
|
|
380
383
|
// Static boards let content be clicked — see grid-binder's staticGuard.
|
|
381
384
|
const staticGuard = (e) => {
|
|
382
385
|
var _a, _b, _c, _d;
|
|
@@ -823,7 +826,7 @@ export function bindDashboardSplit(api, group, options = {}) {
|
|
|
823
826
|
return;
|
|
824
827
|
if (focusedId !== hit.id || selectedId !== hit.id) {
|
|
825
828
|
focusedId = hit.id;
|
|
826
|
-
|
|
829
|
+
selectWidget(hit.id);
|
|
827
830
|
syncA11y();
|
|
828
831
|
}
|
|
829
832
|
};
|
|
@@ -967,7 +970,26 @@ export function bindDashboardSplit(api, group, options = {}) {
|
|
|
967
970
|
if (layerEl && hostObserver)
|
|
968
971
|
hostObserver.observe(layerEl, { childList: true });
|
|
969
972
|
// -- the handle -------------------------------------------------------------
|
|
970
|
-
|
|
973
|
+
/**
|
|
974
|
+
* The row count the tree's cells are written in. A grid-authored board (or
|
|
975
|
+
* one switched from the grid) carries its members' cells: their extent IS
|
|
976
|
+
* the row count, and switching back must return the same cells — the guess
|
|
977
|
+
* below quantised three 14-row sections on a 758-px fit board to 5 rows
|
|
978
|
+
* (base row height 130), and the grid came back with 14-row inner grids
|
|
979
|
+
* inside 5-row slabs (Quantia, Groups page, Split → Grid). The guess stays
|
|
980
|
+
* for a tree-authored board that never had cells.
|
|
981
|
+
*/
|
|
982
|
+
const rowsAtBind = (() => {
|
|
983
|
+
let max = 0;
|
|
984
|
+
for (const id of members()) {
|
|
985
|
+
const c = persistedCell(id);
|
|
986
|
+
if (!c)
|
|
987
|
+
return undefined;
|
|
988
|
+
max = Math.max(max, c.y + c.h);
|
|
989
|
+
}
|
|
990
|
+
return max > 0 ? max : undefined;
|
|
991
|
+
})();
|
|
992
|
+
const rowsGuess = () => rowsAtBind !== null && rowsAtBind !== void 0 ? rowsAtBind : Math.max(1, Math.round(frame().height / (baseRowHeight + gap)));
|
|
971
993
|
const handle = {
|
|
972
994
|
sync() {
|
|
973
995
|
if (disposed)
|
|
@@ -1021,7 +1043,7 @@ export function bindDashboardSplit(api, group, options = {}) {
|
|
|
1021
1043
|
if (!((_a = group.members) !== null && _a !== void 0 ? _a : new Set()).has(id) || !diagram.getNode(id))
|
|
1022
1044
|
return false;
|
|
1023
1045
|
focusedId = id;
|
|
1024
|
-
|
|
1046
|
+
selectWidget(id);
|
|
1025
1047
|
syncA11y();
|
|
1026
1048
|
(_c = (_b = hostOf(id)) === null || _b === void 0 ? void 0 : _b.focus) === null || _c === void 0 ? void 0 : _c.call(_b, { preventScroll: true });
|
|
1027
1049
|
return true;
|
|
@@ -1168,8 +1190,14 @@ export function bindDashboardSplit(api, group, options = {}) {
|
|
|
1168
1190
|
api.renderNow();
|
|
1169
1191
|
// A split board on a CONTAINER (item 7) sits inside a parent grid: register
|
|
1170
1192
|
// as a peer so the parent's hitTest hands presses on our tiles to us.
|
|
1171
|
-
const
|
|
1193
|
+
const selfPeer = {
|
|
1172
1194
|
group,
|
|
1195
|
+
clearSelection: () => {
|
|
1196
|
+
if (selectedId === undefined)
|
|
1197
|
+
return;
|
|
1198
|
+
selectedId = undefined;
|
|
1199
|
+
syncA11y();
|
|
1200
|
+
},
|
|
1173
1201
|
hasItem: (id) => { var _a; return ((_a = group.members) !== null && _a !== void 0 ? _a : new Set()).has(id); },
|
|
1174
1202
|
memberCell: (id) => handle.cellOf(id),
|
|
1175
1203
|
resizeMemberBy: () => ({ changed: false }),
|
|
@@ -1180,7 +1208,10 @@ export function bindDashboardSplit(api, group, options = {}) {
|
|
|
1180
1208
|
return f.width * f.height;
|
|
1181
1209
|
},
|
|
1182
1210
|
adopt: () => null,
|
|
1183
|
-
}
|
|
1211
|
+
};
|
|
1212
|
+
const unregisterPeer = registerBoardPeer(api.container, selfPeer);
|
|
1213
|
+
selfPeerRef = selfPeer;
|
|
1214
|
+
api.container.style.setProperty('--axdb-gap', `${gap}px`);
|
|
1184
1215
|
const disposeHandle = handle.dispose.bind(handle);
|
|
1185
1216
|
handle.dispose = () => {
|
|
1186
1217
|
unregisterPeer();
|
|
@@ -235,7 +235,10 @@ const CSS = `
|
|
|
235
235
|
.grafloria-node-host > .axdb-grip--inside { top: 2px; }
|
|
236
236
|
}
|
|
237
237
|
/* OUTSIDE: a tab on the card's top edge, its corners in line with the card's. */
|
|
238
|
-
|
|
238
|
+
/* The OUTSIDE tab lives in the gap between tiles: never taller than the gap
|
|
239
|
+
less a pixel (an 11-px tab in a 10-px gap sat on the tile above), never
|
|
240
|
+
shorter than 6 px so the dots still read. */
|
|
241
|
+
.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); }
|
|
239
242
|
.grafloria-node-host > .axdb-grip--outside.axdb-grip--left { left: 10px; }
|
|
240
243
|
.grafloria-node-host > .axdb-grip--outside.axdb-grip--right { right: 10px; }
|
|
241
244
|
.grafloria-node-host > .axdb-grip--center { left: 50%; transform: translateX(-50%); }
|