@grafloria/element 0.4.34 → 0.4.36
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 +3 -0
- package/src/lib/dashboard-kit/dashboard.js +57 -26
- package/src/lib/dashboard-kit/grid-binder.d.ts +6 -0
- package/src/lib/dashboard-kit/grid-binder.js +272 -60
- package/src/lib/dashboard-kit/styles.d.ts +2 -0
- package/src/lib/dashboard-kit/styles.js +93 -56
- package/src/lib/load.js +5 -3
|
@@ -21,5 +21,7 @@
|
|
|
21
21
|
* (pages own their card innerHTML).
|
|
22
22
|
*/
|
|
23
23
|
export declare const DASHBOARD_KIT_STYLE_ID = "grafloria-dashboard-kit-styles";
|
|
24
|
+
/** The kit stylesheet as text — for a host that bundles its own CSS, and for the styling spec. */
|
|
25
|
+
export declare const DASHBOARD_KIT_CSS = "\n/* ===== reflow glide (armed on the html layer for the whole gesture) ===== */\n.grafloria-html-layer.axdb-glide > .grafloria-node-host {\n transition: left .28s cubic-bezier(.2, 0, .2, 1), top .28s cubic-bezier(.2, 0, .2, 1),\n width .28s cubic-bezier(.2, 0, .2, 1), height .28s cubic-bezier(.2, 0, .2, 1);\n}\n\n/* Motion is a preference (WCAG 2.3.3): no glide for those who asked for none. */\n@media (prefers-reduced-motion: reduce) {\n .grafloria-html-layer.axdb-glide > .grafloria-node-host { transition: none; }\n}\n\n/* ===== keyboard focus: the roving tab stop shows where it is (WCAG 2.4.7) ===== */\n.grafloria-html-layer > .grafloria-node-host:focus-visible {\n outline: 2px solid var(--axdb-accent, #3b52d9);\n outline-offset: 2px;\n border-radius: var(--axdb-rs-radius, 8px);\n}\n\n/* ===== the held tile: transition-exempt ghost, above everything ===== */\n.grafloria-html-layer > .grafloria-node-host.axdb-ghost,\n.grafloria-html-layer.axdb-glide > .grafloria-node-host.axdb-ghost {\n transition: none;\n z-index: 30;\n opacity: .85;\n cursor: grabbing;\n filter: drop-shadow(0 10px 16px rgba(16, 24, 40, .3));\n}\n/* Outside the board: release will REMOVE \u2014 dim the ghost to say so. */\n.grafloria-html-layer > .grafloria-node-host.axdb-ghost.axdb-out { opacity: .35; filter: grayscale(.6); }\n\n/* ===== the placeholder: dashed slab, truthful, never animated ===== */\n.grafloria-html-layer > .axdb-ph {\n position: absolute;\n border-radius: var(--axdb-rs-radius, 8px);\n background: rgba(30, 34, 45, .14);\n border: 2px dashed rgba(30, 34, 45, .28);\n box-sizing: border-box;\n pointer-events: none;\n z-index: 0;\n transition: none;\n}\n/* A slab move asked for a cell it cannot have (a locked section in the way): the wanted cell in the danger tone. */\n.grafloria-html-layer > .axdb-ph.axdb-ph--no { border-color: var(--axdb-danger, #b3123c); background: var(--axdb-danger-soft, rgba(179, 18, 60, .07)); }\n\n@media (prefers-color-scheme: dark) {\n .grafloria-html-layer > .axdb-ph { background: rgba(220, 225, 240, .12); border-color: rgba(220, 225, 240, .3); }\n}\n\n/* ===== corner resize handle (hover-revealed, radius-matched) ===== */\n:is(.grafloria-node-host, .axdb-slab) > .axdb-rs {\n position: absolute;\n right: 0;\n bottom: 0;\n width: 24px; /* WCAG 2.5.8: a 24-px minimum target (was 18) */\n height: 24px;\n cursor: nwse-resize;\n border-right: 3px solid rgba(120, 130, 148, .55);\n border-bottom: 3px solid rgba(120, 130, 148, .55);\n border-bottom-right-radius: var(--axdb-rs-radius, 8px);\n opacity: 0;\n transition: opacity .12s;\n z-index: 5;\n}\n.grafloria-node-host:hover > .axdb-rs { opacity: 1; }\n/* A finger never hovers: on touch devices the corner handle is always visible\n (review D8 \u2014 hover-only meant no resize at all on a tablet). */\n@media (hover: none) { .grafloria-node-host > .axdb-rs { opacity: .8; } }\n.grafloria-node-host > .axdb-rs:hover { border-color: #3b52d9; }\n/* RTL boards grow leftwards, so the grab corner mirrors with them. */\n:is(.grafloria-node-host, .axdb-slab) > .axdb-rs.axdb-rs--rtl {\n right: auto;\n left: 0;\n cursor: nesw-resize;\n border-right: none;\n border-left: 3px solid rgba(120, 130, 148, .55);\n border-bottom-right-radius: 0;\n border-bottom-left-radius: var(--axdb-rs-radius, 8px);\n}\n\n/* ===== palette drag-in chip (screen-space clone following the cursor) ===== */\n.axdb-drag-chip {\n position: fixed;\n z-index: 9999;\n pointer-events: none;\n opacity: .9;\n filter: drop-shadow(0 8px 14px rgba(16, 24, 40, .3));\n transition: opacity .12s, filter .12s;\n}\n/* A bounded board with no room refuses the entry: the chip dims to say so\n (the same signal a tile ghost gives outside the board). Without this rule\n the class was set and nothing showed \u2014 a refusal a user could not see. */\n.axdb-drag-chip.axdb-out { opacity: .35; filter: grayscale(.7) drop-shadow(0 8px 14px rgba(16, 24, 40, .2)); }\n/* A tab dragged off its strip: VS Code carries the TAB, not the editor, so\n what follows the pointer is a chip wearing the tab's own label. */\n.axdb-tab-chip {\n padding: 5px 12px;\n max-width: 220px;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n font: 500 12px/1.4 var(--axdb-font, system-ui, -apple-system, \"Segoe UI\", Roboto, sans-serif);\n color: var(--axdb-tabs-on-fg, #1f2430);\n background: var(--axdb-tabs-on-bg, #fff);\n border-radius: var(--axdb-tab-radius, 6px);\n box-shadow: 0 1px 2px rgba(16, 24, 40, .1), 0 0 0 1px rgba(31, 36, 48, .08);\n}\n@media (prefers-color-scheme: dark) {\n .axdb-tab-chip {\n color: var(--axdb-tabs-on-fg, #eceef4);\n background: var(--axdb-tabs-on-bg, #232836);\n box-shadow: 0 1px 2px rgba(0, 0, 0, .45), 0 0 0 1px rgba(236, 238, 244, .12);\n }\n}\n/* A tab held over ANOTHER group: it will join that group, not the board. The\n group's frame lights up, and its strip marks the slot the tab will take \u2014\n a bar before the tab it lands in front of, or after the last one. */\n.grafloria-html-layer > .axdb-join {\n position: absolute;\n z-index: 3;\n pointer-events: none;\n box-sizing: border-box;\n border: 2px dashed var(--axdb-accent, #3b52d9);\n background: var(--axdb-accent-soft, rgba(59, 82, 217, .08));\n border-radius: var(--axdb-rs-radius, 8px);\n}\n.axdb-tabs.axdb-tabs--drop { box-shadow: inset 0 -2px 0 var(--axdb-accent, #3b52d9); }\n.axdb-tab.axdb-tab--drop-before { position: relative; }\n.axdb-tab.axdb-tab--drop-before::before {\n content: \"\"; position: absolute; left: -3px; top: 4px; bottom: 4px; width: 3px;\n border-radius: 2px; background: var(--axdb-accent, #3b52d9);\n}\n.axdb-tabs.axdb-tabs--drop-end::after {\n content: \"\"; display: inline-block; width: 3px; height: 18px; margin: 0 4px;\n border-radius: 2px; background: var(--axdb-accent, #3b52d9); vertical-align: middle; align-self: center;\n}\n\n/* ===========================================================================\n BUILT-IN WIDGET CARDS \u2014 what widgets.ts paints when a page writes no\n renderWidget. Class-prefixed (never element selectors), so this can only\n ever style DOM the kit itself produced. Colours are CSS variables on the\n card, so a page re-skins every built-in widget by setting them once; the\n corner radius follows --axdb-rs-radius, which is also the placeholder's and\n the resize handle's, so the three can never drift apart.\n =========================================================================== */\n.axdb-widget {\n --axdb-ink: #1f2430;\n /* 5.95:1 on the card \u2014 WCAG 1.4.3 for the 9\u201311px captions this paints\n (the #7a8496 it replaced sat at 3.77:1, axe-core's first finding). */\n --axdb-muted: #5a6478;\n --axdb-grid: rgba(120, 130, 148, .22);\n --axdb-card: #fff;\n --axdb-line: #e5e8ef;\n --axdb-soft: rgba(120, 130, 148, .14);\n --axdb-up: #0f7a3d; /* 5.42:1 */\n --axdb-down: #be123c; /* 6.29:1 */\n /* The categorical palette, as tokens so the dark card can carry its own\n steps: every entry clears 3:1 against its card (WCAG 1.4.11). */\n --axdb-c1: #3b52d9; --axdb-c2: #0369a1; --axdb-c3: #0f766e;\n --axdb-c4: #b45309; --axdb-c5: #6d28d9; --axdb-c6: #475569;\n /* The header band: a panel wears its title in a band across its top, the\n way DevExpress, Grafana and Metabase panels do \u2014 tinted ground, hairline\n below, sentence case (0.4.36; the tracked uppercase micro-label before). */\n --axdb-head-bg: #f7f8fb;\n --axdb-head-fg: #2a3140;\n box-sizing: border-box;\n width: 100%;\n height: 100%;\n display: flex;\n flex-direction: column;\n overflow: hidden;\n padding: 0;\n background: var(--axdb-card);\n border: 1px solid var(--axdb-line);\n border-radius: var(--axdb-rs-radius, 8px);\n box-shadow: 0 1px 2px rgba(16, 24, 40, .04);\n font-family: system-ui, -apple-system, \"Segoe UI\", sans-serif;\n color: var(--axdb-ink);\n}\n/* The host is the card's size container (named, so a widget's inner parts can\n ask the TILE's size as well as their own body's), so the card can shed its\n padding as the tile gets short instead of clipping its own content. */\n.grafloria-html-layer > .grafloria-node-host { container: axdb-tile / size; }\n/* Two class hops on the header: these blocks precede the header's own rule\n below and would lose the cascade at equal specificity \u2014 at 54 px the margin\n stayed 8 px, the body shrank to 14 px and the KPI figure hid. */\n@container axdb-tile (max-height: 90px) {\n .axdb-widget > .axdb-widget-h { padding: 5px 12px; font-size: 11.5px; }\n .axdb-widget > .axdb-widget-b { padding: 6px 12px; }\n}\n/* The row floor: the band gives way to an inline label beside the figure. */\n@container axdb-tile (max-height: 46px) {\n .axdb-widget > .axdb-widget-h { padding: 0 0 0 12px; background: none; border-bottom: 0; }\n .axdb-widget > .axdb-widget-b { padding: 3px 12px; }\n}\n@container axdb-tile (max-height: 26px) {\n .axdb-widget > .axdb-widget-h { display: none; }\n .axdb-widget > .axdb-widget-b { padding: 1px 12px; }\n}\n.axdb-widget-h {\n display: flex;\n align-items: center;\n gap: 8px;\n flex: none;\n min-width: 0;\n padding: 9px 14px;\n background: var(--axdb-head-bg);\n border-bottom: 1px solid var(--axdb-line);\n font: 600 12.5px/1.3 system-ui, -apple-system, \"Segoe UI\", sans-serif;\n letter-spacing: -.005em;\n color: var(--axdb-head-fg);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n/* Drag-handle mode (DevExpress drags an item by its caption): the header is\n the grip and says so with a dot pattern and a grab cursor; the body keeps\n its own cursor because a press there starts nothing. */\n.axdb-drag-handle .axdb-widget > .axdb-widget-h { cursor: grab; }\n.axdb-drag-handle .axdb-widget > .axdb-widget-h::before {\n content: ''; flex: none; width: 8px; height: 12px; opacity: .55;\n background: radial-gradient(circle, currentColor 1.1px, transparent 1.5px) 0 0 / 4px 4px;\n}\n/* A PAINTED GRIP (dragHandle: { grip: true }): the only drag zone, a small\n dotted tab along the card's top edge. Inside sits in the header band and\n the header makes room for it; outside is a tab above the card, the\n DevExpress item bar. The host must not clip it. */\n.grafloria-node-host > .axdb-grip {\n position: absolute; z-index: 4; box-sizing: border-box; width: 24px; height: 12px;\n border: 1px solid var(--axdb-line, #e7eaf1); border-radius: 3px; background: var(--axdb-card, #fff);\n color: var(--axdb-muted, #5a6478); cursor: grab;\n /* Shown on the SELECTED widget only (the DevExpress designer shows an item's\n bar on the selected item): a press or keyboard focus selects, a void\n click clears. Hidden, it takes no pointer either. */\n opacity: 0; pointer-events: none; transition: opacity .12s ease;\n}\n.grafloria-node-host.axdb-selected > .axdb-grip, .grafloria-node-host:focus-within > .axdb-grip { opacity: 1; pointer-events: auto; }\n.grafloria-node-host > .axdb-grip::before {\n content: ''; position: absolute; left: 5px; top: 2px; width: 12px; height: 6px;\n background: radial-gradient(circle, currentColor 1px, transparent 1.4px) 0 0 / 4px 3px;\n}\n.grafloria-node-host > .axdb-grip:hover { border-color: var(--axdb-accent, #3b52d9); color: var(--axdb-accent, #3b52d9); }\n/* The selected card says so, quietly. --axdb-accent themes the grip's hover, the\n ring and the focus outline together; --axdb-accent-ring the ring alone. */\n.grafloria-node-host.axdb-selected > .axdb-widget { box-shadow: 0 0 0 1.5px var(--axdb-accent-ring, rgba(59, 82, 217, .55)), 0 1px 2px rgba(16, 24, 40, .05), 0 1px 3px rgba(16, 24, 40, .05); }\n/* While a resize edge is near, the host and everything in it (a chart canvas\n with its own cursor included) show the edge's cursor. */\n.grafloria-node-host[data-axdb-edge=\"ns-resize\"], .grafloria-node-host[data-axdb-edge=\"ns-resize\"] * { cursor: ns-resize !important; }\n.grafloria-node-host[data-axdb-edge=\"ew-resize\"], .grafloria-node-host[data-axdb-edge=\"ew-resize\"] * { cursor: ew-resize !important; }\n.grafloria-node-host[data-axdb-edge=\"nwse-resize\"], .grafloria-node-host[data-axdb-edge=\"nwse-resize\"] * { cursor: nwse-resize !important; }\n.grafloria-node-host[data-axdb-edge=\"nesw-resize\"], .grafloria-node-host[data-axdb-edge=\"nesw-resize\"] * { cursor: nesw-resize !important; }\n/* INSIDE: centred on the header's text line and flush with the card padding,\n per size tier (padding 13/15, then 8/14, 4/12, 2/12). */\n.grafloria-node-host > .axdb-grip--inside { top: 12px; }\n.grafloria-node-host > .axdb-grip--inside.axdb-grip--left { left: 15px; }\n.grafloria-node-host > .axdb-grip--inside.axdb-grip--right { right: 15px; }\n.grafloria-node-host > .axdb-grip--inside.axdb-grip--center { top: 3px; }\n@container axdb-tile (max-height: 90px) {\n .grafloria-node-host > .axdb-grip--inside { top: 9px; }\n .grafloria-node-host > .axdb-grip--inside.axdb-grip--left { left: 14px; }\n .grafloria-node-host > .axdb-grip--inside.axdb-grip--right { right: 14px; }\n .grafloria-node-host > .axdb-grip--inside.axdb-grip--center { top: 2px; }\n .grafloria-node-host.axdb-gp-inside.axdb-gp-left .axdb-widget > .axdb-widget-h { padding-left: 46px; }\n .grafloria-node-host.axdb-gp-inside.axdb-gp-right .axdb-widget > .axdb-widget-h { padding-right: 46px; }\n .grafloria-node-host.axdb-gp-inside.axdb-gp-center .axdb-widget > .axdb-widget-h { padding-top: 15px; }\n}\n@container axdb-tile (max-height: 46px) {\n .grafloria-node-host > .axdb-grip--inside { top: 5px; }\n .grafloria-node-host > .axdb-grip--inside.axdb-grip--left { left: 12px; }\n .grafloria-node-host > .axdb-grip--inside.axdb-grip--right { right: 12px; }\n .grafloria-node-host > .axdb-grip--inside.axdb-grip--center { top: 1px; }\n .grafloria-node-host.axdb-gp-inside.axdb-gp-left .axdb-widget > .axdb-widget-h { padding-left: 44px; }\n .grafloria-node-host.axdb-gp-inside.axdb-gp-right .axdb-widget > .axdb-widget-h { padding-right: 44px; }\n .grafloria-node-host.axdb-gp-inside.axdb-gp-center .axdb-widget > .axdb-widget-h { padding-top: 13px; }\n}\n@container axdb-tile (max-height: 26px) {\n .grafloria-node-host > .axdb-grip--inside { top: 2px; }\n}\n/* OUTSIDE: a tab on the card's top edge, its corners in line with the card's. */\n/* The OUTSIDE tab lives in the gap between tiles: never taller than the gap\n less a pixel (an 11-px tab in a 10-px gap sat on the tile above), never\n shorter than 6 px so the dots still read. */\n.grafloria-node-host > .axdb-grip--outside { top: calc(-1 * clamp(6px, var(--axdb-gap, 11px) - 1px, 11px)); height: clamp(6px, calc(var(--axdb-gap, 11px) - 1px), 11px); border-bottom-left-radius: 0; border-bottom-right-radius: 0; border-bottom: 0; box-shadow: 0 -1px 2px rgba(16, 24, 40, .08); }\n.grafloria-node-host > .axdb-grip--outside.axdb-grip--left { left: 10px; }\n.grafloria-node-host > .axdb-grip--outside.axdb-grip--right { right: 10px; }\n.grafloria-node-host > .axdb-grip--center { left: 50%; transform: translateX(-50%); }\n/* The header makes room for an inside grip on its side \u2014 inset + the 24-px\n grip + an 8-px gap, per tier (32 px under the band's 14-px inset hid the\n first letter of the title) \u2014 and a centred one sits above the text. */\n.grafloria-node-host.axdb-gp-inside.axdb-gp-left .axdb-widget > .axdb-widget-h { padding-left: 47px; }\n.grafloria-node-host.axdb-gp-inside.axdb-gp-right .axdb-widget > .axdb-widget-h { padding-right: 47px; }\n.grafloria-node-host.axdb-gp-inside.axdb-gp-center .axdb-widget > .axdb-widget-h { padding-top: 17px; }\n.axdb-widget-b { flex: 1; min-height: 0; position: relative; padding: 12px 14px; }\n/* A drag across a STATIC board (nothing prevents the press's default there)\n used to select every label on it; kit cards are not prose. Tables stay\n copyable \u2014 a figure in a grid is the one thing a viewer selects. */\n.axdb-widget { user-select: none; -webkit-user-select: none; }\n.axdb-widget .axdb-table { user-select: text; -webkit-user-select: text; }\n.axdb-widget-b > svg { display: block; width: 100%; height: 100%; }\n.axdb-widget-b.axdb-scroll { overflow: auto; }\n/* A chart WITH a legend under it: the plot yields height, the legend keeps its\n own (without this the 100%-tall svg pushes the legend out of the card). */\n.axdb-widget-b.axdb-has-lg { display: flex; flex-direction: column; }\n.axdb-widget-b.axdb-has-lg > svg { flex: 1; height: auto; min-height: 0; }\n/* The data behind a chart, for readers that cannot see the chart: present in\n the accessibility tree, absent from the picture (WCAG 1.1.1). */\n.axdb-sr {\n position: absolute; top: 0; left: 0; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden;\n clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap; border: 0;\n}\n.axdb-widget-empty {\n display: flex; align-items: center; justify-content: center; height: 100%;\n font: 500 11.5px/1.3 system-ui, sans-serif; color: var(--axdb-muted);\n}\n\n/* kpi: value + delta stack, the spark yields its height before they do */\n/* A SIZE CONTAINER, so the card's type scales with the tile instead of\n clipping. Fit-mode shrinks tiles when rows are added (dropping a chart above\n the KPI row took these from 122px to 85px) and fixed 30px/12px lines were\n sliced mid-glyph by the card's overflow \u2014 the audited \"trimmed\" widgets.\n cqh = 1% of the body's own height, clamped so full-size tiles look exactly\n as before and short tiles compress instead of cutting. */\n.axdb-widget-b.axdb-kpi { display: flex; flex-direction: column; container: axdb-kpi / size; }\n/* STEP DOWN, NEVER CLIP. As the body gets shorter the sparkline goes first, then the\n delta, then the value \u2014 the header alone at the row floor. Before this a 99-px row\n drew the sparkline as a 13-px sliver and a 28-px row cut the value mid-glyph (the\n fluid board, after a drag pushed the KPI row down). Thresholds are body heights. */\n/* Two class hops, so these outrank \".axdb-widget-b > svg { display: block }\" \u2014\n a bare .axdb-kpi-s lost that cascade and the sparkline stayed. */\n@container (max-height: 78px) { .axdb-kpi > .axdb-kpi-s { display: none; } }\n@container (max-height: 40px) { .axdb-kpi > .axdb-kpi-d { display: none; } }\n@container (max-height: 16px) { .axdb-kpi > .axdb-kpi-v { display: none; } }\n.axdb-kpi-v { font: 700 clamp(15px, 44cqh, 30px)/1.05 system-ui, sans-serif; letter-spacing: -.02em; color: var(--axdb-ink); white-space: nowrap; }\n.axdb-kpi-d { margin-top: clamp(1px, 5cqh, 6px); font: 600 clamp(9px, 19cqh, 12px)/1.2 system-ui, sans-serif; white-space: nowrap; }\n.axdb-kpi-d span { color: var(--axdb-muted); font-weight: 500; }\n.axdb-kpi-d.up { color: var(--axdb-up); }\n.axdb-kpi-d.down { color: var(--axdb-down); }\n/* Grows into a tall tile (a 3-row KPI is not a number over a void) and never\n takes more than two fifths of the body. */\n.axdb-kpi-s { margin-top: auto; height: auto; min-height: 0; flex: 1 1 34px; max-height: 40%; }\n/* SHORT AND WIDE \u2014 a one-row KPI on a fluid board. Stacked, a short tile dropped\n its sparkline and left a small figure in the corner of an empty card. Here the\n figure, its delta and the sparkline sit in a row and the spark fills the width.\n The outer query asks the TILE (host), the inner one the body, so a strip too\n thin for a readable spark (under 24 px) keeps the figure alone. 340 px is what\n a figure, its delta, the gaps and a spark worth reading need \u2014 the builder's\n 236-px tiles got a 40-px wedge of spark beside the delta. */\n@container axdb-tile (max-height: 125px) and (min-width: 340px) {\n .axdb-widget--kpi > .axdb-kpi { flex-direction: row; align-items: center; gap: 14px; }\n .axdb-widget--kpi .axdb-kpi-d { margin-top: 0; }\n @container axdb-kpi (min-height: 24px) {\n .axdb-kpi > .axdb-kpi-s { display: block; flex: 1 1 40%; min-width: 80px; height: 100%; max-height: 100%; margin-top: 0; align-self: stretch; }\n }\n}\n/* THE STRIP (row floor): header and figure on one line, the spark beside them\n when the tile is wide enough. */\n@container axdb-tile (max-height: 46px) {\n .axdb-widget--kpi { flex-direction: row; align-items: center; gap: 12px; }\n .axdb-widget--kpi > .axdb-widget-h { flex: none; padding: 0 0 0 12px; background: none; border-bottom: 0; }\n /* The body is a SIZE container: in a centred row it would collapse to zero\n and every height query would fire. It takes the card's full inner height. */\n .axdb-widget--kpi > .axdb-kpi { flex: 1; align-self: stretch; }\n}\n\n/* SPLIT LAYOUT chrome: a divider sits in the gap between two siblings (a\n 24-px hit zone around a 10-px gap) and shows its line on hover or while\n dragged; the insertion line marks the edge a dragged widget will land on. */\n.grafloria-html-layer > .axdb-div { position: absolute; z-index: 2; pointer-events: auto; background: transparent; }\n.grafloria-html-layer > .axdb-div--row { cursor: col-resize; }\n.grafloria-html-layer > .axdb-div--column { cursor: row-resize; }\n.grafloria-html-layer > .axdb-div::after {\n content: \"\"; position: absolute; border-radius: 2px; background: #3b52d9; opacity: 0; transition: opacity .12s ease;\n}\n.grafloria-html-layer > .axdb-div--row::after { left: 50%; top: 8px; bottom: 8px; width: 4px; margin-left: -2px; }\n.grafloria-html-layer > .axdb-div--column::after { top: 50%; left: 8px; right: 8px; height: 4px; margin-top: -2px; }\n.grafloria-html-layer > .axdb-div:hover::after, .grafloria-html-layer > .axdb-div.axdb-active::after { opacity: .9; }\n.grafloria-html-layer > .axdb-ins {\n position: absolute; z-index: 3; pointer-events: none; border-radius: 2px;\n background: #3b52d9; box-shadow: 0 0 0 3px rgba(59, 82, 217, .22);\n}\n@media (prefers-reduced-motion: reduce) { .grafloria-html-layer > .axdb-div::after { transition: none; } }\n\n/* donut: ring beside its legend. The ring takes the body's height (a tall\n tile gets a bigger ring, not dead card), square, capped so its centre figure\n stays a figure and not a headline. */\n.axdb-widget-b.axdb-donut { display: flex; align-items: center; gap: 10px; }\n/* A legend taller than a SHORT body (a 2-row donut on a squeezed board) is\n clipped at its own foot, not centred over the title above it. */\n.axdb-widget-b.axdb-donut > .axdb-lg--col { max-height: 100%; min-height: 0; overflow: hidden; }\n.axdb-widget-b.axdb-donut > svg {\n flex: 0 0 auto; width: auto; height: 100%; max-height: 260px; max-width: 60%; aspect-ratio: 1 / 1;\n}\n\n/* READABILITY TIERS (widgets.ts chartTier): the text is always in the DOM;\n what a short body cannot afford is hidden, not dropped. */\n.axdb-tier-1 .axdb-yt--q, .axdb-tier-1 .axdb-yl--q,\n.axdb-tier-2 .axdb-yt--q, .axdb-tier-2 .axdb-yl--q,\n.axdb-tier-2 .axdb-yt--h, .axdb-tier-2 .axdb-yl--h,\n.axdb-tier-2 .axdb-xt, .axdb-tier-2 .axdb-vt,\n.axdb-lg--off { display: none; }\n\n/* SECTION CHROME: a pointer-transparent overlay on every member group. It\n wears the selection ring and, while selected, the corner handle. */\n.grafloria-html-layer > .axdb-slab { position: absolute; pointer-events: none; border-radius: var(--axdb-rs-radius, 8px); z-index: 4; }\n/* Selected, the overlay rises above the tiles so ITS corner handle wins a\n corner it shares with a child's; unselected, its handle takes no presses. */\n.grafloria-html-layer > .axdb-slab.axdb-slab--selected { z-index: 6; box-shadow: 0 0 0 1.5px var(--axdb-accent-ring, rgba(59, 82, 217, .55)); }\n.grafloria-html-layer > .axdb-slab > .axdb-rs { pointer-events: none; opacity: 0; }\n.grafloria-html-layer > .axdb-slab.axdb-slab--selected > .axdb-rs { pointer-events: auto; opacity: 1; }\n.grafloria-html-layer > .axdb-slab.axdb-slab--static > .axdb-rs { display: none; }\n\n/* SECTION CAPTION (0.4.22): the band on the slab. Geometry is inline (the\n reserve and the pixels come from one function); everything visual is a\n variable the options write and a theme may override. The band takes the\n pointer \u2014 the slab does not \u2014 so a press on it is the section's. */\n.grafloria-html-layer > .axdb-slab > .axdb-slab-h {\n position: absolute; box-sizing: border-box; pointer-events: auto; z-index: 1;\n display: flex; align-items: center; gap: 6px; min-width: 0;\n padding: var(--axdb-caption-pad, 0 12px);\n background: var(--axdb-caption-bg, rgba(31, 36, 48, .045));\n /* A hairline by default: two bands meeting (a captioned section inside a\n captioned section) read as two headers, not one grey block. */\n border-bottom: var(--axdb-caption-border, 1px solid rgba(31, 36, 48, .1));\n color: var(--axdb-caption-fg, #1f2430);\n font: var(--axdb-caption-font-weight, 600) var(--axdb-caption-font-size, 13px)/1.2 var(--axdb-caption-font-family, system-ui, -apple-system, \"Segoe UI\", sans-serif);\n text-transform: var(--axdb-caption-transform, none);\n letter-spacing: -.005em;\n border-radius: var(--axdb-rs-radius, 8px) var(--axdb-rs-radius, 8px) 0 0;\n cursor: default; user-select: none; -webkit-user-select: none; overflow: hidden;\n transition: opacity .12s;\n}\n/* The modifiers carry the band's own selector so they outrank its defaults. */\n.grafloria-html-layer > .axdb-slab > .axdb-slab-h.axdb-slab-h--center { justify-content: center; }\n.grafloria-html-layer > .axdb-slab > .axdb-slab-h.axdb-slab-h--end { justify-content: flex-end; }\n.grafloria-html-layer > .axdb-slab > .axdb-slab-h.axdb-slab-h--vtop { align-items: flex-start; }\n.grafloria-html-layer > .axdb-slab > .axdb-slab-h.axdb-slab-h--vbottom { align-items: flex-end; }\n/* 'tab': the same band sized to its text, a chip at the leading corner. It\n reserves its height like 'inside' \u2014 see captionReserve. */\n.grafloria-html-layer > .axdb-slab > .axdb-slab-h.axdb-slab-h--tab {\n max-width: 100%; border-radius: var(--axdb-rs-radius, 8px);\n border-bottom: var(--axdb-caption-border-tab, none);\n box-shadow: inset 0 0 0 1px rgba(31, 36, 48, .08);\n}\n/* 'hover': an overlay. It reserves nothing, so while hidden it must not take\n the pointer (an invisible band swallowed clicks on the content beneath),\n and while shown it paints OPAQUE \u2014 a 5% tint over a chart is a smear. */\n.grafloria-html-layer > .axdb-slab > .axdb-slab-h.axdb-slab-h--hover {\n opacity: 0; pointer-events: none;\n background: var(--axdb-caption-bg, #fff);\n box-shadow: 0 1px 4px rgba(31, 36, 48, .16);\n}\n/* .axdb-slab--hot is set by the binder from the live pointer (the overlay\n takes no pointer of its own, so CSS :hover can never fire on it). */\n.grafloria-html-layer > .axdb-slab.axdb-slab--hot > .axdb-slab-h.axdb-slab-h--hover,\n.grafloria-html-layer > .axdb-slab.axdb-slab--selected > .axdb-slab-h.axdb-slab-h--hover { pointer-events: auto; opacity: 1; }\n/* the actions of a hovered or selected section */\n.grafloria-html-layer > .axdb-slab.axdb-slab--hot > .axdb-slab-h > .axdb-slab-h-actions { opacity: 1; }\n/* the tight tier: a section under 90 px */\n.grafloria-html-layer > .axdb-slab > .axdb-slab-h.axdb-slab-h--tight { font-size: min(var(--axdb-caption-font-size, 12px), 12px); gap: 4px; }\n.axdb-slab-h--tight .axdb-slab-h-sub, .axdb-slab-h--tight .axdb-slab-h-actions { display: none; }\n.axdb-slab-h-icon { flex: none; }\n.axdb-slab-h-body { display: flex; flex-direction: column; justify-content: center; min-width: 0; flex: 0 1 auto; }\n.axdb-slab-h-text, .axdb-slab-h-sub { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }\n.axdb-slab-h-sub { font-size: .88em; font-weight: 500; opacity: .7; }\n.axdb-slab-h-info { flex: none; opacity: .6; font-size: .9em; cursor: help; }\n.axdb-slab-h-actions { flex: none; display: flex; gap: 2px; margin-inline-start: auto; opacity: 0; transition: opacity .12s; }\n.axdb-slab-h--center .axdb-slab-h-actions, .axdb-slab-h--end .axdb-slab-h-actions { margin-inline-start: 0; }\n.axdb-slab-h:hover > .axdb-slab-h-actions, .axdb-slab--selected > .axdb-slab-h > .axdb-slab-h-actions, .axdb-slab-h:focus-within > .axdb-slab-h-actions { opacity: 1; }\n.axdb-slab-h-action {\n all: unset; box-sizing: border-box; width: 26px; height: 26px; display: inline-flex; align-items: center; justify-content: center;\n border-radius: 6px; cursor: pointer; font-size: 14px; line-height: 1; color: inherit; transition: background-color .12s;\n}\n.axdb-slab-h-action:hover { background: rgba(31, 36, 48, .08); }\n.axdb-slab-h-action:focus-visible { outline: 2px solid var(--axdb-accent-ring, rgba(59, 82, 217, .55)); outline-offset: -2px; }\n.axdb-slab-h-action[disabled] { opacity: .4; cursor: default; }\n.axdb-slab-h-action[disabled]:hover { background: none; }\n.grafloria-html-layer > .axdb-slab.axdb-slab--static > .axdb-slab-h { cursor: default; }\n@media (prefers-color-scheme: dark) {\n /* The band sits on the PAGE, whose ground the kit does not own (a host that\n stays light while the kit goes dark is common): a solid ink, not a tint,\n so the light caption always reads. */\n .grafloria-html-layer > .axdb-slab > .axdb-slab-h {\n background: var(--axdb-caption-bg, #1b2029);\n color: var(--axdb-caption-fg, #eceef4);\n border-bottom: var(--axdb-caption-border, 1px solid rgba(236, 238, 244, .12));\n }\n .grafloria-html-layer > .axdb-slab > .axdb-slab-h.axdb-slab-h--tab { box-shadow: inset 0 0 0 1px rgba(236, 238, 244, .12); }\n .grafloria-html-layer > .axdb-slab > .axdb-slab-h.axdb-slab-h--hover { background: var(--axdb-caption-bg, #1a1d25); box-shadow: 0 1px 4px rgba(0, 0, 0, .5); }\n .axdb-slab-h-action:hover { background: rgba(236, 238, 244, .1); }\n}\n\n/* TAB CONTAINER (0.4.27): a strip of pages across the container's top. The\n strip takes the pointer (the tabs are real buttons); the pages below it are\n ordinary boards. */\n.grafloria-html-layer > .axdb-tabs {\n position: absolute; box-sizing: border-box; pointer-events: auto; z-index: 5;\n display: flex; align-items: center; gap: 2px; padding: 3px 6px; overflow-x: auto; overflow-y: hidden;\n background: var(--axdb-tabs-bg, #f1f3f8);\n border-bottom: 1px solid var(--axdb-tabs-line, #e5e8ef);\n border-radius: var(--axdb-rs-radius, 8px) var(--axdb-rs-radius, 8px) 0 0;\n scrollbar-width: thin;\n}\n.axdb-tabs--center { justify-content: center; }\n.axdb-tabs--end { justify-content: flex-end; }\n.axdb-tabs--stretch > .axdb-tab { flex: 1 1 0; }\n/* A tab is a PILL on the track (a segmented control): quiet until hovered,\n the active one lifted in the card's own ground. */\n.axdb-tab {\n all: unset; box-sizing: border-box; flex: 0 0 auto; max-width: 200px;\n padding: 0 12px; height: 100%; display: inline-flex; align-items: center;\n font: 500 12.5px/1 system-ui, -apple-system, \"Segoe UI\", sans-serif;\n letter-spacing: -.005em;\n color: var(--axdb-tabs-fg, #5a6478); cursor: pointer;\n border-radius: var(--axdb-tab-radius, 6px);\n white-space: nowrap; overflow: hidden; text-overflow: ellipsis;\n transition: background-color .12s, color .12s, box-shadow .12s;\n}\n.axdb-tab:hover { background: rgba(31, 36, 48, .06); color: var(--axdb-tabs-on-fg, #1f2430); }\n.axdb-tab:focus-visible { outline: 2px solid var(--axdb-accent-ring, rgba(59, 82, 217, .55)); outline-offset: -2px; }\n.axdb-tab.axdb-tab--on {\n background: var(--axdb-tabs-on-bg, #fff);\n color: var(--axdb-tabs-on-fg, #1f2430);\n font-weight: 600;\n box-shadow: 0 1px 2px rgba(16, 24, 40, .1), 0 0 0 1px rgba(31, 36, 48, .06);\n}\n@media (prefers-reduced-motion: reduce) { .axdb-tab { transition: none; } }\n@media (prefers-color-scheme: dark) {\n /* Solid for the same reason as the caption band: the track sits on the page. */\n .grafloria-html-layer > .axdb-tabs { background: var(--axdb-tabs-bg, #1b2029); border-bottom-color: var(--axdb-tabs-line, rgba(236, 238, 244, .12)); }\n .axdb-tab { color: var(--axdb-tabs-fg, #98a1b4); }\n .axdb-tab:hover { background: rgba(236, 238, 244, .08); color: var(--axdb-tabs-on-fg, #eceef4); }\n .axdb-tab.axdb-tab--on { background: var(--axdb-tabs-on-bg, #232836); color: var(--axdb-tabs-on-fg, #eceef4); box-shadow: 0 1px 2px rgba(0, 0, 0, .45), 0 0 0 1px rgba(236, 238, 244, .1); }\n}\n\n/* legend chips, shared by line and donut */\n.axdb-lg { display: flex; flex-wrap: wrap; gap: 4px 12px; margin-top: 9px; }\n.axdb-lg--col { flex-direction: column; flex-wrap: nowrap; gap: 6px; margin-top: 0; }\n.axdb-lg i {\n display: inline-flex; align-items: center; gap: 5px; font-style: normal;\n font: 500 11px/1.3 system-ui, sans-serif; color: var(--axdb-muted);\n}\n.axdb-lg i b { width: 9px; height: 9px; border-radius: 3px; flex: none; }\n\n/* table */\n.axdb-table { width: 100%; border-collapse: collapse; font-size: 12px; color: var(--axdb-ink); }\n.axdb-table th {\n text-align: left; padding: 4px 8px 7px; border-bottom: 1px solid var(--axdb-line);\n font: 600 10px/1.3 system-ui, sans-serif; letter-spacing: .4px; text-transform: uppercase;\n color: var(--axdb-muted);\n}\n.axdb-table td { padding: 6px 8px; border-bottom: 1px solid var(--axdb-line); white-space: nowrap; }\n.axdb-table td.num { text-align: right; font-variant-numeric: tabular-nums; }\n.axdb-table tr:last-child td { border-bottom: none; }\n\n@media (prefers-color-scheme: dark) {\n .axdb-widget {\n --axdb-ink: #eceef4;\n --axdb-muted: #98a1b4;\n --axdb-grid: rgba(150, 160, 182, .2);\n --axdb-card: #161a22;\n --axdb-line: #2a3040;\n --axdb-head-bg: rgba(255, 255, 255, .035);\n --axdb-head-fg: #e6e9f0;\n --axdb-soft: rgba(150, 160, 182, .16);\n --axdb-up: #4ade80;\n --axdb-down: #fb7185;\n --axdb-c1: #8b9cff; --axdb-c2: #38bdf8; --axdb-c3: #2dd4bf;\n --axdb-c4: #fbbf24; --axdb-c5: #a78bfa; --axdb-c6: #94a3b8;\n }\n}\n";
|
|
24
26
|
/** Idempotently inject the kit stylesheet (safe to call per binder). */
|
|
25
27
|
export declare function ensureDashboardKitStyles(doc?: Document): void;
|
|
@@ -37,7 +37,7 @@ const CSS = `
|
|
|
37
37
|
.grafloria-html-layer > .grafloria-node-host:focus-visible {
|
|
38
38
|
outline: 2px solid var(--axdb-accent, #3b52d9);
|
|
39
39
|
outline-offset: 2px;
|
|
40
|
-
border-radius: var(--axdb-rs-radius,
|
|
40
|
+
border-radius: var(--axdb-rs-radius, 8px);
|
|
41
41
|
}
|
|
42
42
|
|
|
43
43
|
/* ===== the held tile: transition-exempt ghost, above everything ===== */
|
|
@@ -55,7 +55,7 @@ const CSS = `
|
|
|
55
55
|
/* ===== the placeholder: dashed slab, truthful, never animated ===== */
|
|
56
56
|
.grafloria-html-layer > .axdb-ph {
|
|
57
57
|
position: absolute;
|
|
58
|
-
border-radius: var(--axdb-rs-radius,
|
|
58
|
+
border-radius: var(--axdb-rs-radius, 8px);
|
|
59
59
|
background: rgba(30, 34, 45, .14);
|
|
60
60
|
border: 2px dashed rgba(30, 34, 45, .28);
|
|
61
61
|
box-sizing: border-box;
|
|
@@ -63,6 +63,9 @@ const CSS = `
|
|
|
63
63
|
z-index: 0;
|
|
64
64
|
transition: none;
|
|
65
65
|
}
|
|
66
|
+
/* A slab move asked for a cell it cannot have (a locked section in the way): the wanted cell in the danger tone. */
|
|
67
|
+
.grafloria-html-layer > .axdb-ph.axdb-ph--no { border-color: var(--axdb-danger, #b3123c); background: var(--axdb-danger-soft, rgba(179, 18, 60, .07)); }
|
|
68
|
+
|
|
66
69
|
@media (prefers-color-scheme: dark) {
|
|
67
70
|
.grafloria-html-layer > .axdb-ph { background: rgba(220, 225, 240, .12); border-color: rgba(220, 225, 240, .3); }
|
|
68
71
|
}
|
|
@@ -77,7 +80,7 @@ const CSS = `
|
|
|
77
80
|
cursor: nwse-resize;
|
|
78
81
|
border-right: 3px solid rgba(120, 130, 148, .55);
|
|
79
82
|
border-bottom: 3px solid rgba(120, 130, 148, .55);
|
|
80
|
-
border-bottom-right-radius: var(--axdb-rs-radius,
|
|
83
|
+
border-bottom-right-radius: var(--axdb-rs-radius, 8px);
|
|
81
84
|
opacity: 0;
|
|
82
85
|
transition: opacity .12s;
|
|
83
86
|
z-index: 5;
|
|
@@ -95,7 +98,7 @@ const CSS = `
|
|
|
95
98
|
border-right: none;
|
|
96
99
|
border-left: 3px solid rgba(120, 130, 148, .55);
|
|
97
100
|
border-bottom-right-radius: 0;
|
|
98
|
-
border-bottom-left-radius: var(--axdb-rs-radius,
|
|
101
|
+
border-bottom-left-radius: var(--axdb-rs-radius, 8px);
|
|
99
102
|
}
|
|
100
103
|
|
|
101
104
|
/* ===== palette drag-in chip (screen-space clone following the cursor) ===== */
|
|
@@ -122,14 +125,14 @@ const CSS = `
|
|
|
122
125
|
font: 500 12px/1.4 var(--axdb-font, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif);
|
|
123
126
|
color: var(--axdb-tabs-on-fg, #1f2430);
|
|
124
127
|
background: var(--axdb-tabs-on-bg, #fff);
|
|
125
|
-
border-radius: var(--axdb-
|
|
126
|
-
box-shadow: 0 0 0 1px rgba(31, 36, 48, .
|
|
128
|
+
border-radius: var(--axdb-tab-radius, 6px);
|
|
129
|
+
box-shadow: 0 1px 2px rgba(16, 24, 40, .1), 0 0 0 1px rgba(31, 36, 48, .08);
|
|
127
130
|
}
|
|
128
131
|
@media (prefers-color-scheme: dark) {
|
|
129
132
|
.axdb-tab-chip {
|
|
130
133
|
color: var(--axdb-tabs-on-fg, #eceef4);
|
|
131
|
-
background: var(--axdb-tabs-on-bg, #
|
|
132
|
-
box-shadow: 0 0 0 1px rgba(236, 238, 244, .
|
|
134
|
+
background: var(--axdb-tabs-on-bg, #232836);
|
|
135
|
+
box-shadow: 0 1px 2px rgba(0, 0, 0, .45), 0 0 0 1px rgba(236, 238, 244, .12);
|
|
133
136
|
}
|
|
134
137
|
}
|
|
135
138
|
/* A tab held over ANOTHER group: it will join that group, not the board. The
|
|
@@ -142,7 +145,7 @@ const CSS = `
|
|
|
142
145
|
box-sizing: border-box;
|
|
143
146
|
border: 2px dashed var(--axdb-accent, #3b52d9);
|
|
144
147
|
background: var(--axdb-accent-soft, rgba(59, 82, 217, .08));
|
|
145
|
-
border-radius: var(--axdb-rs-radius,
|
|
148
|
+
border-radius: var(--axdb-rs-radius, 8px);
|
|
146
149
|
}
|
|
147
150
|
.axdb-tabs.axdb-tabs--drop { box-shadow: inset 0 -2px 0 var(--axdb-accent, #3b52d9); }
|
|
148
151
|
.axdb-tab.axdb-tab--drop-before { position: relative; }
|
|
@@ -170,7 +173,7 @@ const CSS = `
|
|
|
170
173
|
--axdb-muted: #5a6478;
|
|
171
174
|
--axdb-grid: rgba(120, 130, 148, .22);
|
|
172
175
|
--axdb-card: #fff;
|
|
173
|
-
--axdb-line: #
|
|
176
|
+
--axdb-line: #e5e8ef;
|
|
174
177
|
--axdb-soft: rgba(120, 130, 148, .14);
|
|
175
178
|
--axdb-up: #0f7a3d; /* 5.42:1 */
|
|
176
179
|
--axdb-down: #be123c; /* 6.29:1 */
|
|
@@ -178,17 +181,22 @@ const CSS = `
|
|
|
178
181
|
steps: every entry clears 3:1 against its card (WCAG 1.4.11). */
|
|
179
182
|
--axdb-c1: #3b52d9; --axdb-c2: #0369a1; --axdb-c3: #0f766e;
|
|
180
183
|
--axdb-c4: #b45309; --axdb-c5: #6d28d9; --axdb-c6: #475569;
|
|
184
|
+
/* The header band: a panel wears its title in a band across its top, the
|
|
185
|
+
way DevExpress, Grafana and Metabase panels do — tinted ground, hairline
|
|
186
|
+
below, sentence case (0.4.36; the tracked uppercase micro-label before). */
|
|
187
|
+
--axdb-head-bg: #f7f8fb;
|
|
188
|
+
--axdb-head-fg: #2a3140;
|
|
181
189
|
box-sizing: border-box;
|
|
182
190
|
width: 100%;
|
|
183
191
|
height: 100%;
|
|
184
192
|
display: flex;
|
|
185
193
|
flex-direction: column;
|
|
186
194
|
overflow: hidden;
|
|
187
|
-
padding:
|
|
195
|
+
padding: 0;
|
|
188
196
|
background: var(--axdb-card);
|
|
189
197
|
border: 1px solid var(--axdb-line);
|
|
190
|
-
border-radius: var(--axdb-rs-radius,
|
|
191
|
-
box-shadow: 0 1px 2px rgba(16, 24, 40, .
|
|
198
|
+
border-radius: var(--axdb-rs-radius, 8px);
|
|
199
|
+
box-shadow: 0 1px 2px rgba(16, 24, 40, .04);
|
|
192
200
|
font-family: system-ui, -apple-system, "Segoe UI", sans-serif;
|
|
193
201
|
color: var(--axdb-ink);
|
|
194
202
|
}
|
|
@@ -200,25 +208,33 @@ const CSS = `
|
|
|
200
208
|
below and would lose the cascade at equal specificity — at 54 px the margin
|
|
201
209
|
stayed 8 px, the body shrank to 14 px and the KPI figure hid. */
|
|
202
210
|
@container axdb-tile (max-height: 90px) {
|
|
203
|
-
.axdb-widget { padding:
|
|
204
|
-
.axdb-widget > .axdb-widget-
|
|
211
|
+
.axdb-widget > .axdb-widget-h { padding: 5px 12px; font-size: 11.5px; }
|
|
212
|
+
.axdb-widget > .axdb-widget-b { padding: 6px 12px; }
|
|
205
213
|
}
|
|
214
|
+
/* The row floor: the band gives way to an inline label beside the figure. */
|
|
206
215
|
@container axdb-tile (max-height: 46px) {
|
|
207
|
-
.axdb-widget { padding:
|
|
208
|
-
.axdb-widget > .axdb-widget-
|
|
216
|
+
.axdb-widget > .axdb-widget-h { padding: 0 0 0 12px; background: none; border-bottom: 0; }
|
|
217
|
+
.axdb-widget > .axdb-widget-b { padding: 3px 12px; }
|
|
209
218
|
}
|
|
210
219
|
@container axdb-tile (max-height: 26px) {
|
|
211
|
-
.axdb-widget {
|
|
220
|
+
.axdb-widget > .axdb-widget-h { display: none; }
|
|
221
|
+
.axdb-widget > .axdb-widget-b { padding: 1px 12px; }
|
|
212
222
|
}
|
|
213
223
|
.axdb-widget-h {
|
|
214
224
|
display: flex;
|
|
215
225
|
align-items: center;
|
|
216
|
-
gap:
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
226
|
+
gap: 8px;
|
|
227
|
+
flex: none;
|
|
228
|
+
min-width: 0;
|
|
229
|
+
padding: 9px 14px;
|
|
230
|
+
background: var(--axdb-head-bg);
|
|
231
|
+
border-bottom: 1px solid var(--axdb-line);
|
|
232
|
+
font: 600 12.5px/1.3 system-ui, -apple-system, "Segoe UI", sans-serif;
|
|
233
|
+
letter-spacing: -.005em;
|
|
234
|
+
color: var(--axdb-head-fg);
|
|
235
|
+
white-space: nowrap;
|
|
236
|
+
overflow: hidden;
|
|
237
|
+
text-overflow: ellipsis;
|
|
222
238
|
}
|
|
223
239
|
/* Drag-handle mode (DevExpress drags an item by its caption): the header is
|
|
224
240
|
the grip and says so with a dot pattern and a grab cursor; the body keeps
|
|
@@ -258,7 +274,7 @@ const CSS = `
|
|
|
258
274
|
.grafloria-node-host[data-axdb-edge="nesw-resize"], .grafloria-node-host[data-axdb-edge="nesw-resize"] * { cursor: nesw-resize !important; }
|
|
259
275
|
/* INSIDE: centred on the header's text line and flush with the card padding,
|
|
260
276
|
per size tier (padding 13/15, then 8/14, 4/12, 2/12). */
|
|
261
|
-
.grafloria-node-host > .axdb-grip--inside { top:
|
|
277
|
+
.grafloria-node-host > .axdb-grip--inside { top: 12px; }
|
|
262
278
|
.grafloria-node-host > .axdb-grip--inside.axdb-grip--left { left: 15px; }
|
|
263
279
|
.grafloria-node-host > .axdb-grip--inside.axdb-grip--right { right: 15px; }
|
|
264
280
|
.grafloria-node-host > .axdb-grip--inside.axdb-grip--center { top: 3px; }
|
|
@@ -267,12 +283,18 @@ const CSS = `
|
|
|
267
283
|
.grafloria-node-host > .axdb-grip--inside.axdb-grip--left { left: 14px; }
|
|
268
284
|
.grafloria-node-host > .axdb-grip--inside.axdb-grip--right { right: 14px; }
|
|
269
285
|
.grafloria-node-host > .axdb-grip--inside.axdb-grip--center { top: 2px; }
|
|
286
|
+
.grafloria-node-host.axdb-gp-inside.axdb-gp-left .axdb-widget > .axdb-widget-h { padding-left: 46px; }
|
|
287
|
+
.grafloria-node-host.axdb-gp-inside.axdb-gp-right .axdb-widget > .axdb-widget-h { padding-right: 46px; }
|
|
288
|
+
.grafloria-node-host.axdb-gp-inside.axdb-gp-center .axdb-widget > .axdb-widget-h { padding-top: 15px; }
|
|
270
289
|
}
|
|
271
290
|
@container axdb-tile (max-height: 46px) {
|
|
272
291
|
.grafloria-node-host > .axdb-grip--inside { top: 5px; }
|
|
273
292
|
.grafloria-node-host > .axdb-grip--inside.axdb-grip--left { left: 12px; }
|
|
274
293
|
.grafloria-node-host > .axdb-grip--inside.axdb-grip--right { right: 12px; }
|
|
275
294
|
.grafloria-node-host > .axdb-grip--inside.axdb-grip--center { top: 1px; }
|
|
295
|
+
.grafloria-node-host.axdb-gp-inside.axdb-gp-left .axdb-widget > .axdb-widget-h { padding-left: 44px; }
|
|
296
|
+
.grafloria-node-host.axdb-gp-inside.axdb-gp-right .axdb-widget > .axdb-widget-h { padding-right: 44px; }
|
|
297
|
+
.grafloria-node-host.axdb-gp-inside.axdb-gp-center .axdb-widget > .axdb-widget-h { padding-top: 13px; }
|
|
276
298
|
}
|
|
277
299
|
@container axdb-tile (max-height: 26px) {
|
|
278
300
|
.grafloria-node-host > .axdb-grip--inside { top: 2px; }
|
|
@@ -285,11 +307,13 @@ const CSS = `
|
|
|
285
307
|
.grafloria-node-host > .axdb-grip--outside.axdb-grip--left { left: 10px; }
|
|
286
308
|
.grafloria-node-host > .axdb-grip--outside.axdb-grip--right { right: 10px; }
|
|
287
309
|
.grafloria-node-host > .axdb-grip--center { left: 50%; transform: translateX(-50%); }
|
|
288
|
-
/* The header makes room for an inside grip on its side
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
.grafloria-node-host.axdb-gp-inside.axdb-gp-
|
|
292
|
-
.axdb-
|
|
310
|
+
/* The header makes room for an inside grip on its side — inset + the 24-px
|
|
311
|
+
grip + an 8-px gap, per tier (32 px under the band's 14-px inset hid the
|
|
312
|
+
first letter of the title) — and a centred one sits above the text. */
|
|
313
|
+
.grafloria-node-host.axdb-gp-inside.axdb-gp-left .axdb-widget > .axdb-widget-h { padding-left: 47px; }
|
|
314
|
+
.grafloria-node-host.axdb-gp-inside.axdb-gp-right .axdb-widget > .axdb-widget-h { padding-right: 47px; }
|
|
315
|
+
.grafloria-node-host.axdb-gp-inside.axdb-gp-center .axdb-widget > .axdb-widget-h { padding-top: 17px; }
|
|
316
|
+
.axdb-widget-b { flex: 1; min-height: 0; position: relative; padding: 12px 14px; }
|
|
293
317
|
/* A drag across a STATIC board (nothing prevents the press's default there)
|
|
294
318
|
used to select every label on it; kit cards are not prose. Tables stay
|
|
295
319
|
copyable — a figure in a grid is the one thing a viewer selects. */
|
|
@@ -355,7 +379,7 @@ const CSS = `
|
|
|
355
379
|
when the tile is wide enough. */
|
|
356
380
|
@container axdb-tile (max-height: 46px) {
|
|
357
381
|
.axdb-widget--kpi { flex-direction: row; align-items: center; gap: 12px; }
|
|
358
|
-
.axdb-widget--kpi > .axdb-widget-h { flex: none; }
|
|
382
|
+
.axdb-widget--kpi > .axdb-widget-h { flex: none; padding: 0 0 0 12px; background: none; border-bottom: 0; }
|
|
359
383
|
/* The body is a SIZE container: in a centred row it would collapse to zero
|
|
360
384
|
and every height query would fire. It takes the card's full inner height. */
|
|
361
385
|
.axdb-widget--kpi > .axdb-kpi { flex: 1; align-self: stretch; }
|
|
@@ -400,7 +424,7 @@ const CSS = `
|
|
|
400
424
|
|
|
401
425
|
/* SECTION CHROME: a pointer-transparent overlay on every member group. It
|
|
402
426
|
wears the selection ring and, while selected, the corner handle. */
|
|
403
|
-
.grafloria-html-layer > .axdb-slab { position: absolute; pointer-events: none; border-radius: var(--axdb-rs-radius,
|
|
427
|
+
.grafloria-html-layer > .axdb-slab { position: absolute; pointer-events: none; border-radius: var(--axdb-rs-radius, 8px); z-index: 4; }
|
|
404
428
|
/* Selected, the overlay rises above the tiles so ITS corner handle wins a
|
|
405
429
|
corner it shares with a child's; unselected, its handle takes no presses. */
|
|
406
430
|
.grafloria-html-layer > .axdb-slab.axdb-slab--selected { z-index: 6; box-shadow: 0 0 0 1.5px var(--axdb-accent-ring, rgba(59, 82, 217, .55)); }
|
|
@@ -415,16 +439,16 @@ const CSS = `
|
|
|
415
439
|
.grafloria-html-layer > .axdb-slab > .axdb-slab-h {
|
|
416
440
|
position: absolute; box-sizing: border-box; pointer-events: auto; z-index: 1;
|
|
417
441
|
display: flex; align-items: center; gap: 6px; min-width: 0;
|
|
418
|
-
padding: var(--axdb-caption-pad, 0
|
|
419
|
-
background: var(--axdb-caption-bg, rgba(31, 36, 48, .
|
|
442
|
+
padding: var(--axdb-caption-pad, 0 12px);
|
|
443
|
+
background: var(--axdb-caption-bg, rgba(31, 36, 48, .045));
|
|
420
444
|
/* A hairline by default: two bands meeting (a captioned section inside a
|
|
421
445
|
captioned section) read as two headers, not one grey block. */
|
|
422
446
|
border-bottom: var(--axdb-caption-border, 1px solid rgba(31, 36, 48, .1));
|
|
423
447
|
color: var(--axdb-caption-fg, #1f2430);
|
|
424
448
|
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);
|
|
425
449
|
text-transform: var(--axdb-caption-transform, none);
|
|
426
|
-
letter-spacing:
|
|
427
|
-
border-radius: var(--axdb-rs-radius,
|
|
450
|
+
letter-spacing: -.005em;
|
|
451
|
+
border-radius: var(--axdb-rs-radius, 8px) var(--axdb-rs-radius, 8px) 0 0;
|
|
428
452
|
cursor: default; user-select: none; -webkit-user-select: none; overflow: hidden;
|
|
429
453
|
transition: opacity .12s;
|
|
430
454
|
}
|
|
@@ -436,7 +460,7 @@ const CSS = `
|
|
|
436
460
|
/* 'tab': the same band sized to its text, a chip at the leading corner. It
|
|
437
461
|
reserves its height like 'inside' — see captionReserve. */
|
|
438
462
|
.grafloria-html-layer > .axdb-slab > .axdb-slab-h.axdb-slab-h--tab {
|
|
439
|
-
max-width: 100%; border-radius: var(--axdb-rs-radius,
|
|
463
|
+
max-width: 100%; border-radius: var(--axdb-rs-radius, 8px);
|
|
440
464
|
border-bottom: var(--axdb-caption-border-tab, none);
|
|
441
465
|
box-shadow: inset 0 0 0 1px rgba(31, 36, 48, .08);
|
|
442
466
|
}
|
|
@@ -460,14 +484,14 @@ const CSS = `
|
|
|
460
484
|
.axdb-slab-h-icon { flex: none; }
|
|
461
485
|
.axdb-slab-h-body { display: flex; flex-direction: column; justify-content: center; min-width: 0; flex: 0 1 auto; }
|
|
462
486
|
.axdb-slab-h-text, .axdb-slab-h-sub { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
|
463
|
-
.axdb-slab-h-sub { font-size: .
|
|
487
|
+
.axdb-slab-h-sub { font-size: .88em; font-weight: 500; opacity: .7; }
|
|
464
488
|
.axdb-slab-h-info { flex: none; opacity: .6; font-size: .9em; cursor: help; }
|
|
465
489
|
.axdb-slab-h-actions { flex: none; display: flex; gap: 2px; margin-inline-start: auto; opacity: 0; transition: opacity .12s; }
|
|
466
490
|
.axdb-slab-h--center .axdb-slab-h-actions, .axdb-slab-h--end .axdb-slab-h-actions { margin-inline-start: 0; }
|
|
467
491
|
.axdb-slab-h:hover > .axdb-slab-h-actions, .axdb-slab--selected > .axdb-slab-h > .axdb-slab-h-actions, .axdb-slab-h:focus-within > .axdb-slab-h-actions { opacity: 1; }
|
|
468
492
|
.axdb-slab-h-action {
|
|
469
|
-
all: unset; box-sizing: border-box; width:
|
|
470
|
-
border-radius:
|
|
493
|
+
all: unset; box-sizing: border-box; width: 26px; height: 26px; display: inline-flex; align-items: center; justify-content: center;
|
|
494
|
+
border-radius: 6px; cursor: pointer; font-size: 14px; line-height: 1; color: inherit; transition: background-color .12s;
|
|
471
495
|
}
|
|
472
496
|
.axdb-slab-h-action:hover { background: rgba(31, 36, 48, .08); }
|
|
473
497
|
.axdb-slab-h-action:focus-visible { outline: 2px solid var(--axdb-accent-ring, rgba(59, 82, 217, .55)); outline-offset: -2px; }
|
|
@@ -475,8 +499,11 @@ const CSS = `
|
|
|
475
499
|
.axdb-slab-h-action[disabled]:hover { background: none; }
|
|
476
500
|
.grafloria-html-layer > .axdb-slab.axdb-slab--static > .axdb-slab-h { cursor: default; }
|
|
477
501
|
@media (prefers-color-scheme: dark) {
|
|
502
|
+
/* The band sits on the PAGE, whose ground the kit does not own (a host that
|
|
503
|
+
stays light while the kit goes dark is common): a solid ink, not a tint,
|
|
504
|
+
so the light caption always reads. */
|
|
478
505
|
.grafloria-html-layer > .axdb-slab > .axdb-slab-h {
|
|
479
|
-
background: var(--axdb-caption-bg,
|
|
506
|
+
background: var(--axdb-caption-bg, #1b2029);
|
|
480
507
|
color: var(--axdb-caption-fg, #eceef4);
|
|
481
508
|
border-bottom: var(--axdb-caption-border, 1px solid rgba(236, 238, 244, .12));
|
|
482
509
|
}
|
|
@@ -490,36 +517,42 @@ const CSS = `
|
|
|
490
517
|
ordinary boards. */
|
|
491
518
|
.grafloria-html-layer > .axdb-tabs {
|
|
492
519
|
position: absolute; box-sizing: border-box; pointer-events: auto; z-index: 5;
|
|
493
|
-
display: flex; align-items:
|
|
494
|
-
background: var(--axdb-tabs-bg,
|
|
495
|
-
border-bottom: 1px solid var(--axdb-tabs-line,
|
|
496
|
-
border-radius: var(--axdb-rs-radius,
|
|
520
|
+
display: flex; align-items: center; gap: 2px; padding: 3px 6px; overflow-x: auto; overflow-y: hidden;
|
|
521
|
+
background: var(--axdb-tabs-bg, #f1f3f8);
|
|
522
|
+
border-bottom: 1px solid var(--axdb-tabs-line, #e5e8ef);
|
|
523
|
+
border-radius: var(--axdb-rs-radius, 8px) var(--axdb-rs-radius, 8px) 0 0;
|
|
497
524
|
scrollbar-width: thin;
|
|
498
525
|
}
|
|
499
526
|
.axdb-tabs--center { justify-content: center; }
|
|
500
527
|
.axdb-tabs--end { justify-content: flex-end; }
|
|
501
528
|
.axdb-tabs--stretch > .axdb-tab { flex: 1 1 0; }
|
|
529
|
+
/* A tab is a PILL on the track (a segmented control): quiet until hovered,
|
|
530
|
+
the active one lifted in the card's own ground. */
|
|
502
531
|
.axdb-tab {
|
|
503
532
|
all: unset; box-sizing: border-box; flex: 0 0 auto; max-width: 200px;
|
|
504
|
-
padding: 0 12px; height:
|
|
505
|
-
font:
|
|
533
|
+
padding: 0 12px; height: 100%; display: inline-flex; align-items: center;
|
|
534
|
+
font: 500 12.5px/1 system-ui, -apple-system, "Segoe UI", sans-serif;
|
|
535
|
+
letter-spacing: -.005em;
|
|
506
536
|
color: var(--axdb-tabs-fg, #5a6478); cursor: pointer;
|
|
507
|
-
border-radius: var(--axdb-
|
|
537
|
+
border-radius: var(--axdb-tab-radius, 6px);
|
|
508
538
|
white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
|
|
539
|
+
transition: background-color .12s, color .12s, box-shadow .12s;
|
|
509
540
|
}
|
|
510
|
-
.axdb-tab:hover { background: rgba(31, 36, 48, .06); }
|
|
541
|
+
.axdb-tab:hover { background: rgba(31, 36, 48, .06); color: var(--axdb-tabs-on-fg, #1f2430); }
|
|
511
542
|
.axdb-tab:focus-visible { outline: 2px solid var(--axdb-accent-ring, rgba(59, 82, 217, .55)); outline-offset: -2px; }
|
|
512
|
-
/* The active tab reads as the front page: the card's own ground, lifted. */
|
|
513
543
|
.axdb-tab.axdb-tab--on {
|
|
514
544
|
background: var(--axdb-tabs-on-bg, #fff);
|
|
515
545
|
color: var(--axdb-tabs-on-fg, #1f2430);
|
|
516
|
-
|
|
546
|
+
font-weight: 600;
|
|
547
|
+
box-shadow: 0 1px 2px rgba(16, 24, 40, .1), 0 0 0 1px rgba(31, 36, 48, .06);
|
|
517
548
|
}
|
|
549
|
+
@media (prefers-reduced-motion: reduce) { .axdb-tab { transition: none; } }
|
|
518
550
|
@media (prefers-color-scheme: dark) {
|
|
519
|
-
|
|
551
|
+
/* Solid for the same reason as the caption band: the track sits on the page. */
|
|
552
|
+
.grafloria-html-layer > .axdb-tabs { background: var(--axdb-tabs-bg, #1b2029); border-bottom-color: var(--axdb-tabs-line, rgba(236, 238, 244, .12)); }
|
|
520
553
|
.axdb-tab { color: var(--axdb-tabs-fg, #98a1b4); }
|
|
521
|
-
.axdb-tab:hover { background: rgba(236, 238, 244, .08); }
|
|
522
|
-
.axdb-tab.axdb-tab--on { background: var(--axdb-tabs-on-bg, #
|
|
554
|
+
.axdb-tab:hover { background: rgba(236, 238, 244, .08); color: var(--axdb-tabs-on-fg, #eceef4); }
|
|
555
|
+
.axdb-tab.axdb-tab--on { background: var(--axdb-tabs-on-bg, #232836); color: var(--axdb-tabs-on-fg, #eceef4); box-shadow: 0 1px 2px rgba(0, 0, 0, .45), 0 0 0 1px rgba(236, 238, 244, .1); }
|
|
523
556
|
}
|
|
524
557
|
|
|
525
558
|
/* legend chips, shared by line and donut */
|
|
@@ -547,8 +580,10 @@ const CSS = `
|
|
|
547
580
|
--axdb-ink: #eceef4;
|
|
548
581
|
--axdb-muted: #98a1b4;
|
|
549
582
|
--axdb-grid: rgba(150, 160, 182, .2);
|
|
550
|
-
--axdb-card: #
|
|
551
|
-
--axdb-line: #
|
|
583
|
+
--axdb-card: #161a22;
|
|
584
|
+
--axdb-line: #2a3040;
|
|
585
|
+
--axdb-head-bg: rgba(255, 255, 255, .035);
|
|
586
|
+
--axdb-head-fg: #e6e9f0;
|
|
552
587
|
--axdb-soft: rgba(150, 160, 182, .16);
|
|
553
588
|
--axdb-up: #4ade80;
|
|
554
589
|
--axdb-down: #fb7185;
|
|
@@ -557,6 +592,8 @@ const CSS = `
|
|
|
557
592
|
}
|
|
558
593
|
}
|
|
559
594
|
`;
|
|
595
|
+
/** The kit stylesheet as text — for a host that bundles its own CSS, and for the styling spec. */
|
|
596
|
+
export const DASHBOARD_KIT_CSS = CSS;
|
|
560
597
|
/** Idempotently inject the kit stylesheet (safe to call per binder). */
|
|
561
598
|
export function ensureDashboardKitStyles(doc) {
|
|
562
599
|
// SERVER-SAFE: `dashboard()` is a pure spec builder and runs where there is
|
package/src/lib/load.js
CHANGED
|
@@ -238,6 +238,7 @@ export function fromDocument(document, options = {}) {
|
|
|
238
238
|
: {},
|
|
239
239
|
active: (_x = activeGroup === null || activeGroup === void 0 ? void 0 : activeGroup.id) !== null && _x !== void 0 ? _x : 'main',
|
|
240
240
|
apiRef: null,
|
|
241
|
+
container: null,
|
|
241
242
|
};
|
|
242
243
|
const handle = createDashboardHandle(ctx);
|
|
243
244
|
const renderCustomNode = (node, host) => {
|
|
@@ -255,7 +256,7 @@ export function fromDocument(document, options = {}) {
|
|
|
255
256
|
(_b = getNodeType(node.type)) === null || _b === void 0 ? void 0 : _b(node, host);
|
|
256
257
|
};
|
|
257
258
|
const finalize = (api) => {
|
|
258
|
-
var _a, _b, _c, _d, _e, _f;
|
|
259
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
259
260
|
const a = api;
|
|
260
261
|
if (!a)
|
|
261
262
|
return;
|
|
@@ -296,6 +297,7 @@ export function fromDocument(document, options = {}) {
|
|
|
296
297
|
// work immediately — no camera move, no showView, so the paint is byte-for-
|
|
297
298
|
// byte what a boards-only load produced.
|
|
298
299
|
ctx.apiRef = a;
|
|
300
|
+
ctx.container = (_e = a.container) !== null && _e !== void 0 ? _e : null;
|
|
299
301
|
for (const group of groups) {
|
|
300
302
|
const board = group.getMetadata('dashboardBoard');
|
|
301
303
|
if (!board)
|
|
@@ -308,7 +310,7 @@ export function fromDocument(document, options = {}) {
|
|
|
308
310
|
}
|
|
309
311
|
// Tab containers, with the same runtime dashboard() uses — a reloaded
|
|
310
312
|
// document must switch pages exactly like a board built from a literal.
|
|
311
|
-
attachTabsRuntime(ctx, model, (
|
|
313
|
+
attachTabsRuntime(ctx, model, (_f = a.container) !== null && _f !== void 0 ? _f : null, handle);
|
|
312
314
|
// LIVE LAYOUT SWITCH on a loaded document — the same contract dashboard()
|
|
313
315
|
// finalize offers: cells persisted where the grid reads them, any tree and
|
|
314
316
|
// column cache cleared, the board's `layout` flag flipped, a fresh binder.
|
|
@@ -355,7 +357,7 @@ export function fromDocument(document, options = {}) {
|
|
|
355
357
|
}
|
|
356
358
|
// A loaded board follows the history exactly as an authored one does: an
|
|
357
359
|
// undo re-syncs every binder without the consumer calling refresh().
|
|
358
|
-
(
|
|
360
|
+
(_g = ctx.attachHistory) === null || _g === void 0 ? void 0 : _g.call(ctx);
|
|
359
361
|
};
|
|
360
362
|
return Object.assign({ nodes, edges: model.getLinks(), renderCustomNode,
|
|
361
363
|
finalize,
|