basalt-ui 1.30.2 → 1.32.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 +54 -0
- package/MIGRATING.md +256 -12
- package/agent/rules/basalt-controls.md +19 -19
- package/agent/rules/basalt-mantine.md +5 -5
- package/configs/oxlint-plugin.js +371 -4
- package/configs/oxlint-plugin.test.ts +327 -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 +47 -82
- 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 +138 -145
- 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 +68 -111
- package/dist/charts/kinds/band-chrome.d.ts +31 -0
- package/dist/charts/kinds/band-chrome.d.ts.map +1 -0
- package/dist/charts/kinds/band-chrome.js +71 -0
- 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 +264 -145
- 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 +56 -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 +116 -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 +86 -73
- package/dist/charts/primitives/ChartTooltip.d.ts +5 -0
- package/dist/charts/primitives/ChartTooltip.d.ts.map +1 -1
- package/dist/charts/primitives/ChartTooltip.js +20 -4
- package/dist/charts/primitives/Crosshair.js +2 -2
- package/dist/charts/primitives/HoverOverlay.d.ts +29 -9
- package/dist/charts/primitives/HoverOverlay.d.ts.map +1 -1
- package/dist/charts/primitives/HoverOverlay.js +54 -27
- 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 +33 -0
- package/dist/charts/primitives/chart-card-context.d.ts.map +1 -0
- package/dist/charts/primitives/chart-card-context.js +18 -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 +75 -34
- 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/primitives/legend-disclosure.d.ts +27 -0
- package/dist/charts/primitives/legend-disclosure.d.ts.map +1 -0
- package/dist/charts/primitives/legend-disclosure.js +153 -0
- 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 +13 -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 +2 -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 +33 -66
- package/dist/dashboard/widget-grid.d.ts.map +1 -1
- package/dist/dashboard/widget-grid.module.css +11 -38
- 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-state.d.ts +83 -0
- package/dist/data/data-table-state.d.ts.map +1 -0
- package/dist/data/data-table-state.js +139 -0
- package/dist/data/data-table.d.ts +56 -2
- package/dist/data/data-table.d.ts.map +1 -1
- package/dist/data/data-table.js +436 -336
- 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 +20 -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 +68 -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/notifications/center.d.ts.map +1 -1
- package/dist/provider/index.d.ts +20 -1
- package/dist/provider/index.d.ts.map +1 -1
- package/dist/provider/index.js +30 -8
- 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/aside-docking.d.ts +31 -0
- package/dist/shell/aside-docking.d.ts.map +1 -0
- package/dist/shell/aside-docking.js +19 -0
- package/dist/shell/index.d.ts +1 -0
- package/dist/shell/index.d.ts.map +1 -1
- package/dist/shell/index.js +56 -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 +37 -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/theme-lab/derive-controls.d.ts.map +1 -1
- 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 +18 -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 +25 -61
- package/src/charts/kinds/Donut.tsx +153 -62
- package/src/charts/kinds/DualPanel.tsx +21 -26
- package/src/charts/kinds/Heatmap.tsx +61 -22
- package/src/charts/kinds/MirroredBars.tsx +23 -65
- package/src/charts/kinds/band-chrome.tsx +91 -0
- 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 -111
- package/src/charts/primitives/ChartCard.tsx +119 -48
- package/src/charts/primitives/ChartFrame.tsx +204 -60
- package/src/charts/primitives/ChartLegend.tsx +115 -78
- package/src/charts/primitives/ChartTooltip.tsx +24 -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 +62 -20
- package/src/charts/primitives/chart-card-context.ts +46 -0
- package/src/charts/primitives/chart-frame-layout.ts +207 -182
- package/src/charts/primitives/chart-layout.ts +318 -0
- package/src/charts/primitives/chart-tier.tsx +66 -21
- package/src/charts/primitives/legend-disclosure.tsx +177 -0
- 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 +13 -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 +2 -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 +33 -66
- package/src/dashboard/widget-grid.module.css +11 -38
- 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-state.ts +218 -0
- 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 +547 -299
- package/src/data/virtual-list.tsx +31 -0
- package/src/forms/form-layout.module.css +20 -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 +188 -3
- package/src/guard/types.ts +3 -2
- package/src/index.ts +2 -0
- package/src/notifications/center.tsx +3 -0
- package/src/provider/index.tsx +60 -7
- 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/aside-docking.ts +54 -0
- package/src/shell/index.tsx +178 -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 +37 -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/theme-lab/derive-controls.tsx +2 -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
|
@@ -4,25 +4,37 @@ import { scaleLinear, scaleLog, scalePoint } from '@visx/scale'
|
|
|
4
4
|
import { useMemo, useRef, useState } from 'react'
|
|
5
5
|
import type { ReactNode } from 'react'
|
|
6
6
|
import type { BasaltProps } from '../../common/props'
|
|
7
|
-
import { VX } from '../../tokens'
|
|
7
|
+
import { VX, alpha } from '../../tokens'
|
|
8
8
|
import type { ChartMargin } from '../../tokens'
|
|
9
|
+
import { classifyDomain } from '../cursor/resolve'
|
|
9
10
|
import type { CursorResolution } from '../cursor/resolve'
|
|
10
11
|
import { useChartCursor } from '../hooks/useChartCursor'
|
|
11
12
|
import { autoMargin, probeAxisLabels } from '../layout/auto-margin'
|
|
13
|
+
import type { AutoMarginInput } from '../layout/auto-margin'
|
|
14
|
+
import { END_LABEL_GAP, planEndLabels } from '../layout/end-labels'
|
|
12
15
|
import { logTickValues, niceLogDomain } from '../layout/log-ticks'
|
|
13
16
|
import { deriveLegend, deriveTooltipRows } from '../series'
|
|
14
17
|
import type { ChartLegendConfig, ChartSeries } from '../series'
|
|
15
18
|
import { padAutoLower, padAutoUpper } from '../utils/domain'
|
|
16
19
|
import { fmtAxisDate } from '../utils/format'
|
|
17
|
-
import {
|
|
18
|
-
import { AxisBottomDate, AxisLeftNumeric, AxisRightNumeric } from './Axes'
|
|
20
|
+
import { rotatedXLabelPx, smartTicks, smartTicksEvery, xLabelPxFor } from '../utils/ticks'
|
|
21
|
+
import { AxisBottomDate, AxisLeftNumeric, AxisRightNumeric, TICK_FONT_FAMILY } from './Axes'
|
|
19
22
|
import { ChartFrame, resolveLegend } from './ChartFrame'
|
|
20
23
|
import type { ResponsiveChartHeight } from './ChartFrame'
|
|
21
|
-
import {
|
|
24
|
+
import {
|
|
25
|
+
compactNumber,
|
|
26
|
+
isTightClass,
|
|
27
|
+
planXLabels,
|
|
28
|
+
resolveMarginFloor,
|
|
29
|
+
resolveYPlacement,
|
|
30
|
+
shouldCompactYLabels,
|
|
31
|
+
yTickCount,
|
|
32
|
+
} from './chart-layout'
|
|
33
|
+
import { useChartContainerClass, useChartMetrics } from './chart-tier'
|
|
22
34
|
import type { ChartState } from './ChartPending'
|
|
23
|
-
import { ChartTooltipFloat, TooltipBody, TooltipHeader,
|
|
35
|
+
import { ChartTooltipFloat, TooltipBody, TooltipHeader, TooltipRows } from './ChartTooltip'
|
|
24
36
|
import { Crosshair, SeriesDot } from './Crosshair'
|
|
25
|
-
import { HoverOverlay } from './HoverOverlay'
|
|
37
|
+
import { cursorSliderProps, HoverOverlay } from './HoverOverlay'
|
|
26
38
|
import { XZoneRects } from './XZoneRects'
|
|
27
39
|
import type { XZoneSpec } from './XZoneRects'
|
|
28
40
|
import { ZoneRects } from './ZoneRects'
|
|
@@ -35,9 +47,11 @@ import type { ZoneSpec } from './ZoneRects'
|
|
|
35
47
|
type ContinuousScale = ReturnType<typeof scaleLinear<number>> | ReturnType<typeof scaleLog<number>>
|
|
36
48
|
type PointScale = ReturnType<typeof scalePoint<string>>
|
|
37
49
|
|
|
38
|
-
const DEFAULT_TICKS = 5
|
|
39
50
|
const DEFAULT_AUTO_PAD = 1.1
|
|
40
51
|
|
|
52
|
+
/** Faint tint a dual-axis chart lays over its tick labels: the series colour, kept quiet. */
|
|
53
|
+
const AXIS_TINT_ALPHA = 0.8
|
|
54
|
+
|
|
41
55
|
/** One y-axis, fully described. Collapses the removed `yDomain` / `yAutoMaxFloor` / `yAutoMinCeil` /
|
|
42
56
|
* `yAutoPad` / `numTicksY` / `formatYTick` prop soup into a single object per axis. */
|
|
43
57
|
export type AxisConfig<T> = {
|
|
@@ -195,14 +209,15 @@ export type CartesianChartProps<T> = BasaltProps & {
|
|
|
195
209
|
* bottom gutter deepens by the rotated label's projected height, measured like every other side
|
|
196
210
|
* (`docs/CHARTS-SPEC.md` §1), so nothing clips.
|
|
197
211
|
*
|
|
198
|
-
* This is the phone answer to a
|
|
199
|
-
* the axis would otherwise have to thin away — which is
|
|
200
|
-
*
|
|
201
|
-
*
|
|
202
|
-
*
|
|
212
|
+
* This is the phone answer to a **categorical** axis whose labels are too wide to repeat
|
|
213
|
+
* horizontally: rotating keeps every tick the axis would otherwise have to thin away — which is
|
|
214
|
+
* why, LEFT UNSET, it is now the phone tier's DEFAULT for a band domain, below
|
|
215
|
+
* `CONTAINER_CLASSES.regular` (`planXLabels`). A time or number domain never auto-rotates (or
|
|
216
|
+
* wraps) — it thins instead, however narrow the plot. Nothing changes at desktop width, and
|
|
217
|
+
* nothing changes for a chart that already passes a value.
|
|
203
218
|
*
|
|
204
219
|
* **`0` is the opt-out** — "never rotate, thin the axis instead" — and is the only way to get
|
|
205
|
-
* the pre-tier behaviour back on a phone-width chart with very wide labels.
|
|
220
|
+
* the pre-tier behaviour back on a phone-width categorical chart with very wide labels.
|
|
206
221
|
*/
|
|
207
222
|
xLabelRotate?: 0 | 45 | 90
|
|
208
223
|
/** Value-range bands on the left scale, drawn behind the marks. */
|
|
@@ -321,6 +336,41 @@ export function resolveAxisDomain<T>(
|
|
|
321
336
|
return [lower, upper]
|
|
322
337
|
}
|
|
323
338
|
|
|
339
|
+
/**
|
|
340
|
+
* Probe one y axis' labels. With no consumer `format`, labels over 4 chars switch to the compact
|
|
341
|
+
* number format (`12500` -> `12.5k`, ladder step 1); an explicit `format` is never touched.
|
|
342
|
+
*/
|
|
343
|
+
function probeYAxis(opts: {
|
|
344
|
+
domain: [number, number]
|
|
345
|
+
ticks: number
|
|
346
|
+
nice: boolean
|
|
347
|
+
scale: 'linear' | 'log' | undefined
|
|
348
|
+
format: ((v: number) => string) | undefined
|
|
349
|
+
}): { labels: string[]; format: (v: number) => string } {
|
|
350
|
+
const { domain, ticks, nice, scale, format } = opts
|
|
351
|
+
const base = probeAxisLabels({
|
|
352
|
+
domain,
|
|
353
|
+
ticks,
|
|
354
|
+
nice,
|
|
355
|
+
...(scale !== undefined && { scale }),
|
|
356
|
+
...(format !== undefined && { format }),
|
|
357
|
+
})
|
|
358
|
+
if (format !== undefined || !shouldCompactYLabels(base.labels)) {
|
|
359
|
+
return base
|
|
360
|
+
}
|
|
361
|
+
const compact = (v: number): string => compactNumber(v) ?? base.format(v)
|
|
362
|
+
return {
|
|
363
|
+
labels: probeAxisLabels({
|
|
364
|
+
domain,
|
|
365
|
+
ticks,
|
|
366
|
+
nice,
|
|
367
|
+
format: compact,
|
|
368
|
+
...(scale !== undefined && { scale }),
|
|
369
|
+
}).labels,
|
|
370
|
+
format: compact,
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
|
|
324
374
|
/**
|
|
325
375
|
* One y-axis' real scale, built from its resolved domain — shared by the left and right axis so
|
|
326
376
|
* the log branch (`scaleLog` + `nice` + the 1-2-5 tick override) is written and commented exactly
|
|
@@ -354,6 +404,54 @@ function buildYScale<T>(
|
|
|
354
404
|
})
|
|
355
405
|
}
|
|
356
406
|
|
|
407
|
+
/**
|
|
408
|
+
* The x-axis margin ladder, as one pure decision. The flat (unrotated) margin is what every case
|
|
409
|
+
* resolves to except a rotated or wrapped axis, and it is what the rotation decision itself is
|
|
410
|
+
* measured against — deciding needs an `xMax`, and `xMax` needs a margin, so the flat pass breaks
|
|
411
|
+
* the loop. An explicit `xLabelRotate` always wins — including `0`, the documented "never rotate"
|
|
412
|
+
* opt-out; unset, only a categorical (band) axis wraps, and rotates only when even wrapped it
|
|
413
|
+
* doesn't fit (`planXLabels`) — a time or number domain only thins.
|
|
414
|
+
*
|
|
415
|
+
* Per rotation: 45° reuses its pre-measured margin and measures its tick room through
|
|
416
|
+
* {@link rotatedXLabelPx} (a rotated label projects wider than its glyph box); 90° measures fresh
|
|
417
|
+
* (one line box wide, so the flat label room governs its thinning); a wrapped flat axis re-measures
|
|
418
|
+
* for its extra line(s) and thins by the wrapped width.
|
|
419
|
+
*/
|
|
420
|
+
function resolveXAxisLayout(input: {
|
|
421
|
+
marginInput: AutoMarginInput
|
|
422
|
+
flatMargin: ChartMargin
|
|
423
|
+
rotated45Margin: ChartMargin
|
|
424
|
+
flatLabelPx: number
|
|
425
|
+
xLabels: string[]
|
|
426
|
+
fontPx: number
|
|
427
|
+
plotWidth: number
|
|
428
|
+
categorical: boolean
|
|
429
|
+
xLabelRotate: 0 | 45 | 90 | undefined
|
|
430
|
+
}): {
|
|
431
|
+
baseMargin: ChartMargin
|
|
432
|
+
rotate: 0 | 45 | 90
|
|
433
|
+
wrapWidth: number | undefined
|
|
434
|
+
tickLabelPx: number
|
|
435
|
+
} {
|
|
436
|
+
const { marginInput, flatMargin, rotated45Margin, flatLabelPx, xLabels, fontPx, categorical } =
|
|
437
|
+
input
|
|
438
|
+
const flatXMax = Math.max(input.plotWidth - flatMargin.left - flatMargin.right, 0)
|
|
439
|
+
const xPlan = planXLabels({ labels: xLabels, plotWidth: flatXMax, fontPx, categorical })
|
|
440
|
+
const rotate = input.xLabelRotate ?? (categorical ? xPlan.rotate : 0)
|
|
441
|
+
if (rotate === 45) {
|
|
442
|
+
const tickLabelPx = rotatedXLabelPx(xLabels, fontPx)
|
|
443
|
+
return { baseMargin: rotated45Margin, rotate, wrapWidth: undefined, tickLabelPx }
|
|
444
|
+
}
|
|
445
|
+
if (rotate === 90) {
|
|
446
|
+
const baseMargin = autoMargin({ ...marginInput, rotate })
|
|
447
|
+
return { baseMargin, rotate, wrapWidth: undefined, tickLabelPx: flatLabelPx }
|
|
448
|
+
}
|
|
449
|
+
if (!xPlan.wrap)
|
|
450
|
+
return { baseMargin: flatMargin, rotate, wrapWidth: undefined, tickLabelPx: flatLabelPx }
|
|
451
|
+
const baseMargin = autoMargin({ ...marginInput, bottomLines: xPlan.lines })
|
|
452
|
+
return { baseMargin, rotate, wrapWidth: xPlan.wrapPx, tickLabelPx: xPlan.labelPx }
|
|
453
|
+
}
|
|
454
|
+
|
|
357
455
|
/**
|
|
358
456
|
* The cartesian chart primitive — the rung that was missing between `ChartFrame` and raw visx
|
|
359
457
|
* (`docs/CHARTS-SPEC.md` §2).
|
|
@@ -458,7 +556,7 @@ type CartesianPlotProps<T> = Omit<
|
|
|
458
556
|
| 'className'
|
|
459
557
|
| 'style'
|
|
460
558
|
> & {
|
|
461
|
-
plot: { width: number; height: number; hidden: ReadonlySet<string
|
|
559
|
+
plot: { width: number; height: number; hidden: ReadonlySet<string>; legendInHeader: boolean }
|
|
462
560
|
highlighted: string | null
|
|
463
561
|
marginOverride?: Partial<ChartMargin>
|
|
464
562
|
}
|
|
@@ -492,7 +590,9 @@ function CartesianPlot<T>({
|
|
|
492
590
|
// The tier `ChartFrame` resolved from its own MEASURED width. Everything sized by it here is
|
|
493
591
|
// the half a hook cannot reach: the tick font the margins are MEASURED at, and the margin
|
|
494
592
|
// floors themselves (`docs/CHARTS-SPEC.md` §8).
|
|
495
|
-
const tier =
|
|
593
|
+
const tier = useChartMetrics()
|
|
594
|
+
const containerClass = useChartContainerClass()
|
|
595
|
+
const tight = isTightClass(containerClass)
|
|
496
596
|
|
|
497
597
|
const visible = useMemo(() => series.filter((s) => !hidden.has(s.key)), [series, hidden])
|
|
498
598
|
const leftSeries = useMemo(() => visible.filter((s) => s.axis !== 'right'), [visible])
|
|
@@ -526,17 +626,21 @@ function CartesianPlot<T>({
|
|
|
526
626
|
[y2, data, rightSeries, rightBounds],
|
|
527
627
|
)
|
|
528
628
|
|
|
529
|
-
|
|
530
|
-
|
|
629
|
+
// Tick count is a law of the plot height (one per ~44px, 2 under `VX.minPlotHeight`), estimated
|
|
630
|
+
// from the token floors because the real margins depend on the labels these ticks produce. A
|
|
631
|
+
// consumer's `ticks` always wins.
|
|
632
|
+
const estimatedPlotHeight = plot.height - tier.margin.top - tier.margin.bottom
|
|
633
|
+
const leftTicks = y?.ticks ?? yTickCount(estimatedPlotHeight)
|
|
634
|
+
const rightTicks = y2?.ticks ?? yTickCount(estimatedPlotHeight)
|
|
531
635
|
|
|
532
636
|
const { labels: leftLabels, format: leftFormat } = useMemo(
|
|
533
637
|
() =>
|
|
534
|
-
|
|
638
|
+
probeYAxis({
|
|
535
639
|
domain: leftDomain,
|
|
536
640
|
ticks: leftTicks,
|
|
537
641
|
nice: y?.nice ?? false,
|
|
538
|
-
|
|
539
|
-
|
|
642
|
+
scale: y?.scale,
|
|
643
|
+
format: y?.format,
|
|
540
644
|
}),
|
|
541
645
|
[leftDomain, leftTicks, y?.format, y?.nice, y?.scale],
|
|
542
646
|
)
|
|
@@ -545,110 +649,153 @@ function CartesianPlot<T>({
|
|
|
545
649
|
() =>
|
|
546
650
|
rightDomain === null
|
|
547
651
|
? { labels: [], format: (v: number) => String(v) }
|
|
548
|
-
:
|
|
652
|
+
: probeYAxis({
|
|
549
653
|
domain: rightDomain,
|
|
550
654
|
ticks: rightTicks,
|
|
551
655
|
nice: y2?.nice ?? false,
|
|
552
|
-
|
|
553
|
-
|
|
656
|
+
scale: y2?.scale,
|
|
657
|
+
format: y2?.format,
|
|
554
658
|
}),
|
|
555
659
|
[rightDomain, rightTicks, y2?.format, y2?.nice, y2?.scale],
|
|
556
660
|
)
|
|
557
661
|
|
|
558
662
|
const xLabels = useMemo(() => keys.map(formatX), [keys, formatX])
|
|
559
663
|
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
664
|
+
// Ladder step 5: at compact/micro the y labels sit inside the plot (micro: no y axis), so the
|
|
665
|
+
// left gutter drops to its floor. An explicit `margin.left` means the consumer laid out an
|
|
666
|
+
// outside gutter — it keeps the outside axis. `CartesianChart` always has a left axis.
|
|
667
|
+
const yPlacement = resolveYPlacement(containerClass, { marginOverrideLeft: marginOverride?.left })
|
|
668
|
+
// A whitespace test misclassified any spaced date format ("Mar 01") as categorical, which then
|
|
669
|
+
// wrapped or rotated a time axis that should only ever thin (§ ladder step 4). Domain kind is
|
|
670
|
+
// the actual law: only a band (category) domain wraps or rotates; time and number domains thin.
|
|
671
|
+
const categorical = tight && classifyDomain(keys) === 'band'
|
|
672
|
+
|
|
673
|
+
// The room one flat x tick label needs at the tick font the axis will actually paint
|
|
674
|
+
// (`docs/CHARTS-SPEC.md` §8) — keyed on the labels and font alone, so a y-label or margin change
|
|
675
|
+
// never re-measures every x label.
|
|
676
|
+
const flatLabelPx = useMemo(() => xLabelPxFor(xLabels, tier.axisFont), [xLabels, tier.axisFont])
|
|
677
|
+
|
|
678
|
+
// Everything `autoMargin` measures except the rotation, measured once for every pass so a label
|
|
679
|
+
// can never differ between the one that DECIDES and the one that PAINTS — plus the 45° margin (a
|
|
680
|
+
// rotated label reaches into the left gutter).
|
|
681
|
+
const measured = useMemo(() => {
|
|
682
|
+
const marginInput: AutoMarginInput = {
|
|
683
|
+
left: yPlacement === 'outside' ? leftLabels : [],
|
|
571
684
|
right: rightLabels,
|
|
572
685
|
bottom: xLabels,
|
|
573
686
|
fontPx: tier.axisFont,
|
|
574
|
-
|
|
687
|
+
// An inside-placed y label paints IN the plot, so the left gutter it used to reserve
|
|
688
|
+
// (the phone tier's own margin floor) is dead space — only the inside-label floor applies.
|
|
689
|
+
floor: resolveMarginFloor(yPlacement, tier.margin),
|
|
690
|
+
anchorTerminals: tight,
|
|
575
691
|
...(marginOverride !== undefined && { override: marginOverride }),
|
|
576
|
-
}
|
|
577
|
-
|
|
692
|
+
}
|
|
693
|
+
return {
|
|
694
|
+
marginInput,
|
|
695
|
+
flatMargin: autoMargin(marginInput),
|
|
696
|
+
rotated45Margin: autoMargin({ ...marginInput, rotate: 45 }),
|
|
697
|
+
}
|
|
698
|
+
}, [
|
|
699
|
+
leftLabels,
|
|
700
|
+
rightLabels,
|
|
701
|
+
xLabels,
|
|
702
|
+
tier.axisFont,
|
|
703
|
+
tier.margin,
|
|
704
|
+
marginOverride,
|
|
705
|
+
tight,
|
|
706
|
+
yPlacement,
|
|
707
|
+
])
|
|
708
|
+
const { baseMargin, rotate, wrapWidth, tickLabelPx } = useMemo(
|
|
709
|
+
() =>
|
|
710
|
+
resolveXAxisLayout({
|
|
711
|
+
...measured,
|
|
712
|
+
flatLabelPx,
|
|
713
|
+
xLabels,
|
|
714
|
+
fontPx: tier.axisFont,
|
|
715
|
+
plotWidth: plot.width,
|
|
716
|
+
categorical,
|
|
717
|
+
xLabelRotate,
|
|
718
|
+
}),
|
|
719
|
+
[measured, flatLabelPx, xLabels, tier.axisFont, plot.width, categorical, xLabelRotate],
|
|
578
720
|
)
|
|
579
721
|
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
const
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
722
|
+
// End-of-line labels are planned FIRST, against the base margin: the right gutter is reserved
|
|
723
|
+
// only when the labels will actually draw. `yScale` depends on the plot height alone, so it is
|
|
724
|
+
// safe to build before the right margin is settled.
|
|
725
|
+
const yMax = Math.max(plot.height - baseMargin.top - baseMargin.bottom, 0)
|
|
726
|
+
const yScale = useMemo(
|
|
727
|
+
() => buildYScale(y, leftDomain, yMax, leftTicks),
|
|
728
|
+
[y, leftDomain, yMax, leftTicks],
|
|
729
|
+
)
|
|
730
|
+
const endPlan = useMemo(
|
|
731
|
+
() =>
|
|
732
|
+
planEndLabels({
|
|
733
|
+
containerClass,
|
|
734
|
+
hasY2: y2 !== undefined,
|
|
735
|
+
hasMarginRight: marginOverride?.right !== undefined,
|
|
736
|
+
hasHeaderLegend: plot.legendInHeader,
|
|
737
|
+
visible,
|
|
738
|
+
data,
|
|
739
|
+
yScale,
|
|
740
|
+
yMax,
|
|
741
|
+
fontPx: tier.axisFont,
|
|
742
|
+
plotWidth: plot.width - baseMargin.left - baseMargin.right,
|
|
743
|
+
}),
|
|
744
|
+
[
|
|
745
|
+
containerClass,
|
|
746
|
+
y2,
|
|
747
|
+
marginOverride?.right,
|
|
748
|
+
plot.legendInHeader,
|
|
749
|
+
visible,
|
|
750
|
+
data,
|
|
751
|
+
yScale,
|
|
752
|
+
yMax,
|
|
753
|
+
tier.axisFont,
|
|
754
|
+
plot.width,
|
|
755
|
+
baseMargin.left,
|
|
756
|
+
baseMargin.right,
|
|
757
|
+
],
|
|
758
|
+
)
|
|
759
|
+
const endLabels = endPlan?.labels ?? []
|
|
602
760
|
const margin = useMemo(
|
|
603
761
|
() =>
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
: rotate === 45
|
|
607
|
-
? rotated45Margin
|
|
608
|
-
: autoMargin({ ...marginInput, rotate }),
|
|
609
|
-
[flatMargin, rotated45Margin, marginInput, rotate],
|
|
762
|
+
endPlan === null ? baseMargin : { ...baseMargin, right: baseMargin.right + endPlan.gutter },
|
|
763
|
+
[baseMargin, endPlan],
|
|
610
764
|
)
|
|
611
765
|
|
|
612
766
|
// ── Pass 2: the real scales, now that the plot rect is known ────────────────────────────────
|
|
613
767
|
const xMax = Math.max(plot.width - margin.left - margin.right, 0)
|
|
614
|
-
const yMax = Math.max(plot.height - margin.top - margin.bottom, 0)
|
|
615
768
|
|
|
616
769
|
const xScale = useMemo(
|
|
617
770
|
() => scalePoint<string>({ domain: keys, range: [0, xMax], padding: 0.5 }),
|
|
618
771
|
[keys, xMax],
|
|
619
772
|
)
|
|
620
|
-
const yScale = useMemo(
|
|
621
|
-
() => buildYScale(y, leftDomain, yMax, leftTicks),
|
|
622
|
-
[y, leftDomain, yMax, leftTicks],
|
|
623
|
-
)
|
|
624
773
|
const y2Scale = useMemo(
|
|
625
774
|
() => (rightDomain === null ? null : buildYScale(y2, rightDomain, yMax, rightTicks)),
|
|
626
775
|
[y2, rightDomain, yMax, rightTicks],
|
|
627
776
|
)
|
|
628
777
|
|
|
629
778
|
// Resolution order: explicit VALUES win, then an explicit COUNT, then as many as fit.
|
|
630
|
-
const tickValues = useMemo(
|
|
631
|
-
()
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
: smartTicksEvery(keys, xTicks),
|
|
639
|
-
[keys, xMax, xTicks, xTickValues, xLabelPx, rotate],
|
|
640
|
-
)
|
|
779
|
+
const tickValues = useMemo(() => {
|
|
780
|
+
if (xTickValues !== undefined) return [...xTickValues(keys, xMax)]
|
|
781
|
+
if (xTicks !== undefined) return smartTicksEvery(keys, xTicks)
|
|
782
|
+
// Micro: the two terminals only.
|
|
783
|
+
if (containerClass === 'micro')
|
|
784
|
+
return keys.length > 1 ? [keys[0]!, keys[keys.length - 1]!] : keys
|
|
785
|
+
return smartTicks(keys, xMax, rotate === 0 || rotate === 45 ? tickLabelPx : undefined, tight)
|
|
786
|
+
}, [keys, xMax, xTicks, xTickValues, tickLabelPx, rotate, containerClass, tight])
|
|
641
787
|
|
|
788
|
+
const svgRef = useRef<SVGSVGElement>(null)
|
|
642
789
|
const cursor = useChartCursor<T>({
|
|
643
790
|
data,
|
|
644
791
|
chartId,
|
|
645
792
|
getKey: getX,
|
|
646
793
|
xScale,
|
|
647
794
|
marginLeft: margin.left,
|
|
795
|
+
boundaryRef: svgRef,
|
|
648
796
|
...(cursorResolution !== undefined && { resolution: cursorResolution }),
|
|
649
797
|
})
|
|
650
798
|
|
|
651
|
-
const svgRef = useRef<SVGSVGElement>(null)
|
|
652
799
|
const tooltipEnabled = tooltip !== false
|
|
653
800
|
const tooltipCfg = tooltip === false ? undefined : tooltip
|
|
654
801
|
const point = cursor.point
|
|
@@ -668,6 +815,12 @@ function CartesianPlot<T>({
|
|
|
668
815
|
[visible, leftFormat, rightFormat],
|
|
669
816
|
)
|
|
670
817
|
|
|
818
|
+
// Dual axis with exactly one visible series per side: the tick labels carry that series' colour,
|
|
819
|
+
// so the axis says which line it reads. Any other split leaves both axes neutral.
|
|
820
|
+
const tintAxes = y2 !== undefined && leftSeries.length === 1 && rightSeries.length === 1
|
|
821
|
+
const leftTint = tintAxes ? alpha(leftSeries[0]!.color, AXIS_TINT_ALPHA) : undefined
|
|
822
|
+
const rightTint = tintAxes ? alpha(rightSeries[0]!.color, AXIS_TINT_ALPHA) : undefined
|
|
823
|
+
|
|
671
824
|
const rows =
|
|
672
825
|
tooltipEnabled && point !== null ? deriveTooltipRows(tooltipSeries, point, leftFormat) : []
|
|
673
826
|
|
|
@@ -679,8 +832,13 @@ function CartesianPlot<T>({
|
|
|
679
832
|
|
|
680
833
|
// Anchored mode resolves the crosshair's plot-local x into viewport space; following mode uses
|
|
681
834
|
// the pointer position `useChartCursor` already recorded. A follower render always anchors, since
|
|
682
|
-
// `follow`'s pointer-tracking path has no pointer to read on this chart.
|
|
683
|
-
|
|
835
|
+
// `follow`'s pointer-tracking path has no pointer to read on this chart. An unset `follow` also
|
|
836
|
+
// anchors on touch (spec §5) — the readout would otherwise sit under the finger; an EXPLICIT
|
|
837
|
+
// `follow` (true or false) always wins over that default.
|
|
838
|
+
const useAnchoredPosition =
|
|
839
|
+
tooltipCfg?.follow === false ||
|
|
840
|
+
(tooltipCfg?.follow === undefined && cursor.isTouch) ||
|
|
841
|
+
isFollowerRender
|
|
684
842
|
const anchorX = useAnchoredPosition && point !== null && cursorX !== null ? cursorX : null
|
|
685
843
|
const svgRect = anchorX === null ? undefined : svgRef.current?.getBoundingClientRect()
|
|
686
844
|
const tooltipAnchor =
|
|
@@ -704,7 +862,14 @@ function CartesianPlot<T>({
|
|
|
704
862
|
|
|
705
863
|
return (
|
|
706
864
|
<>
|
|
707
|
-
<svg
|
|
865
|
+
<svg
|
|
866
|
+
ref={svgRef}
|
|
867
|
+
width={plot.width}
|
|
868
|
+
height={plot.height}
|
|
869
|
+
// `touch-action` on the plot `<svg>`, not the overlay `<rect>`: Chrome ignores it on an SVG
|
|
870
|
+
// child, so a horizontal scrub was claimed as a pan and cancelled (`R2C-2`).
|
|
871
|
+
style={{ touchAction: 'pan-y' }}
|
|
872
|
+
>
|
|
708
873
|
<Group left={margin.left} top={margin.top}>
|
|
709
874
|
{(y?.grid ?? true) && (
|
|
710
875
|
<GridRows scale={yScale} width={xMax} stroke={VX.grid} numTicks={leftTicks} />
|
|
@@ -762,17 +927,22 @@ function CartesianPlot<T>({
|
|
|
762
927
|
</>
|
|
763
928
|
)}
|
|
764
929
|
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
930
|
+
{yPlacement !== 'none' && (
|
|
931
|
+
<AxisLeftNumeric
|
|
932
|
+
scale={yScale}
|
|
933
|
+
numTicks={leftTicks}
|
|
934
|
+
tickFormat={(v) => leftFormat(Number(v))}
|
|
935
|
+
inside={yPlacement === 'inside'}
|
|
936
|
+
{...(leftTint !== undefined && { tickColor: leftTint })}
|
|
937
|
+
/>
|
|
938
|
+
)}
|
|
770
939
|
{y2Scale !== null && (
|
|
771
940
|
<AxisRightNumeric
|
|
772
941
|
scale={y2Scale}
|
|
773
942
|
left={xMax}
|
|
774
943
|
numTicks={rightTicks}
|
|
775
944
|
tickFormat={(v) => rightFormat(Number(v))}
|
|
945
|
+
{...(rightTint !== undefined && { tickColor: rightTint })}
|
|
776
946
|
/>
|
|
777
947
|
)}
|
|
778
948
|
<AxisBottomDate
|
|
@@ -781,21 +951,32 @@ function CartesianPlot<T>({
|
|
|
781
951
|
tickValues={tickValues}
|
|
782
952
|
tickFormat={(v) => formatX(String(v))}
|
|
783
953
|
{...(rotate !== 0 && { rotate })}
|
|
954
|
+
anchorTerminals={tight && rotate === 0}
|
|
955
|
+
{...(wrapWidth !== undefined && { wrapWidth })}
|
|
784
956
|
/>
|
|
785
957
|
|
|
958
|
+
{endLabels.map(({ series: s, y: labelY }) => (
|
|
959
|
+
<text
|
|
960
|
+
key={`end-${s.key}`}
|
|
961
|
+
x={xMax + END_LABEL_GAP}
|
|
962
|
+
y={labelY}
|
|
963
|
+
dy="0.35em"
|
|
964
|
+
fill={s.color}
|
|
965
|
+
fontFamily={TICK_FONT_FAMILY}
|
|
966
|
+
fontSize={tier.axisFont}
|
|
967
|
+
opacity={
|
|
968
|
+
highlighted === null || s.key === highlighted || s.parent === highlighted ? 1 : 0.25
|
|
969
|
+
}
|
|
970
|
+
pointerEvents="none"
|
|
971
|
+
>
|
|
972
|
+
{s.label}
|
|
973
|
+
</text>
|
|
974
|
+
))}
|
|
975
|
+
|
|
786
976
|
<HoverOverlay
|
|
787
977
|
width={xMax}
|
|
788
978
|
height={yMax}
|
|
789
|
-
|
|
790
|
-
onLeave={cursor.onPointerLeave}
|
|
791
|
-
onKeyDown={cursor.onKeyDown}
|
|
792
|
-
onBlur={cursor.onBlur}
|
|
793
|
-
valueMax={Math.max(data.length - 1, 0)}
|
|
794
|
-
{...(point !== null && {
|
|
795
|
-
valueNow: data.indexOf(point),
|
|
796
|
-
valueText: formatX(getX(point)),
|
|
797
|
-
})}
|
|
798
|
-
{...(ariaLabel !== undefined && { ariaLabel })}
|
|
979
|
+
{...cursorSliderProps({ cursor: cursor, data: data, getX, formatX, ariaLabel })}
|
|
799
980
|
/>
|
|
800
981
|
</Group>
|
|
801
982
|
</svg>
|
|
@@ -811,17 +992,7 @@ function CartesianPlot<T>({
|
|
|
811
992
|
/>
|
|
812
993
|
<TooltipBody>
|
|
813
994
|
{tooltipCfg?.prependRows?.(point, { visible, hidden })}
|
|
814
|
-
{rows
|
|
815
|
-
<TooltipRow
|
|
816
|
-
key={row.key}
|
|
817
|
-
color={row.color}
|
|
818
|
-
label={row.label}
|
|
819
|
-
value={row.value}
|
|
820
|
-
shape={row.shape}
|
|
821
|
-
dashed={row.dashed}
|
|
822
|
-
{...(row.strokeWidth !== undefined && { strokeWidth: row.strokeWidth })}
|
|
823
|
-
/>
|
|
824
|
-
))}
|
|
995
|
+
<TooltipRows rows={rows} />
|
|
825
996
|
{tooltipCfg?.extraRows?.(point, { visible, hidden })}
|
|
826
997
|
</TooltipBody>
|
|
827
998
|
</ChartTooltipFloat>
|