@grafloria/element 0.4.22 → 0.4.24
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/index.d.ts +1 -1
- package/src/index.js +1 -1
- package/src/lib/dashboard-kit/caption.d.ts +62 -5
- package/src/lib/dashboard-kit/caption.js +74 -17
- package/src/lib/dashboard-kit/grid-binder.js +48 -4
- package/src/lib/dashboard-kit/split-binder.js +9 -3
- package/src/lib/dashboard-kit/styles.js +32 -10
package/package.json
CHANGED
package/src/index.d.ts
CHANGED
|
@@ -79,7 +79,7 @@ export type { JoinColumn, JoinEnd, MatchTier, JoinGuidanceApi, JoinGuidanceOptio
|
|
|
79
79
|
export type { ErColumn, ErEntitySpec, ErRelationshipSpec, ErCardinality, ErSide, ErDiagramOptions, UmlClassSpec, UmlRelationshipSpec, UmlRelationKind, UmlSide, UmlDiagramOptions, ErEntityDelta, UmlClassDelta, CardEditingHandle, } from './lib/diagram-kit/index.js';
|
|
80
80
|
export { erTable, umlClass, erTables, umlClasses, CardHandle, ErTable, ErField, ErColumnList, UmlClass, UmlMemberList, } from './lib/diagram-kit/index.js';
|
|
81
81
|
export type { HandleApi } from './lib/diagram-kit/index.js';
|
|
82
|
-
export { bindDashboardGrid, rowHeightFor, boardHeightFor, columnUnitFor, cellToRect, pointToCell, sizeToSpan, gridItemFromCell, cellFromGridItem, buildCommitCommands, ensureDashboardKitStyles, DASHBOARD_KIT_STYLE_ID, dashboard, defaultWidgetRenderer, renderKpiWidget, renderLineWidget, renderBarWidget, renderDonutWidget, renderFunnelWidget, renderTableWidget, BUILT_IN_WIDGET_KINDS, } from './lib/dashboard-kit/index.js';
|
|
82
|
+
export { bindDashboardGrid, normalizeCaption, captionReserve, captionBandHeight, paintCaptionBand, CAPTION_HEIGHT, CAPTION_HEIGHT_SUBTITLE, CAPTION_HEIGHT_TIGHT, CAPTION_PASS_THROUGH, rowHeightFor, boardHeightFor, columnUnitFor, cellToRect, pointToCell, sizeToSpan, gridItemFromCell, cellFromGridItem, buildCommitCommands, ensureDashboardKitStyles, DASHBOARD_KIT_STYLE_ID, dashboard, defaultWidgetRenderer, renderKpiWidget, renderLineWidget, renderBarWidget, renderDonutWidget, renderFunnelWidget, renderTableWidget, BUILT_IN_WIDGET_KINDS, } from './lib/dashboard-kit/index.js';
|
|
83
83
|
export type { DashboardGridApi, DashboardGridOptions, DashboardGridHandle, CellRect, WorldRect, DashboardGridGeometry, TileDelta, DashboardOptions, DashboardSpec, DashboardSnapshot, DragHandleOption, DragGripOptions, DashboardHandle, DashboardViewSpec, DashboardWidgetSpec, SectionCaption, SectionCaptionOptions, SectionCaptionAction, SectionCaptionFont, WidgetHandle, WidgetRenderer, KpiWidgetData, LineWidgetData, LineSeries, BarWidgetData, DonutWidgetData, FunnelWidgetData, TableWidgetData, } from './lib/dashboard-kit/index.js';
|
|
84
84
|
export { bindStencilPalette, bindShapeDataPanel, ensureStencilKitStyles } from './lib/stencil-kit/index.js';
|
|
85
85
|
export type { StencilPaletteApi, StencilPaletteOptions, StencilPaletteHandle, ShapeDataPanelApi, ShapeDataPanelOptions, ShapeDataPanelHandle, } from './lib/stencil-kit/index.js';
|
package/src/index.js
CHANGED
|
@@ -230,7 +230,7 @@ export { erTable, umlClass, erTables, umlClasses, CardHandle, ErTable, ErField,
|
|
|
230
230
|
// GridItemConfig. Element-local code, so the Phase-0 wholesale re-export
|
|
231
231
|
// below does NOT cover it — this curated block is its only door.
|
|
232
232
|
// ===========================================================================
|
|
233
|
-
export { bindDashboardGrid, rowHeightFor, boardHeightFor, columnUnitFor, cellToRect, pointToCell, sizeToSpan, gridItemFromCell, cellFromGridItem, buildCommitCommands, ensureDashboardKitStyles, DASHBOARD_KIT_STYLE_ID,
|
|
233
|
+
export { bindDashboardGrid, normalizeCaption, captionReserve, captionBandHeight, paintCaptionBand, CAPTION_HEIGHT, CAPTION_HEIGHT_SUBTITLE, CAPTION_HEIGHT_TIGHT, CAPTION_PASS_THROUGH, rowHeightFor, boardHeightFor, columnUnitFor, cellToRect, pointToCell, sizeToSpan, gridItemFromCell, cellFromGridItem, buildCommitCommands, ensureDashboardKitStyles, DASHBOARD_KIT_STYLE_ID,
|
|
234
234
|
// The DATA-FIRST authoring API — the erDiagram()/umlDiagram() equivalent.
|
|
235
235
|
dashboard,
|
|
236
236
|
// …and the built-in renderers behind `kind`, so a dashboard is useful with
|
|
@@ -40,7 +40,12 @@ export interface SectionCaptionOptions {
|
|
|
40
40
|
description?: string;
|
|
41
41
|
/** A glyph before the text (emoji or short text). */
|
|
42
42
|
icon?: string;
|
|
43
|
-
/**
|
|
43
|
+
/**
|
|
44
|
+
* 'inside' (default): a band across the top of the frame. 'tab': the same
|
|
45
|
+
* band sized to its text, a label chip at the leading corner. BOTH reserve
|
|
46
|
+
* their height inside the SECTION's own cell — a header that hangs over the
|
|
47
|
+
* neighbour above is the overlap bug this feature exists to end.
|
|
48
|
+
*/
|
|
44
49
|
position?: 'inside' | 'tab';
|
|
45
50
|
/** Horizontal alignment (default 'start', mirrored on RTL). */
|
|
46
51
|
align?: 'start' | 'center' | 'end';
|
|
@@ -73,6 +78,10 @@ export declare const CAPTION_HEIGHT_SUBTITLE = 44;
|
|
|
73
78
|
export declare const CAPTION_HEIGHT_TIGHT = 22;
|
|
74
79
|
/** A section shorter than this steps its band down to the tight tier. */
|
|
75
80
|
export declare const CAPTION_TIGHT_BELOW = 90;
|
|
81
|
+
/** A band never paints thinner than this. */
|
|
82
|
+
export declare const CAPTION_MIN_HEIGHT = 16;
|
|
83
|
+
/** …nor takes the last pixels: the children keep at least this much. */
|
|
84
|
+
export declare const CAPTION_MIN_CONTENT = 20;
|
|
76
85
|
export declare const CAPTION_PASS_THROUGH = "button, a, input, select, textarea, [data-axdb-pass]";
|
|
77
86
|
/** The caption as options, or null when there is none. `title` fills the text. */
|
|
78
87
|
export declare function normalizeCaption(c: SectionCaption | undefined, title?: string): SectionCaptionOptions | null;
|
|
@@ -82,11 +91,54 @@ export declare function captionOfGroup(grp: {
|
|
|
82
91
|
name?: string;
|
|
83
92
|
} | undefined): SectionCaptionOptions | null;
|
|
84
93
|
export declare function pairOf(v: number | [number, number] | undefined, dflt: [number, number]): [number, number];
|
|
94
|
+
/** The shape of the model and groups this module needs — structural, so the kit's
|
|
95
|
+
* caption logic stays free of engine imports. */
|
|
96
|
+
interface CaptionGroup {
|
|
97
|
+
id: string;
|
|
98
|
+
name?: string;
|
|
99
|
+
members?: {
|
|
100
|
+
has(id: string): boolean;
|
|
101
|
+
} | null;
|
|
102
|
+
getMetadata(key: string): unknown;
|
|
103
|
+
}
|
|
104
|
+
interface CaptionDiagram {
|
|
105
|
+
getGroups?(): CaptionGroup[];
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Does the board that HOLDS this section paint section chrome? Only the grid
|
|
109
|
+
* binder draws the slab overlay the band lives on, so a section inside a
|
|
110
|
+
* SPLIT board has no band — and must not reserve space for one.
|
|
111
|
+
*
|
|
112
|
+
* Read from the parent group's persisted layout rather than from the live
|
|
113
|
+
* binder registry: membership and metadata both exist before either binder is
|
|
114
|
+
* built, so the answer is the same on the first projection as on the hundredth
|
|
115
|
+
* (asking the registry made the reserve depend on which binder registered
|
|
116
|
+
* first, and a child binds BEFORE its parent).
|
|
117
|
+
*/
|
|
118
|
+
export declare function parentPaintsSectionChrome(diagram: CaptionDiagram, group: CaptionGroup): boolean;
|
|
119
|
+
/** A section's own caption reserve: its band's pixels, or 0 when nothing paints one. */
|
|
120
|
+
export declare function sectionCaptionReserve(diagram: CaptionDiagram, group: CaptionGroup & {
|
|
121
|
+
size?: {
|
|
122
|
+
height: number;
|
|
123
|
+
} | null;
|
|
124
|
+
}, isStatic: boolean): number;
|
|
85
125
|
/** Painted at all? `show: 'design'` disappears under static. */
|
|
86
126
|
export declare function captionPainted(c: SectionCaptionOptions | null, isStatic: boolean): boolean;
|
|
87
|
-
/**
|
|
127
|
+
/**
|
|
128
|
+
* The band's height for a section of `sectionH` px: the authored height (or
|
|
129
|
+
* the tier), CLAMPED so the children always keep `CAPTION_MIN_CONTENT` px. A
|
|
130
|
+
* one-row section used to hand its whole 34 px to a 22 px band and paint a
|
|
131
|
+
* 12 px sliver of a child.
|
|
132
|
+
*/
|
|
88
133
|
export declare function captionBandHeight(c: SectionCaptionOptions, sectionH: number): number;
|
|
89
|
-
/**
|
|
134
|
+
/**
|
|
135
|
+
* Pixels the nested board's frame gives up at the top: the band plus its
|
|
136
|
+
* vertical margins. A TAB reserves too — it is a narrower band, not a header
|
|
137
|
+
* hanging over whatever the parent board put above the section (the stress
|
|
138
|
+
* page had one lying across a chart's legend, and one clipped off the top of
|
|
139
|
+
* the canvas). Only `show: 'hover'` reserves nothing: it is an overlay by
|
|
140
|
+
* definition, and it paints opaque so the content beneath stays readable.
|
|
141
|
+
*/
|
|
90
142
|
export declare function captionReserve(c: SectionCaptionOptions | null, ctx: {
|
|
91
143
|
static: boolean;
|
|
92
144
|
sectionH: number;
|
|
@@ -96,8 +148,12 @@ export declare function captionKey(c: SectionCaptionOptions, ctx: {
|
|
|
96
148
|
rtl: boolean;
|
|
97
149
|
static: boolean;
|
|
98
150
|
}): string;
|
|
99
|
-
/**
|
|
100
|
-
|
|
151
|
+
/**
|
|
152
|
+
* Is the band too short to carry a subtitle, actions or full-size text? Asked
|
|
153
|
+
* of the band's ACTUAL height, so an authored `height` that survives on a
|
|
154
|
+
* short section keeps its content instead of being styled as squeezed.
|
|
155
|
+
*/
|
|
156
|
+
export declare const captionTight: (c: SectionCaptionOptions, sectionH: number) => boolean;
|
|
101
157
|
/** The per-sync geometry of a painted band: height and tier follow the section's live size. */
|
|
102
158
|
export declare function sizeCaptionBand(band: HTMLElement, c: SectionCaptionOptions, sectionH: number): void;
|
|
103
159
|
/**
|
|
@@ -121,3 +177,4 @@ export declare function paintCaptionBand(band: HTMLElement, c: SectionCaptionOpt
|
|
|
121
177
|
render?: (host: HTMLElement) => void;
|
|
122
178
|
onAction?: (actionId: string) => void;
|
|
123
179
|
}): void;
|
|
180
|
+
export {};
|
|
@@ -19,6 +19,10 @@ export const CAPTION_HEIGHT_SUBTITLE = 44;
|
|
|
19
19
|
export const CAPTION_HEIGHT_TIGHT = 22;
|
|
20
20
|
/** A section shorter than this steps its band down to the tight tier. */
|
|
21
21
|
export const CAPTION_TIGHT_BELOW = 90;
|
|
22
|
+
/** A band never paints thinner than this. */
|
|
23
|
+
export const CAPTION_MIN_HEIGHT = 16;
|
|
24
|
+
/** …nor takes the last pixels: the children keep at least this much. */
|
|
25
|
+
export const CAPTION_MIN_CONTENT = 20;
|
|
22
26
|
export const CAPTION_PASS_THROUGH = 'button, a, input, select, textarea, [data-axdb-pass]';
|
|
23
27
|
/** The caption as options, or null when there is none. `title` fills the text. */
|
|
24
28
|
export function normalizeCaption(c, title) {
|
|
@@ -44,24 +48,69 @@ export function pairOf(v, dflt) {
|
|
|
44
48
|
return dflt;
|
|
45
49
|
return typeof v === 'number' ? [v, v] : [v[0], v[1]];
|
|
46
50
|
}
|
|
51
|
+
/**
|
|
52
|
+
* Does the board that HOLDS this section paint section chrome? Only the grid
|
|
53
|
+
* binder draws the slab overlay the band lives on, so a section inside a
|
|
54
|
+
* SPLIT board has no band — and must not reserve space for one.
|
|
55
|
+
*
|
|
56
|
+
* Read from the parent group's persisted layout rather than from the live
|
|
57
|
+
* binder registry: membership and metadata both exist before either binder is
|
|
58
|
+
* built, so the answer is the same on the first projection as on the hundredth
|
|
59
|
+
* (asking the registry made the reserve depend on which binder registered
|
|
60
|
+
* first, and a child binds BEFORE its parent).
|
|
61
|
+
*/
|
|
62
|
+
export function parentPaintsSectionChrome(diagram, group) {
|
|
63
|
+
var _a, _b, _c, _d;
|
|
64
|
+
const groups = (_b = (_a = diagram.getGroups) === null || _a === void 0 ? void 0 : _a.call(diagram)) !== null && _b !== void 0 ? _b : [];
|
|
65
|
+
for (const g of groups) {
|
|
66
|
+
if (g === group || !((_c = g.members) === null || _c === void 0 ? void 0 : _c.has(group.id)))
|
|
67
|
+
continue;
|
|
68
|
+
const board = g.getMetadata('dashboardBoard');
|
|
69
|
+
return ((_d = board === null || board === void 0 ? void 0 : board.layout) !== null && _d !== void 0 ? _d : 'grid') !== 'split';
|
|
70
|
+
}
|
|
71
|
+
return false; // not a member of any board: a view, which carries no caption
|
|
72
|
+
}
|
|
73
|
+
/** A section's own caption reserve: its band's pixels, or 0 when nothing paints one. */
|
|
74
|
+
export function sectionCaptionReserve(diagram, group, isStatic) {
|
|
75
|
+
var _a, _b;
|
|
76
|
+
if (!parentPaintsSectionChrome(diagram, group))
|
|
77
|
+
return 0;
|
|
78
|
+
return captionReserve(captionOfGroup(group), { static: isStatic, sectionH: (_b = (_a = group.size) === null || _a === void 0 ? void 0 : _a.height) !== null && _b !== void 0 ? _b : 0 });
|
|
79
|
+
}
|
|
47
80
|
/** Painted at all? `show: 'design'` disappears under static. */
|
|
48
81
|
export function captionPainted(c, isStatic) {
|
|
49
82
|
if (!c)
|
|
50
83
|
return false;
|
|
51
84
|
return !(c.show === 'design' && isStatic);
|
|
52
85
|
}
|
|
53
|
-
/**
|
|
86
|
+
/**
|
|
87
|
+
* The band's height for a section of `sectionH` px: the authored height (or
|
|
88
|
+
* the tier), CLAMPED so the children always keep `CAPTION_MIN_CONTENT` px. A
|
|
89
|
+
* one-row section used to hand its whole 34 px to a 22 px band and paint a
|
|
90
|
+
* 12 px sliver of a child.
|
|
91
|
+
*/
|
|
54
92
|
export function captionBandHeight(c, sectionH) {
|
|
55
93
|
var _a;
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
94
|
+
// An authored `height` WINS over the squeezed tier — the tier is a default
|
|
95
|
+
// for a short section, not an override of what the app asked for. The clamp
|
|
96
|
+
// still protects the children either way.
|
|
97
|
+
const want = (_a = c.height) !== null && _a !== void 0 ? _a : (sectionH > 0 && sectionH < CAPTION_TIGHT_BELOW ? CAPTION_HEIGHT_TIGHT : c.subtitle ? CAPTION_HEIGHT_SUBTITLE : CAPTION_HEIGHT);
|
|
98
|
+
if (sectionH <= 0)
|
|
99
|
+
return want;
|
|
100
|
+
return Math.max(CAPTION_MIN_HEIGHT, Math.min(want, sectionH - CAPTION_MIN_CONTENT));
|
|
59
101
|
}
|
|
60
|
-
/**
|
|
102
|
+
/**
|
|
103
|
+
* Pixels the nested board's frame gives up at the top: the band plus its
|
|
104
|
+
* vertical margins. A TAB reserves too — it is a narrower band, not a header
|
|
105
|
+
* hanging over whatever the parent board put above the section (the stress
|
|
106
|
+
* page had one lying across a chart's legend, and one clipped off the top of
|
|
107
|
+
* the canvas). Only `show: 'hover'` reserves nothing: it is an overlay by
|
|
108
|
+
* definition, and it paints opaque so the content beneath stays readable.
|
|
109
|
+
*/
|
|
61
110
|
export function captionReserve(c, ctx) {
|
|
62
111
|
if (!c || !captionPainted(c, ctx.static))
|
|
63
112
|
return 0;
|
|
64
|
-
if (c.
|
|
113
|
+
if (c.show === 'hover')
|
|
65
114
|
return 0;
|
|
66
115
|
const [mv] = pairOf(c.margin, [0, 0]);
|
|
67
116
|
return captionBandHeight(c, ctx.sectionH) + 2 * mv;
|
|
@@ -70,15 +119,22 @@ export function captionReserve(c, ctx) {
|
|
|
70
119
|
export function captionKey(c, ctx) {
|
|
71
120
|
return JSON.stringify([c, ctx.rtl, ctx.static]);
|
|
72
121
|
}
|
|
73
|
-
/**
|
|
74
|
-
|
|
122
|
+
/**
|
|
123
|
+
* Is the band too short to carry a subtitle, actions or full-size text? Asked
|
|
124
|
+
* of the band's ACTUAL height, so an authored `height` that survives on a
|
|
125
|
+
* short section keeps its content instead of being styled as squeezed.
|
|
126
|
+
*/
|
|
127
|
+
export const captionTight = (c, sectionH) => captionBandHeight(c, sectionH) <= CAPTION_HEIGHT_TIGHT;
|
|
128
|
+
/** Where the band's box sits horizontally: a tab hugs the LEADING edge, mirrored on RTL. */
|
|
129
|
+
function bandSides(c, mh, rtl) {
|
|
130
|
+
if (c.position !== 'tab')
|
|
131
|
+
return { left: `${mh}px`, right: `${mh}px` };
|
|
132
|
+
return rtl ? { left: 'auto', right: `${mh}px` } : { left: `${mh}px`, right: 'auto' };
|
|
133
|
+
}
|
|
75
134
|
/** The per-sync geometry of a painted band: height and tier follow the section's live size. */
|
|
76
135
|
export function sizeCaptionBand(band, c, sectionH) {
|
|
77
|
-
|
|
78
|
-
band.
|
|
79
|
-
band.style.height = `${h}px`;
|
|
80
|
-
if (c.position === 'tab')
|
|
81
|
-
band.style.top = `${-h}px`;
|
|
136
|
+
band.classList.toggle('axdb-slab-h--tight', captionTight(c, sectionH));
|
|
137
|
+
band.style.height = `${captionBandHeight(c, sectionH)}px`;
|
|
82
138
|
}
|
|
83
139
|
/**
|
|
84
140
|
* Is a press on `target`, inside `band`, content rather than the band? An
|
|
@@ -119,7 +175,7 @@ export function paintCaptionBand(band, c, ctx) {
|
|
|
119
175
|
const cls = (name, on) => band.classList.toggle(name, on);
|
|
120
176
|
cls('axdb-slab-h--tab', c.position === 'tab');
|
|
121
177
|
cls('axdb-slab-h--hover', c.show === 'hover');
|
|
122
|
-
cls('axdb-slab-h--tight', captionTight(ctx.sectionH));
|
|
178
|
+
cls('axdb-slab-h--tight', captionTight(c, ctx.sectionH));
|
|
123
179
|
cls('axdb-slab-h--center', c.align === 'center');
|
|
124
180
|
cls('axdb-slab-h--end', c.align === 'end');
|
|
125
181
|
cls('axdb-slab-h--vtop', c.valign === 'top');
|
|
@@ -129,9 +185,10 @@ export function paintCaptionBand(band, c, ctx) {
|
|
|
129
185
|
band.classList.add(k);
|
|
130
186
|
band.setAttribute('dir', ctx.rtl ? 'rtl' : 'ltr');
|
|
131
187
|
band.style.height = `${h}px`;
|
|
132
|
-
band.style.top =
|
|
133
|
-
|
|
134
|
-
band.style.
|
|
188
|
+
band.style.top = `${mv}px`;
|
|
189
|
+
const sides = bandSides(c, mh, ctx.rtl);
|
|
190
|
+
band.style.left = sides.left;
|
|
191
|
+
band.style.right = sides.right;
|
|
135
192
|
const v = (name, value) => {
|
|
136
193
|
if (value === undefined)
|
|
137
194
|
band.style.removeProperty(name);
|
|
@@ -54,7 +54,7 @@ import { AddToGroupCommand, BatchCommand, Command, GridPackEngine, RemoveFromGro
|
|
|
54
54
|
import { LiveRegionController, registerTool } from '@grafloria/renderer';
|
|
55
55
|
import { buildCommitCommands, cellFromGridItem, cellToRect, columnUnitFor, gridItemFromCell, pointToCell, rowHeightFor, sizeToSpan, } from './grid-mapping.js';
|
|
56
56
|
import { ensureDashboardKitStyles } from './styles.js';
|
|
57
|
-
import { captionOfGroup, captionPainted, captionPassThrough, captionKey,
|
|
57
|
+
import { captionOfGroup, captionPainted, captionPassThrough, captionKey, paintCaptionBand, sectionCaptionReserve, sizeCaptionBand } from './caption.js';
|
|
58
58
|
/** The class of the painted grip element (a child of the node host). */
|
|
59
59
|
export const GRIP_CLASS = 'axdb-grip';
|
|
60
60
|
/** The container class that turns the caption strip's grip dots on. */
|
|
@@ -453,7 +453,7 @@ export function bindDashboardGrid(api, group, options = {}) {
|
|
|
453
453
|
* band is outside `containsWorld`, so a press on it is the PARENT's (it
|
|
454
454
|
* selects the section) rather than an empty press of this board.
|
|
455
455
|
*/
|
|
456
|
-
const ownReserve = () =>
|
|
456
|
+
const ownReserve = () => sectionCaptionReserve(diagram, group, isStatic);
|
|
457
457
|
const frame = () => {
|
|
458
458
|
var _a, _b, _c, _d;
|
|
459
459
|
const r = ownReserve();
|
|
@@ -1177,6 +1177,7 @@ export function bindDashboardGrid(api, group, options = {}) {
|
|
|
1177
1177
|
for (const [id, el] of slabEls) {
|
|
1178
1178
|
if (!seen.has(id)) {
|
|
1179
1179
|
el.remove();
|
|
1180
|
+
hoverSlabs.delete(el);
|
|
1180
1181
|
slabEls.delete(id);
|
|
1181
1182
|
}
|
|
1182
1183
|
}
|
|
@@ -1193,9 +1194,18 @@ export function bindDashboardGrid(api, group, options = {}) {
|
|
|
1193
1194
|
let band = el.querySelector(':scope > .axdb-slab-h');
|
|
1194
1195
|
if (!cap || !captionPainted(cap, isStatic)) {
|
|
1195
1196
|
band === null || band === void 0 ? void 0 : band.remove();
|
|
1197
|
+
hoverSlabs.delete(el);
|
|
1198
|
+
el.classList.remove('axdb-slab--hot');
|
|
1196
1199
|
el.removeAttribute('aria-label');
|
|
1200
|
+
el.removeAttribute('role');
|
|
1197
1201
|
return;
|
|
1198
1202
|
}
|
|
1203
|
+
if (cap.show === 'hover')
|
|
1204
|
+
hoverSlabs.add(el);
|
|
1205
|
+
else {
|
|
1206
|
+
hoverSlabs.delete(el);
|
|
1207
|
+
el.classList.remove('axdb-slab--hot');
|
|
1208
|
+
}
|
|
1199
1209
|
const ctx = { rtl, static: isStatic, sectionH };
|
|
1200
1210
|
const key = captionKey(cap, ctx);
|
|
1201
1211
|
if (band && band.getAttribute('data-key') === key) {
|
|
@@ -1208,10 +1218,18 @@ export function bindDashboardGrid(api, group, options = {}) {
|
|
|
1208
1218
|
const render = options.renderCaption;
|
|
1209
1219
|
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); } }));
|
|
1210
1220
|
band.setAttribute('data-key', key);
|
|
1211
|
-
|
|
1221
|
+
// A named group, so the caption text is the section's name in the
|
|
1222
|
+
// accessibility tree rather than a stray label on an unnamed div. (The
|
|
1223
|
+
// band is not a tab stop yet — the actions are deliberately out of the
|
|
1224
|
+
// tab order so a board keeps exactly ONE stop; see the plan.)
|
|
1225
|
+
if (cap.text) {
|
|
1226
|
+
el.setAttribute('role', 'group');
|
|
1212
1227
|
el.setAttribute('aria-label', cap.text);
|
|
1213
|
-
|
|
1228
|
+
}
|
|
1229
|
+
else {
|
|
1230
|
+
el.removeAttribute('role');
|
|
1214
1231
|
el.removeAttribute('aria-label');
|
|
1232
|
+
}
|
|
1215
1233
|
};
|
|
1216
1234
|
const insideMemberGroupFrame = (x, y) => {
|
|
1217
1235
|
var _a;
|
|
@@ -2412,10 +2430,33 @@ export function bindDashboardGrid(api, group, options = {}) {
|
|
|
2412
2430
|
* container; the corner handle keeps its own cursor from the stylesheet.
|
|
2413
2431
|
*/
|
|
2414
2432
|
let hoverHost = null;
|
|
2433
|
+
/**
|
|
2434
|
+
* A `show: 'hover'` caption cannot ride CSS `:hover`: the slab overlay takes
|
|
2435
|
+
* no pointer (by design — it must never steal a press), and while the band
|
|
2436
|
+
* is hidden it takes none either, so nothing in the section is ever hovered
|
|
2437
|
+
* in CSS terms. The binder already tracks the pointer; it marks the section
|
|
2438
|
+
* under it instead.
|
|
2439
|
+
*/
|
|
2440
|
+
/** Only the sections carrying a `show: 'hover'` band — usually none, so the
|
|
2441
|
+
* pointer handler costs nothing on a board that has no hover caption. */
|
|
2442
|
+
const hoverSlabs = new Set();
|
|
2443
|
+
const markHotSection = (clientX, clientY) => {
|
|
2444
|
+
if (!hoverSlabs.size)
|
|
2445
|
+
return;
|
|
2446
|
+
for (const el of hoverSlabs) {
|
|
2447
|
+
const r = el.getBoundingClientRect();
|
|
2448
|
+
el.classList.toggle('axdb-slab--hot', clientX >= r.left && clientX <= r.right && clientY >= r.top && clientY <= r.bottom);
|
|
2449
|
+
}
|
|
2450
|
+
};
|
|
2451
|
+
const onHoverLeave = () => {
|
|
2452
|
+
for (const el of hoverSlabs)
|
|
2453
|
+
el.classList.remove('axdb-slab--hot');
|
|
2454
|
+
};
|
|
2415
2455
|
const onHover = (e) => {
|
|
2416
2456
|
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
2417
2457
|
if (disposed || gesture)
|
|
2418
2458
|
return;
|
|
2459
|
+
markHotSection(e.clientX, e.clientY);
|
|
2419
2460
|
// The event may target the host, its content, or (when a host's content
|
|
2420
2461
|
// is pointer-transparent) the canvas under it — find the member host by
|
|
2421
2462
|
// the pointer's position in that case.
|
|
@@ -2465,6 +2506,7 @@ export function bindDashboardGrid(api, group, options = {}) {
|
|
|
2465
2506
|
host.removeAttribute('data-axdb-edge');
|
|
2466
2507
|
};
|
|
2467
2508
|
api.container.addEventListener('pointermove', onHover, { passive: true });
|
|
2509
|
+
api.container.addEventListener('pointerleave', onHoverLeave, { passive: true });
|
|
2468
2510
|
/**
|
|
2469
2511
|
* STATIC BOARDS LET CONTENT BE CLICKED. The renderer prevents the default of
|
|
2470
2512
|
* every press a tool claims, which cancels the compatibility mouse events —
|
|
@@ -3041,6 +3083,7 @@ export function bindDashboardGrid(api, group, options = {}) {
|
|
|
3041
3083
|
peersOnCanvas().delete(selfPeer);
|
|
3042
3084
|
unregisterTool();
|
|
3043
3085
|
api.container.removeEventListener('pointermove', onHover);
|
|
3086
|
+
api.container.removeEventListener('pointerleave', onHoverLeave);
|
|
3044
3087
|
guardedLayer === null || guardedLayer === void 0 ? void 0 : guardedLayer.removeEventListener('pointerdown', staticGuard);
|
|
3045
3088
|
api.container.removeEventListener('focusin', onFocusIn);
|
|
3046
3089
|
api.container.removeEventListener('keydown', onKey);
|
|
@@ -3059,6 +3102,7 @@ export function bindDashboardGrid(api, group, options = {}) {
|
|
|
3059
3102
|
for (const el of slabEls.values())
|
|
3060
3103
|
el.remove();
|
|
3061
3104
|
slabEls.clear();
|
|
3105
|
+
hoverSlabs.clear();
|
|
3062
3106
|
// A rebind inside the 60 ms window (a layout switch right after an undo)
|
|
3063
3107
|
// disposed this binder with the timer pending — the host kept its
|
|
3064
3108
|
// lifted ghost for good (visual gate, nested-containers ⑤).
|
|
@@ -29,7 +29,7 @@ import { anyEdge, clearOtherSelections, dragHandleSelector, gripHostOf, gripOf,
|
|
|
29
29
|
import { cellFromGridItem } from './grid-mapping.js';
|
|
30
30
|
import { addSplitLeaf, cellsFromSplit, cloneSplit, dividersOf, groupRectsOf, insertSplitLeaf, moveSplitDivider, normalizeSplit, pathToLeaf, projectSplit, removeSplitLeaf, splitFromCells, splitLeaves, } from './split-layout.js';
|
|
31
31
|
import { ensureDashboardKitStyles } from './styles.js';
|
|
32
|
-
import {
|
|
32
|
+
import { sectionCaptionReserve } from './caption.js';
|
|
33
33
|
/** Group metadata key the tree persists under. */
|
|
34
34
|
export const SPLIT_TREE_KEY = 'dashboardTree';
|
|
35
35
|
/**
|
|
@@ -105,8 +105,14 @@ export function bindDashboardSplit(api, group, options = {}) {
|
|
|
105
105
|
let focusedId;
|
|
106
106
|
const live = liveRegionFor(api.container);
|
|
107
107
|
// -- geometry ---------------------------------------------------------------
|
|
108
|
-
/**
|
|
109
|
-
|
|
108
|
+
/**
|
|
109
|
+
* Our own caption band's pixels, given up at the top of the pane — but only
|
|
110
|
+
* when the parent board actually paints one (see BinderPeer.paintsCaptions).
|
|
111
|
+
* A split board paints no section chrome, so a captioned section inside one
|
|
112
|
+
* reserves nothing until the tab-container round gives the split binder its
|
|
113
|
+
* own overlays.
|
|
114
|
+
*/
|
|
115
|
+
const ownReserve = () => sectionCaptionReserve(diagram, group, isStatic);
|
|
110
116
|
const frame = () => {
|
|
111
117
|
var _a, _b, _c, _d;
|
|
112
118
|
const r = ownReserve();
|
|
@@ -373,8 +373,10 @@ const CSS = `
|
|
|
373
373
|
position: absolute; box-sizing: border-box; pointer-events: auto; z-index: 1;
|
|
374
374
|
display: flex; align-items: center; gap: 6px; min-width: 0;
|
|
375
375
|
padding: var(--axdb-caption-pad, 0 10px);
|
|
376
|
-
background: var(--axdb-caption-bg, rgba(31, 36, 48, .
|
|
377
|
-
|
|
376
|
+
background: var(--axdb-caption-bg, rgba(31, 36, 48, .055));
|
|
377
|
+
/* A hairline by default: two bands meeting (a captioned section inside a
|
|
378
|
+
captioned section) read as two headers, not one grey block. */
|
|
379
|
+
border-bottom: var(--axdb-caption-border, 1px solid rgba(31, 36, 48, .1));
|
|
378
380
|
color: var(--axdb-caption-fg, #1f2430);
|
|
379
381
|
font: var(--axdb-caption-font-weight, 600) var(--axdb-caption-font-size, 13px)/1.2 var(--axdb-caption-font-family, system-ui, -apple-system, "Segoe UI", sans-serif);
|
|
380
382
|
text-transform: var(--axdb-caption-transform, none);
|
|
@@ -388,13 +390,27 @@ const CSS = `
|
|
|
388
390
|
.grafloria-html-layer > .axdb-slab > .axdb-slab-h.axdb-slab-h--end { justify-content: flex-end; }
|
|
389
391
|
.grafloria-html-layer > .axdb-slab > .axdb-slab-h.axdb-slab-h--vtop { align-items: flex-start; }
|
|
390
392
|
.grafloria-html-layer > .axdb-slab > .axdb-slab-h.axdb-slab-h--vbottom { align-items: flex-end; }
|
|
391
|
-
/* 'tab':
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
393
|
+
/* 'tab': the same band sized to its text, a chip at the leading corner. It
|
|
394
|
+
reserves its height like 'inside' — see captionReserve. */
|
|
395
|
+
.grafloria-html-layer > .axdb-slab > .axdb-slab-h.axdb-slab-h--tab {
|
|
396
|
+
max-width: 100%; border-radius: var(--axdb-rs-radius, 3px);
|
|
397
|
+
border-bottom: var(--axdb-caption-border-tab, none);
|
|
398
|
+
box-shadow: inset 0 0 0 1px rgba(31, 36, 48, .08);
|
|
399
|
+
}
|
|
400
|
+
/* 'hover': an overlay. It reserves nothing, so while hidden it must not take
|
|
401
|
+
the pointer (an invisible band swallowed clicks on the content beneath),
|
|
402
|
+
and while shown it paints OPAQUE — a 5% tint over a chart is a smear. */
|
|
403
|
+
.grafloria-html-layer > .axdb-slab > .axdb-slab-h.axdb-slab-h--hover {
|
|
404
|
+
opacity: 0; pointer-events: none;
|
|
405
|
+
background: var(--axdb-caption-bg, #fff);
|
|
406
|
+
box-shadow: 0 1px 4px rgba(31, 36, 48, .16);
|
|
407
|
+
}
|
|
408
|
+
/* .axdb-slab--hot is set by the binder from the live pointer (the overlay
|
|
409
|
+
takes no pointer of its own, so CSS :hover can never fire on it). */
|
|
410
|
+
.grafloria-html-layer > .axdb-slab.axdb-slab--hot > .axdb-slab-h.axdb-slab-h--hover,
|
|
411
|
+
.grafloria-html-layer > .axdb-slab.axdb-slab--selected > .axdb-slab-h.axdb-slab-h--hover { pointer-events: auto; opacity: 1; }
|
|
412
|
+
/* the actions of a hovered or selected section */
|
|
413
|
+
.grafloria-html-layer > .axdb-slab.axdb-slab--hot > .axdb-slab-h > .axdb-slab-h-actions { opacity: 1; }
|
|
398
414
|
/* the tight tier: a section under 90 px */
|
|
399
415
|
.grafloria-html-layer > .axdb-slab > .axdb-slab-h.axdb-slab-h--tight { font-size: min(var(--axdb-caption-font-size, 12px), 12px); gap: 4px; }
|
|
400
416
|
.axdb-slab-h--tight .axdb-slab-h-sub, .axdb-slab-h--tight .axdb-slab-h-actions { display: none; }
|
|
@@ -416,7 +432,13 @@ const CSS = `
|
|
|
416
432
|
.axdb-slab-h-action[disabled]:hover { background: none; }
|
|
417
433
|
.grafloria-html-layer > .axdb-slab.axdb-slab--static > .axdb-slab-h { cursor: default; }
|
|
418
434
|
@media (prefers-color-scheme: dark) {
|
|
419
|
-
.grafloria-html-layer > .axdb-slab > .axdb-slab-h {
|
|
435
|
+
.grafloria-html-layer > .axdb-slab > .axdb-slab-h {
|
|
436
|
+
background: var(--axdb-caption-bg, rgba(236, 238, 244, .07));
|
|
437
|
+
color: var(--axdb-caption-fg, #eceef4);
|
|
438
|
+
border-bottom: var(--axdb-caption-border, 1px solid rgba(236, 238, 244, .12));
|
|
439
|
+
}
|
|
440
|
+
.grafloria-html-layer > .axdb-slab > .axdb-slab-h.axdb-slab-h--tab { box-shadow: inset 0 0 0 1px rgba(236, 238, 244, .12); }
|
|
441
|
+
.grafloria-html-layer > .axdb-slab > .axdb-slab-h.axdb-slab-h--hover { background: var(--axdb-caption-bg, #1a1d25); box-shadow: 0 1px 4px rgba(0, 0, 0, .5); }
|
|
420
442
|
.axdb-slab-h-action:hover { background: rgba(236, 238, 244, .1); }
|
|
421
443
|
}
|
|
422
444
|
|