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,7 +1,10 @@
|
|
|
1
|
-
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { useState } from "react";
|
|
1
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useCallback, useEffect, useId, useRef, useState } from "react";
|
|
3
|
+
import { createPortal } from "react-dom";
|
|
3
4
|
import { alpha, VX } from "../../tokens/index.js";
|
|
4
|
-
import {
|
|
5
|
+
import { useDismissOnOutside } from "../cursor/touch-pin.js";
|
|
6
|
+
import { DOTS_HIT_GAP, LEGEND_DOT_SIZE, LEGEND_ROW_GAP, orderEntries } from "./chart-frame-layout.js";
|
|
7
|
+
import { useChartMetrics, useCoarsePointer } from "./chart-tier.js";
|
|
5
8
|
const LEGEND_ITEM_BASE = {
|
|
6
9
|
display: "flex",
|
|
7
10
|
alignItems: "center",
|
|
@@ -20,33 +23,68 @@ const LEGEND_ITEM_BUTTON = {
|
|
|
20
23
|
color: "inherit",
|
|
21
24
|
textAlign: "inherit"
|
|
22
25
|
};
|
|
23
|
-
const GROUP_ORDER = ["series", "overlay", "reference"];
|
|
24
26
|
const NO_HIDDEN = /* @__PURE__ */ new Set();
|
|
25
|
-
const
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
27
|
+
const DISCLOSURE_PANEL = {
|
|
28
|
+
position: "fixed",
|
|
29
|
+
zIndex: VX.zIndexFloating,
|
|
30
|
+
display: "flex",
|
|
31
|
+
flexDirection: "column",
|
|
32
|
+
gap: LEGEND_ROW_GAP,
|
|
33
|
+
// theme-allow raw-scroll-container — a fixed floating panel of one legend, not app chrome
|
|
34
|
+
overflowY: "auto",
|
|
35
|
+
// `<dialog open>` brings UA chrome (centring margin, border, fit-content box) this resets.
|
|
36
|
+
margin: 0,
|
|
37
|
+
border: "none",
|
|
38
|
+
width: "auto",
|
|
39
|
+
height: "auto",
|
|
40
|
+
padding: 10,
|
|
41
|
+
boxSizing: "border-box",
|
|
42
|
+
backgroundColor: VX.surface.overlay,
|
|
43
|
+
boxShadow: VX.shadowOverlay,
|
|
44
|
+
color: VX.muted,
|
|
45
|
+
borderRadius: VX.radiusCard
|
|
46
|
+
};
|
|
47
|
+
const DISCLOSURE_MIN_HEIGHT = 96;
|
|
48
|
+
function disclosurePlacement(chip, coarse) {
|
|
49
|
+
if (coarse) {
|
|
50
|
+
return {
|
|
51
|
+
left: 0,
|
|
52
|
+
right: 0,
|
|
53
|
+
bottom: 0,
|
|
54
|
+
maxHeight: "60dvh",
|
|
55
|
+
borderBottomLeftRadius: 0,
|
|
56
|
+
borderBottomRightRadius: 0,
|
|
57
|
+
padding: "12px 16px calc(12px + env(safe-area-inset-bottom, 0px))"
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
const below = window.innerHeight - chip.bottom;
|
|
61
|
+
const left = Math.max(8, Math.min(chip.left, window.innerWidth - 248));
|
|
62
|
+
const base = { left, right: "auto", minWidth: 200, maxWidth: "calc(100vw - 16px)" };
|
|
63
|
+
return below >= 200 || below >= chip.top ? { ...base, top: chip.bottom + 4, maxHeight: Math.max(below - 12, DISCLOSURE_MIN_HEIGHT) } : {
|
|
64
|
+
...base,
|
|
65
|
+
bottom: window.innerHeight - chip.top + 4,
|
|
66
|
+
maxHeight: Math.max(chip.top - 12, DISCLOSURE_MIN_HEIGHT)
|
|
67
|
+
};
|
|
38
68
|
}
|
|
39
|
-
function wrapperStyle(placement, fontSize) {
|
|
69
|
+
function wrapperStyle(placement, fontSize, dots) {
|
|
40
70
|
const vertical = placement === "left" || placement === "right";
|
|
71
|
+
const gap = dots ? DOTS_HIT_GAP : VX.legendGap;
|
|
41
72
|
return {
|
|
42
73
|
display: "flex",
|
|
43
74
|
flexDirection: vertical ? "column" : "row",
|
|
44
75
|
flexWrap: vertical ? "nowrap" : "wrap",
|
|
45
76
|
alignItems: vertical ? "flex-start" : "center",
|
|
46
|
-
justifyContent:
|
|
47
|
-
columnGap:
|
|
48
|
-
|
|
49
|
-
|
|
77
|
+
justifyContent: "flex-start",
|
|
78
|
+
columnGap: gap,
|
|
79
|
+
// Always the tight row gap (R2C-9): the wider dots COLUMN gap is a hit-overlap cap, not
|
|
80
|
+
// breathing room between wrapped rows — a dots-mode header legend is 1 row by law
|
|
81
|
+
// (`LEGEND_ROWS.header` in `chart-layout.ts`) anyway, so this only ever shows in the band.
|
|
82
|
+
rowGap: LEGEND_ROW_GAP,
|
|
83
|
+
// Caps each entry's `[data-basalt-hit]` overlay at the real inter-entry gap (styles.css).
|
|
84
|
+
...{ "--vx-hit-gap": `${gap}px` },
|
|
85
|
+
// A dots-mode row has no text baseline to breathe around — the header-fold law (wave 8) is
|
|
86
|
+
// the reason this row exists at all, so it gets the same tight treatment.
|
|
87
|
+
padding: dots ? "4px 0 2px" : "8px 0 2px",
|
|
50
88
|
fontSize,
|
|
51
89
|
color: VX.muted
|
|
52
90
|
};
|
|
@@ -145,6 +183,21 @@ const LEGEND_CHILD_STYLE = {
|
|
|
145
183
|
fontSize: "0.85em",
|
|
146
184
|
opacity: 0.75
|
|
147
185
|
};
|
|
186
|
+
function LegendDot({ item }) {
|
|
187
|
+
return /* @__PURE__ */ jsx(
|
|
188
|
+
"span",
|
|
189
|
+
{
|
|
190
|
+
style: {
|
|
191
|
+
width: LEGEND_DOT_SIZE,
|
|
192
|
+
height: LEGEND_DOT_SIZE,
|
|
193
|
+
borderRadius: "50%",
|
|
194
|
+
// theme-allow raw-surface — a circle shape, not a surface corner
|
|
195
|
+
backgroundColor: item.color,
|
|
196
|
+
flexShrink: 0
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
);
|
|
200
|
+
}
|
|
148
201
|
function LegendChildSwatch({ item }) {
|
|
149
202
|
if (item.shape === "bar") {
|
|
150
203
|
return /* @__PURE__ */ jsx(
|
|
@@ -193,59 +246,105 @@ function ChartLegend({
|
|
|
193
246
|
placement = "bottom",
|
|
194
247
|
groups = false,
|
|
195
248
|
maxRows,
|
|
249
|
+
mode = "chips",
|
|
196
250
|
className,
|
|
197
251
|
style
|
|
198
252
|
}) {
|
|
199
|
-
const tier =
|
|
200
|
-
const
|
|
201
|
-
const
|
|
202
|
-
|
|
203
|
-
|
|
253
|
+
const tier = useChartMetrics();
|
|
254
|
+
const coarse = useCoarsePointer();
|
|
255
|
+
const [disclosure, setDisclosure] = useState(null);
|
|
256
|
+
const triggerRef = useRef(null);
|
|
257
|
+
const panelRef = useRef(null);
|
|
258
|
+
const open = disclosure !== null;
|
|
259
|
+
const panelId = useId();
|
|
260
|
+
const wasOpen = useRef(false);
|
|
261
|
+
const openDisclosure = (trigger) => {
|
|
262
|
+
triggerRef.current = trigger;
|
|
263
|
+
setDisclosure(disclosurePlacement(trigger.getBoundingClientRect(), coarse));
|
|
204
264
|
};
|
|
265
|
+
useEffect(() => {
|
|
266
|
+
if (open) panelRef.current?.focus();
|
|
267
|
+
else if (wasOpen.current) triggerRef.current?.focus();
|
|
268
|
+
wasOpen.current = open;
|
|
269
|
+
}, [open]);
|
|
270
|
+
const isInsidePanel = useCallback(
|
|
271
|
+
(target) => panelRef.current?.contains(target) === true || triggerRef.current?.contains(target) === true,
|
|
272
|
+
[]
|
|
273
|
+
);
|
|
274
|
+
useDismissOnOutside({
|
|
275
|
+
enabled: open,
|
|
276
|
+
isInside: isInsidePanel,
|
|
277
|
+
onDismiss: () => setDisclosure(null),
|
|
278
|
+
dismissOnResize: true,
|
|
279
|
+
scrollExemptInside: true
|
|
280
|
+
});
|
|
281
|
+
useEffect(() => {
|
|
282
|
+
if (!open || !coarse) return;
|
|
283
|
+
const panel = panelRef.current;
|
|
284
|
+
if (panel === null) return;
|
|
285
|
+
const onKeyDown = (e) => {
|
|
286
|
+
if (e.key !== "Tab") return;
|
|
287
|
+
const focusable = panel.querySelectorAll(
|
|
288
|
+
'button, [tabindex]:not([tabindex="-1"])'
|
|
289
|
+
);
|
|
290
|
+
const first = focusable[0];
|
|
291
|
+
const last = focusable[focusable.length - 1];
|
|
292
|
+
if (first === void 0 || last === void 0) return;
|
|
293
|
+
if (e.shiftKey && document.activeElement === first) {
|
|
294
|
+
e.preventDefault();
|
|
295
|
+
last.focus();
|
|
296
|
+
} else if (!e.shiftKey && document.activeElement === last) {
|
|
297
|
+
e.preventDefault();
|
|
298
|
+
first.focus();
|
|
299
|
+
}
|
|
300
|
+
};
|
|
301
|
+
panel.addEventListener("keydown", onKeyDown);
|
|
302
|
+
return () => panel.removeEventListener("keydown", onKeyDown);
|
|
303
|
+
}, [open, coarse]);
|
|
304
|
+
const handleEnter = (key) => onHighlight?.(key);
|
|
205
305
|
const handleLeave = () => onHighlight?.(null);
|
|
206
|
-
const handleToggle = (e) => {
|
|
207
|
-
const key = e.currentTarget.dataset["legendKey"];
|
|
208
|
-
if (key !== void 0) onToggle?.(key);
|
|
209
|
-
};
|
|
210
306
|
const idPrefix = chartId ? `${chartId}-` : "";
|
|
211
307
|
const vertical = placement === "left" || placement === "right";
|
|
212
308
|
const { entries, dividerAfter } = orderEntries(items, groups);
|
|
213
|
-
const
|
|
214
|
-
const
|
|
215
|
-
const
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
"button",
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
},
|
|
234
|
-
...onToggle !== void 0 && { onClick: handleToggle },
|
|
235
|
-
onMouseEnter: handleEnter,
|
|
236
|
-
onMouseLeave: handleLeave,
|
|
237
|
-
onFocus: handleEnter,
|
|
238
|
-
onBlur: handleLeave,
|
|
239
|
-
children: [
|
|
240
|
-
/* @__PURE__ */ jsx(LegendSwatch, { item, idPrefix }),
|
|
241
|
-
/* @__PURE__ */ jsx("span", { children: item.label }),
|
|
242
|
-
item.note ? /* @__PURE__ */ jsx("span", { style: LEGEND_NOTE_STYLE, children: item.note }) : null,
|
|
243
|
-
item.children?.map((child) => /* @__PURE__ */ jsx(LegendChild, { item: child }, child.key))
|
|
244
|
-
]
|
|
309
|
+
const rolledUp = maxRows === void 0 ? 0 : Math.max(entries.length - maxRows, 0);
|
|
310
|
+
const visible = rolledUp > 0 ? entries.slice(0, maxRows) : entries;
|
|
311
|
+
const entryButton = (item, inPanel = false) => {
|
|
312
|
+
const isDotTrigger = !inPanel && mode === "dots";
|
|
313
|
+
return /* @__PURE__ */ jsx(
|
|
314
|
+
"button",
|
|
315
|
+
{
|
|
316
|
+
type: "button",
|
|
317
|
+
"data-basalt-hit": true,
|
|
318
|
+
...!inPanel && { "data-legend-key": item.key },
|
|
319
|
+
"aria-label": item.note ? `${item.label} \u2014 ${item.note}` : item.label,
|
|
320
|
+
...isDotTrigger && { title: item.label, "aria-haspopup": "dialog" },
|
|
321
|
+
...isDotTrigger ? { "aria-expanded": open, "aria-controls": panelId } : onToggle !== void 0 && { "aria-pressed": !hidden.has(item.key) },
|
|
322
|
+
style: {
|
|
323
|
+
...LEGEND_ITEM_BUTTON,
|
|
324
|
+
cursor: onToggle === void 0 && !isDotTrigger ? "default" : "pointer",
|
|
325
|
+
// Toggled-off wins over hover-dimming: a hidden series must read as hidden even while
|
|
326
|
+
// it is the one being hovered.
|
|
327
|
+
opacity: hidden.has(item.key) ? 0.35 : highlighted === null || highlighted === item.key ? 1 : 0.3,
|
|
328
|
+
textDecoration: hidden.has(item.key) ? "line-through" : "none"
|
|
245
329
|
},
|
|
246
|
-
item.key
|
|
247
|
-
|
|
330
|
+
...isDotTrigger ? { onClick: (e) => openDisclosure(e.currentTarget) } : onToggle !== void 0 && { onClick: () => onToggle(item.key) },
|
|
331
|
+
onMouseEnter: () => handleEnter(item.key),
|
|
332
|
+
onMouseLeave: handleLeave,
|
|
333
|
+
onFocus: () => handleEnter(item.key),
|
|
334
|
+
onBlur: handleLeave,
|
|
335
|
+
children: !inPanel && mode === "dots" ? /* @__PURE__ */ jsx(LegendDot, { item }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
336
|
+
/* @__PURE__ */ jsx(LegendSwatch, { item, idPrefix: inPanel ? `${idPrefix}disclosure-` : idPrefix }),
|
|
337
|
+
/* @__PURE__ */ jsx("span", { children: item.label }),
|
|
338
|
+
item.note ? /* @__PURE__ */ jsx("span", { style: LEGEND_NOTE_STYLE, children: item.note }) : null,
|
|
339
|
+
item.children?.map((child) => /* @__PURE__ */ jsx(LegendChild, { item: child }, child.key))
|
|
340
|
+
] })
|
|
341
|
+
},
|
|
342
|
+
item.key
|
|
248
343
|
);
|
|
344
|
+
};
|
|
345
|
+
const nodes = [];
|
|
346
|
+
visible.forEach((item, i) => {
|
|
347
|
+
nodes.push(entryButton(item));
|
|
249
348
|
if (dividerAfter.has(i))
|
|
250
349
|
nodes.push(/* @__PURE__ */ jsx(LegendDivider, { columnLayout: vertical }, `divider-${item.key}`));
|
|
251
350
|
});
|
|
@@ -255,21 +354,63 @@ function ChartLegend({
|
|
|
255
354
|
"button",
|
|
256
355
|
{
|
|
257
356
|
type: "button",
|
|
258
|
-
"
|
|
259
|
-
|
|
357
|
+
"data-basalt-hit": true,
|
|
358
|
+
"aria-expanded": open,
|
|
359
|
+
"aria-haspopup": "dialog",
|
|
360
|
+
"aria-controls": panelId,
|
|
361
|
+
onClick: (e) => open ? setDisclosure(null) : openDisclosure(e.currentTarget),
|
|
260
362
|
style: { ...LEGEND_ITEM_BUTTON, cursor: "pointer", textDecoration: "underline" },
|
|
261
|
-
children:
|
|
363
|
+
children: `All ${entries.length}`
|
|
262
364
|
},
|
|
263
365
|
"legend-more"
|
|
264
366
|
)
|
|
265
367
|
);
|
|
266
368
|
}
|
|
267
|
-
return /* @__PURE__ */
|
|
369
|
+
return /* @__PURE__ */ jsxs(
|
|
268
370
|
"div",
|
|
269
371
|
{
|
|
270
372
|
...className !== void 0 && { className },
|
|
271
|
-
style: { ...wrapperStyle(placement, tier.legendFontSize), ...style },
|
|
272
|
-
children:
|
|
373
|
+
style: { ...wrapperStyle(placement, tier.legendFontSize, mode === "dots"), ...style },
|
|
374
|
+
children: [
|
|
375
|
+
nodes,
|
|
376
|
+
open && createPortal(
|
|
377
|
+
/* @__PURE__ */ jsxs(Fragment, { children: [
|
|
378
|
+
coarse && /* @__PURE__ */ jsx(
|
|
379
|
+
"div",
|
|
380
|
+
{
|
|
381
|
+
"aria-hidden": true,
|
|
382
|
+
onPointerDown: (e) => {
|
|
383
|
+
e.stopPropagation();
|
|
384
|
+
setDisclosure(null);
|
|
385
|
+
},
|
|
386
|
+
style: {
|
|
387
|
+
position: "fixed",
|
|
388
|
+
inset: 0,
|
|
389
|
+
// One below the panel it sits behind — still `VX.zIndexFloating`-derived, never a
|
|
390
|
+
// second unrelated literal (B14).
|
|
391
|
+
zIndex: VX.zIndexFloating - 1,
|
|
392
|
+
backgroundColor: alpha(VX.neutral, 0.35)
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
),
|
|
396
|
+
/* @__PURE__ */ jsx(
|
|
397
|
+
"dialog",
|
|
398
|
+
{
|
|
399
|
+
ref: panelRef,
|
|
400
|
+
id: panelId,
|
|
401
|
+
open: true,
|
|
402
|
+
tabIndex: -1,
|
|
403
|
+
"aria-label": "Legend",
|
|
404
|
+
...coarse && { "aria-modal": true },
|
|
405
|
+
"data-basalt-legend-disclosure": coarse ? "sheet" : "popover",
|
|
406
|
+
style: { ...DISCLOSURE_PANEL, fontSize: tier.legendFontSize, ...disclosure },
|
|
407
|
+
children: entries.map((item) => entryButton(item, true))
|
|
408
|
+
}
|
|
409
|
+
)
|
|
410
|
+
] }),
|
|
411
|
+
document.body
|
|
412
|
+
)
|
|
413
|
+
]
|
|
273
414
|
}
|
|
274
415
|
);
|
|
275
416
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ChartTooltip.d.ts","sourceRoot":"","sources":["../../../src/charts/primitives/ChartTooltip.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAiB,SAAS,EAAE,MAAM,OAAO,CAAA;AAuBrD,uFAAuF;AACvF,wBAAgB,aAAa,CAAC,EAC5B,IAAI,EACJ,KAAK,EACL,UAAU,EACV,MAAuB,GACxB,EAAE;IACD,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB;;;sGAGkG;IAClG,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAA;CAClC,+BAoCA;AAED,4CAA4C;AAC5C,wBAAgB,UAAU,CAAC,EACzB,KAAK,EACL,KAAK,EACL,KAAK,EACL,UAAU,EACV,KAAK,EACL,WAAW,EACX,MAAM,GACP,EAAE;IACD,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,MAAM,CAAA;IACb,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,KAAK,CAAC,EAAE,KAAK,GAAG,MAAM,GAAG,KAAK,CAAA;IAC9B,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,uEAAuE;IACvE,MAAM,CAAC,EAAE,OAAO,CAAA;CACjB,+BA0CA;AAED,wBAAgB,WAAW,CAAC,EAAE,QAAQ,EAAE,EAAE;IAAE,QAAQ,EAAE,SAAS,CAAA;CAAE,+BAEhE;AAsBD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,iBAAiB,CAAC,EAChC,MAAM,EACN,QAAe,EACf,QAAQ,GACT,EAAE;IACD,MAAM,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAA;IACvC;;;8CAG0C;IAC1C,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,QAAQ,EAAE,SAAS,CAAA;CACpB,
|
|
1
|
+
{"version":3,"file":"ChartTooltip.d.ts","sourceRoot":"","sources":["../../../src/charts/primitives/ChartTooltip.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAiB,SAAS,EAAE,MAAM,OAAO,CAAA;AAuBrD,uFAAuF;AACvF,wBAAgB,aAAa,CAAC,EAC5B,IAAI,EACJ,KAAK,EACL,UAAU,EACV,MAAuB,GACxB,EAAE;IACD,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB;;;sGAGkG;IAClG,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAA;CAClC,+BAoCA;AAED,4CAA4C;AAC5C,wBAAgB,UAAU,CAAC,EACzB,KAAK,EACL,KAAK,EACL,KAAK,EACL,UAAU,EACV,KAAK,EACL,WAAW,EACX,MAAM,GACP,EAAE;IACD,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,MAAM,CAAA;IACb,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,KAAK,CAAC,EAAE,KAAK,GAAG,MAAM,GAAG,KAAK,CAAA;IAC9B,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,uEAAuE;IACvE,MAAM,CAAC,EAAE,OAAO,CAAA;CACjB,+BA0CA;AAED,wBAAgB,WAAW,CAAC,EAAE,QAAQ,EAAE,EAAE;IAAE,QAAQ,EAAE,SAAS,CAAA;CAAE,+BAEhE;AAsBD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,iBAAiB,CAAC,EAChC,MAAM,EACN,QAAe,EACf,QAAQ,GACT,EAAE;IACD,MAAM,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAA;IACvC;;;8CAG0C;IAC1C,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,QAAQ,EAAE,SAAS,CAAA;CACpB,sCA2DA"}
|
|
@@ -3,11 +3,11 @@ import { useLayoutEffect, useRef, useState, useSyncExternalStore } from "react";
|
|
|
3
3
|
import { createPortal } from "react-dom";
|
|
4
4
|
import { VX } from "../../tokens/index.js";
|
|
5
5
|
import { fmtTooltipDate } from "../utils/format.js";
|
|
6
|
-
import {
|
|
6
|
+
import { useChartMetrics } from "./chart-tier.js";
|
|
7
7
|
const TOOLTIP_STYLES = {
|
|
8
8
|
position: "fixed",
|
|
9
9
|
pointerEvents: "none",
|
|
10
|
-
zIndex:
|
|
10
|
+
zIndex: VX.zIndexFloating,
|
|
11
11
|
backgroundColor: VX.surface.panel,
|
|
12
12
|
borderRadius: "var(--vx-radius-floating)",
|
|
13
13
|
padding: "0",
|
|
@@ -132,7 +132,7 @@ function ChartTooltipFloat({
|
|
|
132
132
|
const ref = useRef(null);
|
|
133
133
|
const [box, setBox] = useState(null);
|
|
134
134
|
const isClient = useIsClient();
|
|
135
|
-
const { tooltipMinWidth } =
|
|
135
|
+
const { tooltipMinWidth } = useChartMetrics();
|
|
136
136
|
useLayoutEffect(() => {
|
|
137
137
|
const el = ref.current;
|
|
138
138
|
if (el === null) return;
|
|
@@ -147,6 +147,7 @@ function ChartTooltipFloat({
|
|
|
147
147
|
const height = box?.height ?? 0;
|
|
148
148
|
const vw = window.innerWidth;
|
|
149
149
|
const vh = window.innerHeight;
|
|
150
|
+
if (anchor.y < 0 || anchor.y > vh) return null;
|
|
150
151
|
const flipsLeft = anchor.x + TOOLTIP_GAP + width + VIEWPORT_MARGIN > vw;
|
|
151
152
|
const left = flipsLeft ? Math.max(VIEWPORT_MARGIN, anchor.x - TOOLTIP_GAP - width) : anchor.x + TOOLTIP_GAP;
|
|
152
153
|
const top = Math.min(
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { jsx } from "react/jsx-runtime";
|
|
2
2
|
import { VX } from "../../tokens/index.js";
|
|
3
|
-
import {
|
|
3
|
+
import { useChartMetrics } from "./chart-tier.js";
|
|
4
4
|
function Crosshair({
|
|
5
5
|
x,
|
|
6
6
|
top,
|
|
@@ -14,7 +14,7 @@ function SeriesDot({
|
|
|
14
14
|
color,
|
|
15
15
|
r
|
|
16
16
|
}) {
|
|
17
|
-
const { dotR } =
|
|
17
|
+
const { dotR } = useChartMetrics();
|
|
18
18
|
return /* @__PURE__ */ jsx("circle", { cx, cy, r: r ?? dotR, fill: color, stroke: VX.dotStroke, strokeWidth: 2 });
|
|
19
19
|
}
|
|
20
20
|
export {
|
|
@@ -6,10 +6,15 @@ import type { FocusEventHandler, KeyboardEventHandler, PointerEventHandler } fro
|
|
|
6
6
|
* With `onKeyDown` it also becomes the chart's keyboard affordance: the rect takes focus and
|
|
7
7
|
* ←/→ scrub the cursor, so a chart's values are reachable without a pointer.
|
|
8
8
|
*/
|
|
9
|
-
export declare function HoverOverlay({ width, height, onMove, onLeave, onKeyDown, onBlur, ariaLabel, valueNow, valueMax, valueText, }: {
|
|
9
|
+
export declare function HoverOverlay({ width, height, onMove, onDown, onUp, onLeave, onKeyDown, onBlur, ariaLabel, valueNow, valueMax, valueText, }: {
|
|
10
10
|
width: number;
|
|
11
11
|
height: number;
|
|
12
12
|
onMove: PointerEventHandler<SVGRectElement>;
|
|
13
|
+
/** Coarse-pointer tap: resolves + shows the tooltip immediately, with no preceding `pointermove`
|
|
14
|
+
* (`docs/CHARTS-SPEC.md` §4, Tooltip). The press is provisional; `onUp` commits it. */
|
|
15
|
+
onDown?: PointerEventHandler<SVGRectElement>;
|
|
16
|
+
/** Commits the provisional touch press so the pin survives the lift. */
|
|
17
|
+
onUp?: PointerEventHandler<SVGRectElement>;
|
|
13
18
|
onLeave: PointerEventHandler<SVGRectElement>;
|
|
14
19
|
/** Present = the overlay is focusable and scrubs on ←/→. */
|
|
15
20
|
onKeyDown?: KeyboardEventHandler<SVGRectElement>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"HoverOverlay.d.ts","sourceRoot":"","sources":["../../../src/charts/primitives/HoverOverlay.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,oBAAoB,EAAE,mBAAmB,EAAE,MAAM,OAAO,CAAA;
|
|
1
|
+
{"version":3,"file":"HoverOverlay.d.ts","sourceRoot":"","sources":["../../../src/charts/primitives/HoverOverlay.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,oBAAoB,EAAE,mBAAmB,EAAE,MAAM,OAAO,CAAA;AAGzF;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,EAC3B,KAAK,EACL,MAAM,EACN,MAAM,EACN,MAAM,EACN,IAAI,EACJ,OAAO,EACP,SAAS,EACT,MAAM,EACN,SAAS,EACT,QAAQ,EACR,QAAQ,EACR,SAAS,GACV,EAAE;IACD,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,mBAAmB,CAAC,cAAc,CAAC,CAAA;IAC3C;2FACuF;IACvF,MAAM,CAAC,EAAE,mBAAmB,CAAC,cAAc,CAAC,CAAA;IAC5C,wEAAwE;IACxE,IAAI,CAAC,EAAE,mBAAmB,CAAC,cAAc,CAAC,CAAA;IAC1C,OAAO,EAAE,mBAAmB,CAAC,cAAc,CAAC,CAAA;IAC5C,4DAA4D;IAC5D,SAAS,CAAC,EAAE,oBAAoB,CAAC,cAAc,CAAC,CAAA;IAChD,MAAM,CAAC,EAAE,iBAAiB,CAAC,cAAc,CAAC,CAAA;IAC1C,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,qEAAqE;IACrE,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,gCAAgC;IAChC,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,wEAAwE;IACxE,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB,+BAiCA"}
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import { jsx } from "react/jsx-runtime";
|
|
2
|
+
import styles from "./HoverOverlay.module.css";
|
|
2
3
|
function HoverOverlay({
|
|
3
4
|
width,
|
|
4
5
|
height,
|
|
5
6
|
onMove,
|
|
7
|
+
onDown,
|
|
8
|
+
onUp,
|
|
6
9
|
onLeave,
|
|
7
10
|
onKeyDown,
|
|
8
11
|
onBlur,
|
|
@@ -11,32 +14,40 @@ function HoverOverlay({
|
|
|
11
14
|
valueMax,
|
|
12
15
|
valueText
|
|
13
16
|
}) {
|
|
14
|
-
return
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
17
|
+
return (
|
|
18
|
+
// `touch-action: pan-y` lives on the plot's `<svg>` (each kind's own), NOT here: Chrome ignores
|
|
19
|
+
// `touch-action` on an SVG child `<rect>`, so the browser claims a horizontal pan and cancels
|
|
20
|
+
// the pointer (`R2C-2`). The `overlay` class owns the focus ring: no UA outline on tap, a VX
|
|
21
|
+
// accent inset under `:focus-visible` (`R2C-15`).
|
|
22
|
+
/* @__PURE__ */ jsx(
|
|
23
|
+
"rect",
|
|
24
|
+
{
|
|
25
|
+
className: styles.overlay,
|
|
26
|
+
width,
|
|
27
|
+
height,
|
|
28
|
+
fill: "transparent",
|
|
29
|
+
...onKeyDown !== void 0 && {
|
|
30
|
+
tabIndex: 0,
|
|
31
|
+
// `slider`, not `application`: `application` would drop this node out of the screen
|
|
32
|
+
// reader's normal browse mode entirely, which is a far bigger hammer than an arrow-key
|
|
33
|
+
// scrub needs. A slider announces both the affordance and the focused point.
|
|
34
|
+
role: "slider",
|
|
35
|
+
"aria-label": ariaLabel ?? "Chart data \u2014 use arrow keys to scrub",
|
|
36
|
+
"aria-orientation": "horizontal",
|
|
37
|
+
"aria-valuemin": 0,
|
|
38
|
+
...valueMax !== void 0 && { "aria-valuemax": valueMax },
|
|
39
|
+
...valueNow !== void 0 && { "aria-valuenow": valueNow },
|
|
40
|
+
...valueText !== void 0 && { "aria-valuetext": valueText },
|
|
41
|
+
onKeyDown,
|
|
42
|
+
...onBlur !== void 0 && { onBlur }
|
|
43
|
+
},
|
|
44
|
+
onPointerMove: onMove,
|
|
45
|
+
...onDown !== void 0 && { onPointerDown: onDown },
|
|
46
|
+
...onUp !== void 0 && { onPointerUp: onUp },
|
|
47
|
+
onPointerLeave: onLeave,
|
|
48
|
+
onPointerCancel: onLeave
|
|
49
|
+
}
|
|
50
|
+
)
|
|
40
51
|
);
|
|
41
52
|
}
|
|
42
53
|
export {
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* `HoverOverlay` — the transparent plot rect every cartesian kind scrubs through. A tap focuses it
|
|
3
|
+
* on pointerdown, so the UA's own outline would ring the whole plot on every phone tap (`R2C-15`);
|
|
4
|
+
* keyboard focus keeps a VX accent inset instead.
|
|
5
|
+
*/
|
|
6
|
+
.overlay {
|
|
7
|
+
outline: none;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
.overlay:focus-visible {
|
|
11
|
+
outline: 1px solid var(--vx-accent);
|
|
12
|
+
outline-offset: -1px;
|
|
13
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `ChartCard`'s internal context: `inCard` (a frame inside a card treats its computed height as a
|
|
3
|
+
* MINIMUM and grows into the card body), the legend slot — the empty header-row box a chart will portal its legend into
|
|
4
|
+
* (`docs/CHARTS-SPEC.md` §5, Legend), so a card has one chrome band, not two — and `short`, the same
|
|
5
|
+
* `measuredHeight < CARD_SHORT_HEIGHT` flag `ChartCard` already computes for the header-fold law
|
|
6
|
+
* (`data-basalt-card-short`), threaded through so `ChartFrame` can fold its header-slot legend to
|
|
7
|
+
* 'dots' before it eats the plot floor (`docs/CHARTS-SPEC.md` §5, wave 11). `null`/`false` until the slot
|
|
8
|
+
* mounts, and outside any card. Deliberately not exported from any barrel.
|
|
9
|
+
*
|
|
10
|
+
* `claimLegendSlot`/`releaseLegendSlot` arbitrate the slot when a card hosts more than one
|
|
11
|
+
* `ChartFrame` (`docs/waves/PLAN.md` wave 3, P2-9): the first frame to claim an id keeps it for as
|
|
12
|
+
* long as it holds it, every later claimant is told no and falls back to its own band legend
|
|
13
|
+
* instead of a second frame silently portalling into the same node. Outside any card there is no
|
|
14
|
+
* slot to contend for, so the default always grants the claim.
|
|
15
|
+
*
|
|
16
|
+
* `legendSlotVersion` bumps every time the owner releases the slot (round 3's carried gap): a
|
|
17
|
+
* denied frame's claim attempt runs once, in its own mount effect, so with no signal that the slot
|
|
18
|
+
* freed up it would sit denied forever even after the owning frame unmounts. Frames read it only to
|
|
19
|
+
* put it in a dependency array — the effect re-running is the entire point, not the number itself.
|
|
20
|
+
*/
|
|
21
|
+
export declare const ChartCardContext: import("react").Context<{
|
|
22
|
+
legendSlot: HTMLDivElement | null;
|
|
23
|
+
inCard: boolean;
|
|
24
|
+
short: boolean;
|
|
25
|
+
claimLegendSlot: (id: string) => boolean;
|
|
26
|
+
releaseLegendSlot: (id: string) => void;
|
|
27
|
+
legendSlotVersion: number;
|
|
28
|
+
}>;
|
|
29
|
+
//# sourceMappingURL=chart-card-context.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chart-card-context.d.ts","sourceRoot":"","sources":["../../../src/charts/primitives/chart-card-context.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;;GAmBG;AACH,eAAO,MAAM,gBAAgB;gBACf,cAAc,GAAG,IAAI;YACzB,OAAO;WACR,OAAO;qBACG,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO;uBACrB,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI;uBACpB,MAAM;EAQzB,CAAA"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { createContext } from "react";
|
|
2
|
+
const ChartCardContext = createContext({
|
|
3
|
+
legendSlot: null,
|
|
4
|
+
inCard: false,
|
|
5
|
+
short: false,
|
|
6
|
+
claimLegendSlot: () => true,
|
|
7
|
+
releaseLegendSlot: () => {
|
|
8
|
+
},
|
|
9
|
+
legendSlotVersion: 0
|
|
10
|
+
});
|
|
11
|
+
export {
|
|
12
|
+
ChartCardContext
|
|
13
|
+
};
|