@grafloria/element 0.4.53 → 0.4.55
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/board-ctx.d.ts +62 -0
- package/src/lib/dashboard-kit/board-ctx.js +2 -0
- package/src/lib/dashboard-kit/chrome.d.ts +60 -0
- package/src/lib/dashboard-kit/chrome.js +461 -0
- package/src/lib/dashboard-kit/dashboard.d.ts +11 -3
- package/src/lib/dashboard-kit/dashboard.js +11 -6
- package/src/lib/dashboard-kit/edges.d.ts +15 -0
- package/src/lib/dashboard-kit/edges.js +30 -0
- package/src/lib/dashboard-kit/grid-binder.d.ts +4 -43
- package/src/lib/dashboard-kit/grid-binder.js +101 -883
- package/src/lib/dashboard-kit/grip.d.ts +37 -0
- package/src/lib/dashboard-kit/grip.js +67 -0
- package/src/lib/dashboard-kit/keyboard.d.ts +41 -0
- package/src/lib/dashboard-kit/keyboard.js +145 -0
- package/src/lib/dashboard-kit/project.d.ts +30 -0
- package/src/lib/dashboard-kit/project.js +177 -0
|
@@ -51,51 +51,21 @@
|
|
|
51
51
|
*/
|
|
52
52
|
import { __awaiter } from "tslib";
|
|
53
53
|
import { AddToGroupCommand, BatchCommand, GridPackEngine, RemoveFromGroupCommand, } from '@grafloria/engine';
|
|
54
|
-
import {
|
|
54
|
+
import { registerTool } from '@grafloria/renderer';
|
|
55
55
|
import { buildCommitCommands, cellFromGridItem, cellToRect, columnUnitFor, gridItemFromCell, pointToCell, rowHeightFor, sizeToSpan, } from './grid-mapping.js';
|
|
56
56
|
import { ensureDashboardKitStyles } from './styles.js';
|
|
57
|
-
import { captionOfGroup,
|
|
57
|
+
import { captionOfGroup, captionPassThrough, sectionCaptionReserve } from './caption.js';
|
|
58
58
|
import { TAB_STRIP_HEIGHT } from './tabs.js';
|
|
59
59
|
import { BESIDE_BAND, resolve as resolveZone, resolveTabZone } from './zones.js';
|
|
60
60
|
import { SequenceCommand, SetGroupCellCommand, tileCommands } from './commit.js';
|
|
61
|
+
import { createProjection } from './project.js';
|
|
62
|
+
import { cursorFor, edgesNear, NO_EDGES, anyEdge } from './edges.js';
|
|
63
|
+
import { DRAG_HANDLE_CLASS, dragHandleSelector, gripHostOf, normalizeDragHandle, pressOnDragHandle, sameDragHandle } from './grip.js';
|
|
64
|
+
import { createChrome, edgeGripFor, isTabsGroup } from './chrome.js';
|
|
65
|
+
import { createKeyboard, describeCell, liveRegionFor } from './keyboard.js';
|
|
66
|
+
export { EDGE_GRIP, anyEdge } from './edges.js';
|
|
67
|
+
export { GRIP_CLASS, DRAG_HANDLE_CLASS, CAPTION_BAND, normalizeDragHandle, dragHandleSelector, gripOf, syncGrip, gripHostOf, pressOnDragHandle } from './grip.js';
|
|
61
68
|
export { SequenceCommand, SetGroupCellCommand } from './commit.js';
|
|
62
|
-
/** The class of the painted grip element (a child of the node host). */
|
|
63
|
-
export const GRIP_CLASS = 'axdb-grip';
|
|
64
|
-
/** The container class that turns the caption strip's grip dots on. */
|
|
65
|
-
export const DRAG_HANDLE_CLASS = 'axdb-drag-handle';
|
|
66
|
-
/** A `dragHandle` value with its defaults filled in — the form the handle reports. */
|
|
67
|
-
export const normalizeDragHandle = (v) => {
|
|
68
|
-
var _a, _b;
|
|
69
|
-
return typeof v === 'object' && v !== null && v.grip
|
|
70
|
-
? { grip: true, position: (_a = v.position) !== null && _a !== void 0 ? _a : 'left', placement: (_b = v.placement) !== null && _b !== void 0 ? _b : 'inside' }
|
|
71
|
-
: v === true ? true : typeof v === 'string' && v.length > 0 ? v : false;
|
|
72
|
-
};
|
|
73
|
-
/** The selector a `dragHandle` value names — `null` when the whole card is the handle. */
|
|
74
|
-
export const dragHandleSelector = (v) => v === true ? '.axdb-widget-h' : typeof v === 'string' ? v : typeof v === 'object' ? '.' + GRIP_CLASS : null;
|
|
75
|
-
/** The grip config of a value, or null when it paints no grip. */
|
|
76
|
-
export const gripOf = (v) => (typeof v === 'object' ? v : null);
|
|
77
|
-
const sameDragHandle = (a, b) => JSON.stringify(a) === JSON.stringify(b);
|
|
78
|
-
/**
|
|
79
|
-
* Paint (or remove) the grip on one host, and stamp the host with the grip's
|
|
80
|
-
* placement so the header can make room for it. `movable` false = no grip.
|
|
81
|
-
*/
|
|
82
|
-
export function syncGrip(host, cfg, movable) {
|
|
83
|
-
var _a, _b, _c, _d, _e;
|
|
84
|
-
const existing = host.querySelector(':scope > .' + GRIP_CLASS);
|
|
85
|
-
host.classList.remove('axdb-gp-inside', 'axdb-gp-outside', 'axdb-gp-left', 'axdb-gp-center', 'axdb-gp-right');
|
|
86
|
-
if (!cfg || !movable) {
|
|
87
|
-
existing === null || existing === void 0 ? void 0 : existing.remove();
|
|
88
|
-
return;
|
|
89
|
-
}
|
|
90
|
-
const el = (_a = existing) !== null && _a !== void 0 ? _a : host.ownerDocument.createElement('div');
|
|
91
|
-
if (!existing) {
|
|
92
|
-
el.setAttribute('aria-hidden', 'true');
|
|
93
|
-
el.setAttribute('title', 'Drag');
|
|
94
|
-
host.appendChild(el);
|
|
95
|
-
}
|
|
96
|
-
el.className = `${GRIP_CLASS} ${GRIP_CLASS}--${(_b = cfg.position) !== null && _b !== void 0 ? _b : 'left'} ${GRIP_CLASS}--${(_c = cfg.placement) !== null && _c !== void 0 ? _c : 'inside'}`;
|
|
97
|
-
host.classList.add(`axdb-gp-${(_d = cfg.placement) !== null && _d !== void 0 ? _d : 'inside'}`, `axdb-gp-${(_e = cfg.position) !== null && _e !== void 0 ? _e : 'left'}`);
|
|
98
|
-
}
|
|
99
69
|
/** The member host a press on a painted grip belongs to (the grip may sit OUTSIDE the host's box). */
|
|
100
70
|
/**
|
|
101
71
|
* Is this press OURS? The renderer's tool registry is PAGE-GLOBAL: every
|
|
@@ -131,33 +101,6 @@ export function ownsPress(container, diagram, ev, hit) {
|
|
|
131
101
|
}
|
|
132
102
|
return true;
|
|
133
103
|
}
|
|
134
|
-
export function gripHostOf(target) {
|
|
135
|
-
var _a, _b;
|
|
136
|
-
const grip = (_a = target === null || target === void 0 ? void 0 : target.closest) === null || _a === void 0 ? void 0 : _a.call(target, '.' + GRIP_CLASS);
|
|
137
|
-
return (_b = grip === null || grip === void 0 ? void 0 : grip.closest('.grafloria-node-host')) !== null && _b !== void 0 ? _b : null;
|
|
138
|
-
}
|
|
139
|
-
/**
|
|
140
|
-
* Did a press land on the drag handle? A press INSIDE the handle element
|
|
141
|
-
* always does. The default handle is a CAPTION BAR the DevExpress way: the
|
|
142
|
-
* strip from the card's top edge down to the header's bottom, padding
|
|
143
|
-
* included — so the pointer need not hit the header's text to grab the tile.
|
|
144
|
-
*/
|
|
145
|
-
export function pressOnDragHandle(sel, target, hostEl, clientX, clientY) {
|
|
146
|
-
var _a, _b;
|
|
147
|
-
const grip = (_b = (_a = target === null || target === void 0 ? void 0 : target.closest) === null || _a === void 0 ? void 0 : _a.call(target, sel)) !== null && _b !== void 0 ? _b : null;
|
|
148
|
-
if (grip && (!hostEl || hostEl.contains(grip)))
|
|
149
|
-
return true;
|
|
150
|
-
if (sel !== '.axdb-widget-h' || !hostEl)
|
|
151
|
-
return false;
|
|
152
|
-
const hr = hostEl.getBoundingClientRect();
|
|
153
|
-
const header = hostEl.querySelector('.axdb-widget-h');
|
|
154
|
-
// A host painting its own content has no kit header: its caption is the top
|
|
155
|
-
// band of the card, so `dragHandle: true` still means something there.
|
|
156
|
-
const bottom = header ? header.getBoundingClientRect().bottom : hr.top + CAPTION_BAND;
|
|
157
|
-
return clientX >= hr.left && clientX <= hr.right && clientY >= hr.top && clientY <= bottom;
|
|
158
|
-
}
|
|
159
|
-
/** The caption strip of a host with no kit header, px from the card's top. */
|
|
160
|
-
export const CAPTION_BAND = 28;
|
|
161
104
|
/** A torn-out page never arrives shorter than this: a strip with no room under it is not a group. */
|
|
162
105
|
export const TEAR_OUT_MIN_ROWS = 2;
|
|
163
106
|
/**
|
|
@@ -204,58 +147,7 @@ export function registerBoardPeer(container, peer) {
|
|
|
204
147
|
s.delete(peer);
|
|
205
148
|
};
|
|
206
149
|
}
|
|
207
|
-
/**
|
|
208
|
-
* ONE aria-live region per canvas, shared by every board on it — the
|
|
209
|
-
* renderer's own controller (coalescing, de-duplicating), so a dashboard
|
|
210
|
-
* announces through the same channel a diagram does. WeakMap: the region
|
|
211
|
-
* follows the container out of memory.
|
|
212
|
-
*/
|
|
213
|
-
const LIVE_REGIONS = new WeakMap();
|
|
214
|
-
function liveRegionFor(container) {
|
|
215
|
-
let live = LIVE_REGIONS.get(container);
|
|
216
|
-
if (!live) {
|
|
217
|
-
live = new LiveRegionController(container);
|
|
218
|
-
LIVE_REGIONS.set(container, live);
|
|
219
|
-
}
|
|
220
|
-
return live;
|
|
221
|
-
}
|
|
222
|
-
function directionName(dx, dy) {
|
|
223
|
-
return dx < 0 ? 'left' : dx > 0 ? 'right' : dy < 0 ? 'up' : 'down';
|
|
224
|
-
}
|
|
225
|
-
/** "column 4, row 2, 3 by 1" — the cell as a person hears it (1-based). */
|
|
226
|
-
function describeCell(c) {
|
|
227
|
-
return `column ${c.x + 1}, row ${c.y + 1}, ${c.w} by ${c.h}`;
|
|
228
|
-
}
|
|
229
150
|
const DRAG_THRESHOLD = 4;
|
|
230
|
-
const GLIDE_OFF_DELAY = 400;
|
|
231
|
-
/** A press this close (CSS px) to a tile's border takes that edge for a resize. */
|
|
232
|
-
export const EDGE_GRIP = 7;
|
|
233
|
-
const NO_EDGES = { n: false, e: false, s: false, w: false };
|
|
234
|
-
/** Which of a host's edges a client point is within EDGE_GRIP of (none when outside). */
|
|
235
|
-
function edgesNear(host, cx, cy) {
|
|
236
|
-
const r = host.getBoundingClientRect();
|
|
237
|
-
if (cx < r.left - 2 || cx > r.right + 2 || cy < r.top - 2 || cy > r.bottom + 2)
|
|
238
|
-
return NO_EDGES;
|
|
239
|
-
return {
|
|
240
|
-
n: cy - r.top <= EDGE_GRIP,
|
|
241
|
-
s: r.bottom - cy <= EDGE_GRIP,
|
|
242
|
-
w: cx - r.left <= EDGE_GRIP,
|
|
243
|
-
e: r.right - cx <= EDGE_GRIP,
|
|
244
|
-
};
|
|
245
|
-
}
|
|
246
|
-
export const anyEdge = (E) => E.n || E.e || E.s || E.w;
|
|
247
|
-
/** The resize cursor for a set of edges ('' when none). */
|
|
248
|
-
function cursorFor(E) {
|
|
249
|
-
const v = E.n || E.s;
|
|
250
|
-
const h = E.e || E.w;
|
|
251
|
-
if (v && h)
|
|
252
|
-
return (E.n && E.w) || (E.s && E.e) ? 'nwse-resize' : 'nesw-resize';
|
|
253
|
-
if (v)
|
|
254
|
-
return 'ns-resize';
|
|
255
|
-
if (h)
|
|
256
|
-
return 'ew-resize';
|
|
257
|
-
return '';
|
|
258
|
-
}
|
|
259
151
|
let binderSeq = 0;
|
|
260
152
|
export function bindDashboardGrid(api, group, options = {}) {
|
|
261
153
|
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o;
|
|
@@ -442,13 +334,10 @@ export function bindDashboardGrid(api, group, options = {}) {
|
|
|
442
334
|
let disposed = false;
|
|
443
335
|
/** Reentrancy guard: our own derived frame writes must not re-project. */
|
|
444
336
|
let writing = false;
|
|
445
|
-
let placeholder = null;
|
|
446
337
|
/** Foreign tile currently adopted from another binder's gesture. */
|
|
447
338
|
let adoptedGhostId = null;
|
|
448
339
|
/** The tab page this board is currently tearing out of a container, if any. */
|
|
449
340
|
let tearing = null;
|
|
450
|
-
let glideTimer = null;
|
|
451
|
-
let ghostTimer = null;
|
|
452
341
|
/**
|
|
453
342
|
* OUR OWN CAPTION RESERVE: a section carrying a caption gives the band's
|
|
454
343
|
* pixels up at the top of its frame, so no child may take them — and the
|
|
@@ -574,172 +463,56 @@ export function bindDashboardGrid(api, group, options = {}) {
|
|
|
574
463
|
grp.setMetadata('gridItem', gridItemFromCell(cell));
|
|
575
464
|
}
|
|
576
465
|
};
|
|
577
|
-
// --
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
}
|
|
604
|
-
}
|
|
605
|
-
};
|
|
606
|
-
/** Enforce the board-frame height the sizing mode implies. */
|
|
607
|
-
const enforceBoardHeight = () => {
|
|
608
|
-
if (designH <= 0)
|
|
609
|
-
return;
|
|
610
|
-
const r = rows();
|
|
611
|
-
// FIT keeps its design height, full stop — the user's rule: "the board
|
|
612
|
-
// stays the same and the widgets change size so all of them fit". Only
|
|
613
|
-
// overflow:'scroll' lets the frame EXTEND to hold the rows at the floor
|
|
614
|
-
// height (and the canvas pan). Grow extends at the base row height.
|
|
615
|
-
const target = sizing === 'fit'
|
|
616
|
-
? overflow === 'scroll'
|
|
617
|
-
? Math.max(designH, 2 * padding + r * minRowHeight + (r - 1) * gap)
|
|
618
|
-
: designH
|
|
619
|
-
: Math.max(designH, 2 * padding + r * baseRowHeight + (r - 1) * gap);
|
|
620
|
-
const f = frame();
|
|
621
|
-
if (Math.abs(f.height - target) > 0.5) {
|
|
466
|
+
// -- the board, as its modules see it (tile first, step 4b-i) --------------
|
|
467
|
+
const ctx = {
|
|
468
|
+
api,
|
|
469
|
+
group,
|
|
470
|
+
diagram,
|
|
471
|
+
options,
|
|
472
|
+
gap,
|
|
473
|
+
padding,
|
|
474
|
+
baseRowHeight,
|
|
475
|
+
minRowHeight,
|
|
476
|
+
overflow,
|
|
477
|
+
engine: () => engine,
|
|
478
|
+
frame,
|
|
479
|
+
geom,
|
|
480
|
+
rows,
|
|
481
|
+
sizing: () => sizing,
|
|
482
|
+
designH: () => designH,
|
|
483
|
+
rtl: () => rtl,
|
|
484
|
+
isStatic: () => isStatic,
|
|
485
|
+
disposed: () => disposed,
|
|
486
|
+
htmlLayer,
|
|
487
|
+
hostOf,
|
|
488
|
+
memberEntity,
|
|
489
|
+
sizeOf,
|
|
490
|
+
ghostId: () => adoptedGhostId !== null && adoptedGhostId !== void 0 ? adoptedGhostId : ((gesture === null || gesture === void 0 ? void 0 : gesture.started) ? gesture.id : null),
|
|
491
|
+
write: (fn) => {
|
|
622
492
|
writing = true;
|
|
623
493
|
try {
|
|
624
|
-
|
|
494
|
+
fn();
|
|
625
495
|
}
|
|
626
496
|
finally {
|
|
627
497
|
writing = false;
|
|
628
498
|
}
|
|
629
|
-
}
|
|
630
|
-
};
|
|
631
|
-
/** Project every member from its engine cells (the ghost is exempt). */
|
|
632
|
-
const project = () => {
|
|
633
|
-
enforceBoardHeight();
|
|
634
|
-
writing = true;
|
|
635
|
-
try {
|
|
636
|
-
const f = frame();
|
|
637
|
-
const g = geom();
|
|
638
|
-
const r = rows();
|
|
639
|
-
for (const item of engine.getItems()) {
|
|
640
|
-
if ((gesture === null || gesture === void 0 ? void 0 : gesture.started) && item.id === gesture.id)
|
|
641
|
-
continue; // the ghost
|
|
642
|
-
if (item.id === adoptedGhostId)
|
|
643
|
-
continue; // a ghost adopted from another binder
|
|
644
|
-
writeRect(item.id, cellToRect(item, f, g, r));
|
|
645
|
-
}
|
|
646
|
-
}
|
|
647
|
-
finally {
|
|
648
|
-
writing = false;
|
|
649
|
-
}
|
|
650
|
-
syncPlaceholder();
|
|
651
|
-
// The section overlays are projected chrome like the placeholder: they
|
|
652
|
-
// follow every frame write, not only a rebuild. Painted at bind time only,
|
|
653
|
-
// a section two levels down kept the geometry of its parent's placeholder
|
|
654
|
-
// frame (100 × 34) after the view board had laid the parent out (the kit
|
|
655
|
-
// lab's L47, 2026-09-08).
|
|
656
|
-
syncSlabs();
|
|
657
|
-
};
|
|
658
|
-
// -- placeholder / ghost chrome --------------------------------------------
|
|
659
|
-
/** The placeholder exists ONLY while a gesture is live — so at any moment
|
|
660
|
-
* the DOM holds at most one `.axdb-ph` per active gesture, not one idle
|
|
661
|
-
* div per bound board. */
|
|
662
|
-
const syncPlaceholder = () => {
|
|
663
|
-
const ghostId = adoptedGhostId !== null && adoptedGhostId !== void 0 ? adoptedGhostId : ((gesture === null || gesture === void 0 ? void 0 : gesture.started) && !gesture.removedFromBoard ? gesture.id : null);
|
|
664
|
-
const item = ghostId ? engine.getItem(ghostId) : undefined;
|
|
665
|
-
const live = !!item;
|
|
666
|
-
if (!live || !item) {
|
|
667
|
-
placeholder === null || placeholder === void 0 ? void 0 : placeholder.remove();
|
|
668
|
-
placeholder = null;
|
|
669
|
-
return;
|
|
670
|
-
}
|
|
671
|
-
const layer = htmlLayer();
|
|
672
|
-
if (!layer)
|
|
673
|
-
return;
|
|
674
|
-
if (!placeholder || placeholder.parentElement !== layer) {
|
|
675
|
-
placeholder === null || placeholder === void 0 ? void 0 : placeholder.remove();
|
|
676
|
-
placeholder = document.createElement('div');
|
|
677
|
-
placeholder.className = 'axdb-ph';
|
|
678
|
-
layer.prepend(placeholder);
|
|
679
|
-
}
|
|
680
|
-
const r = cellToRect(item, frame(), geom(), rows());
|
|
681
|
-
placeholder.style.display = 'block';
|
|
682
|
-
placeholder.style.left = `${r.x}px`;
|
|
683
|
-
placeholder.style.top = `${r.y}px`;
|
|
684
|
-
placeholder.style.width = `${r.width}px`;
|
|
685
|
-
placeholder.style.height = `${r.height}px`;
|
|
686
|
-
};
|
|
687
|
-
const armGlide = () => {
|
|
688
|
-
var _a;
|
|
689
|
-
(_a = htmlLayer()) === null || _a === void 0 ? void 0 : _a.classList.add('axdb-glide');
|
|
690
|
-
if (glideTimer)
|
|
691
|
-
clearTimeout(glideTimer);
|
|
692
|
-
};
|
|
693
|
-
const disarmGlideSoon = () => {
|
|
694
|
-
if (glideTimer)
|
|
695
|
-
clearTimeout(glideTimer);
|
|
696
|
-
glideTimer = setTimeout(() => { var _a; return (_a = htmlLayer()) === null || _a === void 0 ? void 0 : _a.classList.remove('axdb-glide'); }, GLIDE_OFF_DELAY);
|
|
697
|
-
};
|
|
698
|
-
/** The host whose ghost class the pending timer will lift. */
|
|
699
|
-
let ghostHost = null;
|
|
700
|
-
/**
|
|
701
|
-
* Lift a pending ghost NOW. One timer serves every host, so superseding it
|
|
702
|
-
* (a new gesture within 60 ms of the last drop — ③ then ④ in the
|
|
703
|
-
* nested-containers checks — or a dispose on a rebind) used to clear the
|
|
704
|
-
* timer and leave the previous tile lifted for good: a permanent drop
|
|
705
|
-
* shadow the visual gate finally caught.
|
|
706
|
-
*/
|
|
707
|
-
const flushGhost = () => {
|
|
708
|
-
if (ghostTimer)
|
|
709
|
-
clearTimeout(ghostTimer);
|
|
710
|
-
ghostTimer = null;
|
|
711
|
-
ghostHost === null || ghostHost === void 0 ? void 0 : ghostHost.classList.remove('axdb-ghost', 'axdb-out');
|
|
712
|
-
ghostHost = null;
|
|
713
|
-
};
|
|
714
|
-
const setGhost = (id, on) => {
|
|
715
|
-
const host = hostOf(id);
|
|
716
|
-
if (!host)
|
|
717
|
-
return;
|
|
718
|
-
if (ghostHost && ghostHost !== host)
|
|
719
|
-
flushGhost();
|
|
720
|
-
if (on) {
|
|
721
|
-
if (ghostTimer)
|
|
722
|
-
clearTimeout(ghostTimer);
|
|
723
|
-
ghostTimer = null;
|
|
724
|
-
host.classList.add('axdb-ghost');
|
|
725
|
-
host.classList.remove('axdb-out');
|
|
726
|
-
ghostHost = host;
|
|
727
|
-
}
|
|
728
|
-
else {
|
|
729
|
-
host.classList.remove('axdb-out');
|
|
730
|
-
// Keep transition-exemption through the drop write so the snap into the
|
|
731
|
-
// placeholder is INSTANT (gridstack-style), then let glides resume.
|
|
732
|
-
if (ghostTimer)
|
|
733
|
-
clearTimeout(ghostTimer);
|
|
734
|
-
ghostHost = host;
|
|
735
|
-
ghostTimer = setTimeout(() => {
|
|
736
|
-
host.classList.remove('axdb-ghost');
|
|
737
|
-
ghostTimer = null;
|
|
738
|
-
if (ghostHost === host)
|
|
739
|
-
ghostHost = null;
|
|
740
|
-
}, 60);
|
|
741
|
-
}
|
|
499
|
+
},
|
|
742
500
|
};
|
|
501
|
+
// -- projection: cells -> pixels (project.ts) ------------------------------
|
|
502
|
+
const projection = createProjection(ctx, { afterProject: () => syncSlabs() });
|
|
503
|
+
const { writeRect, enforceBoardHeight, project, syncPlaceholder, hidePlaceholder, armGlide, disarmGlideSoon, flushGhost, setGhost } = projection;
|
|
504
|
+
// -- chrome: slabs, frames, captions, handles, cursors (chrome.ts) ---------
|
|
505
|
+
const chrome = createChrome(ctx, {
|
|
506
|
+
selectedId: () => selectedId,
|
|
507
|
+
syncA11y: (only) => syncA11y(only),
|
|
508
|
+
grabbing: () => !!slabGesture,
|
|
509
|
+
gestureRunning: () => !!gesture,
|
|
510
|
+
memberGroupAt: (x, y) => memberGroupAt(x, y),
|
|
511
|
+
slabEdgesNear: (grp, x, y) => slabEdgesNear(grp, x, y),
|
|
512
|
+
dragHandle: () => dragHandle,
|
|
513
|
+
wantHandles,
|
|
514
|
+
});
|
|
515
|
+
const { syncSlabs, syncHandles, setCarried, flushCarried, showRefusal, ensureStaticGuard } = chrome;
|
|
743
516
|
// -- resize handles ---------------------------------------------------------
|
|
744
517
|
/** The accessible name of a widget: its title, else its kind, else its id. */
|
|
745
518
|
const nameOf = (node) => {
|
|
@@ -813,82 +586,6 @@ export function bindDashboardGrid(api, group, options = {}) {
|
|
|
813
586
|
host.classList.toggle('axdb-selected', id === selectedId);
|
|
814
587
|
}
|
|
815
588
|
};
|
|
816
|
-
/**
|
|
817
|
-
* The chrome on every member host: the painted grip (or none) and the corner
|
|
818
|
-
* resize handle — ONE host lookup per member, which the host observer's
|
|
819
|
-
* budget counts (a repaint of one host must cost that host's lookup, not a
|
|
820
|
-
* second pass).
|
|
821
|
-
*/
|
|
822
|
-
const syncHandles = (only) => {
|
|
823
|
-
var _a, _b, _c, _d, _e;
|
|
824
|
-
syncA11y(only);
|
|
825
|
-
if (disposed)
|
|
826
|
-
return;
|
|
827
|
-
ensureStaticGuard();
|
|
828
|
-
syncSlabs();
|
|
829
|
-
const grip = gripOf(dragHandle);
|
|
830
|
-
for (const id of (_a = group.members) !== null && _a !== void 0 ? _a : []) {
|
|
831
|
-
if (only && !only.has(id))
|
|
832
|
-
continue;
|
|
833
|
-
const node = diagram.getNode(id);
|
|
834
|
-
if (!node)
|
|
835
|
-
continue;
|
|
836
|
-
const host = hostOf(id);
|
|
837
|
-
if (!host)
|
|
838
|
-
continue;
|
|
839
|
-
syncGrip(host, grip, ((_b = node.state) === null || _b === void 0 ? void 0 : _b.locked) !== true && !isStatic && ((_c = node.getMetadata) === null || _c === void 0 ? void 0 : _c.call(node, 'widgetMovable')) !== false);
|
|
840
|
-
if (!wantHandles)
|
|
841
|
-
continue;
|
|
842
|
-
const existing = host.querySelector(':scope > .axdb-rs');
|
|
843
|
-
if (((_d = node.state) === null || _d === void 0 ? void 0 : _d.locked) === true || isStatic || ((_e = node.getMetadata) === null || _e === void 0 ? void 0 : _e.call(node, 'widgetResizable')) === false) {
|
|
844
|
-
existing === null || existing === void 0 ? void 0 : existing.remove();
|
|
845
|
-
continue;
|
|
846
|
-
}
|
|
847
|
-
const rs = existing !== null && existing !== void 0 ? existing : document.createElement('div');
|
|
848
|
-
if (!existing) {
|
|
849
|
-
rs.className = 'axdb-rs';
|
|
850
|
-
rs.setAttribute('title', 'Resize');
|
|
851
|
-
host.appendChild(rs);
|
|
852
|
-
}
|
|
853
|
-
// The grab corner mirrors with the board: bottom-right LTR, bottom-left
|
|
854
|
-
// RTL — the same corner the tile actually grows from in each direction.
|
|
855
|
-
rs.classList.toggle('axdb-rs--rtl', rtl);
|
|
856
|
-
}
|
|
857
|
-
};
|
|
858
|
-
/**
|
|
859
|
-
* Only a HOST-LEVEL change matters: a host arriving (a mount) or a host's
|
|
860
|
-
* own children changing (a repaint that wiped the injected handle). A
|
|
861
|
-
* chart's internal churn — most of what a live dashboard mutates — targets
|
|
862
|
-
* deeper nodes and is ignored, and the hosts the records DO name are the
|
|
863
|
-
* only ones re-synced. This was members × repaints `querySelector` calls
|
|
864
|
-
* per wave (9,216 at 96 widgets, review D10); it is now proportional to the
|
|
865
|
-
* hosts that actually changed.
|
|
866
|
-
*/
|
|
867
|
-
const hostObserver = new MutationObserver((records) => {
|
|
868
|
-
const touched = new Set();
|
|
869
|
-
const noteHost = (el) => {
|
|
870
|
-
var _a;
|
|
871
|
-
const e = el;
|
|
872
|
-
if ((_a = e === null || e === void 0 ? void 0 : e.classList) === null || _a === void 0 ? void 0 : _a.contains('grafloria-node-host')) {
|
|
873
|
-
const id = e.getAttribute('data-node-id');
|
|
874
|
-
if (id)
|
|
875
|
-
touched.add(id);
|
|
876
|
-
}
|
|
877
|
-
};
|
|
878
|
-
for (const r of records) {
|
|
879
|
-
// Our own re-injected handle arriving is not a change to answer — it
|
|
880
|
-
// would echo one more pass per repaint.
|
|
881
|
-
const ownEcho = r.removedNodes.length === 0 &&
|
|
882
|
-
r.addedNodes.length > 0 &&
|
|
883
|
-
Array.from(r.addedNodes).every((n) => { var _a; return (_a = n.classList) === null || _a === void 0 ? void 0 : _a.contains('axdb-rs'); });
|
|
884
|
-
if (ownEcho)
|
|
885
|
-
continue;
|
|
886
|
-
noteHost(r.target);
|
|
887
|
-
r.addedNodes.forEach((n) => noteHost(n));
|
|
888
|
-
}
|
|
889
|
-
if (touched.size)
|
|
890
|
-
syncHandles(touched);
|
|
891
|
-
});
|
|
892
589
|
// -- gesture snapshot / commit ---------------------------------------------
|
|
893
590
|
const snapshotAll = () => {
|
|
894
591
|
const cells = new Map();
|
|
@@ -947,23 +644,38 @@ export function bindDashboardGrid(api, group, options = {}) {
|
|
|
947
644
|
*/
|
|
948
645
|
let pendingDrop = null;
|
|
949
646
|
const onMemberAdded = (id) => {
|
|
950
|
-
var _a, _b;
|
|
647
|
+
var _a, _b, _c;
|
|
951
648
|
if (disposed)
|
|
952
649
|
return;
|
|
953
|
-
|
|
954
|
-
|
|
650
|
+
// The cell a waiting tile held for an arriving member of ANOTHER id. The
|
|
651
|
+
// removal settles the board — the tiles the drop pushed float back into
|
|
652
|
+
// the hole — so the member cannot simply be added there: it collided and
|
|
653
|
+
// auto-positioned to the left edge (Quantia's "lands on the cell it was
|
|
654
|
+
// aimed at"). It enters at the bottom edge instead and takes the cell
|
|
655
|
+
// gatelessly, pushing them down again, all inside this one call.
|
|
656
|
+
let claim = null;
|
|
657
|
+
if (pendingDrop && pendingDrop !== id) {
|
|
658
|
+
const ph = engine.getItem(pendingDrop);
|
|
659
|
+
if (ph && !((_a = group.members) !== null && _a !== void 0 ? _a : new Set()).has(pendingDrop)) {
|
|
660
|
+
claim = { x: ph.x, y: ph.y };
|
|
661
|
+
engine.remove(pendingDrop);
|
|
662
|
+
}
|
|
955
663
|
}
|
|
956
664
|
pendingDrop = null;
|
|
957
665
|
if (!engine.getItem(id)) {
|
|
958
666
|
const item = itemFor(id);
|
|
959
|
-
let placed = engine.add(item);
|
|
667
|
+
let placed = claim ? engine.add(Object.assign(Object.assign({}, item), { x: 0, y: engine.rows(), autoPosition: false })) : engine.add(item);
|
|
668
|
+
if (placed && claim) {
|
|
669
|
+
engine.moveCheck(id, claim.x, claim.y, { gate: false, pushSolid: !!placed.solid });
|
|
670
|
+
placed = (_b = engine.getItem(id)) !== null && _b !== void 0 ? _b : placed;
|
|
671
|
+
}
|
|
960
672
|
if (!placed && capacity !== undefined) {
|
|
961
673
|
// Membership is a document fact (an undo just restored it, say); a
|
|
962
674
|
// bounded board must not strand the node invisible. Lift the capacity
|
|
963
675
|
// for this adoption — it floors at the content on the next refresh.
|
|
964
676
|
capacity = undefined;
|
|
965
677
|
engine = engineFrom([...engine.getItems().map((i) => (Object.assign({}, i))), item]);
|
|
966
|
-
placed = (
|
|
678
|
+
placed = (_c = engine.getItem(id)) !== null && _c !== void 0 ? _c : null;
|
|
967
679
|
refreshCapacity();
|
|
968
680
|
}
|
|
969
681
|
if (placed)
|
|
@@ -1150,54 +862,6 @@ export function bindDashboardGrid(api, group, options = {}) {
|
|
|
1150
862
|
const s = sizeOf(grp);
|
|
1151
863
|
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 };
|
|
1152
864
|
};
|
|
1153
|
-
/**
|
|
1154
|
-
* SECTION CHROME. A section (member group) paints no card of its own, so it
|
|
1155
|
-
* had nothing to press: a click on its empty band cleared the selection and
|
|
1156
|
-
* its frame had no handle and no edge — a section with many children could
|
|
1157
|
-
* not be selected at all, and could only be resized by pulling a child
|
|
1158
|
-
* (Quantia, Groups page). Every section gets a pointer-transparent overlay
|
|
1159
|
-
* in the HTML layer that wears the selection ring and, while selected, the
|
|
1160
|
-
* corner handle; its frame edges answer the resize cursor.
|
|
1161
|
-
*/
|
|
1162
|
-
const slabEls = new Map();
|
|
1163
|
-
/**
|
|
1164
|
-
* GROUP FRAME (0.4.43): a TAB CONTAINER wears a frame by default — its slab
|
|
1165
|
-
* is bordered and a tinted surface lies under its pages, first in the layer
|
|
1166
|
-
* so the tiles paint over it. A page torn out with two widgets under its tab
|
|
1167
|
-
* read as a strip floating over two loose cards: nothing said the second
|
|
1168
|
-
* card was the tab's. A plain section keeps its invisible slab.
|
|
1169
|
-
*/
|
|
1170
|
-
const groupBgs = new Map();
|
|
1171
|
-
const syncGroupBg = (layer, id, on, x, y, w, h) => {
|
|
1172
|
-
var _a;
|
|
1173
|
-
let bg = (_a = groupBgs.get(id)) !== null && _a !== void 0 ? _a : null;
|
|
1174
|
-
if (!on) {
|
|
1175
|
-
bg === null || bg === void 0 ? void 0 : bg.remove();
|
|
1176
|
-
groupBgs.delete(id);
|
|
1177
|
-
return;
|
|
1178
|
-
}
|
|
1179
|
-
if (!bg || bg.parentElement !== layer) {
|
|
1180
|
-
bg === null || bg === void 0 ? void 0 : bg.remove();
|
|
1181
|
-
bg = document.createElement('div');
|
|
1182
|
-
bg.className = 'axdb-group-bg';
|
|
1183
|
-
bg.setAttribute('data-group-bg', id);
|
|
1184
|
-
layer.prepend(bg);
|
|
1185
|
-
groupBgs.set(id, bg);
|
|
1186
|
-
}
|
|
1187
|
-
bg.style.left = `${x}px`;
|
|
1188
|
-
bg.style.top = `${y}px`;
|
|
1189
|
-
bg.style.width = `${w}px`;
|
|
1190
|
-
bg.style.height = `${h}px`;
|
|
1191
|
-
};
|
|
1192
|
-
const isTabsGroup = (grp) => { var _a; return ((_a = grp.getMetadata('containerWidget')) === null || _a === void 0 ? void 0 : _a.layout) === 'tabs'; };
|
|
1193
|
-
/**
|
|
1194
|
-
* A TAB CONTAINER's frame is its drag handle (0.4.43): its 8-px margin —
|
|
1195
|
-
* under the strip, beside the pages — moves the group, so the edge-resize
|
|
1196
|
-
* zone shrinks to 3 px there (the corner handle still resizes). A section's
|
|
1197
|
-
* edges keep the full grip: its empty band is a drop target, not a handle.
|
|
1198
|
-
*/
|
|
1199
|
-
const TAB_FRAME_GRIP = 3;
|
|
1200
|
-
const edgeGripFor = (grp) => (isTabsGroup(grp) ? TAB_FRAME_GRIP : EDGE_GRIP);
|
|
1201
865
|
/**
|
|
1202
866
|
* BESIDE (0.4.45): a WIDGET dragged onto a tab container's outer band — a
|
|
1203
867
|
* fifth of its body, the same bands a tab's split uses — lands next to it
|
|
@@ -1290,180 +954,6 @@ export function bindDashboardGrid(api, group, options = {}) {
|
|
|
1290
954
|
}
|
|
1291
955
|
besideOn(g.id, g.spans, z, row);
|
|
1292
956
|
};
|
|
1293
|
-
/**
|
|
1294
|
-
* CARRIED (0.4.43): a group dragged by its strip or band moves as ONE thing.
|
|
1295
|
-
* The held TILE is transition-exempt (the ghost), but a group has no host of
|
|
1296
|
-
* its own: its strip jumped to the pointer while its pages' tiles GLIDED
|
|
1297
|
-
* after it — on the live demo the content trailed the strip by up to 140 px
|
|
1298
|
-
* at every step. Everything in the group's subtree — tiles, strips, slabs,
|
|
1299
|
-
* the surface — is exempt for the gesture and, like the ghost, through the
|
|
1300
|
-
* drop write; then the glides resume.
|
|
1301
|
-
*/
|
|
1302
|
-
const carriedEls = new Set();
|
|
1303
|
-
let carriedTimer = null;
|
|
1304
|
-
const subtreeIds = (id) => {
|
|
1305
|
-
var _a;
|
|
1306
|
-
const groups = [];
|
|
1307
|
-
const nodes = [];
|
|
1308
|
-
const queue = [id];
|
|
1309
|
-
const seen = new Set();
|
|
1310
|
-
while (queue.length) {
|
|
1311
|
-
const cur = queue.shift();
|
|
1312
|
-
if (seen.has(cur))
|
|
1313
|
-
continue;
|
|
1314
|
-
seen.add(cur);
|
|
1315
|
-
const grp = diagram.getGroup(cur);
|
|
1316
|
-
if (grp) {
|
|
1317
|
-
groups.push(cur);
|
|
1318
|
-
for (const m of (_a = grp.members) !== null && _a !== void 0 ? _a : [])
|
|
1319
|
-
queue.push(m);
|
|
1320
|
-
}
|
|
1321
|
-
else if (diagram.getNode(cur))
|
|
1322
|
-
nodes.push(cur);
|
|
1323
|
-
}
|
|
1324
|
-
return { groups, nodes };
|
|
1325
|
-
};
|
|
1326
|
-
const cssId = (id) => (typeof CSS !== 'undefined' && CSS.escape ? CSS.escape(id) : id.replace(/"/g, '\\"'));
|
|
1327
|
-
const setCarried = (id, on) => {
|
|
1328
|
-
const layer = htmlLayer();
|
|
1329
|
-
if (!layer)
|
|
1330
|
-
return;
|
|
1331
|
-
if (carriedTimer) {
|
|
1332
|
-
clearTimeout(carriedTimer);
|
|
1333
|
-
carriedTimer = null;
|
|
1334
|
-
}
|
|
1335
|
-
if (on) {
|
|
1336
|
-
const { groups, nodes } = subtreeIds(id);
|
|
1337
|
-
const els = [];
|
|
1338
|
-
for (const n of nodes) {
|
|
1339
|
-
const h = hostOf(n);
|
|
1340
|
-
if (h)
|
|
1341
|
-
els.push(h);
|
|
1342
|
-
}
|
|
1343
|
-
for (const g of groups) {
|
|
1344
|
-
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)}"]`)));
|
|
1345
|
-
}
|
|
1346
|
-
for (const el of els) {
|
|
1347
|
-
el.classList.add('axdb-carried');
|
|
1348
|
-
carriedEls.add(el);
|
|
1349
|
-
}
|
|
1350
|
-
return;
|
|
1351
|
-
}
|
|
1352
|
-
carriedTimer = setTimeout(() => {
|
|
1353
|
-
for (const el of carriedEls)
|
|
1354
|
-
el.classList.remove('axdb-carried');
|
|
1355
|
-
carriedEls.clear();
|
|
1356
|
-
carriedTimer = null;
|
|
1357
|
-
}, 60);
|
|
1358
|
-
};
|
|
1359
|
-
const flushCarried = () => {
|
|
1360
|
-
if (carriedTimer)
|
|
1361
|
-
clearTimeout(carriedTimer);
|
|
1362
|
-
carriedTimer = null;
|
|
1363
|
-
for (const el of carriedEls)
|
|
1364
|
-
el.classList.remove('axdb-carried');
|
|
1365
|
-
carriedEls.clear();
|
|
1366
|
-
};
|
|
1367
|
-
let slabLayer = null;
|
|
1368
|
-
const syncSlabs = () => {
|
|
1369
|
-
var _a, _b, _c;
|
|
1370
|
-
if (disposed)
|
|
1371
|
-
return;
|
|
1372
|
-
const layer = (slabLayer === null || slabLayer === void 0 ? void 0 : slabLayer.isConnected) ? slabLayer : (slabLayer = htmlLayer());
|
|
1373
|
-
if (!layer)
|
|
1374
|
-
return;
|
|
1375
|
-
const seen = new Set();
|
|
1376
|
-
for (const id of (_a = group.members) !== null && _a !== void 0 ? _a : []) {
|
|
1377
|
-
const grp = diagram.getGroup(id);
|
|
1378
|
-
if (!grp || diagram.getNode(id))
|
|
1379
|
-
continue;
|
|
1380
|
-
seen.add(id);
|
|
1381
|
-
let el = slabEls.get(id);
|
|
1382
|
-
if (!el || el.parentElement !== layer) {
|
|
1383
|
-
el === null || el === void 0 ? void 0 : el.remove();
|
|
1384
|
-
el = document.createElement('div');
|
|
1385
|
-
el.className = 'axdb-slab';
|
|
1386
|
-
el.setAttribute('data-slab-id', id);
|
|
1387
|
-
const rs = document.createElement('div');
|
|
1388
|
-
rs.className = 'axdb-rs';
|
|
1389
|
-
rs.setAttribute('title', 'Resize section');
|
|
1390
|
-
el.appendChild(rs);
|
|
1391
|
-
layer.appendChild(el);
|
|
1392
|
-
slabEls.set(id, el);
|
|
1393
|
-
}
|
|
1394
|
-
const p = grp.position;
|
|
1395
|
-
const sz = sizeOf(grp);
|
|
1396
|
-
el.style.left = `${p.x}px`;
|
|
1397
|
-
el.style.top = `${p.y}px`;
|
|
1398
|
-
el.style.width = `${sz.width}px`;
|
|
1399
|
-
el.style.height = `${sz.height}px`;
|
|
1400
|
-
el.classList.toggle('axdb-slab--selected', selectedId === id);
|
|
1401
|
-
el.classList.toggle('axdb-slab--static', isStatic);
|
|
1402
|
-
(_b = el.querySelector(':scope > .axdb-rs')) === null || _b === void 0 ? void 0 : _b.classList.toggle('axdb-rs--rtl', rtl);
|
|
1403
|
-
const tabs = isTabsGroup(grp);
|
|
1404
|
-
el.classList.toggle('axdb-slab--tabs', tabs);
|
|
1405
|
-
syncGroupBg(layer, id, tabs, p.x, p.y, sz.width, sz.height);
|
|
1406
|
-
syncCaption(el, id, grp, sz.height);
|
|
1407
|
-
}
|
|
1408
|
-
for (const [id, el] of slabEls) {
|
|
1409
|
-
if (!seen.has(id)) {
|
|
1410
|
-
el.remove();
|
|
1411
|
-
hoverSlabs.delete(el);
|
|
1412
|
-
slabEls.delete(id);
|
|
1413
|
-
(_c = groupBgs.get(id)) === null || _c === void 0 ? void 0 : _c.remove();
|
|
1414
|
-
groupBgs.delete(id);
|
|
1415
|
-
}
|
|
1416
|
-
}
|
|
1417
|
-
};
|
|
1418
|
-
/**
|
|
1419
|
-
* THE CAPTION BAND of a section, on its slab overlay. Painted from the
|
|
1420
|
-
* group's persisted caption; repainted only when its identity changes (the
|
|
1421
|
-
* options, RTL, static, the tier) so a custom `renderCaption` is not run
|
|
1422
|
-
* per frame. The band takes the pointer (the slab itself does not): a press
|
|
1423
|
-
* on it selects the section, an action fires, pass-through reaches content.
|
|
1424
|
-
*/
|
|
1425
|
-
const syncCaption = (el, id, grp, sectionH) => {
|
|
1426
|
-
const cap = captionOfGroup(grp);
|
|
1427
|
-
let band = el.querySelector(':scope > .axdb-slab-h');
|
|
1428
|
-
if (!cap || !captionPainted(cap, isStatic)) {
|
|
1429
|
-
band === null || band === void 0 ? void 0 : band.remove();
|
|
1430
|
-
hoverSlabs.delete(el);
|
|
1431
|
-
el.classList.remove('axdb-slab--hot');
|
|
1432
|
-
el.removeAttribute('aria-label');
|
|
1433
|
-
el.removeAttribute('role');
|
|
1434
|
-
return;
|
|
1435
|
-
}
|
|
1436
|
-
if (cap.show === 'hover')
|
|
1437
|
-
hoverSlabs.add(el);
|
|
1438
|
-
else {
|
|
1439
|
-
hoverSlabs.delete(el);
|
|
1440
|
-
el.classList.remove('axdb-slab--hot');
|
|
1441
|
-
}
|
|
1442
|
-
const ctx = { rtl, static: isStatic, sectionH };
|
|
1443
|
-
const key = captionKey(cap, ctx);
|
|
1444
|
-
if (band && band.getAttribute('data-key') === key) {
|
|
1445
|
-
sizeCaptionBand(band, cap, sectionH); // the tier follows the live size
|
|
1446
|
-
return;
|
|
1447
|
-
}
|
|
1448
|
-
band === null || band === void 0 ? void 0 : band.remove();
|
|
1449
|
-
band = document.createElement('div');
|
|
1450
|
-
el.prepend(band);
|
|
1451
|
-
const render = options.renderCaption;
|
|
1452
|
-
paintCaptionBand(band, cap, Object.assign(Object.assign(Object.assign({}, ctx), (render ? { render: (host) => render(id, host) } : {})), { onAction: (actionId) => { var _a; return (_a = options.onCaptionAction) === null || _a === void 0 ? void 0 : _a.call(options, id, actionId); } }));
|
|
1453
|
-
band.setAttribute('data-key', key);
|
|
1454
|
-
// A named group, so the caption text is the section's name in the
|
|
1455
|
-
// accessibility tree rather than a stray label on an unnamed div. (The
|
|
1456
|
-
// band is not a tab stop yet — the actions are deliberately out of the
|
|
1457
|
-
// tab order so a board keeps exactly ONE stop; see the plan.)
|
|
1458
|
-
if (cap.text) {
|
|
1459
|
-
el.setAttribute('role', 'group');
|
|
1460
|
-
el.setAttribute('aria-label', cap.text);
|
|
1461
|
-
}
|
|
1462
|
-
else {
|
|
1463
|
-
el.removeAttribute('role');
|
|
1464
|
-
el.removeAttribute('aria-label');
|
|
1465
|
-
}
|
|
1466
|
-
};
|
|
1467
957
|
const insideMemberGroupFrame = (x, y) => {
|
|
1468
958
|
var _a;
|
|
1469
959
|
for (const id of (_a = group.members) !== null && _a !== void 0 ? _a : []) {
|
|
@@ -1545,29 +1035,6 @@ export function bindDashboardGrid(api, group, options = {}) {
|
|
|
1545
1035
|
capturePointer(slabGesture.pointerId);
|
|
1546
1036
|
api.container.style.cursor = 'grabbing';
|
|
1547
1037
|
};
|
|
1548
|
-
/** The cell a slab move asked for and could not have — painted so the refusal is visible; null clears it. */
|
|
1549
|
-
let refusal = null;
|
|
1550
|
-
const showRefusal = (cell, w, h) => {
|
|
1551
|
-
const layer = htmlLayer();
|
|
1552
|
-
if (!cell || !layer) {
|
|
1553
|
-
refusal === null || refusal === void 0 ? void 0 : refusal.remove();
|
|
1554
|
-
refusal = null;
|
|
1555
|
-
api.container.style.cursor = slabGesture ? 'grabbing' : '';
|
|
1556
|
-
return;
|
|
1557
|
-
}
|
|
1558
|
-
if (!refusal || refusal.parentElement !== layer) {
|
|
1559
|
-
refusal === null || refusal === void 0 ? void 0 : refusal.remove();
|
|
1560
|
-
refusal = document.createElement('div');
|
|
1561
|
-
refusal.className = 'axdb-ph axdb-ph--no';
|
|
1562
|
-
layer.prepend(refusal);
|
|
1563
|
-
}
|
|
1564
|
-
const r = cellToRect({ x: cell.x, y: cell.y, w, h }, frame(), geom(), rows());
|
|
1565
|
-
refusal.style.left = `${r.x}px`;
|
|
1566
|
-
refusal.style.top = `${r.y}px`;
|
|
1567
|
-
refusal.style.width = `${r.width}px`;
|
|
1568
|
-
refusal.style.height = `${r.height}px`;
|
|
1569
|
-
api.container.style.cursor = 'not-allowed';
|
|
1570
|
-
};
|
|
1571
1038
|
const slabMove = (ev) => {
|
|
1572
1039
|
const g = slabGesture;
|
|
1573
1040
|
if (!g)
|
|
@@ -1769,8 +1236,7 @@ export function bindDashboardGrid(api, group, options = {}) {
|
|
|
1769
1236
|
releasePointer(g.pointerId);
|
|
1770
1237
|
api.container.style.cursor = '';
|
|
1771
1238
|
(_a = g.chip) === null || _a === void 0 ? void 0 : _a.remove();
|
|
1772
|
-
|
|
1773
|
-
placeholder = null;
|
|
1239
|
+
hidePlaceholder();
|
|
1774
1240
|
};
|
|
1775
1241
|
const commitGesture = (g) => {
|
|
1776
1242
|
var _a;
|
|
@@ -3375,253 +2841,24 @@ export function bindDashboardGrid(api, group, options = {}) {
|
|
|
3375
2841
|
onToolUp();
|
|
3376
2842
|
};
|
|
3377
2843
|
const unregisterTool = registerTool(tool);
|
|
3378
|
-
|
|
3379
|
-
|
|
3380
|
-
|
|
3381
|
-
|
|
3382
|
-
|
|
3383
|
-
|
|
3384
|
-
|
|
3385
|
-
|
|
3386
|
-
|
|
3387
|
-
|
|
3388
|
-
|
|
3389
|
-
|
|
3390
|
-
|
|
3391
|
-
|
|
3392
|
-
|
|
3393
|
-
|
|
3394
|
-
|
|
3395
|
-
|
|
3396
|
-
return;
|
|
3397
|
-
for (const el of hoverSlabs) {
|
|
3398
|
-
const r = el.getBoundingClientRect();
|
|
3399
|
-
el.classList.toggle('axdb-slab--hot', clientX >= r.left && clientX <= r.right && clientY >= r.top && clientY <= r.bottom);
|
|
3400
|
-
}
|
|
3401
|
-
};
|
|
3402
|
-
const onHoverLeave = () => {
|
|
3403
|
-
for (const el of hoverSlabs)
|
|
3404
|
-
el.classList.remove('axdb-slab--hot');
|
|
3405
|
-
};
|
|
3406
|
-
const onHover = (e) => {
|
|
3407
|
-
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
3408
|
-
if (disposed || gesture)
|
|
3409
|
-
return;
|
|
3410
|
-
markHotSection(e.clientX, e.clientY);
|
|
3411
|
-
// The event may target the host, its content, or (when a host's content
|
|
3412
|
-
// is pointer-transparent) the canvas under it — find the member host by
|
|
3413
|
-
// the pointer's position in that case.
|
|
3414
|
-
let host = (_b = (_a = e.target) === null || _a === void 0 ? void 0 : _a.closest) === null || _b === void 0 ? void 0 : _b.call(_a, '.grafloria-node-host');
|
|
3415
|
-
if (!host) {
|
|
3416
|
-
for (const id of (_c = group.members) !== null && _c !== void 0 ? _c : []) {
|
|
3417
|
-
const h = hostOf(id);
|
|
3418
|
-
if (!h)
|
|
3419
|
-
continue;
|
|
3420
|
-
const r = h.getBoundingClientRect();
|
|
3421
|
-
if (e.clientX >= r.left && e.clientX <= r.right && e.clientY >= r.top && e.clientY <= r.bottom) {
|
|
3422
|
-
host = h;
|
|
3423
|
-
break;
|
|
3424
|
-
}
|
|
3425
|
-
}
|
|
3426
|
-
}
|
|
3427
|
-
if (hoverHost && hoverHost !== host) {
|
|
3428
|
-
hoverHost.style.cursor = '';
|
|
3429
|
-
hoverHost.removeAttribute('data-axdb-edge'); // the affordance follows the pointer off a tile
|
|
3430
|
-
}
|
|
3431
|
-
hoverHost = host;
|
|
3432
|
-
if (!host) {
|
|
3433
|
-
// No tile under the pointer: a SECTION's frame edge still says resize.
|
|
3434
|
-
const wpt = ((_d = api.viewport) === null || _d === void 0 ? void 0 : _d.clientToWorld) ? api.viewport.clientToWorld(e.clientX, e.clientY, api.container.getBoundingClientRect()) : null;
|
|
3435
|
-
const sid = wpt ? memberGroupAt(wpt.x, wpt.y) : null;
|
|
3436
|
-
const grp = sid ? diagram.getGroup(sid) : undefined;
|
|
3437
|
-
const c = grp && wpt && !isStatic ? cursorFor(slabEdgesNear(grp, wpt.x, wpt.y)) : '';
|
|
3438
|
-
if (!slabGesture)
|
|
3439
|
-
api.container.style.cursor = c;
|
|
3440
|
-
return;
|
|
3441
|
-
}
|
|
3442
|
-
if (!slabGesture && api.container.style.cursor)
|
|
3443
|
-
api.container.style.cursor = '';
|
|
3444
|
-
const id = (_e = host.getAttribute('data-node-id')) !== null && _e !== void 0 ? _e : '';
|
|
3445
|
-
if (!((_f = group.members) !== null && _f !== void 0 ? _f : new Set()).has(id))
|
|
3446
|
-
return;
|
|
3447
|
-
const node = diagram.getNode(id);
|
|
3448
|
-
const resizable = !!node && !isStatic && ((_g = node.state) === null || _g === void 0 ? void 0 : _g.locked) !== true && ((_h = node.getMetadata) === null || _h === void 0 ? void 0 : _h.call(node, 'widgetResizable')) !== false;
|
|
3449
|
-
const cursor = resizable ? cursorFor(edgesNear(host, e.clientX, e.clientY)) : '';
|
|
3450
|
-
// The affordance rides on an ATTRIBUTE, not the inline style: a repaint
|
|
3451
|
-
// rewrites the host's style and used to clear the cursor mid-hover, and
|
|
3452
|
-
// content that sets its own cursor (a chart canvas) hid it — the
|
|
3453
|
-
// stylesheet applies the attribute's cursor to the host and everything in it.
|
|
3454
|
-
if (cursor)
|
|
3455
|
-
host.setAttribute('data-axdb-edge', cursor);
|
|
3456
|
-
else
|
|
3457
|
-
host.removeAttribute('data-axdb-edge');
|
|
3458
|
-
};
|
|
3459
|
-
api.container.addEventListener('pointermove', onHover, { passive: true });
|
|
3460
|
-
api.container.addEventListener('pointerleave', onHoverLeave, { passive: true });
|
|
3461
|
-
/**
|
|
3462
|
-
* STATIC BOARDS LET CONTENT BE CLICKED. The renderer prevents the default of
|
|
3463
|
-
* every press a tool claims, which cancels the compatibility mouse events —
|
|
3464
|
-
* a chart inside a read-only board could not be clicked. So under `static`
|
|
3465
|
-
* a press inside a member's CONTENT (not on kit chrome) is stopped on the
|
|
3466
|
-
* HTML layer, in the bubble phase: the content has already received it, the
|
|
3467
|
-
* renderer never does, nothing is prevented.
|
|
3468
|
-
*/
|
|
3469
|
-
const staticGuard = (e) => {
|
|
3470
|
-
var _a, _b, _c, _d;
|
|
3471
|
-
if (!isStatic || disposed)
|
|
3472
|
-
return;
|
|
3473
|
-
const t = e.target;
|
|
3474
|
-
const host = (_a = t === null || t === void 0 ? void 0 : t.closest) === null || _a === void 0 ? void 0 : _a.call(t, '.grafloria-node-host');
|
|
3475
|
-
if (!host || !((_b = group.members) !== null && _b !== void 0 ? _b : new Set()).has((_c = host.getAttribute('data-node-id')) !== null && _c !== void 0 ? _c : ''))
|
|
3476
|
-
return;
|
|
3477
|
-
if ((_d = t === null || t === void 0 ? void 0 : t.closest) === null || _d === void 0 ? void 0 : _d.call(t, '.axdb-rs, .axdb-grip, .axdb-div'))
|
|
3478
|
-
return;
|
|
3479
|
-
e.stopPropagation();
|
|
3480
|
-
};
|
|
3481
|
-
let guardedLayer = null;
|
|
3482
|
-
const ensureStaticGuard = () => {
|
|
3483
|
-
// One lookup, ever: the host observer budgets container lookups per
|
|
3484
|
-
// repaint, and the layer element lives as long as the instance.
|
|
3485
|
-
if (guardedLayer === null || guardedLayer === void 0 ? void 0 : guardedLayer.isConnected)
|
|
3486
|
-
return;
|
|
3487
|
-
const layer = htmlLayer();
|
|
3488
|
-
if (!layer)
|
|
3489
|
-
return;
|
|
3490
|
-
guardedLayer === null || guardedLayer === void 0 ? void 0 : guardedLayer.removeEventListener('pointerdown', staticGuard);
|
|
3491
|
-
guardedLayer = layer;
|
|
3492
|
-
layer.addEventListener('pointerdown', staticGuard);
|
|
3493
|
-
};
|
|
3494
|
-
/**
|
|
3495
|
-
* KEYBOARD OPERATION (WCAG 2.1.1, and the non-drag alternative 2.5.7 asks
|
|
3496
|
-
* for): on a focused member, arrows move it one cell, Shift+arrows resize it
|
|
3497
|
-
* one cell, Home/End jump to the first/last member. Every move and resize is
|
|
3498
|
-
* the same programmatic gesture the API uses — one undoable step, reported
|
|
3499
|
-
* through onLayoutChange — and every outcome is spoken: the tile's new
|
|
3500
|
-
* cell, each neighbour it displaced, or why it was refused. Handled keys
|
|
3501
|
-
* stop here so the renderer's own pixel nudge never fights the grid.
|
|
3502
|
-
*/
|
|
3503
|
-
const memberHostAt = (target) => {
|
|
3504
|
-
var _a, _b, _c;
|
|
3505
|
-
const host = (_a = target === null || target === void 0 ? void 0 : target.closest) === null || _a === void 0 ? void 0 : _a.call(target, '.grafloria-node-host');
|
|
3506
|
-
if (!host)
|
|
3507
|
-
return null;
|
|
3508
|
-
const id = (_b = host.getAttribute('data-node-id')) !== null && _b !== void 0 ? _b : '';
|
|
3509
|
-
if (!((_c = group.members) !== null && _c !== void 0 ? _c : new Set()).has(id) || !diagram.getNode(id))
|
|
3510
|
-
return null;
|
|
3511
|
-
return { id, host };
|
|
3512
|
-
};
|
|
3513
|
-
const onFocusIn = (e) => {
|
|
3514
|
-
const hit = memberHostAt(e.target);
|
|
3515
|
-
if (!hit || disposed)
|
|
3516
|
-
return;
|
|
3517
|
-
if (focusedId !== hit.id || selectedId !== hit.id) {
|
|
3518
|
-
focusedId = hit.id;
|
|
3519
|
-
selectWidget(hit.id);
|
|
3520
|
-
syncA11y();
|
|
3521
|
-
}
|
|
3522
|
-
};
|
|
3523
|
-
const onKey = (e) => {
|
|
3524
|
-
var _a, _b, _c, _d, _e, _f, _g;
|
|
3525
|
-
if (disposed || gesture)
|
|
3526
|
-
return;
|
|
3527
|
-
const hit = memberHostAt(e.target);
|
|
3528
|
-
if (!hit) {
|
|
3529
|
-
// Tab reaches the diagram's own root (the svg) before any widget. An
|
|
3530
|
-
// arrow or Enter there hands focus to the board's tab stop, so a
|
|
3531
|
-
// keyboard user is never parked on "Diagram, 8 nodes" with nowhere to go.
|
|
3532
|
-
const el = e.target;
|
|
3533
|
-
const onRoot = !!el && ((_a = el.tagName) === null || _a === void 0 ? void 0 : _a.toLowerCase()) === 'svg' && ((_b = el.classList) === null || _b === void 0 ? void 0 : _b.contains('grafloria-diagram'));
|
|
3534
|
-
if (onRoot && (e.key.startsWith('Arrow') || e.key === 'Enter' || e.key === ' ')) {
|
|
3535
|
-
const members = [...((_c = group.members) !== null && _c !== void 0 ? _c : [])].filter((id) => !!diagram.getNode(id) && !!hostOf(id));
|
|
3536
|
-
const target = focusedId && members.includes(focusedId) ? focusedId : members[0];
|
|
3537
|
-
if (target && handle.focusWidget(target)) {
|
|
3538
|
-
e.preventDefault();
|
|
3539
|
-
e.stopPropagation();
|
|
3540
|
-
}
|
|
3541
|
-
}
|
|
3542
|
-
return;
|
|
3543
|
-
}
|
|
3544
|
-
const members = [...((_d = group.members) !== null && _d !== void 0 ? _d : [])].filter((id) => !!diagram.getNode(id) && !!hostOf(id));
|
|
3545
|
-
if (e.key === 'Home' || e.key === 'End') {
|
|
3546
|
-
const id = e.key === 'Home' ? members[0] : members[members.length - 1];
|
|
3547
|
-
if (id)
|
|
3548
|
-
handle.focusWidget(id);
|
|
3549
|
-
e.preventDefault();
|
|
3550
|
-
e.stopPropagation();
|
|
3551
|
-
return;
|
|
3552
|
-
}
|
|
3553
|
-
const arrow = { ArrowLeft: [-1, 0], ArrowRight: [1, 0], ArrowUp: [0, -1], ArrowDown: [0, 1] }[e.key];
|
|
3554
|
-
if (!arrow)
|
|
3555
|
-
return;
|
|
3556
|
-
e.preventDefault();
|
|
3557
|
-
e.stopPropagation();
|
|
3558
|
-
if (isStatic)
|
|
3559
|
-
return; // readable, not editable
|
|
3560
|
-
const node = diagram.getNode(hit.id);
|
|
3561
|
-
const item = engine.getItem(hit.id);
|
|
3562
|
-
if (!node || !item)
|
|
3563
|
-
return;
|
|
3564
|
-
const name = nameOf(node);
|
|
3565
|
-
if (((_e = node.state) === null || _e === void 0 ? void 0 : _e.locked) === true) {
|
|
3566
|
-
live.announceError(`${name} is pinned`);
|
|
3567
|
-
return;
|
|
3568
|
-
}
|
|
3569
|
-
const [dx, dy] = arrow;
|
|
3570
|
-
const before = new Map(engine.getItems().map((i) => [i.id, { x: i.x, y: i.y, w: i.w, h: i.h }]));
|
|
3571
|
-
const resize = e.shiftKey;
|
|
3572
|
-
if (resize && ((_f = node.getMetadata) === null || _f === void 0 ? void 0 : _f.call(node, 'widgetResizable')) === false) {
|
|
3573
|
-
live.announceError(`${name} cannot be resized`);
|
|
3574
|
-
return;
|
|
3575
|
-
}
|
|
3576
|
-
if (!resize && ((_g = node.getMetadata) === null || _g === void 0 ? void 0 : _g.call(node, 'widgetMovable')) === false) {
|
|
3577
|
-
live.announceError(`${name} cannot be moved`);
|
|
3578
|
-
return;
|
|
3579
|
-
}
|
|
3580
|
-
// A one-cell keyboard step onto a neighbour would be refused by the
|
|
3581
|
-
// pointer's anti-jitter gate (a 3-wide covering a third of its neighbour
|
|
3582
|
-
// is not "more than half"). A key press is deliberate, so when the step
|
|
3583
|
-
// is refused and a neighbour sits there, aim at the neighbour's far edge —
|
|
3584
|
-
// the same swap a full drag lands on.
|
|
3585
|
-
const stepOrSwap = () => __awaiter(this, void 0, void 0, function* () {
|
|
3586
|
-
if (yield handle.moveTo(hit.id, item.x + dx, item.y + dy))
|
|
3587
|
-
return true;
|
|
3588
|
-
const probe = { x: item.x + dx, y: item.y + dy, w: item.w, h: item.h };
|
|
3589
|
-
const c = engine
|
|
3590
|
-
.getItems()
|
|
3591
|
-
.find((o) => o.id !== hit.id && probe.x < o.x + o.w && o.x < probe.x + probe.w && probe.y < o.y + o.h && o.y < probe.y + probe.h);
|
|
3592
|
-
if (!c)
|
|
3593
|
-
return false;
|
|
3594
|
-
const tx = dx > 0 ? c.x + c.w - item.w : dx < 0 ? c.x : item.x;
|
|
3595
|
-
const ty = dy > 0 ? c.y + c.h - item.h : dy < 0 ? c.y : item.y;
|
|
3596
|
-
return handle.moveTo(hit.id, tx, ty);
|
|
3597
|
-
});
|
|
3598
|
-
const op = resize ? handle.resizeTo(hit.id, item.w + dx, item.h + dy) : stepOrSwap();
|
|
3599
|
-
void op.then((ok) => {
|
|
3600
|
-
var _a, _b;
|
|
3601
|
-
if (disposed)
|
|
3602
|
-
return;
|
|
3603
|
-
const after = engine.getItem(hit.id);
|
|
3604
|
-
if (!ok || !after) {
|
|
3605
|
-
live.announceError(resize ? `Cannot resize ${name} that way` : `Cannot move ${name} ${directionName(dx, dy)}`);
|
|
3606
|
-
return;
|
|
3607
|
-
}
|
|
3608
|
-
const parts = [`${name} ${resize ? 'resized' : 'moved'} to ${describeCell(after)}`];
|
|
3609
|
-
for (const other of engine.getItems()) {
|
|
3610
|
-
if (other.id === hit.id)
|
|
3611
|
-
continue;
|
|
3612
|
-
const was = before.get(other.id);
|
|
3613
|
-
if (!was || (was.x === other.x && was.y === other.y))
|
|
3614
|
-
continue;
|
|
3615
|
-
const o = diagram.getNode(other.id);
|
|
3616
|
-
parts.push(`${o ? nameOf(o) : other.id} moved to ${describeCell(other)}`);
|
|
3617
|
-
}
|
|
3618
|
-
live.announce(parts.join('. '), 'polite', true);
|
|
3619
|
-
syncA11y();
|
|
3620
|
-
(_b = (_a = hostOf(hit.id)) === null || _a === void 0 ? void 0 : _a.focus) === null || _b === void 0 ? void 0 : _b.call(_a, { preventScroll: true });
|
|
3621
|
-
});
|
|
3622
|
-
};
|
|
3623
|
-
api.container.addEventListener('focusin', onFocusIn);
|
|
3624
|
-
api.container.addEventListener('keydown', onKey);
|
|
2844
|
+
api.container.addEventListener('pointermove', chrome.onHover, { passive: true });
|
|
2845
|
+
api.container.addEventListener('pointerleave', chrome.onHoverLeave, { passive: true });
|
|
2846
|
+
// -- keyboard operation (keyboard.ts) --------------------------------------
|
|
2847
|
+
const keyboard = createKeyboard(ctx, {
|
|
2848
|
+
handle: () => handle,
|
|
2849
|
+
live,
|
|
2850
|
+
nameOf,
|
|
2851
|
+
focusedId: () => focusedId,
|
|
2852
|
+
setFocusedId: (id) => {
|
|
2853
|
+
focusedId = id;
|
|
2854
|
+
},
|
|
2855
|
+
selectedId: () => selectedId,
|
|
2856
|
+
selectWidget,
|
|
2857
|
+
syncA11y: () => syncA11y(),
|
|
2858
|
+
gestureRunning: () => !!gesture,
|
|
2859
|
+
});
|
|
2860
|
+
api.container.addEventListener('focusin', keyboard.onFocusIn);
|
|
2861
|
+
api.container.addEventListener('keydown', keyboard.onKey);
|
|
3625
2862
|
// -- tab tear-out -----------------------------------------------------------
|
|
3626
2863
|
/**
|
|
3627
2864
|
* A press that TRAVELLED on a tab drags the whole page onto this board, the
|
|
@@ -4024,8 +3261,7 @@ export function bindDashboardGrid(api, group, options = {}) {
|
|
|
4024
3261
|
engine.moveCheck(z.target.id, z.keep.x, z.keep.y, { gate: false });
|
|
4025
3262
|
const ok = l.place(z.born);
|
|
4026
3263
|
project();
|
|
4027
|
-
|
|
4028
|
-
placeholder = null;
|
|
3264
|
+
hidePlaceholder(); // the accent overlay says which half; the grey placeholder under it is noise
|
|
4029
3265
|
return ok;
|
|
4030
3266
|
};
|
|
4031
3267
|
/** The band a dock is promised: the cell it takes — a BOTTOM dock's lies past the last row, so its tint sits on the board's last rows instead. */
|
|
@@ -4041,8 +3277,7 @@ export function bindDashboardGrid(api, group, options = {}) {
|
|
|
4041
3277
|
l.place(z.cell, true); // DOCKING PUSHES SECTIONS: everything below the band moves down, solid or not
|
|
4042
3278
|
insertRows(z.cell, l);
|
|
4043
3279
|
project();
|
|
4044
|
-
|
|
4045
|
-
placeholder = null;
|
|
3280
|
+
hidePlaceholder();
|
|
4046
3281
|
return true;
|
|
4047
3282
|
};
|
|
4048
3283
|
let zone = { kind: 'board' };
|
|
@@ -4652,19 +3887,17 @@ export function bindDashboardGrid(api, group, options = {}) {
|
|
|
4652
3887
|
},
|
|
4653
3888
|
beginPaletteDrag,
|
|
4654
3889
|
dispose() {
|
|
4655
|
-
var _a, _b, _c
|
|
3890
|
+
var _a, _b, _c;
|
|
4656
3891
|
if (disposed)
|
|
4657
3892
|
return;
|
|
4658
3893
|
cancelActiveGesture(false);
|
|
4659
3894
|
disposed = true;
|
|
4660
3895
|
peersOnCanvas().delete(selfPeer);
|
|
4661
3896
|
unregisterTool();
|
|
4662
|
-
api.container.removeEventListener('pointermove', onHover);
|
|
4663
|
-
api.container.removeEventListener('pointerleave', onHoverLeave);
|
|
4664
|
-
|
|
4665
|
-
api.container.removeEventListener('
|
|
4666
|
-
api.container.removeEventListener('keydown', onKey);
|
|
4667
|
-
hostObserver.disconnect();
|
|
3897
|
+
api.container.removeEventListener('pointermove', chrome.onHover);
|
|
3898
|
+
api.container.removeEventListener('pointerleave', chrome.onHoverLeave);
|
|
3899
|
+
api.container.removeEventListener('focusin', keyboard.onFocusIn);
|
|
3900
|
+
api.container.removeEventListener('keydown', keyboard.onKey);
|
|
4668
3901
|
containerObserver === null || containerObserver === void 0 ? void 0 : containerObserver.disconnect();
|
|
4669
3902
|
tearing = null;
|
|
4670
3903
|
for (const off of subs)
|
|
@@ -4673,23 +3906,8 @@ export function bindDashboardGrid(api, group, options = {}) {
|
|
|
4673
3906
|
// split layout must not keep showing a resize corner it cannot act on.
|
|
4674
3907
|
for (const id of (_a = group.members) !== null && _a !== void 0 ? _a : [])
|
|
4675
3908
|
(_c = (_b = hostOf(id)) === null || _b === void 0 ? void 0 : _b.querySelector(':scope > .axdb-rs')) === null || _c === void 0 ? void 0 : _c.remove();
|
|
4676
|
-
|
|
4677
|
-
placeholder
|
|
4678
|
-
if (glideTimer)
|
|
4679
|
-
clearTimeout(glideTimer);
|
|
4680
|
-
for (const el of slabEls.values())
|
|
4681
|
-
el.remove();
|
|
4682
|
-
slabEls.clear();
|
|
4683
|
-
hoverSlabs.clear();
|
|
4684
|
-
for (const bg of groupBgs.values())
|
|
4685
|
-
bg.remove();
|
|
4686
|
-
groupBgs.clear();
|
|
4687
|
-
// A rebind inside the 60 ms window (a layout switch right after an undo)
|
|
4688
|
-
// disposed this binder with the timer pending — the host kept its
|
|
4689
|
-
// lifted ghost for good (visual gate, nested-containers ⑤).
|
|
4690
|
-
flushGhost();
|
|
4691
|
-
flushCarried();
|
|
4692
|
-
(_d = htmlLayer()) === null || _d === void 0 ? void 0 : _d.classList.remove('axdb-glide');
|
|
3909
|
+
chrome.dispose(); // slabs, frames, the refused cell, the carried subtree, the host observer, the static guard
|
|
3910
|
+
projection.dispose(); // the placeholder, the glide timer, a pending ghost
|
|
4693
3911
|
api.container.style.cursor = '';
|
|
4694
3912
|
},
|
|
4695
3913
|
};
|
|
@@ -4701,7 +3919,7 @@ export function bindDashboardGrid(api, group, options = {}) {
|
|
|
4701
3919
|
rebuild(true);
|
|
4702
3920
|
const layer = htmlLayer();
|
|
4703
3921
|
if (layer)
|
|
4704
|
-
|
|
3922
|
+
chrome.observe(layer);
|
|
4705
3923
|
containerObserver === null || containerObserver === void 0 ? void 0 : containerObserver.observe(api.container);
|
|
4706
3924
|
return handle;
|
|
4707
3925
|
}
|