basalt-ui 1.30.2 → 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 +31 -0
- package/MIGRATING.md +172 -12
- 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 +28 -22
- package/dist/charts/primitives/ChartFrame.d.ts.map +1 -1
- package/dist/charts/primitives/ChartFrame.js +133 -34
- package/dist/charts/primitives/ChartLegend.d.ts +19 -15
- 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 -113
- package/dist/charts/primitives/chart-frame-layout.d.ts.map +1 -1
- package/dist/charts/primitives/chart-frame-layout.js +76 -29
- 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 +11 -1
- package/dist/common/errors.d.ts.map +1 -1
- package/dist/common/errors.js +16 -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 +243 -60
- package/src/charts/primitives/ChartLegend.tsx +257 -73
- 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 +214 -177
- 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 +25 -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
|
@@ -1,9 +1,20 @@
|
|
|
1
1
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useCallback, useMemo, useRef, useState } from "react";
|
|
2
3
|
import { cx } from "../../common/props.js";
|
|
3
4
|
import { VX } from "../../tokens/index.js";
|
|
4
5
|
import { WidgetHeader } from "../../dashboard/widget-header.js";
|
|
6
|
+
import { useChartSize } from "../hooks/useChartSize.js";
|
|
7
|
+
import { useHysteresis } from "../hooks/use-hysteresis.js";
|
|
8
|
+
import { ChartCardContext } from "./chart-card-context.js";
|
|
5
9
|
import { ChartEmpty, ChartError, ChartPending, resolveChartState } from "./ChartPending.js";
|
|
10
|
+
const CARD_SHORT_HEIGHT = 280;
|
|
11
|
+
const SHORT_EXIT_SLACK = 24;
|
|
6
12
|
const cardStyle = {
|
|
13
|
+
display: "flex",
|
|
14
|
+
flexDirection: "column",
|
|
15
|
+
minHeight: 0,
|
|
16
|
+
containerType: "inline-size",
|
|
17
|
+
containerName: "basalt-card",
|
|
7
18
|
borderRadius: VX.radiusCard,
|
|
8
19
|
boxShadow: VX.shadowCard,
|
|
9
20
|
backgroundColor: VX.surface.panel
|
|
@@ -17,11 +28,19 @@ const bodyClipStyle = {
|
|
|
17
28
|
borderBottomLeftRadius: VX.radiusCard,
|
|
18
29
|
borderBottomRightRadius: VX.radiusCard,
|
|
19
30
|
overflow: "hidden",
|
|
31
|
+
flex: "1 1 auto",
|
|
32
|
+
minHeight: 0,
|
|
33
|
+
// A column, so a `ChartFrame` inside can grow into the body a stretched grid row hands the card.
|
|
34
|
+
display: "flex",
|
|
35
|
+
flexDirection: "column",
|
|
20
36
|
padding: "2px var(--vx-space-card-inset-x, 0.8125rem) var(--vx-space-card-inset-y, 0.6875rem)"
|
|
21
37
|
};
|
|
22
38
|
const headerWrapStyle = {
|
|
23
39
|
padding: "var(--vx-space-card-inset-y, 0.6875rem) var(--vx-space-card-inset-x, 0.8125rem) 4px"
|
|
24
40
|
};
|
|
41
|
+
const headerWrapShortStyle = {
|
|
42
|
+
padding: "4px var(--vx-space-card-inset-x, 0.8125rem) 4px"
|
|
43
|
+
};
|
|
25
44
|
function ChartCard({
|
|
26
45
|
title,
|
|
27
46
|
icon,
|
|
@@ -41,38 +60,72 @@ function ChartCard({
|
|
|
41
60
|
style,
|
|
42
61
|
children
|
|
43
62
|
}) {
|
|
63
|
+
const [legendSlot, setLegendSlot] = useState(null);
|
|
64
|
+
const { ref: sizeRef, height: measuredHeight } = useChartSize();
|
|
44
65
|
const resolvedState = resolveChartState({ ...state !== void 0 && { state } });
|
|
45
66
|
const hasHeader = title !== void 0 || info !== void 0 || value !== void 0 || actions !== void 0 || icon !== void 0 || count !== void 0;
|
|
46
|
-
|
|
67
|
+
const short = useHysteresis(
|
|
68
|
+
(wasShort) => measuredHeight > 0 && measuredHeight < (wasShort ? CARD_SHORT_HEIGHT + SHORT_EXIT_SLACK : CARD_SHORT_HEIGHT),
|
|
69
|
+
false
|
|
70
|
+
);
|
|
71
|
+
const legendOwnerRef = useRef(null);
|
|
72
|
+
const [legendSlotVersion, setLegendSlotVersion] = useState(0);
|
|
73
|
+
const claimLegendSlot = useCallback((id) => {
|
|
74
|
+
if (legendOwnerRef.current === null) legendOwnerRef.current = id;
|
|
75
|
+
return legendOwnerRef.current === id;
|
|
76
|
+
}, []);
|
|
77
|
+
const releaseLegendSlot = useCallback((id) => {
|
|
78
|
+
if (legendOwnerRef.current === id) {
|
|
79
|
+
legendOwnerRef.current = null;
|
|
80
|
+
setLegendSlotVersion((v) => v + 1);
|
|
81
|
+
}
|
|
82
|
+
}, []);
|
|
83
|
+
const context = useMemo(
|
|
84
|
+
() => ({
|
|
85
|
+
legendSlot,
|
|
86
|
+
inCard: true,
|
|
87
|
+
short,
|
|
88
|
+
claimLegendSlot,
|
|
89
|
+
releaseLegendSlot,
|
|
90
|
+
legendSlotVersion
|
|
91
|
+
}),
|
|
92
|
+
[legendSlot, short, claimLegendSlot, releaseLegendSlot, legendSlotVersion]
|
|
93
|
+
);
|
|
94
|
+
return /* @__PURE__ */ jsx(ChartCardContext.Provider, { value: context, children: /* @__PURE__ */ jsxs(
|
|
47
95
|
"div",
|
|
48
96
|
{
|
|
97
|
+
ref: sizeRef,
|
|
49
98
|
className: cx(classNames?.root, className),
|
|
50
99
|
style: { ...cardStyle, ...style },
|
|
51
100
|
...resolvedState === "pending" && { "aria-busy": "true" },
|
|
101
|
+
...short && { "data-basalt-card-short": "" },
|
|
52
102
|
children: [
|
|
53
|
-
hasHeader && /* @__PURE__ */
|
|
103
|
+
hasHeader && /* @__PURE__ */ jsxs(
|
|
54
104
|
"div",
|
|
55
105
|
{
|
|
56
106
|
...classNames?.header !== void 0 && { className: classNames.header },
|
|
57
|
-
style: headerWrapStyle,
|
|
58
|
-
children:
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
actions
|
|
107
|
+
style: short ? headerWrapShortStyle : headerWrapStyle,
|
|
108
|
+
children: [
|
|
109
|
+
/* @__PURE__ */ jsx(
|
|
110
|
+
WidgetHeader,
|
|
111
|
+
{
|
|
112
|
+
tier: "widget",
|
|
113
|
+
title: title ?? "",
|
|
114
|
+
...icon !== void 0 && { icon },
|
|
115
|
+
...subtitle !== void 0 && { subtitle },
|
|
116
|
+
...info !== void 0 && { info },
|
|
117
|
+
...value !== void 0 && { value },
|
|
118
|
+
...delta !== void 0 && { delta },
|
|
119
|
+
...deltaPeriod !== void 0 && { deltaPeriod },
|
|
120
|
+
...deltaPolarity !== void 0 && { deltaPolarity },
|
|
121
|
+
...count !== void 0 && { count },
|
|
122
|
+
...actions !== void 0 && {
|
|
123
|
+
actions: /* @__PURE__ */ jsx("span", { "data-basalt-tier": "widget", children: actions })
|
|
124
|
+
}
|
|
73
125
|
}
|
|
74
|
-
|
|
75
|
-
|
|
126
|
+
),
|
|
127
|
+
/* @__PURE__ */ jsx("div", { ref: setLegendSlot, "data-basalt-legend-slot": "" })
|
|
128
|
+
]
|
|
76
129
|
}
|
|
77
130
|
),
|
|
78
131
|
/* @__PURE__ */ jsx(
|
|
@@ -101,7 +154,7 @@ function ChartCard({
|
|
|
101
154
|
)
|
|
102
155
|
]
|
|
103
156
|
}
|
|
104
|
-
);
|
|
157
|
+
) });
|
|
105
158
|
}
|
|
106
159
|
export {
|
|
107
160
|
ChartCard
|
|
@@ -5,7 +5,7 @@ import type { ResponsiveChartHeight } from './chart-frame-layout.js';
|
|
|
5
5
|
import type { ChartState } from './ChartPending.js';
|
|
6
6
|
/** Re-exported for `ChartFrame.test.tsx` and any other consumer that previously reached these
|
|
7
7
|
* through `ChartFrame` — the layout math itself lives in `./chart-frame-layout` (pure, DOM-free). */
|
|
8
|
-
export { legendEntryCap, resolveLegendRollup, resolvePlotRect
|
|
8
|
+
export { legendEntryCap, resolveLegendRollup, resolvePlotRect } from './chart-frame-layout.js';
|
|
9
9
|
export type { ResponsiveChartHeight } from './chart-frame-layout.js';
|
|
10
10
|
/** Legend configuration for {@link ChartFrame}. Omit entirely (or pass `{}`) for the default
|
|
11
11
|
* bottom-placed legend; pass `false` only for the sparkline exemption. */
|
|
@@ -13,22 +13,12 @@ export type ChartFrameLegend = {
|
|
|
13
13
|
/** Default 'bottom'. */
|
|
14
14
|
placement?: LegendPlacement;
|
|
15
15
|
/**
|
|
16
|
-
*
|
|
17
|
-
* into
|
|
16
|
+
* @deprecated Removed in 1.32.0 — the legend now fits by measured width every time and folds
|
|
17
|
+
* overflow into an `All N` disclosure (`docs/CHARTS-SPEC.md` §8); drop the prop.
|
|
18
18
|
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
* (`entries.slice(0, cap)`); nothing here measures rows.
|
|
23
|
-
*
|
|
24
|
-
* An explicit value WINS OUTRIGHT — over the phone tier's two-row default (`chartTierMetrics`)
|
|
25
|
-
* AND over a `fill` frame's measured fit (`legendEntryCap`). 1.30.0 fixed only the first half:
|
|
26
|
-
* the fit still ran `Math.min(fitted, caller)` on top, so meteo's 7-entry meteogram legend in a
|
|
27
|
-
* 150px docked row rendered 2 entries at `maxRows` 3, 6 AND 99 — five series drawn in colours
|
|
28
|
-
* nothing named. Both losers are DEFAULTS (the tier's, and `VX.minPlotHeight`); this is the one
|
|
29
|
-
* number a caller stated, so under `fill` the PLOT yields the height instead
|
|
30
|
-
* ({@link resolvePlotRect}'s `legendWins`). State a number too large for the box and the plot
|
|
31
|
-
* gets what is left — visible, and yours.
|
|
19
|
+
* Until then an explicit value still WINS OUTRIGHT over the measured fit, and it counts ENTRIES,
|
|
20
|
+
* not rows: `maxRows: 3` on a 7-entry legend renders three entries. Under `fill` the PLOT yields
|
|
21
|
+
* the height ({@link resolvePlotRect}'s `legendWins`).
|
|
32
22
|
*/
|
|
33
23
|
maxRows?: number;
|
|
34
24
|
/** Visually separate role: series | overlay | reference. */
|
|
@@ -43,19 +33,31 @@ export type ChartFrameLegend = {
|
|
|
43
33
|
* plot, the tooltip, and the auto domain together (`docs/CHARTS-SPEC.md` §5).
|
|
44
34
|
*/
|
|
45
35
|
toggle?: boolean;
|
|
36
|
+
/**
|
|
37
|
+
* Internal escape hatch for a kind that never wants its legend portalled into a `ChartCard`
|
|
38
|
+
* header, whatever the placement — `Donut` sets this (`docs/waves/PLAN.md` wave 3, P1-4): its
|
|
39
|
+
* side/bottom legend is the ring's only key, so it must stay under or beside the ring rather than
|
|
40
|
+
* follow every other frame's default of claiming an available header slot. `false` keeps the
|
|
41
|
+
* legend in its own band regardless of `placement`; omitted (default) behaves as today. Not
|
|
42
|
+
* something a consumer composing a kind's own `legend` prop can reach — kinds compose `ChartFrame`
|
|
43
|
+
* directly to set it.
|
|
44
|
+
*/
|
|
45
|
+
headerSlot?: boolean;
|
|
46
46
|
};
|
|
47
47
|
export type ChartFrameProps = BasaltProps & {
|
|
48
48
|
/** Series identity — drives the derived legend. Pass the SAME array the kind draws + tooltips from. */
|
|
49
49
|
series: readonly SeriesStyle[];
|
|
50
50
|
/**
|
|
51
|
-
* Height in pixels, or one per
|
|
52
|
-
* Default
|
|
51
|
+
* Height in pixels, or one per container step. Used when neither `aspectRatio` nor `fill` is set.
|
|
52
|
+
* Default: derived from the measured width and container class (`resolveChartLayout`). Inside a
|
|
53
|
+
* `ChartCard` the resulting height is a MINIMUM — the frame grows into a taller card body. Passing it WITH `fill` is a wiring mistake, not a fallback — `fill` wins and
|
|
53
54
|
* this is never read, which now warns once in dev (`common/errors.ts`' `ignoredProp`).
|
|
54
55
|
*
|
|
55
|
-
* `{ base: 180,
|
|
56
|
-
* steps are compared against the frame's own MEASURED width, not the viewport,
|
|
57
|
-
* by a `PageAside` gets the short height on a desktop too. A plain number is
|
|
58
|
-
* supported ({@link resolveFrameHeight})
|
|
56
|
+
* `{ base: 180, wide: 260 }` is the declarative escape from a page of fixed-height charts: the
|
|
57
|
+
* `regular`/`wide` steps are compared against the frame's own MEASURED width, not the viewport,
|
|
58
|
+
* so a chart squeezed by a `PageAside` gets the short height on a desktop too. A plain number is
|
|
59
|
+
* an override and stays supported ({@link resolveFrameHeight}); one that leaves the plot under
|
|
60
|
+
* `VX.minPlotHeight` warns once in dev.
|
|
59
61
|
*/
|
|
60
62
|
height?: number | ResponsiveChartHeight;
|
|
61
63
|
/** height = Math.round(containerWidth / aspectRatio). Ignored when `fill` is set — which warns
|
|
@@ -109,6 +111,10 @@ export type PlotRect = {
|
|
|
109
111
|
width: number;
|
|
110
112
|
height: number;
|
|
111
113
|
hidden: ReadonlySet<string>;
|
|
114
|
+
/** Whether THIS frame's legend is currently portalled into a `ChartCard` header slot — a chart
|
|
115
|
+
* composing `ChartFrame` (e.g. `CartesianChart`'s end labels, R2C-10) reads this to skip a
|
|
116
|
+
* band-legend-only affordance once the header already names every series for free. */
|
|
117
|
+
legendInHeader: boolean;
|
|
112
118
|
};
|
|
113
119
|
/** A kind's own hover-dim wiring, merged into the resolved `ChartFrameLegend`. Kinds without
|
|
114
120
|
* per-series highlight state (single-series `ZonedLine`, `DualPanel`) omit this. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ChartFrame.d.ts","sourceRoot":"","sources":["../../../src/charts/primitives/ChartFrame.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAiB,SAAS,EAAE,MAAM,OAAO,CAAA;
|
|
1
|
+
{"version":3,"file":"ChartFrame.d.ts","sourceRoot":"","sources":["../../../src/charts/primitives/ChartFrame.tsx"],"names":[],"mappings":"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"}
|
|
@@ -1,26 +1,24 @@
|
|
|
1
1
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { useCallback, useState } from "react";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
2
|
+
import { useCallback, useContext, useId, useLayoutEffect, useMemo, useRef, useState } from "react";
|
|
3
|
+
import { createPortal } from "react-dom";
|
|
4
|
+
import {
|
|
5
|
+
deprecatedProp,
|
|
6
|
+
ignoredProp,
|
|
7
|
+
legendSlotContention,
|
|
8
|
+
plotBelowFloor
|
|
9
|
+
} from "../../common/errors.js";
|
|
10
|
+
import { reportOnce, useValidateProps } from "../../common/validate.js";
|
|
11
|
+
import { SizeClassHintContext, useSizeClass } from "../../shell/use-size-class.js";
|
|
5
12
|
import { VX } from "../../tokens/index.js";
|
|
6
13
|
import { useChartSize } from "../hooks/useChartSize.js";
|
|
7
14
|
import { deriveLegend } from "../series.js";
|
|
8
|
-
import {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
resolvePlotRect
|
|
13
|
-
} from "./chart-frame-layout.js";
|
|
14
|
-
import { ChartTierProvider } from "./chart-tier.js";
|
|
15
|
+
import { ChartCardContext } from "./chart-card-context.js";
|
|
16
|
+
import { resolveChartLayout, resolveFrameClass } from "./chart-layout.js";
|
|
17
|
+
import { resolveFrameHeight, resolveLegendRollup, resolvePlotRect } from "./chart-frame-layout.js";
|
|
18
|
+
import { ChartTierProvider, useCoarsePointer, useViewportHeight } from "./chart-tier.js";
|
|
15
19
|
import { ChartLegend } from "./ChartLegend.js";
|
|
16
20
|
import { ChartEmpty, ChartError, ChartPending, resolveChartState } from "./ChartPending.js";
|
|
17
|
-
import {
|
|
18
|
-
legendEntryCap,
|
|
19
|
-
resolveLegendRollup as resolveLegendRollup2,
|
|
20
|
-
resolvePlotRect as resolvePlotRect2,
|
|
21
|
-
resolveChartTier as resolveChartTier2
|
|
22
|
-
} from "./chart-frame-layout.js";
|
|
23
|
-
const DEFAULT_HEIGHT = 240;
|
|
21
|
+
import { legendEntryCap, resolveLegendRollup as resolveLegendRollup2, resolvePlotRect as resolvePlotRect2 } from "./chart-frame-layout.js";
|
|
24
22
|
const DEFAULT_MIN_WIDTH = 200;
|
|
25
23
|
const outerStyle = (fill, vertical) => ({
|
|
26
24
|
width: "100%",
|
|
@@ -60,6 +58,61 @@ function ChartFrame({
|
|
|
60
58
|
}) {
|
|
61
59
|
const { ref: containerRef, width: containerW, height: containerH } = useChartSize();
|
|
62
60
|
const { ref: legendRef, width: legendW, height: legendH } = useChartSize();
|
|
61
|
+
const { ref: slotRef, width: slotMeasuredW } = useChartSize();
|
|
62
|
+
const {
|
|
63
|
+
inCard,
|
|
64
|
+
legendSlot,
|
|
65
|
+
short: cardShort,
|
|
66
|
+
claimLegendSlot,
|
|
67
|
+
releaseLegendSlot,
|
|
68
|
+
legendSlotVersion
|
|
69
|
+
} = useContext(ChartCardContext);
|
|
70
|
+
const frameId = useId();
|
|
71
|
+
const [ownsSlot, setOwnsSlot] = useState(false);
|
|
72
|
+
const [slotSeedW, setSlotSeedW] = useState(0);
|
|
73
|
+
const ownsSlotRef = useRef(false);
|
|
74
|
+
const placement = legend === false ? "bottom" : legend.placement ?? "bottom";
|
|
75
|
+
const vertical = placement === "left" || placement === "right";
|
|
76
|
+
const wantsHeaderSlot = legend !== false && legend.headerSlot !== false && !vertical;
|
|
77
|
+
const resolvedState = resolveChartState({ ...state !== void 0 && { state }, isPending });
|
|
78
|
+
const legendVisible = legend !== false && resolvedState === null;
|
|
79
|
+
const tryClaim = useCallback(() => {
|
|
80
|
+
if (legendSlot === null || !wantsHeaderSlot) return false;
|
|
81
|
+
const owns = claimLegendSlot(frameId);
|
|
82
|
+
ownsSlotRef.current = owns;
|
|
83
|
+
setOwnsSlot(owns);
|
|
84
|
+
if (!owns) {
|
|
85
|
+
setSlotSeedW(0);
|
|
86
|
+
if (legendVisible) reportOnce("ChartFrame", legendSlotContention("ChartFrame"));
|
|
87
|
+
return false;
|
|
88
|
+
}
|
|
89
|
+
setSlotSeedW(legendSlot.getBoundingClientRect().width);
|
|
90
|
+
slotRef(legendSlot);
|
|
91
|
+
return true;
|
|
92
|
+
}, [legendSlot, wantsHeaderSlot, legendVisible, frameId, claimLegendSlot, slotRef]);
|
|
93
|
+
const releaseIfOwned = useCallback(() => {
|
|
94
|
+
if (!ownsSlotRef.current) return;
|
|
95
|
+
ownsSlotRef.current = false;
|
|
96
|
+
releaseLegendSlot(frameId);
|
|
97
|
+
slotRef(null);
|
|
98
|
+
}, [releaseLegendSlot, frameId, slotRef]);
|
|
99
|
+
useLayoutEffect(() => {
|
|
100
|
+
if (legendSlot === null || !wantsHeaderSlot) {
|
|
101
|
+
setOwnsSlot(false);
|
|
102
|
+
setSlotSeedW(0);
|
|
103
|
+
return void 0;
|
|
104
|
+
}
|
|
105
|
+
tryClaim();
|
|
106
|
+
return releaseIfOwned;
|
|
107
|
+
}, [legendSlot, wantsHeaderSlot, tryClaim, releaseIfOwned]);
|
|
108
|
+
useLayoutEffect(() => {
|
|
109
|
+
if (ownsSlotRef.current) return;
|
|
110
|
+
tryClaim();
|
|
111
|
+
}, [legendSlotVersion]);
|
|
112
|
+
const viewportClass = useSizeClass();
|
|
113
|
+
const sizeClass = useContext(SizeClassHintContext) === void 0 ? "medium" : viewportClass;
|
|
114
|
+
const coarse = useCoarsePointer();
|
|
115
|
+
const viewportH = useViewportHeight();
|
|
63
116
|
const [hidden, setHidden] = useState(() => /* @__PURE__ */ new Set());
|
|
64
117
|
const toggleKey = useCallback((key) => {
|
|
65
118
|
setHidden((prev) => {
|
|
@@ -85,20 +138,61 @@ function ChartFrame({
|
|
|
85
138
|
],
|
|
86
139
|
[fill, height, aspectRatio]
|
|
87
140
|
);
|
|
88
|
-
const placement = legend === false ? "bottom" : legend.placement ?? "bottom";
|
|
89
|
-
const vertical = placement === "left" || placement === "right";
|
|
90
|
-
const resolvedState = resolveChartState({ ...state !== void 0 && { state }, isPending });
|
|
91
|
-
const legendVisible = legend !== false && resolvedState === null;
|
|
92
|
-
const tier = resolveChartTier(containerW);
|
|
93
|
-
const resolvedHeight = fill ? containerH : aspectRatio !== void 0 ? Math.round(containerW / aspectRatio) : resolveFrameHeight(height ?? DEFAULT_HEIGHT, containerW);
|
|
94
|
-
const sideLegendWidth = legendVisible && vertical ? legendW : 0;
|
|
95
|
-
const topBottomLegendHeight = legendVisible && !vertical ? legendH : 0;
|
|
96
141
|
const legendItems = legend === false ? [] : deriveLegend(series);
|
|
142
|
+
useValidateProps(
|
|
143
|
+
"ChartFrame",
|
|
144
|
+
() => (
|
|
145
|
+
// A cap covering every entry rolls nothing up — it is how `Donut` states "never roll up".
|
|
146
|
+
legend !== false && legend.maxRows !== void 0 && legend.maxRows < legendItems.length ? deprecatedProp(
|
|
147
|
+
"ChartFrame",
|
|
148
|
+
"legend.maxRows",
|
|
149
|
+
"nothing (the legend fits by measured width)",
|
|
150
|
+
"1.32.0",
|
|
151
|
+
"Overflow folds into an `All N` disclosure; an explicit value still wins this release."
|
|
152
|
+
) : null
|
|
153
|
+
),
|
|
154
|
+
[legend, legendItems.length]
|
|
155
|
+
);
|
|
156
|
+
const statedHeight = height === void 0 ? void 0 : resolveFrameHeight(height, resolveFrameClass({ frameW: containerW, sizeClass }));
|
|
157
|
+
const slotW = legendSlot === null || !ownsSlot ? 0 : Math.max(slotMeasuredW, slotSeedW);
|
|
158
|
+
const groups = legend !== false && legend.groups === true;
|
|
159
|
+
const legendKey = legendItems.map((item) => `${item.key}\0${item.label}\0${item.role ?? ""}\0${item.note ?? ""}`).join("");
|
|
160
|
+
const layout = useMemo(
|
|
161
|
+
() => resolveChartLayout({
|
|
162
|
+
frameW: containerW,
|
|
163
|
+
slotW,
|
|
164
|
+
viewportH,
|
|
165
|
+
legendItems,
|
|
166
|
+
sizeClass,
|
|
167
|
+
coarse,
|
|
168
|
+
cardShort,
|
|
169
|
+
groups,
|
|
170
|
+
...statedHeight !== void 0 && { override: { height: statedHeight } }
|
|
171
|
+
}),
|
|
172
|
+
// `legendItems` is a fresh array per render; `legendKey` is its identity.
|
|
173
|
+
// oxlint-disable-next-line react-hooks/exhaustive-deps
|
|
174
|
+
[containerW, slotW, viewportH, legendKey, sizeClass, coarse, cardShort, groups, statedHeight]
|
|
175
|
+
);
|
|
176
|
+
const fit = layout.legend.mode === "dots" || layout.legend.mode === "chips" ? layout.legend : null;
|
|
177
|
+
const legendMode = fit?.mode;
|
|
178
|
+
const showLegend = legendVisible && fit !== null;
|
|
179
|
+
const inHeader = showLegend && !vertical && legendSlot !== null && ownsSlot && fit?.where === "header";
|
|
180
|
+
const sideLegendWidth = showLegend && vertical ? legendW : 0;
|
|
181
|
+
const topBottomLegendHeight = showLegend && !vertical && !inHeader ? legendH : 0;
|
|
182
|
+
const computedHeight = aspectRatio !== void 0 ? Math.round(containerW / aspectRatio) : layout.height;
|
|
183
|
+
const grows = inCard && !fill;
|
|
184
|
+
const resolvedHeight = fill ? containerH : grows ? Math.max(computedHeight, containerH) : computedHeight;
|
|
185
|
+
const cardMinHeight = Math.max(computedHeight, VX.minPlotHeight + topBottomLegendHeight);
|
|
186
|
+
useValidateProps(
|
|
187
|
+
"ChartFrame",
|
|
188
|
+
() => typeof height === "number" && !fill && aspectRatio === void 0 && height - topBottomLegendHeight < VX.minPlotHeight ? plotBelowFloor("ChartFrame", height, VX.minPlotHeight) : null,
|
|
189
|
+
[height, fill, aspectRatio, topBottomLegendHeight]
|
|
190
|
+
);
|
|
97
191
|
const togglable = legend !== false && (legend.toggle ?? legendItems.length > 1);
|
|
98
192
|
const { maxRows, legendWins } = resolveLegendRollup({
|
|
99
193
|
...legend !== false && legend.maxRows !== void 0 && { statedMaxRows: legend.maxRows },
|
|
100
|
-
|
|
101
|
-
fillBand:
|
|
194
|
+
...!vertical && fit !== null && fit.overflow > 0 && { fittedMaxRows: fit.visible },
|
|
195
|
+
fillBand: showLegend && fill && !inHeader && !vertical,
|
|
102
196
|
items: legendItems,
|
|
103
197
|
containerW,
|
|
104
198
|
available: resolvedHeight - VX.minPlotHeight
|
|
@@ -111,7 +205,7 @@ function ChartFrame({
|
|
|
111
205
|
topBottomLegendHeight,
|
|
112
206
|
...legendWins && { legendWins }
|
|
113
207
|
});
|
|
114
|
-
const legendNode = legend === false || resolvedState !== null ? null : /* @__PURE__ */ jsx("div", { ref: legendRef, style: legendWrapperStyle(vertical), children: /* @__PURE__ */ jsx(
|
|
208
|
+
const legendNode = legend === false || resolvedState !== null || !showLegend ? null : /* @__PURE__ */ jsx("div", { ref: legendRef, style: legendWrapperStyle(vertical), children: /* @__PURE__ */ jsx(
|
|
115
209
|
ChartLegend,
|
|
116
210
|
{
|
|
117
211
|
items: legendItems,
|
|
@@ -121,20 +215,26 @@ function ChartFrame({
|
|
|
121
215
|
...chartId !== void 0 && { chartId },
|
|
122
216
|
...legend.groups !== void 0 && { groups: legend.groups },
|
|
123
217
|
...maxRows !== void 0 && { maxRows },
|
|
218
|
+
...legendMode !== void 0 && { mode: legendMode },
|
|
124
219
|
...legend.highlighted !== void 0 && { highlighted: legend.highlighted },
|
|
125
220
|
...legend.onHighlight !== void 0 && { onHighlight: legend.onHighlight }
|
|
126
221
|
}
|
|
127
222
|
) });
|
|
128
|
-
return /* @__PURE__ */ jsx(ChartTierProvider, {
|
|
223
|
+
return /* @__PURE__ */ jsx(ChartTierProvider, { containerClass: layout.containerClass, children: /* @__PURE__ */ jsxs(
|
|
129
224
|
"div",
|
|
130
225
|
{
|
|
131
226
|
ref: containerRef,
|
|
132
227
|
...className !== void 0 && { className },
|
|
133
|
-
style: {
|
|
228
|
+
style: {
|
|
229
|
+
...outerStyle(fill, vertical),
|
|
230
|
+
...grows && { flex: "1 1 0", minHeight: cardMinHeight },
|
|
231
|
+
...style
|
|
232
|
+
},
|
|
134
233
|
...ariaLabel !== void 0 && { role: "group", "aria-label": ariaLabel },
|
|
135
234
|
...resolvedState === "pending" && { "aria-busy": "true" },
|
|
136
235
|
children: [
|
|
137
|
-
legendNode !== null &&
|
|
236
|
+
inHeader && legendNode !== null && legendSlot !== null && createPortal(legendNode, legendSlot),
|
|
237
|
+
!inHeader && legendNode !== null && (placement === "top" || placement === "left") && legendNode,
|
|
138
238
|
plot.width > 0 && plot.height > 0 && (resolvedState === "pending" ? /* @__PURE__ */ jsx(ChartPending, { width: plot.width, height: plot.height }) : resolvedState === "error" ? /* @__PURE__ */ jsx(ChartError, { width: plot.width, height: plot.height, error: state?.error }) : resolvedState === "empty" ? /* @__PURE__ */ jsx(
|
|
139
239
|
ChartEmpty,
|
|
140
240
|
{
|
|
@@ -142,8 +242,8 @@ function ChartFrame({
|
|
|
142
242
|
height: plot.height,
|
|
143
243
|
...typeof state?.empty === "string" && { label: state.empty }
|
|
144
244
|
}
|
|
145
|
-
) : children({ ...plot, hidden })),
|
|
146
|
-
legendNode !== null && (placement === "bottom" || placement === "right") && legendNode
|
|
245
|
+
) : children({ ...plot, hidden, legendInHeader: inHeader })),
|
|
246
|
+
!inHeader && legendNode !== null && (placement === "bottom" || placement === "right") && legendNode
|
|
147
247
|
]
|
|
148
248
|
}
|
|
149
249
|
) });
|
|
@@ -151,7 +251,6 @@ function ChartFrame({
|
|
|
151
251
|
export {
|
|
152
252
|
ChartFrame,
|
|
153
253
|
legendEntryCap,
|
|
154
|
-
resolveChartTier2 as resolveChartTier,
|
|
155
254
|
resolveLegend,
|
|
156
255
|
resolveLegendRollup2 as resolveLegendRollup,
|
|
157
256
|
resolvePlotRect2 as resolvePlotRect
|
|
@@ -28,26 +28,21 @@ export type LegendEntry = {
|
|
|
28
28
|
* static legends without hover interactivity. `chartId` namespaces `split` clipPath ids so two
|
|
29
29
|
* legends on the same page never collide. `groups` renders series → hairline divider → overlay →
|
|
30
30
|
* reference bands instead of one flat smear. `maxRows` caps the number of rendered ENTRIES and
|
|
31
|
-
* rolls the remainder into
|
|
32
|
-
* the name (`entries.slice(0, cap)` below is the whole of it
|
|
33
|
-
*
|
|
34
|
-
* `ChartTierMetrics.legendMaxRows`, so it stays until a minor can rename both.
|
|
31
|
+
* rolls the remainder into an `All N` disclosure chip — it is an entry count, not a row count, in
|
|
32
|
+
* spite of the name (`entries.slice(0, cap)` below is the whole of it). `ChartFrame` derives the
|
|
33
|
+
* cap from the measured fit (`resolveChartLayout().legend`); a caller-stated value wins.
|
|
35
34
|
*
|
|
36
35
|
* `onToggle` makes an entry a real toggle: clicking it hides the series in the plot, the tooltip,
|
|
37
36
|
* and the auto y-domain together (`ChartFrame` owns the `hidden` set — see `docs/CHARTS-SPEC.md`
|
|
38
|
-
* §5).
|
|
37
|
+
* §5). A hidden entry is announced via `aria-pressed`.
|
|
39
38
|
*
|
|
40
|
-
* The
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
* unnamed, and a `+6 more` that is a `<span>` gives a phone reader no way to learn them — a
|
|
46
|
-
* categorical encoding the chart draws and refuses to decode. The chip is a `<button>` carrying
|
|
47
|
-
* `aria-expanded`: expanded, every entry renders, the legend band grows, and `ChartFrame`'s
|
|
48
|
-
* observer re-flows the frame around it (the plot keeps `VX.minPlotHeight`).
|
|
39
|
+
* **The rollup is a disclosure, not a label.** `All N` is a `<button>` carrying `aria-expanded`
|
|
40
|
+
* that opens EVERY entry, toggles included, in a popover under the chip on a fine pointer and a
|
|
41
|
+
* bottom sheet on a coarse one. Both are fixed-position and portalled, so opening one never grows
|
|
42
|
+
* the legend band or moves the plot. Escape, an outside press, a scroll or a resize closes it, and
|
|
43
|
+
* focus returns to the chip on every close.
|
|
49
44
|
*/
|
|
50
|
-
export declare function ChartLegend({ items, highlighted, onHighlight, hidden, onToggle, chartId, placement, groups, maxRows, className, style, }: BasaltProps & {
|
|
45
|
+
export declare function ChartLegend({ items, highlighted, onHighlight, hidden, onToggle, chartId, placement, groups, maxRows, mode, className, style, }: BasaltProps & {
|
|
51
46
|
items: LegendEntry[];
|
|
52
47
|
highlighted?: string | null;
|
|
53
48
|
onHighlight?: (key: string | null) => void;
|
|
@@ -59,5 +54,14 @@ export declare function ChartLegend({ items, highlighted, onHighlight, hidden, o
|
|
|
59
54
|
placement?: LegendPlacement;
|
|
60
55
|
groups?: boolean;
|
|
61
56
|
maxRows?: number;
|
|
57
|
+
/**
|
|
58
|
+
* `'dots'` collapses every VISIBLE entry to a plain color dot with no label (the name still
|
|
59
|
+
* reaches assistive tech via `aria-label`) — `resolveChartLayout`'s header-slot legend forces
|
|
60
|
+
* this for a short `ChartCard` (wave 11, `docs/CHARTS-SPEC.md` §5) so the legend band's height and
|
|
61
|
+
* width both shrink before it eats plot space. The `All N` disclosure panel is unaffected: it
|
|
62
|
+
* always shows the full swatch + label, `mode` only governs the inline row. Default `'chips'`
|
|
63
|
+
* (today's swatch + label rendering) for every other caller.
|
|
64
|
+
*/
|
|
65
|
+
mode?: 'dots' | 'chips';
|
|
62
66
|
}): import("react").JSX.Element;
|
|
63
67
|
//# sourceMappingURL=ChartLegend.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ChartLegend.d.ts","sourceRoot":"","sources":["../../../src/charts/primitives/ChartLegend.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"ChartLegend.d.ts","sourceRoot":"","sources":["../../../src/charts/primitives/ChartLegend.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAA;AAGrD,OAAO,KAAK,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,WAAW,CAAA;AAI5D,MAAM,MAAM,WAAW,GAAG;IACxB,GAAG,EAAE,MAAM,CAAA;IACX,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,MAAM,CAAA;IACb,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,KAAK,CAAC,EAAE,MAAM,GAAG,KAAK,GAAG,OAAO,GAAG,WAAW,CAAA;IAC9C,mFAAmF;IACnF,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,kGAAkG;IAClG,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB;0EACsE;IACtE,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,mFAAmF;IACnF,IAAI,CAAC,EAAE,UAAU,CAAA;IACjB;iFAC6E;IAC7E,QAAQ,CAAC,EAAE,WAAW,EAAE,CAAA;IACxB;gCAC4B;IAC5B,IAAI,CAAC,EAAE,MAAM,CAAA;CACd,CAAA;AAuRD;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,WAAW,CAAC,EAC1B,KAAK,EACL,WAAkB,EAClB,WAAW,EACX,MAAkB,EAClB,QAAQ,EACR,OAAY,EACZ,SAAoB,EACpB,MAAc,EACd,OAAO,EACP,IAAc,EACd,SAAS,EACT,KAAK,GACN,EAAE,WAAW,GAAG;IACf,KAAK,EAAE,WAAW,EAAE,CAAA;IACpB,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC3B,WAAW,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAA;IAC1C,kCAAkC;IAClC,MAAM,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC,CAAA;IAC5B,+CAA+C;IAC/C,QAAQ,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAA;IAChC,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,SAAS,CAAC,EAAE,eAAe,CAAA;IAC3B,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB;;;;;;;OAOG;IACH,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAAA;CACxB,+BA2MA"}
|