@grafloria/element 0.4.18 → 0.4.19
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.d.ts +6 -0
- package/src/lib/dashboard-kit/dashboard.js +2 -2
- package/src/lib/dashboard-kit/grid-binder.d.ts +22 -0
- package/src/lib/dashboard-kit/grid-binder.js +340 -23
- package/src/lib/dashboard-kit/split-binder.d.ts +1 -1
- package/src/lib/dashboard-kit/split-binder.js +4 -0
- package/src/lib/dashboard-kit/styles.js +10 -2
package/package.json
CHANGED
|
@@ -252,6 +252,12 @@ export interface DashboardOptions {
|
|
|
252
252
|
* hook). Omit for a titled placeholder frame.
|
|
253
253
|
*/
|
|
254
254
|
renderWidget?: (widget: DashboardWidgetSpec, host: HTMLElement) => void;
|
|
255
|
+
/**
|
|
256
|
+
* The selection changed on a board: the selected id — a widget or a SECTION
|
|
257
|
+
* (container) — or undefined when cleared, and the view it belongs to. A
|
|
258
|
+
* press on a section's empty band selects the section.
|
|
259
|
+
*/
|
|
260
|
+
onSelect?: (id: string | undefined, viewId: string) => void;
|
|
255
261
|
/** Fires after any committed gesture, with the view whose layout changed. */
|
|
256
262
|
onLayoutChange?: (viewId: string, widgets: DashboardWidgetSpec[]) => void;
|
|
257
263
|
/** Extra binder options, merged last (escape hatch to the layer below). */
|
|
@@ -1264,7 +1264,7 @@ export function dashboard(options) {
|
|
|
1264
1264
|
if (e.type === 'commit')
|
|
1265
1265
|
reportChanged();
|
|
1266
1266
|
(_b = (_a = options.binder) === null || _a === void 0 ? void 0 : _a.onGesture) === null || _b === void 0 ? void 0 : _b.call(_a, e);
|
|
1267
|
-
} });
|
|
1267
|
+
}, onSelect: (id) => { var _a; return (_a = options.onSelect) === null || _a === void 0 ? void 0 : _a.call(options, id, v.id); } });
|
|
1268
1268
|
if (viewLayout === 'split') {
|
|
1269
1269
|
return bindDashboardSplit(a, g, Object.assign(Object.assign(Object.assign({}, common), { columns: (_h = v.columns) !== null && _h !== void 0 ? _h : columns, baseRowHeight: rowHeight, designHeight: viewH(v) }), (v.tree !== undefined ? { tree: v.tree } : {})));
|
|
1270
1270
|
}
|
|
@@ -1282,7 +1282,7 @@ export function dashboard(options) {
|
|
|
1282
1282
|
if (e.type === 'commit')
|
|
1283
1283
|
reportChanged();
|
|
1284
1284
|
(_b = (_a = options.binder) === null || _a === void 0 ? void 0 : _a.onGesture) === null || _b === void 0 ? void 0 : _b.call(_a, e);
|
|
1285
|
-
} });
|
|
1285
|
+
}, onSelect: (id) => { var _a, _b; return (_a = options.onSelect) === null || _a === void 0 ? void 0 : _a.call(options, id, (_b = ctx.viewOfBoard.get(w.id)) !== null && _b !== void 0 ? _b : ctx.active); } });
|
|
1286
1286
|
if (((_h = ctx.layoutOf.get(w.id)) !== null && _h !== void 0 ? _h : w.layout) === 'split') {
|
|
1287
1287
|
// A splitter tree covering the pane; the pane's frame is the parent's
|
|
1288
1288
|
// slab, so no design height of its own.
|
|
@@ -196,6 +196,8 @@ export interface DashboardGridOptions {
|
|
|
196
196
|
changed: boolean;
|
|
197
197
|
}) => void;
|
|
198
198
|
/** Inject the hover-revealed corner resize handle into member hosts (default true). */
|
|
199
|
+
/** The selection on this board changed: the selected member — a widget or a SECTION (container) — or undefined. */
|
|
200
|
+
onSelect?: (id: string | undefined) => void;
|
|
199
201
|
resizeHandles?: boolean;
|
|
200
202
|
/**
|
|
201
203
|
* FLUID board: the group's frame follows the CANVAS CONTAINER — width
|
|
@@ -436,6 +438,20 @@ interface BinderPeer {
|
|
|
436
438
|
* boards after three clicks).
|
|
437
439
|
*/
|
|
438
440
|
clearSelection?(): void;
|
|
441
|
+
/** A nested board's live row bound — what its slab must never shrink below. */
|
|
442
|
+
innerRows?(): number;
|
|
443
|
+
/**
|
|
444
|
+
* SECTION PRESSES. A press on a section's empty band lands on the NESTED
|
|
445
|
+
* board's tool (it claims its own frame), which knows nothing to select;
|
|
446
|
+
* the section is a member of the PARENT. The child asks the parent to
|
|
447
|
+
* select it and, on an edge or the corner handle, to run the section
|
|
448
|
+
* resize — and forwards the rest of the pointer sequence.
|
|
449
|
+
*/
|
|
450
|
+
selectMember?(id: string): void;
|
|
451
|
+
beginSlabResize?(id: string, edges: ResizeEdges, ev: ToolPointerEvent): boolean;
|
|
452
|
+
slabMove?(ev: ToolPointerEvent): void;
|
|
453
|
+
slabUp?(): void;
|
|
454
|
+
slabCancel?(): void;
|
|
439
455
|
/** The member's current cell in this board (undefined when absent). */
|
|
440
456
|
memberCell(id: string): CellRect | undefined;
|
|
441
457
|
/**
|
|
@@ -501,4 +517,10 @@ interface AdoptedLeg {
|
|
|
501
517
|
export declare function clearOtherSelections(container: HTMLElement, self: BinderPeer | null): void;
|
|
502
518
|
export declare function registerBoardPeer(container: HTMLElement, peer: BinderPeer): () => void;
|
|
503
519
|
export type { BinderPeer };
|
|
520
|
+
interface ResizeEdges {
|
|
521
|
+
n: boolean;
|
|
522
|
+
e: boolean;
|
|
523
|
+
s: boolean;
|
|
524
|
+
w: boolean;
|
|
525
|
+
}
|
|
504
526
|
export declare function bindDashboardGrid(api: DashboardGridApi, group: GroupModel, options?: DashboardGridOptions): DashboardGridHandle;
|
|
@@ -465,6 +465,19 @@ export function bindDashboardGrid(api, group, options = {}) {
|
|
|
465
465
|
/** The engine item a member should enter as (cells from GridItemConfig /
|
|
466
466
|
* group metadata; spans falling back to metadata columnSpan/rowSpan; last
|
|
467
467
|
* resort: adopt from the current pixel geometry). */
|
|
468
|
+
/**
|
|
469
|
+
* A SECTION's floor: its nested board's live rows (the peer), else the rows
|
|
470
|
+
* it was mounted with — a section resized by hand never squeezes its
|
|
471
|
+
* children below their cells.
|
|
472
|
+
*/
|
|
473
|
+
const innerRowsOf = (id) => {
|
|
474
|
+
var _a, _b, _c, _d;
|
|
475
|
+
for (const p of (_a = BOARD_REGISTRY.get(api.container)) !== null && _a !== void 0 ? _a : [])
|
|
476
|
+
if (p.group.id === id && p.innerRows)
|
|
477
|
+
return Math.max(1, p.innerRows());
|
|
478
|
+
const meta = (_c = (_b = diagram.getGroup(id)) === null || _b === void 0 ? void 0 : _b.getMetadata) === null || _c === void 0 ? void 0 : _c.call(_b, 'containerWidget');
|
|
479
|
+
return Math.max(1, (_d = meta === null || meta === void 0 ? void 0 : meta.maxRows) !== null && _d !== void 0 ? _d : 1);
|
|
480
|
+
};
|
|
468
481
|
const itemFor = (id) => {
|
|
469
482
|
var _a, _b, _c, _d, _e, _f, _g;
|
|
470
483
|
const node = diagram.getNode(id);
|
|
@@ -708,23 +721,27 @@ export function bindDashboardGrid(api, group, options = {}) {
|
|
|
708
721
|
*/
|
|
709
722
|
let selectedId;
|
|
710
723
|
const selectWidget = (id) => {
|
|
724
|
+
var _a;
|
|
711
725
|
if (id !== undefined)
|
|
712
726
|
clearOtherSelections(api.container, selfPeerRef);
|
|
713
727
|
if (id === selectedId)
|
|
714
728
|
return;
|
|
715
729
|
selectedId = id;
|
|
716
730
|
syncA11y();
|
|
731
|
+
syncSlabs();
|
|
732
|
+
(_a = options.onSelect) === null || _a === void 0 ? void 0 : _a.call(options, id);
|
|
717
733
|
};
|
|
718
734
|
/** Set once the peer object exists (below); `selectWidget` runs before that only on boot. */
|
|
719
735
|
let selfPeerRef = null;
|
|
720
736
|
const syncA11y = (only) => {
|
|
721
|
-
var _a, _b;
|
|
737
|
+
var _a, _b, _c;
|
|
722
738
|
if (disposed)
|
|
723
739
|
return;
|
|
724
740
|
const members = [...((_a = group.members) !== null && _a !== void 0 ? _a : [])].filter((id) => !!diagram.getNode(id));
|
|
725
741
|
if (focusedId && !members.includes(focusedId))
|
|
726
742
|
focusedId = undefined;
|
|
727
|
-
|
|
743
|
+
// A selected SECTION is a group member, not a node: keep it as long as it is a member.
|
|
744
|
+
if (selectedId && !members.includes(selectedId) && !(((_b = group.members) === null || _b === void 0 ? void 0 : _b.has(selectedId)) && memberEntity(selectedId)))
|
|
728
745
|
selectedId = undefined;
|
|
729
746
|
const stop = focusedId !== null && focusedId !== void 0 ? focusedId : members[0];
|
|
730
747
|
for (const id of members) {
|
|
@@ -738,7 +755,7 @@ export function bindDashboardGrid(api, group, options = {}) {
|
|
|
738
755
|
const bits = [nameOf(node)];
|
|
739
756
|
if (cell)
|
|
740
757
|
bits.push(describeCell(cell));
|
|
741
|
-
if (((
|
|
758
|
+
if (((_c = node.state) === null || _c === void 0 ? void 0 : _c.locked) === true)
|
|
742
759
|
bits.push('pinned');
|
|
743
760
|
host.setAttribute('role', 'group');
|
|
744
761
|
host.setAttribute('aria-roledescription', 'dashboard widget');
|
|
@@ -759,6 +776,7 @@ export function bindDashboardGrid(api, group, options = {}) {
|
|
|
759
776
|
if (disposed)
|
|
760
777
|
return;
|
|
761
778
|
ensureStaticGuard();
|
|
779
|
+
syncSlabs();
|
|
762
780
|
const grip = gripOf(dragHandle);
|
|
763
781
|
for (const id of (_a = group.members) !== null && _a !== void 0 ? _a : []) {
|
|
764
782
|
if (only && !only.has(id))
|
|
@@ -975,6 +993,7 @@ export function bindDashboardGrid(api, group, options = {}) {
|
|
|
975
993
|
finally {
|
|
976
994
|
writing = false;
|
|
977
995
|
}
|
|
996
|
+
syncSlabs();
|
|
978
997
|
};
|
|
979
998
|
const applyColumns = (n, layout) => {
|
|
980
999
|
var _a;
|
|
@@ -1044,6 +1063,80 @@ export function bindDashboardGrid(api, group, options = {}) {
|
|
|
1044
1063
|
const band = rowHeightFor(geom(), rows()) + gap;
|
|
1045
1064
|
return x >= f.x && x <= f.x + f.width && y >= f.y && y <= f.y + boardVisualHeight() + band;
|
|
1046
1065
|
};
|
|
1066
|
+
/** The member SECTION whose frame holds the world point, if any. */
|
|
1067
|
+
const memberGroupAt = (x, y) => {
|
|
1068
|
+
var _a;
|
|
1069
|
+
for (const id of (_a = group.members) !== null && _a !== void 0 ? _a : []) {
|
|
1070
|
+
const grp = diagram.getGroup(id);
|
|
1071
|
+
if (!grp)
|
|
1072
|
+
continue;
|
|
1073
|
+
const p = grp.position;
|
|
1074
|
+
const s = sizeOf(grp);
|
|
1075
|
+
if (x >= p.x && x <= p.x + s.width && y >= p.y && y <= p.y + s.height)
|
|
1076
|
+
return id;
|
|
1077
|
+
}
|
|
1078
|
+
return null;
|
|
1079
|
+
};
|
|
1080
|
+
/** Which of a section frame's edges a world point is within EDGE_GRIP of. */
|
|
1081
|
+
const slabEdgesNear = (grp, x, y) => {
|
|
1082
|
+
const p = grp.position;
|
|
1083
|
+
const s = sizeOf(grp);
|
|
1084
|
+
return { n: y - p.y <= EDGE_GRIP, s: p.y + s.height - y <= EDGE_GRIP, w: x - p.x <= EDGE_GRIP, e: p.x + s.width - x <= EDGE_GRIP };
|
|
1085
|
+
};
|
|
1086
|
+
/**
|
|
1087
|
+
* SECTION CHROME. A section (member group) paints no card of its own, so it
|
|
1088
|
+
* had nothing to press: a click on its empty band cleared the selection and
|
|
1089
|
+
* its frame had no handle and no edge — a section with many children could
|
|
1090
|
+
* not be selected at all, and could only be resized by pulling a child
|
|
1091
|
+
* (Quantia, Groups page). Every section gets a pointer-transparent overlay
|
|
1092
|
+
* in the HTML layer that wears the selection ring and, while selected, the
|
|
1093
|
+
* corner handle; its frame edges answer the resize cursor.
|
|
1094
|
+
*/
|
|
1095
|
+
const slabEls = new Map();
|
|
1096
|
+
let slabLayer = null;
|
|
1097
|
+
const syncSlabs = () => {
|
|
1098
|
+
var _a, _b;
|
|
1099
|
+
if (disposed)
|
|
1100
|
+
return;
|
|
1101
|
+
const layer = (slabLayer === null || slabLayer === void 0 ? void 0 : slabLayer.isConnected) ? slabLayer : (slabLayer = htmlLayer());
|
|
1102
|
+
if (!layer)
|
|
1103
|
+
return;
|
|
1104
|
+
const seen = new Set();
|
|
1105
|
+
for (const id of (_a = group.members) !== null && _a !== void 0 ? _a : []) {
|
|
1106
|
+
const grp = diagram.getGroup(id);
|
|
1107
|
+
if (!grp || diagram.getNode(id))
|
|
1108
|
+
continue;
|
|
1109
|
+
seen.add(id);
|
|
1110
|
+
let el = slabEls.get(id);
|
|
1111
|
+
if (!el || el.parentElement !== layer) {
|
|
1112
|
+
el === null || el === void 0 ? void 0 : el.remove();
|
|
1113
|
+
el = document.createElement('div');
|
|
1114
|
+
el.className = 'axdb-slab';
|
|
1115
|
+
el.setAttribute('data-slab-id', id);
|
|
1116
|
+
const rs = document.createElement('div');
|
|
1117
|
+
rs.className = 'axdb-rs';
|
|
1118
|
+
rs.setAttribute('title', 'Resize section');
|
|
1119
|
+
el.appendChild(rs);
|
|
1120
|
+
layer.appendChild(el);
|
|
1121
|
+
slabEls.set(id, el);
|
|
1122
|
+
}
|
|
1123
|
+
const p = grp.position;
|
|
1124
|
+
const sz = sizeOf(grp);
|
|
1125
|
+
el.style.left = `${p.x}px`;
|
|
1126
|
+
el.style.top = `${p.y}px`;
|
|
1127
|
+
el.style.width = `${sz.width}px`;
|
|
1128
|
+
el.style.height = `${sz.height}px`;
|
|
1129
|
+
el.classList.toggle('axdb-slab--selected', selectedId === id);
|
|
1130
|
+
el.classList.toggle('axdb-slab--static', isStatic);
|
|
1131
|
+
(_b = el.querySelector(':scope > .axdb-rs')) === null || _b === void 0 ? void 0 : _b.classList.toggle('axdb-rs--rtl', rtl);
|
|
1132
|
+
}
|
|
1133
|
+
for (const [id, el] of slabEls) {
|
|
1134
|
+
if (!seen.has(id)) {
|
|
1135
|
+
el.remove();
|
|
1136
|
+
slabEls.delete(id);
|
|
1137
|
+
}
|
|
1138
|
+
}
|
|
1139
|
+
};
|
|
1047
1140
|
const insideMemberGroupFrame = (x, y) => {
|
|
1048
1141
|
var _a;
|
|
1049
1142
|
for (const id of (_a = group.members) !== null && _a !== void 0 ? _a : []) {
|
|
@@ -1057,6 +1150,131 @@ export function bindDashboardGrid(api, group, options = {}) {
|
|
|
1057
1150
|
}
|
|
1058
1151
|
return false;
|
|
1059
1152
|
};
|
|
1153
|
+
let slabGesture = null;
|
|
1154
|
+
/** The PARENT running a resize of OUR section from a press this tool claimed. */
|
|
1155
|
+
let forwardSlab = null;
|
|
1156
|
+
const frameOfGroup = (grp) => ({ x: grp.position.x, y: grp.position.y, width: sizeOf(grp).width, height: sizeOf(grp).height });
|
|
1157
|
+
const beginSlabResize = (id, edges, ev) => {
|
|
1158
|
+
const grp = diagram.getGroup(id);
|
|
1159
|
+
const it = engine.getItem(id);
|
|
1160
|
+
if (!grp || !it || gesture || slabGesture)
|
|
1161
|
+
return;
|
|
1162
|
+
engine.beginGesture();
|
|
1163
|
+
const snap = snapshotAll();
|
|
1164
|
+
slabGesture = {
|
|
1165
|
+
id,
|
|
1166
|
+
edges,
|
|
1167
|
+
pointerId: typeof PointerEvent !== 'undefined' && ev.source instanceof PointerEvent ? ev.source.pointerId : null,
|
|
1168
|
+
started: false,
|
|
1169
|
+
downScreen: { x: ev.screen.x, y: ev.screen.y },
|
|
1170
|
+
startCells: snap.cells,
|
|
1171
|
+
startGeom: snap.geoms,
|
|
1172
|
+
cellBefore: { x: it.x, y: it.y, w: it.w, h: it.h },
|
|
1173
|
+
frameBefore: frameOfGroup(grp),
|
|
1174
|
+
grab: {
|
|
1175
|
+
dx: edges.e ? grp.position.x + sizeOf(grp).width - ev.world.x : edges.w ? grp.position.x - ev.world.x : 0,
|
|
1176
|
+
dy: edges.s ? grp.position.y + sizeOf(grp).height - ev.world.y : edges.n ? grp.position.y - ev.world.y : 0,
|
|
1177
|
+
},
|
|
1178
|
+
};
|
|
1179
|
+
capturePointer(slabGesture.pointerId);
|
|
1180
|
+
api.container.style.cursor = cursorFor(edges);
|
|
1181
|
+
};
|
|
1182
|
+
const slabMove = (ev) => {
|
|
1183
|
+
const g = slabGesture;
|
|
1184
|
+
if (!g)
|
|
1185
|
+
return;
|
|
1186
|
+
if (!g.started) {
|
|
1187
|
+
if (Math.abs(ev.screen.x - g.downScreen.x) + Math.abs(ev.screen.y - g.downScreen.y) < DRAG_THRESHOLD)
|
|
1188
|
+
return;
|
|
1189
|
+
g.started = true;
|
|
1190
|
+
armGlide();
|
|
1191
|
+
}
|
|
1192
|
+
const it = engine.getItem(g.id);
|
|
1193
|
+
if (!it)
|
|
1194
|
+
return;
|
|
1195
|
+
const f = frame();
|
|
1196
|
+
const gg = geom();
|
|
1197
|
+
const cu = columnUnitFor(gg, f.width);
|
|
1198
|
+
const rh = rowHeightFor(gg, rows());
|
|
1199
|
+
// The grid line nearest the pointer, in cells (mirrored on RTL).
|
|
1200
|
+
const colAt = (wx) => Math.round((rtl ? f.x + f.width - padding - wx : wx - f.x - padding) / (cu + gap));
|
|
1201
|
+
const rowAt = (wy) => Math.round((wy - f.y - padding) / (rh + gap));
|
|
1202
|
+
let { x, y, w, h } = it;
|
|
1203
|
+
const px = ev.world.x + g.grab.dx;
|
|
1204
|
+
const py = ev.world.y + g.grab.dy;
|
|
1205
|
+
if (g.edges.e)
|
|
1206
|
+
w = Math.max(1, colAt(px) - x);
|
|
1207
|
+
if (g.edges.s)
|
|
1208
|
+
h = Math.max(1, rowAt(py) - y);
|
|
1209
|
+
if (g.edges.w) {
|
|
1210
|
+
const nx = Math.max(0, Math.min(x + w - 1, colAt(px)));
|
|
1211
|
+
w = x + w - nx;
|
|
1212
|
+
x = nx;
|
|
1213
|
+
}
|
|
1214
|
+
if (g.edges.n) {
|
|
1215
|
+
const ny = Math.max(0, Math.min(y + h - 1, rowAt(py)));
|
|
1216
|
+
h = y + h - ny;
|
|
1217
|
+
y = ny;
|
|
1218
|
+
}
|
|
1219
|
+
w = Math.max(1, Math.min(w, columns - x));
|
|
1220
|
+
// Never below the children's rows (the nested board's live bound).
|
|
1221
|
+
const floor = innerRowsOf(g.id);
|
|
1222
|
+
if (h < floor) {
|
|
1223
|
+
if (g.edges.n)
|
|
1224
|
+
y = y + h - floor;
|
|
1225
|
+
h = floor;
|
|
1226
|
+
}
|
|
1227
|
+
let changed = false;
|
|
1228
|
+
if (x !== it.x || y !== it.y)
|
|
1229
|
+
changed = engine.moveCheck(g.id, x, y, { gate: false }).changed || changed;
|
|
1230
|
+
if (w !== it.w || h !== it.h)
|
|
1231
|
+
changed = engine.resizeCheck(g.id, w, h).changed || changed;
|
|
1232
|
+
if (changed)
|
|
1233
|
+
project();
|
|
1234
|
+
};
|
|
1235
|
+
const slabUp = () => {
|
|
1236
|
+
var _a;
|
|
1237
|
+
const g = slabGesture;
|
|
1238
|
+
if (!g)
|
|
1239
|
+
return;
|
|
1240
|
+
slabGesture = null;
|
|
1241
|
+
releasePointer(g.pointerId);
|
|
1242
|
+
api.container.style.cursor = '';
|
|
1243
|
+
if (!g.started) {
|
|
1244
|
+
engine.endGesture();
|
|
1245
|
+
return;
|
|
1246
|
+
}
|
|
1247
|
+
engine.endGesture();
|
|
1248
|
+
project();
|
|
1249
|
+
const it = engine.getItem(g.id);
|
|
1250
|
+
const grp = diagram.getGroup(g.id);
|
|
1251
|
+
const commands = buildCommitCommands(deltasSince(g.startCells, g.startGeom, g.id));
|
|
1252
|
+
const b = g.cellBefore;
|
|
1253
|
+
if (it && grp && (b.x !== it.x || b.y !== it.y || b.w !== it.w || b.h !== it.h)) {
|
|
1254
|
+
commands.push(new SetGroupCellCommand(g.id, b, { x: it.x, y: it.y, w: it.w, h: it.h }, g.frameBefore, frameOfGroup(grp)));
|
|
1255
|
+
}
|
|
1256
|
+
const changed = execute('Resize section', commands);
|
|
1257
|
+
disarmGlideSoon();
|
|
1258
|
+
syncHandles();
|
|
1259
|
+
api.renderNow();
|
|
1260
|
+
(_a = options.onGesture) === null || _a === void 0 ? void 0 : _a.call(options, { type: 'commit', kind: 'resize', nodeId: g.id, changed });
|
|
1261
|
+
};
|
|
1262
|
+
const slabCancel = () => {
|
|
1263
|
+
var _a;
|
|
1264
|
+
const g = slabGesture;
|
|
1265
|
+
if (!g)
|
|
1266
|
+
return;
|
|
1267
|
+
slabGesture = null;
|
|
1268
|
+
releasePointer(g.pointerId);
|
|
1269
|
+
api.container.style.cursor = '';
|
|
1270
|
+
if (g.started)
|
|
1271
|
+
engine.cancelGesture();
|
|
1272
|
+
else
|
|
1273
|
+
engine.endGesture();
|
|
1274
|
+
project();
|
|
1275
|
+
disarmGlideSoon();
|
|
1276
|
+
(_a = options.onGesture) === null || _a === void 0 ? void 0 : _a.call(options, { type: 'cancel', kind: 'resize', nodeId: g.id, changed: false });
|
|
1277
|
+
};
|
|
1060
1278
|
const capturePointer = (pointerId) => {
|
|
1061
1279
|
var _a, _b;
|
|
1062
1280
|
if (pointerId === null)
|
|
@@ -1163,7 +1381,13 @@ export function bindDashboardGrid(api, group, options = {}) {
|
|
|
1163
1381
|
(_a = options.onGesture) === null || _a === void 0 ? void 0 : _a.call(options, { type: 'commit', kind: g.kind, nodeId: g.id, changed });
|
|
1164
1382
|
};
|
|
1165
1383
|
const cancelActiveGesture = (notify = true) => {
|
|
1166
|
-
var _a, _b, _c;
|
|
1384
|
+
var _a, _b, _c, _d;
|
|
1385
|
+
if (forwardSlab) {
|
|
1386
|
+
(_a = forwardSlab.slabCancel) === null || _a === void 0 ? void 0 : _a.call(forwardSlab);
|
|
1387
|
+
forwardSlab = null;
|
|
1388
|
+
}
|
|
1389
|
+
if (slabGesture)
|
|
1390
|
+
slabCancel();
|
|
1167
1391
|
const g = gesture;
|
|
1168
1392
|
if (!g)
|
|
1169
1393
|
return;
|
|
@@ -1184,7 +1408,7 @@ export function bindDashboardGrid(api, group, options = {}) {
|
|
|
1184
1408
|
engine.endGesture();
|
|
1185
1409
|
const items = [];
|
|
1186
1410
|
for (const [id, c] of g.startCells) {
|
|
1187
|
-
const lockedNode = ((
|
|
1411
|
+
const lockedNode = ((_c = (_b = diagram.getNode(id)) === null || _b === void 0 ? void 0 : _b.state) === null || _c === void 0 ? void 0 : _c.locked) === true;
|
|
1188
1412
|
items.push(Object.assign(Object.assign({ id }, c), { locked: lockedNode || isGroupMember(id) }));
|
|
1189
1413
|
}
|
|
1190
1414
|
engine = engineFrom(items);
|
|
@@ -1232,7 +1456,7 @@ export function bindDashboardGrid(api, group, options = {}) {
|
|
|
1232
1456
|
enforceBoardHeight();
|
|
1233
1457
|
api.renderNow();
|
|
1234
1458
|
if (notify) {
|
|
1235
|
-
(
|
|
1459
|
+
(_d = options.onGesture) === null || _d === void 0 ? void 0 : _d.call(options, { type: 'cancel', kind: g.kind, nodeId: g.id, changed: false });
|
|
1236
1460
|
}
|
|
1237
1461
|
};
|
|
1238
1462
|
/** Centre a w×h-span tile's top-left under the cursor, in world px. */
|
|
@@ -1802,11 +2026,31 @@ export function bindDashboardGrid(api, group, options = {}) {
|
|
|
1802
2026
|
const selfPeer = {
|
|
1803
2027
|
group,
|
|
1804
2028
|
clearSelection: () => {
|
|
2029
|
+
var _a;
|
|
1805
2030
|
if (selectedId === undefined)
|
|
1806
2031
|
return;
|
|
1807
2032
|
selectedId = undefined;
|
|
1808
2033
|
syncA11y();
|
|
2034
|
+
syncSlabs();
|
|
2035
|
+
(_a = options.onSelect) === null || _a === void 0 ? void 0 : _a.call(options, undefined);
|
|
1809
2036
|
},
|
|
2037
|
+
innerRows: () => maxRows !== null && maxRows !== void 0 ? maxRows : rows(),
|
|
2038
|
+
selectMember: (id) => {
|
|
2039
|
+
var _a;
|
|
2040
|
+
if (((_a = group.members) !== null && _a !== void 0 ? _a : new Set()).has(id)) {
|
|
2041
|
+
selectWidget(id);
|
|
2042
|
+
api.render();
|
|
2043
|
+
}
|
|
2044
|
+
},
|
|
2045
|
+
beginSlabResize: (id, edges, ev) => {
|
|
2046
|
+
if (isStatic)
|
|
2047
|
+
return false;
|
|
2048
|
+
beginSlabResize(id, edges, ev);
|
|
2049
|
+
return (slabGesture === null || slabGesture === void 0 ? void 0 : slabGesture.id) === id;
|
|
2050
|
+
},
|
|
2051
|
+
slabMove: (ev) => slabMove(ev),
|
|
2052
|
+
slabUp: () => slabUp(),
|
|
2053
|
+
slabCancel: () => slabCancel(),
|
|
1810
2054
|
hasItem: (id) => !!engine.getItem(id),
|
|
1811
2055
|
memberCell: (id) => {
|
|
1812
2056
|
const it = engine.getItem(id);
|
|
@@ -1853,7 +2097,7 @@ export function bindDashboardGrid(api, group, options = {}) {
|
|
|
1853
2097
|
var _a, _b;
|
|
1854
2098
|
if (disposed)
|
|
1855
2099
|
return false;
|
|
1856
|
-
if (gesture)
|
|
2100
|
+
if (gesture || slabGesture || forwardSlab)
|
|
1857
2101
|
return true; // own the rest of an in-flight gesture
|
|
1858
2102
|
if (!ownsPress(api.container, diagram, ev, hit))
|
|
1859
2103
|
return false;
|
|
@@ -1882,7 +2126,7 @@ export function bindDashboardGrid(api, group, options = {}) {
|
|
|
1882
2126
|
return insideMemberGroupFrame(ev.world.x, ev.world.y) || worldInsideBoard(ev.world.x, ev.world.y);
|
|
1883
2127
|
},
|
|
1884
2128
|
onPointerDown(ev, hit) {
|
|
1885
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m;
|
|
2129
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s;
|
|
1886
2130
|
if (gesture)
|
|
1887
2131
|
return; // mid-palette
|
|
1888
2132
|
const target = ((_b = (_a = ev.source) === null || _a === void 0 ? void 0 : _a.target) !== null && _b !== void 0 ? _b : null);
|
|
@@ -1893,9 +2137,43 @@ export function bindDashboardGrid(api, group, options = {}) {
|
|
|
1893
2137
|
const gripId = (_c = gripHost === null || gripHost === void 0 ? void 0 : gripHost.getAttribute('data-node-id')) !== null && _c !== void 0 ? _c : null;
|
|
1894
2138
|
const onGrip = !!gripId && ((_d = group.members) !== null && _d !== void 0 ? _d : new Set()).has(gripId);
|
|
1895
2139
|
if (!hit.node && !onGrip) {
|
|
2140
|
+
// A press on a SECTION — its empty band, its corner handle or its
|
|
2141
|
+
// frame edge — selects the section; the handle or an edge resizes it.
|
|
2142
|
+
const slabHandle = (_e = target === null || target === void 0 ? void 0 : target.closest) === null || _e === void 0 ? void 0 : _e.call(target, '.axdb-slab > .axdb-rs');
|
|
2143
|
+
const slabId = (_g = (_f = slabHandle === null || slabHandle === void 0 ? void 0 : slabHandle.parentElement) === null || _f === void 0 ? void 0 : _f.getAttribute('data-slab-id')) !== null && _g !== void 0 ? _g : memberGroupAt(ev.world.x, ev.world.y);
|
|
2144
|
+
const grp = slabId && ((_h = group.members) !== null && _h !== void 0 ? _h : new Set()).has(slabId) ? diagram.getGroup(slabId) : undefined;
|
|
2145
|
+
if (slabId && grp) {
|
|
2146
|
+
selectWidget(slabId);
|
|
2147
|
+
api.render();
|
|
2148
|
+
if (isStatic)
|
|
2149
|
+
return;
|
|
2150
|
+
const edges = slabHandle
|
|
2151
|
+
? rtl ? { n: false, e: false, s: true, w: true } : { n: false, e: true, s: true, w: false }
|
|
2152
|
+
: slabEdgesNear(grp, ev.world.x, ev.world.y);
|
|
2153
|
+
if (anyEdge(edges))
|
|
2154
|
+
beginSlabResize(slabId, edges, ev);
|
|
2155
|
+
return;
|
|
2156
|
+
}
|
|
2157
|
+
// OUR OWN empty band, and we are a section of a parent board: the
|
|
2158
|
+
// press selects the section there, and its edge or corner handle
|
|
2159
|
+
// starts the section resize, which the parent runs while this tool
|
|
2160
|
+
// forwards the pointer sequence.
|
|
2161
|
+
const parent = parentPeer();
|
|
2162
|
+
if ((parent === null || parent === void 0 ? void 0 : parent.selectMember) && worldInsideBoard(ev.world.x, ev.world.y)) {
|
|
2163
|
+
const ownHandle = ((_j = slabHandle === null || slabHandle === void 0 ? void 0 : slabHandle.parentElement) === null || _j === void 0 ? void 0 : _j.getAttribute('data-slab-id')) === group.id;
|
|
2164
|
+
parent.selectMember(group.id);
|
|
2165
|
+
if (!isStatic && parent.beginSlabResize) {
|
|
2166
|
+
const edges = ownHandle
|
|
2167
|
+
? rtl ? { n: false, e: false, s: true, w: true } : { n: false, e: true, s: true, w: false }
|
|
2168
|
+
: slabEdgesNear(group, ev.world.x, ev.world.y);
|
|
2169
|
+
if (anyEdge(edges) && parent.beginSlabResize(group.id, edges, ev))
|
|
2170
|
+
forwardSlab = parent;
|
|
2171
|
+
}
|
|
2172
|
+
return;
|
|
2173
|
+
}
|
|
1896
2174
|
// The board's own empty area: a void click. Nothing to drag, and the
|
|
1897
2175
|
// selection clears exactly as a click outside any board would.
|
|
1898
|
-
(
|
|
2176
|
+
(_l = (_k = diagram).clearSelection) === null || _l === void 0 ? void 0 : _l.call(_k);
|
|
1899
2177
|
selectWidget(undefined);
|
|
1900
2178
|
api.render();
|
|
1901
2179
|
return;
|
|
@@ -1904,17 +2182,17 @@ export function bindDashboardGrid(api, group, options = {}) {
|
|
|
1904
2182
|
if (!node)
|
|
1905
2183
|
return;
|
|
1906
2184
|
selectWidget(node.id); // a press selects, whether or not it starts a gesture
|
|
1907
|
-
if (((
|
|
2185
|
+
if (((_m = node.state) === null || _m === void 0 ? void 0 : _m.locked) === true)
|
|
1908
2186
|
return; // pinned: refuse; click still focuses
|
|
1909
2187
|
if (isStatic)
|
|
1910
2188
|
return; // a static board: claimed and deadened, click still focuses
|
|
1911
2189
|
// Which edges did the press take? The corner handle names its own (s+e,
|
|
1912
2190
|
// or s+w on RTL); a bare press within EDGE_GRIP of the tile's border
|
|
1913
2191
|
// takes that border; anywhere else is a move. A grip press is a move.
|
|
1914
|
-
const onHandle = !!((
|
|
2192
|
+
const onHandle = !!((_o = target === null || target === void 0 ? void 0 : target.closest) === null || _o === void 0 ? void 0 : _o.call(target, '.axdb-rs'));
|
|
1915
2193
|
const hostEl = hostOf(node.id);
|
|
1916
|
-
const resizable = ((
|
|
1917
|
-
const movable = ((
|
|
2194
|
+
const resizable = ((_p = node.getMetadata) === null || _p === void 0 ? void 0 : _p.call(node, 'widgetResizable')) !== false;
|
|
2195
|
+
const movable = ((_q = node.getMetadata) === null || _q === void 0 ? void 0 : _q.call(node, 'widgetMovable')) !== false;
|
|
1918
2196
|
// `ev.screen` is ELEMENT-LOCAL px; host rects are in client px. Compare
|
|
1919
2197
|
// like with like — the source event's clientX/Y when there is one, else
|
|
1920
2198
|
// the container's origin plus the local offset.
|
|
@@ -1962,7 +2240,7 @@ export function bindDashboardGrid(api, group, options = {}) {
|
|
|
1962
2240
|
startSize: { width: node.size.width, height: node.size.height },
|
|
1963
2241
|
startPos: { x: node.position.x, y: node.position.y },
|
|
1964
2242
|
edges,
|
|
1965
|
-
spans: { w: (
|
|
2243
|
+
spans: { w: (_r = it === null || it === void 0 ? void 0 : it.w) !== null && _r !== void 0 ? _r : 1, h: (_s = it === null || it === void 0 ? void 0 : it.h) !== null && _s !== void 0 ? _s : 1 },
|
|
1966
2244
|
removedFromBoard: false,
|
|
1967
2245
|
leg: null,
|
|
1968
2246
|
lastWorld: null,
|
|
@@ -1973,10 +2251,24 @@ export function bindDashboardGrid(api, group, options = {}) {
|
|
|
1973
2251
|
};
|
|
1974
2252
|
},
|
|
1975
2253
|
onPointerMove(ev) {
|
|
1976
|
-
|
|
2254
|
+
var _a;
|
|
2255
|
+
if (forwardSlab)
|
|
2256
|
+
(_a = forwardSlab.slabMove) === null || _a === void 0 ? void 0 : _a.call(forwardSlab, ev);
|
|
2257
|
+
else if (slabGesture)
|
|
2258
|
+
slabMove(ev);
|
|
2259
|
+
else
|
|
2260
|
+
onToolMove(ev);
|
|
1977
2261
|
},
|
|
1978
2262
|
onPointerUp() {
|
|
1979
|
-
|
|
2263
|
+
var _a;
|
|
2264
|
+
if (forwardSlab) {
|
|
2265
|
+
(_a = forwardSlab.slabUp) === null || _a === void 0 ? void 0 : _a.call(forwardSlab);
|
|
2266
|
+
forwardSlab = null;
|
|
2267
|
+
}
|
|
2268
|
+
else if (slabGesture)
|
|
2269
|
+
slabUp();
|
|
2270
|
+
else
|
|
2271
|
+
onToolUp();
|
|
1980
2272
|
},
|
|
1981
2273
|
onCancel() {
|
|
1982
2274
|
cancelActiveGesture();
|
|
@@ -1990,7 +2282,7 @@ export function bindDashboardGrid(api, group, options = {}) {
|
|
|
1990
2282
|
*/
|
|
1991
2283
|
let hoverHost = null;
|
|
1992
2284
|
const onHover = (e) => {
|
|
1993
|
-
var _a, _b, _c, _d, _e, _f, _g;
|
|
2285
|
+
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
1994
2286
|
if (disposed || gesture)
|
|
1995
2287
|
return;
|
|
1996
2288
|
// The event may target the host, its content, or (when a host's content
|
|
@@ -2014,13 +2306,23 @@ export function bindDashboardGrid(api, group, options = {}) {
|
|
|
2014
2306
|
hoverHost.removeAttribute('data-axdb-edge'); // the affordance follows the pointer off a tile
|
|
2015
2307
|
}
|
|
2016
2308
|
hoverHost = host;
|
|
2017
|
-
if (!host)
|
|
2309
|
+
if (!host) {
|
|
2310
|
+
// No tile under the pointer: a SECTION's frame edge still says resize.
|
|
2311
|
+
const wpt = ((_d = api.viewport) === null || _d === void 0 ? void 0 : _d.clientToWorld) ? api.viewport.clientToWorld(e.clientX, e.clientY, api.container.getBoundingClientRect()) : null;
|
|
2312
|
+
const sid = wpt ? memberGroupAt(wpt.x, wpt.y) : null;
|
|
2313
|
+
const grp = sid ? diagram.getGroup(sid) : undefined;
|
|
2314
|
+
const c = grp && wpt && !isStatic ? cursorFor(slabEdgesNear(grp, wpt.x, wpt.y)) : '';
|
|
2315
|
+
if (!slabGesture)
|
|
2316
|
+
api.container.style.cursor = c;
|
|
2018
2317
|
return;
|
|
2019
|
-
|
|
2020
|
-
if (!
|
|
2318
|
+
}
|
|
2319
|
+
if (!slabGesture && api.container.style.cursor)
|
|
2320
|
+
api.container.style.cursor = '';
|
|
2321
|
+
const id = (_e = host.getAttribute('data-node-id')) !== null && _e !== void 0 ? _e : '';
|
|
2322
|
+
if (!((_f = group.members) !== null && _f !== void 0 ? _f : new Set()).has(id))
|
|
2021
2323
|
return;
|
|
2022
2324
|
const node = diagram.getNode(id);
|
|
2023
|
-
const resizable = !!node && !isStatic && ((
|
|
2325
|
+
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;
|
|
2024
2326
|
const cursor = resizable ? cursorFor(edgesNear(host, e.clientX, e.clientY)) : '';
|
|
2025
2327
|
// The affordance rides on an ATTRIBUTE, not the inline style: a repaint
|
|
2026
2328
|
// rewrites the host's style and used to clear the cursor mid-hover, and
|
|
@@ -2354,6 +2656,17 @@ export function bindDashboardGrid(api, group, options = {}) {
|
|
|
2354
2656
|
writing = false;
|
|
2355
2657
|
}
|
|
2356
2658
|
const commands = buildCommitCommands(deltasSince(snap.cells, snap.geoms));
|
|
2659
|
+
if (isGroupMember(id)) {
|
|
2660
|
+
// A SECTION: its cell lives in group metadata and its frame is written
|
|
2661
|
+
// by project(); the node commands above skip groups (D5 of the review).
|
|
2662
|
+
const it = engine.getItem(id);
|
|
2663
|
+
const grp = diagram.getGroup(id);
|
|
2664
|
+
const cb = snap.cells.get(id);
|
|
2665
|
+
const gb = snap.geoms.get(id);
|
|
2666
|
+
if (it && grp && cb && gb && (cb.x !== it.x || cb.y !== it.y || cb.w !== it.w || cb.h !== it.h)) {
|
|
2667
|
+
commands.push(new SetGroupCellCommand(id, cb, { x: it.x, y: it.y, w: it.w, h: it.h }, { x: gb.pos.x, y: gb.pos.y, width: gb.size.width, height: gb.size.height }, frameOfGroup(grp)));
|
|
2668
|
+
}
|
|
2669
|
+
}
|
|
2357
2670
|
engine.endGesture();
|
|
2358
2671
|
disarmGlideSoon();
|
|
2359
2672
|
enforceBoardHeight();
|
|
@@ -2452,7 +2765,7 @@ export function bindDashboardGrid(api, group, options = {}) {
|
|
|
2452
2765
|
var _a;
|
|
2453
2766
|
if (disposed)
|
|
2454
2767
|
return false;
|
|
2455
|
-
if (id !== undefined && (!((_a = group.members) !== null && _a !== void 0 ? _a : new Set()).has(id) || !
|
|
2768
|
+
if (id !== undefined && (!((_a = group.members) !== null && _a !== void 0 ? _a : new Set()).has(id) || !memberEntity(id)))
|
|
2456
2769
|
return false;
|
|
2457
2770
|
selectWidget(id);
|
|
2458
2771
|
return true;
|
|
@@ -2581,7 +2894,8 @@ export function bindDashboardGrid(api, group, options = {}) {
|
|
|
2581
2894
|
return programmatic('Move widget', id, () => engine.moveCheck(id, x, y).changed);
|
|
2582
2895
|
},
|
|
2583
2896
|
resizeTo(id, w, h) {
|
|
2584
|
-
|
|
2897
|
+
const hh = isGroupMember(id) ? Math.max(h, innerRowsOf(id)) : h; // a section: never below its children
|
|
2898
|
+
return programmatic('Resize widget', id, () => engine.resizeCheck(id, w, hh).changed);
|
|
2585
2899
|
},
|
|
2586
2900
|
beginPaletteDrag,
|
|
2587
2901
|
dispose() {
|
|
@@ -2608,6 +2922,9 @@ export function bindDashboardGrid(api, group, options = {}) {
|
|
|
2608
2922
|
placeholder = null;
|
|
2609
2923
|
if (glideTimer)
|
|
2610
2924
|
clearTimeout(glideTimer);
|
|
2925
|
+
for (const el of slabEls.values())
|
|
2926
|
+
el.remove();
|
|
2927
|
+
slabEls.clear();
|
|
2611
2928
|
// A rebind inside the 60 ms window (a layout switch right after an undo)
|
|
2612
2929
|
// disposed this binder with the timer pending — the host kept its
|
|
2613
2930
|
// lifted ghost for good (visual gate, nested-containers ⑤).
|
|
@@ -27,7 +27,7 @@ import type { DashboardGridApi, DashboardGridHandle, DashboardGridOptions } from
|
|
|
27
27
|
import { type SplitNode } from './split-layout.js';
|
|
28
28
|
/** Group metadata key the tree persists under. */
|
|
29
29
|
export declare const SPLIT_TREE_KEY = "dashboardTree";
|
|
30
|
-
export interface DashboardSplitOptions extends Pick<DashboardGridOptions, 'columns' | 'gap' | 'padding' | 'rtl' | 'fluid' | 'static' | 'dragHandle' | 'squeeze' | 'designHeight' | 'baseRowHeight' | 'dragOut' | 'removeZone' | 'onRemoveRequest' | 'onDropIn' | 'onGesture'> {
|
|
30
|
+
export interface DashboardSplitOptions extends Pick<DashboardGridOptions, 'columns' | 'gap' | 'padding' | 'rtl' | 'fluid' | 'static' | 'dragHandle' | 'squeeze' | 'designHeight' | 'baseRowHeight' | 'dragOut' | 'removeZone' | 'onRemoveRequest' | 'onDropIn' | 'onGesture' | 'onSelect'> {
|
|
31
31
|
/** An authored tree. Default: the persisted one, else derived from the members' cells. */
|
|
32
32
|
tree?: SplitNode | null;
|
|
33
33
|
}
|
|
@@ -372,12 +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
|
+
var _a;
|
|
375
376
|
if (id !== undefined)
|
|
376
377
|
clearOtherSelections(api.container, selfPeerRef);
|
|
377
378
|
if (id === selectedId)
|
|
378
379
|
return;
|
|
379
380
|
selectedId = id;
|
|
380
381
|
syncA11y();
|
|
382
|
+
(_a = options.onSelect) === null || _a === void 0 ? void 0 : _a.call(options, id);
|
|
381
383
|
};
|
|
382
384
|
let selfPeerRef = null;
|
|
383
385
|
// Static boards let content be clicked — see grid-binder's staticGuard.
|
|
@@ -1193,10 +1195,12 @@ export function bindDashboardSplit(api, group, options = {}) {
|
|
|
1193
1195
|
const selfPeer = {
|
|
1194
1196
|
group,
|
|
1195
1197
|
clearSelection: () => {
|
|
1198
|
+
var _a;
|
|
1196
1199
|
if (selectedId === undefined)
|
|
1197
1200
|
return;
|
|
1198
1201
|
selectedId = undefined;
|
|
1199
1202
|
syncA11y();
|
|
1203
|
+
(_a = options.onSelect) === null || _a === void 0 ? void 0 : _a.call(options, undefined);
|
|
1200
1204
|
},
|
|
1201
1205
|
hasItem: (id) => { var _a; return ((_a = group.members) !== null && _a !== void 0 ? _a : new Set()).has(id); },
|
|
1202
1206
|
memberCell: (id) => handle.cellOf(id),
|
|
@@ -68,7 +68,7 @@ const CSS = `
|
|
|
68
68
|
}
|
|
69
69
|
|
|
70
70
|
/* ===== corner resize handle (hover-revealed, radius-matched) ===== */
|
|
71
|
-
.grafloria-node-host > .axdb-rs {
|
|
71
|
+
:is(.grafloria-node-host, .axdb-slab) > .axdb-rs {
|
|
72
72
|
position: absolute;
|
|
73
73
|
right: 0;
|
|
74
74
|
bottom: 0;
|
|
@@ -88,7 +88,7 @@ const CSS = `
|
|
|
88
88
|
@media (hover: none) { .grafloria-node-host > .axdb-rs { opacity: .8; } }
|
|
89
89
|
.grafloria-node-host > .axdb-rs:hover { border-color: #3b52d9; }
|
|
90
90
|
/* RTL boards grow leftwards, so the grab corner mirrors with them. */
|
|
91
|
-
.grafloria-node-host > .axdb-rs.axdb-rs--rtl {
|
|
91
|
+
:is(.grafloria-node-host, .axdb-slab) > .axdb-rs.axdb-rs--rtl {
|
|
92
92
|
right: auto;
|
|
93
93
|
left: 0;
|
|
94
94
|
cursor: nesw-resize;
|
|
@@ -355,6 +355,14 @@ const CSS = `
|
|
|
355
355
|
.axdb-tier-2 .axdb-xt, .axdb-tier-2 .axdb-vt,
|
|
356
356
|
.axdb-lg--off { display: none; }
|
|
357
357
|
|
|
358
|
+
/* SECTION CHROME: a pointer-transparent overlay on every member group. It
|
|
359
|
+
wears the selection ring and, while selected, the corner handle. */
|
|
360
|
+
.grafloria-html-layer > .axdb-slab { position: absolute; pointer-events: none; border-radius: var(--axdb-rs-radius, 3px); z-index: 4; }
|
|
361
|
+
.grafloria-html-layer > .axdb-slab.axdb-slab--selected { box-shadow: 0 0 0 1.5px var(--axdb-accent-ring, rgba(59, 82, 217, .55)); }
|
|
362
|
+
.grafloria-html-layer > .axdb-slab > .axdb-rs { pointer-events: auto; opacity: 0; }
|
|
363
|
+
.grafloria-html-layer > .axdb-slab.axdb-slab--selected > .axdb-rs { opacity: 1; }
|
|
364
|
+
.grafloria-html-layer > .axdb-slab.axdb-slab--static > .axdb-rs { display: none; }
|
|
365
|
+
|
|
358
366
|
/* legend chips, shared by line and donut */
|
|
359
367
|
.axdb-lg { display: flex; flex-wrap: wrap; gap: 4px 12px; margin-top: 9px; }
|
|
360
368
|
.axdb-lg--col { flex-direction: column; flex-wrap: nowrap; gap: 6px; margin-top: 0; }
|