basalt-ui 1.30.1 → 1.31.0
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/AGENTS.md +1 -1
- package/CHANGELOG.md +38 -0
- package/MIGRATING.md +218 -14
- package/agent/rules/basalt-controls.md +19 -19
- package/agent/rules/basalt-mantine.md +1 -1
- package/configs/oxlint-plugin.js +254 -4
- package/configs/oxlint-plugin.test.ts +145 -2
- package/configs/oxlint.json +2 -1
- package/dist/agent-chat/composer.d.ts.map +1 -1
- package/dist/agent-chat/composer.js +41 -43
- package/dist/agent-chat/thread-workspace.d.ts +2 -2
- package/dist/agent-chat/thread-workspace.d.ts.map +1 -1
- package/dist/agent-chat/thread-workspace.js +3 -4
- package/dist/agent-chat/use-keyboard-inset.d.ts +2 -0
- package/dist/agent-chat/use-keyboard-inset.d.ts.map +1 -0
- package/dist/agent-chat/use-keyboard-inset.js +34 -0
- package/dist/charts/cursor/touch-pin.d.ts +45 -0
- package/dist/charts/cursor/touch-pin.d.ts.map +1 -0
- package/dist/charts/cursor/touch-pin.js +79 -0
- package/dist/charts/hooks/use-discrete-cursor.d.ts +66 -0
- package/dist/charts/hooks/use-discrete-cursor.d.ts.map +1 -0
- package/dist/charts/hooks/use-discrete-cursor.js +198 -0
- package/dist/charts/hooks/use-hysteresis.d.ts +18 -0
- package/dist/charts/hooks/use-hysteresis.d.ts.map +1 -0
- package/dist/charts/hooks/use-hysteresis.js +12 -0
- package/dist/charts/hooks/useBandPlot.d.ts +7 -0
- package/dist/charts/hooks/useBandPlot.d.ts.map +1 -1
- package/dist/charts/hooks/useBandPlot.js +62 -18
- package/dist/charts/hooks/useChartCursor.d.ts +40 -11
- package/dist/charts/hooks/useChartCursor.d.ts.map +1 -1
- package/dist/charts/hooks/useChartCursor.js +102 -11
- package/dist/charts/index.d.ts +0 -1
- package/dist/charts/index.d.ts.map +1 -1
- package/dist/charts/index.js +0 -10
- package/dist/charts/kinds/BandStrip.d.ts.map +1 -1
- package/dist/charts/kinds/BandStrip.js +54 -41
- package/dist/charts/kinds/Donut.d.ts +9 -3
- package/dist/charts/kinds/Donut.d.ts.map +1 -1
- package/dist/charts/kinds/Donut.js +110 -48
- package/dist/charts/kinds/DualPanel.d.ts.map +1 -1
- package/dist/charts/kinds/DualPanel.js +145 -131
- package/dist/charts/kinds/Heatmap.d.ts +7 -0
- package/dist/charts/kinds/Heatmap.d.ts.map +1 -1
- package/dist/charts/kinds/Heatmap.js +37 -16
- package/dist/charts/kinds/MirroredBars.d.ts.map +1 -1
- package/dist/charts/kinds/MirroredBars.js +83 -68
- package/dist/charts/kinds/donut-layout.d.ts +47 -0
- package/dist/charts/kinds/donut-layout.d.ts.map +1 -0
- package/dist/charts/kinds/donut-layout.js +52 -0
- package/dist/charts/layout/auto-margin.d.ts +7 -2
- package/dist/charts/layout/auto-margin.d.ts.map +1 -1
- package/dist/charts/layout/auto-margin.js +12 -3
- package/dist/charts/layout/end-labels.d.ts +64 -0
- package/dist/charts/layout/end-labels.d.ts.map +1 -0
- package/dist/charts/layout/end-labels.js +76 -0
- package/dist/charts/primitives/Axes.d.ts +32 -4
- package/dist/charts/primitives/Axes.d.ts.map +1 -1
- package/dist/charts/primitives/Axes.js +62 -16
- package/dist/charts/primitives/CartesianChart.d.ts +7 -6
- package/dist/charts/primitives/CartesianChart.d.ts.map +1 -1
- package/dist/charts/primitives/CartesianChart.js +271 -125
- package/dist/charts/primitives/ChartCard.d.ts +0 -14
- package/dist/charts/primitives/ChartCard.d.ts.map +1 -1
- package/dist/charts/primitives/ChartCard.js +74 -21
- package/dist/charts/primitives/ChartFrame.d.ts +31 -17
- package/dist/charts/primitives/ChartFrame.d.ts.map +1 -1
- package/dist/charts/primitives/ChartFrame.js +149 -32
- package/dist/charts/primitives/ChartLegend.d.ts +20 -14
- package/dist/charts/primitives/ChartLegend.d.ts.map +1 -1
- package/dist/charts/primitives/ChartLegend.js +212 -71
- package/dist/charts/primitives/ChartTooltip.d.ts.map +1 -1
- package/dist/charts/primitives/ChartTooltip.js +4 -3
- package/dist/charts/primitives/Crosshair.js +2 -2
- package/dist/charts/primitives/HoverOverlay.d.ts +6 -1
- package/dist/charts/primitives/HoverOverlay.d.ts.map +1 -1
- package/dist/charts/primitives/HoverOverlay.js +37 -26
- package/dist/charts/primitives/HoverOverlay.module.css +13 -0
- package/dist/charts/primitives/HoverOverlay.module.css.d.ts +8 -0
- package/dist/charts/primitives/chart-card-context.d.ts +29 -0
- package/dist/charts/primitives/chart-card-context.d.ts.map +1 -0
- package/dist/charts/primitives/chart-card-context.js +13 -0
- package/dist/charts/primitives/chart-frame-layout.d.ts +93 -112
- package/dist/charts/primitives/chart-frame-layout.d.ts.map +1 -1
- package/dist/charts/primitives/chart-frame-layout.js +83 -31
- package/dist/charts/primitives/chart-layout.d.ts +112 -0
- package/dist/charts/primitives/chart-layout.d.ts.map +1 -0
- package/dist/charts/primitives/chart-layout.js +175 -0
- package/dist/charts/primitives/chart-tier.d.ts +13 -11
- package/dist/charts/primitives/chart-tier.d.ts.map +1 -1
- package/dist/charts/primitives/chart-tier.js +42 -13
- package/dist/charts/series.d.ts +4 -1
- package/dist/charts/series.d.ts.map +1 -1
- package/dist/charts/utils/ticks.d.ts +27 -8
- package/dist/charts/utils/ticks.d.ts.map +1 -1
- package/dist/charts/utils/ticks.js +35 -11
- package/dist/common/errors.d.ts +22 -1
- package/dist/common/errors.d.ts.map +1 -1
- package/dist/common/errors.js +20 -2
- package/dist/common/hit-floor.d.ts +10 -0
- package/dist/common/hit-floor.d.ts.map +1 -0
- package/dist/common/hit-floor.js +6 -0
- package/dist/{shell → common}/isomorphic-layout-effect.d.ts +4 -3
- package/dist/common/isomorphic-layout-effect.d.ts.map +1 -0
- package/dist/common/use-measured-widths.d.ts +37 -0
- package/dist/common/use-measured-widths.d.ts.map +1 -0
- package/dist/common/use-measured-widths.js +51 -0
- package/dist/common/use-media-query.d.ts +6 -0
- package/dist/common/use-media-query.d.ts.map +1 -0
- package/dist/common/use-media-query.js +40 -0
- package/dist/common/validate.d.ts +9 -0
- package/dist/common/validate.d.ts.map +1 -1
- package/dist/common/validate.js +9 -4
- package/dist/content/article-layout.d.ts +4 -2
- package/dist/content/article-layout.d.ts.map +1 -1
- package/dist/content/article-layout.js +43 -3
- package/dist/content/article-layout.module.css +45 -4
- package/dist/content/article-layout.module.css.d.ts +3 -0
- package/dist/content/code-block.d.ts.map +1 -1
- package/dist/content/code-block.js +40 -2
- package/dist/content/code-block.module.css +32 -2
- package/dist/content/prose.module.css +13 -8
- package/dist/controls/actions.d.ts +29 -6
- package/dist/controls/actions.d.ts.map +1 -1
- package/dist/controls/actions.js +158 -27
- package/dist/controls/actions.module.css +14 -1
- package/dist/controls/actions.module.css.d.ts +1 -0
- package/dist/controls/controls.module.css +8 -31
- package/dist/controls/enum-filter.js +2 -2
- package/dist/controls/filter-context.d.ts +10 -0
- package/dist/controls/filter-context.d.ts.map +1 -1
- package/dist/controls/filter-context.js +2 -0
- package/dist/controls/filter-pill.d.ts.map +1 -1
- package/dist/controls/filter-pill.js +1 -0
- package/dist/controls/index.d.ts +1 -1
- package/dist/controls/index.d.ts.map +1 -1
- package/dist/controls/index.js +0 -2
- package/dist/controls/multi-select-filter.js +2 -2
- package/dist/controls/sync-button.module.css +1 -1
- package/dist/dashboard/section.d.ts.map +1 -1
- package/dist/dashboard/section.js +1 -0
- package/dist/dashboard/section.module.css +1 -26
- package/dist/dashboard/stat-card.d.ts +1 -1
- package/dist/dashboard/stat-card.d.ts.map +1 -1
- package/dist/dashboard/stat-card.js +4 -1
- package/dist/dashboard/stat-card.module.css +6 -9
- package/dist/dashboard/stat-group.module.css +16 -4
- package/dist/dashboard/widget-grid.d.ts.map +1 -1
- package/dist/dashboard/widget-header.d.ts +14 -4
- package/dist/dashboard/widget-header.d.ts.map +1 -1
- package/dist/dashboard/widget-header.js +23 -6
- package/dist/dashboard/widget-header.module.css +145 -133
- package/dist/dashboard/widget-header.module.css.d.ts +2 -0
- package/dist/data/data-table.d.ts +23 -0
- package/dist/data/data-table.d.ts.map +1 -1
- package/dist/data/data-table.js +339 -141
- package/dist/data/data-table.module.css +94 -0
- package/dist/data/data-table.module.css.d.ts +11 -0
- package/dist/data/virtual-list.d.ts.map +1 -1
- package/dist/data/virtual-list.js +10 -0
- package/dist/forms/form-layout.d.ts.map +1 -1
- package/dist/forms/form-layout.js +3 -3
- package/dist/forms/form-layout.module.css +30 -5
- package/dist/forms/form-layout.module.css.d.ts +1 -0
- package/dist/guard/index.d.ts +8 -2
- package/dist/guard/index.d.ts.map +1 -1
- package/dist/guard/index.js +67 -2
- package/dist/guard/types.d.ts +3 -3
- package/dist/guard/types.d.ts.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -1
- package/dist/provider/index.d.ts +20 -1
- package/dist/provider/index.d.ts.map +1 -1
- package/dist/provider/index.js +22 -2
- package/dist/shell/app-brand.d.ts.map +1 -1
- package/dist/shell/app-brand.js +1 -0
- package/dist/shell/app-brand.module.css +2 -2
- package/dist/shell/app-header.module.css +18 -8
- package/dist/shell/app-main.module.css +54 -3
- package/dist/shell/app-main.module.css.d.ts +1 -0
- package/dist/shell/app-mobile-nav.module.css +16 -4
- package/dist/shell/app-sidebar.module.css +4 -6
- package/dist/shell/index.d.ts +12 -0
- package/dist/shell/index.d.ts.map +1 -1
- package/dist/shell/index.js +57 -16
- package/dist/shell/mobile-nav-model.d.ts +1 -1
- package/dist/shell/page-aside.d.ts +22 -3
- package/dist/shell/page-aside.d.ts.map +1 -1
- package/dist/shell/page-aside.js +27 -13
- package/dist/shell/page-bar.d.ts.map +1 -1
- package/dist/shell/page-bar.js +10 -5
- package/dist/shell/page-bar.module.css +10 -34
- package/dist/shell/use-breakpoint.d.ts +3 -4
- package/dist/shell/use-breakpoint.d.ts.map +1 -1
- package/dist/shell/use-breakpoint.js +3 -25
- package/dist/shell/use-size-class.d.ts +6 -0
- package/dist/shell/use-size-class.d.ts.map +1 -0
- package/dist/shell/use-size-class.js +19 -0
- package/dist/state/persisted-or-local.d.ts +4 -1
- package/dist/state/persisted-or-local.d.ts.map +1 -1
- package/dist/state/persisted-or-local.js +4 -3
- package/dist/state/persisted.d.ts +10 -0
- package/dist/state/persisted.d.ts.map +1 -1
- package/dist/state/persisted.js +44 -14
- package/dist/styles.css +41 -0
- package/dist/surfaces.d.ts +4 -4
- package/dist/surfaces.d.ts.map +1 -1
- package/dist/surfaces.js +11 -3
- package/dist/theme/controls.module.css +7 -0
- package/dist/theme/ctl-theme.d.ts +1 -0
- package/dist/theme/ctl-theme.d.ts.map +1 -1
- package/dist/theme/ctl-theme.js +12 -4
- package/dist/theme/floating.module.css +36 -0
- package/dist/theme/floating.module.css.d.ts +3 -0
- package/dist/theme/index.d.ts +2 -2
- package/dist/theme/index.d.ts.map +1 -1
- package/dist/theme/index.js +34 -5
- package/dist/theme/nav-link.module.css +7 -0
- package/dist/tokens/index.d.ts +7 -16
- package/dist/tokens/index.d.ts.map +1 -1
- package/dist/tokens/index.js +23 -28
- package/dist/tokens/palette.d.ts +5 -27
- package/dist/tokens/palette.d.ts.map +1 -1
- package/dist/tokens/palette.js +8 -30
- package/dist/tokens/size-classes.d.ts +40 -0
- package/dist/tokens/size-classes.d.ts.map +1 -0
- package/dist/tokens/size-classes.js +29 -0
- package/dist/tokens.css +8 -4
- package/llms.txt +3 -3
- package/package.json +17 -17
- package/src/agent-chat/composer.tsx +74 -47
- package/src/agent-chat/thread-workspace.tsx +8 -11
- package/src/agent-chat/use-keyboard-inset.ts +68 -0
- package/src/charts/cursor/touch-pin.ts +138 -0
- package/src/charts/hooks/use-discrete-cursor.ts +335 -0
- package/src/charts/hooks/use-hysteresis.ts +26 -0
- package/src/charts/hooks/useBandPlot.ts +90 -27
- package/src/charts/hooks/useChartCursor.ts +211 -26
- package/src/charts/index.ts +0 -8
- package/src/charts/kinds/BandStrip.tsx +11 -1
- package/src/charts/kinds/Donut.tsx +153 -62
- package/src/charts/kinds/DualPanel.tsx +19 -4
- package/src/charts/kinds/Heatmap.tsx +61 -22
- package/src/charts/kinds/MirroredBars.tsx +15 -3
- package/src/charts/kinds/donut-layout.ts +82 -0
- package/src/charts/layout/auto-margin.ts +24 -5
- package/src/charts/layout/end-labels.ts +135 -0
- package/src/charts/primitives/Axes.tsx +112 -21
- package/src/charts/primitives/CartesianChart.tsx +282 -68
- package/src/charts/primitives/ChartCard.tsx +141 -48
- package/src/charts/primitives/ChartFrame.tsx +272 -53
- package/src/charts/primitives/ChartLegend.tsx +258 -72
- package/src/charts/primitives/ChartTooltip.tsx +8 -3
- package/src/charts/primitives/Crosshair.tsx +2 -2
- package/src/charts/primitives/HoverOverlay.module.css +13 -0
- package/src/charts/primitives/HoverOverlay.module.css.d.ts +8 -0
- package/src/charts/primitives/HoverOverlay.tsx +15 -4
- package/src/charts/primitives/chart-card-context.ts +37 -0
- package/src/charts/primitives/chart-frame-layout.ts +227 -156
- package/src/charts/primitives/chart-layout.ts +318 -0
- package/src/charts/primitives/chart-tier.tsx +66 -21
- package/src/charts/series.ts +4 -1
- package/src/charts/utils/ticks.ts +95 -19
- package/src/common/errors.ts +39 -1
- package/src/common/hit-floor.ts +9 -0
- package/src/{shell → common}/isomorphic-layout-effect.ts +4 -3
- package/src/common/use-measured-widths.ts +88 -0
- package/src/common/use-media-query.ts +56 -0
- package/src/common/validate.ts +17 -4
- package/src/content/article-layout.module.css +45 -4
- package/src/content/article-layout.module.css.d.ts +3 -0
- package/src/content/article-layout.tsx +60 -5
- package/src/content/code-block.module.css +32 -2
- package/src/content/code-block.tsx +47 -3
- package/src/content/prose.module.css +13 -8
- package/src/controls/actions.module.css +14 -1
- package/src/controls/actions.module.css.d.ts +1 -0
- package/src/controls/actions.tsx +223 -75
- package/src/controls/controls.module.css +8 -31
- package/src/controls/enum-filter.tsx +2 -2
- package/src/controls/filter-context.tsx +11 -0
- package/src/controls/filter-pill.tsx +1 -0
- package/src/controls/index.ts +0 -1
- package/src/controls/multi-select-filter.tsx +2 -2
- package/src/controls/sync-button.module.css +1 -1
- package/src/dashboard/section.module.css +1 -26
- package/src/dashboard/section.tsx +1 -0
- package/src/dashboard/stat-card.module.css +6 -9
- package/src/dashboard/stat-card.tsx +6 -3
- package/src/dashboard/stat-group.module.css +16 -4
- package/src/dashboard/widget-grid.tsx +7 -6
- package/src/dashboard/widget-header.module.css +145 -133
- package/src/dashboard/widget-header.module.css.d.ts +2 -0
- package/src/dashboard/widget-header.tsx +45 -11
- package/src/data/data-table.module.css +94 -0
- package/src/data/data-table.module.css.d.ts +11 -0
- package/src/data/data-table.tsx +503 -164
- package/src/data/virtual-list.tsx +31 -0
- package/src/forms/form-layout.module.css +30 -5
- package/src/forms/form-layout.module.css.d.ts +1 -0
- package/src/forms/form-layout.tsx +12 -10
- package/src/guard/index.ts +183 -3
- package/src/guard/types.ts +3 -2
- package/src/index.ts +2 -0
- package/src/provider/index.tsx +53 -1
- package/src/shell/app-brand.module.css +2 -2
- package/src/shell/app-brand.tsx +1 -0
- package/src/shell/app-header.module.css +18 -8
- package/src/shell/app-main.module.css +54 -3
- package/src/shell/app-main.module.css.d.ts +1 -0
- package/src/shell/app-mobile-nav.module.css +16 -4
- package/src/shell/app-sidebar.module.css +4 -6
- package/src/shell/index.tsx +200 -129
- package/src/shell/mobile-nav-model.ts +1 -1
- package/src/shell/page-aside.tsx +70 -21
- package/src/shell/page-bar.module.css +10 -34
- package/src/shell/page-bar.tsx +10 -5
- package/src/shell/use-breakpoint.ts +9 -45
- package/src/shell/use-size-class.ts +31 -0
- package/src/state/persisted-or-local.ts +10 -4
- package/src/state/persisted.ts +76 -17
- package/src/styles.css +41 -0
- package/src/surfaces.ts +9 -1
- package/src/theme/controls.module.css +7 -0
- package/src/theme/ctl-theme.tsx +15 -3
- package/src/theme/floating.module.css +36 -0
- package/src/theme/floating.module.css.d.ts +3 -0
- package/src/theme/index.ts +46 -8
- package/src/theme/nav-link.module.css +7 -0
- package/src/tokens/index.ts +20 -30
- package/src/tokens/palette.ts +6 -28
- package/src/tokens/size-classes.ts +46 -0
- package/dist/shell/isomorphic-layout-effect.d.ts.map +0 -1
- /package/dist/{shell → common}/isomorphic-layout-effect.js +0 -0
|
@@ -12,15 +12,43 @@
|
|
|
12
12
|
* The header renders only when at least one of title/info/value/actions/icon/count is set — ending
|
|
13
13
|
* the `''`-as-hidden-header sentinel a consumer used to reach for otherwise.
|
|
14
14
|
*/
|
|
15
|
+
import { useCallback, useMemo, useRef, useState } from 'react'
|
|
15
16
|
import type { CSSProperties, ReactNode } from 'react'
|
|
16
17
|
import { cx } from '../../common/props'
|
|
17
18
|
import type { BasaltProps, SlotStylesProps } from '../../common/props'
|
|
18
19
|
import { VX } from '../../tokens'
|
|
19
20
|
import { WidgetHeader } from '../../dashboard/widget-header'
|
|
20
21
|
import type { DeltaPolarity } from '../../dashboard/delta-badge'
|
|
22
|
+
import { useChartSize } from '../hooks/useChartSize'
|
|
23
|
+
import { useHysteresis } from '../hooks/use-hysteresis'
|
|
24
|
+
import { ChartCardContext } from './chart-card-context'
|
|
21
25
|
import { ChartEmpty, ChartError, ChartPending, resolveChartState } from './ChartPending'
|
|
22
26
|
import type { ChartState } from './ChartPending'
|
|
23
27
|
|
|
28
|
+
/**
|
|
29
|
+
* Below this rendered height the header folds its subtitle regardless of width (wave 8's
|
|
30
|
+
* header-economy law, `docs/CHARTS-SPEC.md` §5) — a short-but-wide card (a KPI-style
|
|
31
|
+
* chart card) still needs the vertical space back. A `@container basalt-card` SIZE query would be
|
|
32
|
+
* the natural expression, but `container-type: size` on a box with no explicit height (the common,
|
|
33
|
+
* non-grid-stretched case) collapses it to 0 — CSS containment requires a definite size in the
|
|
34
|
+
* contained axis, and a ChartCard's height is ordinarily derived from its content (VERIFIED: making
|
|
35
|
+
* the root a size container drops `card-chrome.layout.test.ts`'s unstretched short-card fixture to
|
|
36
|
+
* a 0px box). `useChartSize` (the same measuring primitive `ChartFrame` uses) reads the rendered
|
|
37
|
+
* height instead, and `data-basalt-card-short` is the CSS hook `widget-header.module.css` reacts to
|
|
38
|
+
* with the same subtitle-fold declarations as the width query.
|
|
39
|
+
*/
|
|
40
|
+
const CARD_SHORT_HEIGHT = 280
|
|
41
|
+
/**
|
|
42
|
+
* Hysteresis band on the flag above (`docs/waves/PLAN.md` wave 3, P0-1). `short` folds the header,
|
|
43
|
+
* which changes the card's OWN measured height by the fold delta — a card whose unfolded height
|
|
44
|
+
* lands in `[280, 280 + Δ)` is bistable, and the async RO → rAF → debounce → setState → commit path
|
|
45
|
+
* can sit one reading behind, which turned the bistability into a perpetual flip-flop on the
|
|
46
|
+
* "Sales by channel" dashboard card (27-52 times per 2s, every viewport). Entering short still
|
|
47
|
+
* happens below `CARD_SHORT_HEIGHT`; LEAVING it needs `CARD_SHORT_HEIGHT + SHORT_EXIT_SLACK`, sized
|
|
48
|
+
* to clear the largest fold delta (subtitle line + inset + value row) with headroom.
|
|
49
|
+
*/
|
|
50
|
+
const SHORT_EXIT_SLACK = 24
|
|
51
|
+
|
|
24
52
|
// Surfaces resolve per theme via CSS vars, so the styles are static (no useMemo/isDark).
|
|
25
53
|
// Depth = `shadow-card` (a whisper shadow + a 1px ring baked into the same value), never a
|
|
26
54
|
// `border` property (docs/DESIGN-SPEC.md §5) — so a ChartCard reads IDENTICALLY to a Mantine
|
|
@@ -31,7 +59,16 @@ import type { ChartState } from './ChartPending'
|
|
|
31
59
|
// `WidgetHeader`'s info bubble (`widget-header.module.css`'s `.infoBubble`, which is where the
|
|
32
60
|
// tooltip this file used to own now lives) opens downward past the header's own box, and must be
|
|
33
61
|
// able to overhang the card edge without being invisibly cut off.
|
|
62
|
+
//
|
|
63
|
+
// The root is a flex column and a `basalt-card` inline-size container (the card's own width drives
|
|
64
|
+
// its header chrome, `docs/CHARTS-SPEC.md` §8): the body takes the row's spare height when a grid stretches
|
|
65
|
+
// the card.
|
|
34
66
|
const cardStyle: CSSProperties = {
|
|
67
|
+
display: 'flex',
|
|
68
|
+
flexDirection: 'column',
|
|
69
|
+
minHeight: 0,
|
|
70
|
+
containerType: 'inline-size',
|
|
71
|
+
containerName: 'basalt-card',
|
|
35
72
|
borderRadius: VX.radiusCard,
|
|
36
73
|
boxShadow: VX.shadowCard,
|
|
37
74
|
backgroundColor: VX.surface.panel,
|
|
@@ -45,6 +82,11 @@ const bodyClipStyle: CSSProperties = {
|
|
|
45
82
|
borderBottomLeftRadius: VX.radiusCard,
|
|
46
83
|
borderBottomRightRadius: VX.radiusCard,
|
|
47
84
|
overflow: 'hidden',
|
|
85
|
+
flex: '1 1 auto',
|
|
86
|
+
minHeight: 0,
|
|
87
|
+
// A column, so a `ChartFrame` inside can grow into the body a stretched grid row hands the card.
|
|
88
|
+
display: 'flex',
|
|
89
|
+
flexDirection: 'column',
|
|
48
90
|
padding: '2px var(--vx-space-card-inset-x, 0.8125rem) var(--vx-space-card-inset-y, 0.6875rem)',
|
|
49
91
|
}
|
|
50
92
|
// Card inset = the `cardInsetY`/`cardInsetX` role tokens (vertical / horizontal), the SAME two
|
|
@@ -60,6 +102,13 @@ const bodyClipStyle: CSSProperties = {
|
|
|
60
102
|
const headerWrapStyle: CSSProperties = {
|
|
61
103
|
padding: 'var(--vx-space-card-inset-y, 0.6875rem) var(--vx-space-card-inset-x, 0.8125rem) 4px',
|
|
62
104
|
}
|
|
105
|
+
// A short card (wave 8) trims the header's own top inset — the subtitle line and the wide-tier
|
|
106
|
+
// value/delta row are both already gone by the time `short` is true, so the full card inset reads
|
|
107
|
+
// as dead air rather than rhythm. Bottom stays 4px (unchanged) so the header-to-body seam matches
|
|
108
|
+
// every other ChartCard.
|
|
109
|
+
const headerWrapShortStyle: CSSProperties = {
|
|
110
|
+
padding: '4px var(--vx-space-card-inset-x, 0.8125rem) 4px',
|
|
111
|
+
}
|
|
63
112
|
|
|
64
113
|
/**
|
|
65
114
|
* The three boxes a `ChartCard` paints, and therefore the slot set a consumer may class
|
|
@@ -143,6 +192,8 @@ export function ChartCard({
|
|
|
143
192
|
style,
|
|
144
193
|
children,
|
|
145
194
|
}: ChartCardProps) {
|
|
195
|
+
const [legendSlot, setLegendSlot] = useState<HTMLDivElement | null>(null)
|
|
196
|
+
const { ref: sizeRef, height: measuredHeight } = useChartSize()
|
|
146
197
|
const resolvedState = resolveChartState({ ...(state !== undefined && { state }) })
|
|
147
198
|
const hasHeader =
|
|
148
199
|
title !== undefined ||
|
|
@@ -151,59 +202,101 @@ export function ChartCard({
|
|
|
151
202
|
actions !== undefined ||
|
|
152
203
|
icon !== undefined ||
|
|
153
204
|
count !== undefined
|
|
205
|
+
// Hysteresis (P0-1, `SHORT_EXIT_SLACK`'s own doc): which threshold applies depends on the
|
|
206
|
+
// PREVIOUS committed flag, so a card whose unfolded height sits in the bistable band settles
|
|
207
|
+
// instead of flip-flopping forever.
|
|
208
|
+
const short = useHysteresis(
|
|
209
|
+
(wasShort) =>
|
|
210
|
+
measuredHeight > 0 &&
|
|
211
|
+
measuredHeight < (wasShort ? CARD_SHORT_HEIGHT + SHORT_EXIT_SLACK : CARD_SHORT_HEIGHT),
|
|
212
|
+
false,
|
|
213
|
+
)
|
|
214
|
+
|
|
215
|
+
const legendOwnerRef = useRef<string | null>(null)
|
|
216
|
+
// Bumped on every release so a previously-denied sibling's claim effect (keyed on the context
|
|
217
|
+
// value it reads, see `chart-card-context.ts`) re-runs and gets a chance to re-claim the slot the
|
|
218
|
+
// owner just gave up — without it a frame told "no" once stayed denied even after the owner
|
|
219
|
+
// unmounted.
|
|
220
|
+
const [legendSlotVersion, setLegendSlotVersion] = useState(0)
|
|
221
|
+
const claimLegendSlot = useCallback((id: string) => {
|
|
222
|
+
if (legendOwnerRef.current === null) legendOwnerRef.current = id
|
|
223
|
+
return legendOwnerRef.current === id
|
|
224
|
+
}, [])
|
|
225
|
+
const releaseLegendSlot = useCallback((id: string) => {
|
|
226
|
+
if (legendOwnerRef.current === id) {
|
|
227
|
+
legendOwnerRef.current = null
|
|
228
|
+
setLegendSlotVersion((v) => v + 1)
|
|
229
|
+
}
|
|
230
|
+
}, [])
|
|
231
|
+
const context = useMemo(
|
|
232
|
+
() => ({
|
|
233
|
+
legendSlot,
|
|
234
|
+
inCard: true,
|
|
235
|
+
short,
|
|
236
|
+
claimLegendSlot,
|
|
237
|
+
releaseLegendSlot,
|
|
238
|
+
legendSlotVersion,
|
|
239
|
+
}),
|
|
240
|
+
[legendSlot, short, claimLegendSlot, releaseLegendSlot, legendSlotVersion],
|
|
241
|
+
)
|
|
154
242
|
|
|
155
243
|
return (
|
|
156
|
-
<
|
|
157
|
-
className={cx(classNames?.root, className)}
|
|
158
|
-
style={{ ...cardStyle, ...style }}
|
|
159
|
-
{...(resolvedState === 'pending' && { 'aria-busy': 'true' })}
|
|
160
|
-
>
|
|
161
|
-
{hasHeader && (
|
|
162
|
-
<div
|
|
163
|
-
{...(classNames?.header !== undefined && { className: classNames.header })}
|
|
164
|
-
style={headerWrapStyle}
|
|
165
|
-
>
|
|
166
|
-
<WidgetHeader
|
|
167
|
-
tier="widget"
|
|
168
|
-
title={title ?? ''}
|
|
169
|
-
{...(icon !== undefined && { icon })}
|
|
170
|
-
{...(subtitle !== undefined && { subtitle })}
|
|
171
|
-
{...(info !== undefined && { info })}
|
|
172
|
-
{...(value !== undefined && { value })}
|
|
173
|
-
{...(delta !== undefined && { delta })}
|
|
174
|
-
{...(deltaPeriod !== undefined && { deltaPeriod })}
|
|
175
|
-
{...(deltaPolarity !== undefined && { deltaPolarity })}
|
|
176
|
-
{...(count !== undefined && { count })}
|
|
177
|
-
{...(actions !== undefined && {
|
|
178
|
-
actions: <span data-basalt-tier="widget">{actions}</span>,
|
|
179
|
-
})}
|
|
180
|
-
/>
|
|
181
|
-
</div>
|
|
182
|
-
)}
|
|
244
|
+
<ChartCardContext.Provider value={context}>
|
|
183
245
|
<div
|
|
184
|
-
{
|
|
185
|
-
|
|
246
|
+
ref={sizeRef}
|
|
247
|
+
className={cx(classNames?.root, className)}
|
|
248
|
+
style={{ ...cardStyle, ...style }}
|
|
249
|
+
{...(resolvedState === 'pending' && { 'aria-busy': 'true' })}
|
|
250
|
+
{...(short && { 'data-basalt-card-short': '' })}
|
|
186
251
|
>
|
|
187
|
-
{
|
|
188
|
-
<
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
252
|
+
{hasHeader && (
|
|
253
|
+
<div
|
|
254
|
+
{...(classNames?.header !== undefined && { className: classNames.header })}
|
|
255
|
+
style={short ? headerWrapShortStyle : headerWrapStyle}
|
|
256
|
+
>
|
|
257
|
+
<WidgetHeader
|
|
258
|
+
tier="widget"
|
|
259
|
+
title={title ?? ''}
|
|
260
|
+
{...(icon !== undefined && { icon })}
|
|
261
|
+
{...(subtitle !== undefined && { subtitle })}
|
|
262
|
+
{...(info !== undefined && { info })}
|
|
263
|
+
{...(value !== undefined && { value })}
|
|
264
|
+
{...(delta !== undefined && { delta })}
|
|
265
|
+
{...(deltaPeriod !== undefined && { deltaPeriod })}
|
|
266
|
+
{...(deltaPolarity !== undefined && { deltaPolarity })}
|
|
267
|
+
{...(count !== undefined && { count })}
|
|
268
|
+
{...(actions !== undefined && {
|
|
269
|
+
actions: <span data-basalt-tier="widget">{actions}</span>,
|
|
270
|
+
})}
|
|
271
|
+
/>
|
|
272
|
+
<div ref={setLegendSlot} data-basalt-legend-slot="" />
|
|
273
|
+
</div>
|
|
205
274
|
)}
|
|
275
|
+
<div
|
|
276
|
+
{...(classNames?.body !== undefined && { className: classNames.body })}
|
|
277
|
+
style={bodyClipStyle}
|
|
278
|
+
>
|
|
279
|
+
{resolvedState === 'pending' ? (
|
|
280
|
+
<ChartPending width="100%" height={placeholderHeight} />
|
|
281
|
+
) : resolvedState === 'error' ? (
|
|
282
|
+
<ChartError
|
|
283
|
+
width="100%"
|
|
284
|
+
height={placeholderHeight}
|
|
285
|
+
error={state?.error}
|
|
286
|
+
action={stateAction}
|
|
287
|
+
/>
|
|
288
|
+
) : resolvedState === 'empty' ? (
|
|
289
|
+
<ChartEmpty
|
|
290
|
+
width="100%"
|
|
291
|
+
height={placeholderHeight}
|
|
292
|
+
{...(typeof state?.empty === 'string' && { label: state.empty })}
|
|
293
|
+
action={stateAction}
|
|
294
|
+
/>
|
|
295
|
+
) : (
|
|
296
|
+
children
|
|
297
|
+
)}
|
|
298
|
+
</div>
|
|
206
299
|
</div>
|
|
207
|
-
</
|
|
300
|
+
</ChartCardContext.Provider>
|
|
208
301
|
)
|
|
209
302
|
}
|