basalt-ui 1.30.0 → 1.30.2
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/CHANGELOG.md +15 -0
- package/MIGRATING.md +284 -30
- package/dist/charts/index.d.ts +1 -1
- package/dist/charts/index.d.ts.map +1 -1
- package/dist/charts/kinds/BandStrip.d.ts +5 -3
- package/dist/charts/kinds/BandStrip.d.ts.map +1 -1
- package/dist/charts/kinds/Bars.d.ts +4 -2
- package/dist/charts/kinds/Bars.d.ts.map +1 -1
- package/dist/charts/kinds/Donut.d.ts +4 -2
- package/dist/charts/kinds/Donut.d.ts.map +1 -1
- package/dist/charts/kinds/DualPanel.d.ts +4 -2
- package/dist/charts/kinds/DualPanel.d.ts.map +1 -1
- package/dist/charts/kinds/Heatmap.d.ts +5 -2
- package/dist/charts/kinds/Heatmap.d.ts.map +1 -1
- package/dist/charts/kinds/MirroredBars.d.ts +4 -2
- package/dist/charts/kinds/MirroredBars.d.ts.map +1 -1
- package/dist/charts/kinds/MultiLine.d.ts +4 -2
- package/dist/charts/kinds/MultiLine.d.ts.map +1 -1
- package/dist/charts/kinds/StackedArea.d.ts +4 -2
- package/dist/charts/kinds/StackedArea.d.ts.map +1 -1
- package/dist/charts/kinds/ZonedLine.d.ts +4 -2
- package/dist/charts/kinds/ZonedLine.d.ts.map +1 -1
- package/dist/charts/primitives/CartesianChart.d.ts +4 -1
- package/dist/charts/primitives/CartesianChart.d.ts.map +1 -1
- package/dist/charts/primitives/ChartFrame.d.ts +20 -11
- package/dist/charts/primitives/ChartFrame.d.ts.map +1 -1
- package/dist/charts/primitives/ChartFrame.js +39 -14
- package/dist/charts/primitives/ChartLegend.d.ts +5 -3
- package/dist/charts/primitives/ChartLegend.d.ts.map +1 -1
- package/dist/charts/primitives/chart-frame-layout.d.ts +64 -9
- package/dist/charts/primitives/chart-frame-layout.d.ts.map +1 -1
- package/dist/charts/primitives/chart-frame-layout.js +35 -6
- package/dist/common/errors.d.ts +11 -0
- package/dist/common/errors.d.ts.map +1 -1
- package/dist/common/errors.js +4 -0
- package/llms.txt +2 -2
- package/package.json +1 -1
- package/src/charts/index.ts +1 -0
- package/src/charts/kinds/BandStrip.tsx +5 -3
- package/src/charts/kinds/Bars.tsx +4 -2
- package/src/charts/kinds/Donut.tsx +4 -2
- package/src/charts/kinds/DualPanel.tsx +4 -2
- package/src/charts/kinds/Heatmap.tsx +5 -2
- package/src/charts/kinds/MirroredBars.tsx +4 -2
- package/src/charts/kinds/MultiLine.tsx +4 -2
- package/src/charts/kinds/StackedArea.tsx +4 -2
- package/src/charts/kinds/ZonedLine.tsx +4 -2
- package/src/charts/primitives/CartesianChart.tsx +4 -1
- package/src/charts/primitives/ChartFrame.tsx +72 -37
- package/src/charts/primitives/ChartLegend.tsx +5 -3
- package/src/charts/primitives/chart-frame-layout.ts +123 -16
- package/src/common/errors.ts +14 -0
package/dist/common/errors.js
CHANGED
|
@@ -17,6 +17,9 @@ function deprecatedProp(component, oldProp, newProp, removeIn) {
|
|
|
17
17
|
`prop "${oldProp}" is deprecated \u2014 use "${newProp}" instead. "${oldProp}" is removed in ${removeIn}.`
|
|
18
18
|
);
|
|
19
19
|
}
|
|
20
|
+
function ignoredProp(component, prop, because) {
|
|
21
|
+
return message(component, `prop "${prop}" is ignored because ${because}`);
|
|
22
|
+
}
|
|
20
23
|
function missingLayer(component, layer, remedy) {
|
|
21
24
|
return message(component, `needs ${layer}, which is not available \u2014 ${remedy}.`);
|
|
22
25
|
}
|
|
@@ -57,6 +60,7 @@ export {
|
|
|
57
60
|
deprecatedProp,
|
|
58
61
|
duplicateMount,
|
|
59
62
|
errorStatus,
|
|
63
|
+
ignoredProp,
|
|
60
64
|
missingLayer,
|
|
61
65
|
oneOf,
|
|
62
66
|
requiredProp,
|
package/llms.txt
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# basalt-ui v1.30.
|
|
1
|
+
# basalt-ui v1.30.2
|
|
2
2
|
# Opinionated framework for Mantine-based React apps — theme, app shell, visx charts, toolchain presets
|
|
3
3
|
#
|
|
4
4
|
# basalt-ui is an opinionated framework for Mantine-based React apps.
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
# ./tokens.css subpath. Guide: docs/FRAMEWORK-FREE.md in the repo.
|
|
20
20
|
|
|
21
21
|
Package: basalt-ui
|
|
22
|
-
Version: 1.30.
|
|
22
|
+
Version: 1.30.2
|
|
23
23
|
|
|
24
24
|
## basalt-ui
|
|
25
25
|
Import: import { ... } from 'basalt-ui'
|
package/package.json
CHANGED
package/src/charts/index.ts
CHANGED
|
@@ -10,6 +10,7 @@ import { useBandPlot } from '../hooks/useBandPlot'
|
|
|
10
10
|
import type { BandFold, BandTooltipConfig } from '../hooks/useBandPlot'
|
|
11
11
|
import { AxisBottomDate } from '../primitives/Axes'
|
|
12
12
|
import { ChartFrame, resolveLegend } from '../primitives/ChartFrame'
|
|
13
|
+
import type { ResponsiveChartHeight } from '../primitives/ChartFrame'
|
|
13
14
|
import {
|
|
14
15
|
ChartTooltipFloat,
|
|
15
16
|
TooltipBody,
|
|
@@ -94,9 +95,10 @@ export type BandStripProps<T> = BasaltProps & {
|
|
|
94
95
|
series: readonly BandStripSeries<T>[]
|
|
95
96
|
/** Which state each datum is in. */
|
|
96
97
|
getBand: (d: T) => BandSpan
|
|
97
|
-
/**
|
|
98
|
-
*
|
|
99
|
-
|
|
98
|
+
/** Height in px, or one per size step (`{ base, sm?, md?, lg? }`), forwarded to `ChartFrame`.
|
|
99
|
+
* The band row gets whatever the measured margins and legend band leave. Default 240.
|
|
100
|
+
* `ChartFrameProps.height` owns the law — steps resolve off the MEASURED width. */
|
|
101
|
+
height?: number | ResponsiveChartHeight
|
|
100
102
|
/** Fill the parent flex/grid cell's measured height instead of a fixed one. */
|
|
101
103
|
fill?: boolean
|
|
102
104
|
/** X tick label formatter. Default `fmtAxisDate` (DD.MM). */
|
|
@@ -7,6 +7,7 @@ import { VX } from '../../tokens'
|
|
|
7
7
|
import type { ChartMargin } from '../../tokens'
|
|
8
8
|
import { CartesianChart } from '../primitives/CartesianChart'
|
|
9
9
|
import type { AxisConfig, CartesianTooltipConfig, PlotContext } from '../primitives/CartesianChart'
|
|
10
|
+
import type { ResponsiveChartHeight } from '../primitives/ChartFrame'
|
|
10
11
|
import type { ZoneSpec } from '../primitives/ZoneRects'
|
|
11
12
|
import type { ChartState } from '../primitives/ChartPending'
|
|
12
13
|
import { curveFor, LINE_OVERLAY_STROKE_WIDTH } from '../series'
|
|
@@ -65,8 +66,9 @@ export type BarsRefLine = {
|
|
|
65
66
|
|
|
66
67
|
export type BarsProps<T> = BasaltProps & {
|
|
67
68
|
data: T[]
|
|
68
|
-
/**
|
|
69
|
-
|
|
69
|
+
/** Height in px, or one per size step (`{ base, sm?, md?, lg? }`). Default 240. Forwarded
|
|
70
|
+
* unchanged; `ChartFrameProps.height` owns the law — steps resolve off the MEASURED width. */
|
|
71
|
+
height?: number | ResponsiveChartHeight
|
|
70
72
|
chartId: string
|
|
71
73
|
getX: (d: T) => string
|
|
72
74
|
/** Generic value accessor — given a data point and a bar/line key, returns the value or null. */
|
|
@@ -6,6 +6,7 @@ import { assertRequiredProps } from '../../common/validate'
|
|
|
6
6
|
import type { BasaltProps } from '../../common/props'
|
|
7
7
|
import { ChartTooltipFloat, TooltipBody, TooltipRow } from '../primitives/ChartTooltip'
|
|
8
8
|
import { ChartFrame } from '../primitives/ChartFrame'
|
|
9
|
+
import type { ResponsiveChartHeight } from '../primitives/ChartFrame'
|
|
9
10
|
import type { ChartState } from '../primitives/ChartPending'
|
|
10
11
|
import { VX } from '../../tokens'
|
|
11
12
|
import type { SeriesStyle } from '../series'
|
|
@@ -20,8 +21,9 @@ export type DonutDatum<K extends string = SeriesKey> = { key: K; value: number }
|
|
|
20
21
|
|
|
21
22
|
export type DonutProps<K extends string = SeriesKey> = BasaltProps & {
|
|
22
23
|
data: DonutDatum<K>[]
|
|
23
|
-
/**
|
|
24
|
-
|
|
24
|
+
/** Height in px, or one per size step (`{ base, sm?, md?, lg? }`). Default 240. Forwarded
|
|
25
|
+
* unchanged; `ChartFrameProps.height` owns the law — steps resolve off the MEASURED width. */
|
|
26
|
+
height?: number | ResponsiveChartHeight
|
|
25
27
|
colorForKey: (key: K) => string
|
|
26
28
|
formatValue: (v: number) => string
|
|
27
29
|
seriesLabel?: (key: string) => string
|
|
@@ -15,6 +15,7 @@ import {
|
|
|
15
15
|
TooltipRow,
|
|
16
16
|
} from '../primitives/ChartTooltip'
|
|
17
17
|
import { ChartFrame, resolveLegend } from '../primitives/ChartFrame'
|
|
18
|
+
import type { ResponsiveChartHeight } from '../primitives/ChartFrame'
|
|
18
19
|
import { Crosshair, SeriesDot } from '../primitives/Crosshair'
|
|
19
20
|
import { HoverOverlay } from '../primitives/HoverOverlay'
|
|
20
21
|
import { ZoneRects } from '../primitives/ZoneRects'
|
|
@@ -31,8 +32,9 @@ import { smartTicks, xLabelPxFor } from '../utils/ticks'
|
|
|
31
32
|
|
|
32
33
|
export type DualPanelProps<T> = BasaltProps & {
|
|
33
34
|
data: T[]
|
|
34
|
-
/**
|
|
35
|
-
|
|
35
|
+
/** Height in px, or one per size step (`{ base, sm?, md?, lg? }`). Default 240. Forwarded
|
|
36
|
+
* unchanged; `ChartFrameProps.height` owns the law — steps resolve off the MEASURED width. */
|
|
37
|
+
height?: number | ResponsiveChartHeight
|
|
36
38
|
chartId: string
|
|
37
39
|
getX: (d: T) => string
|
|
38
40
|
/** X tick label formatter. Default `fmtAxisDate` (DD.MM) — same default `CartesianChart` uses,
|
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
TooltipRow,
|
|
11
11
|
} from '../primitives/ChartTooltip'
|
|
12
12
|
import { ChartFrame } from '../primitives/ChartFrame'
|
|
13
|
+
import type { ResponsiveChartHeight } from '../primitives/ChartFrame'
|
|
13
14
|
import { useChartTierMetrics } from '../primitives/chart-tier'
|
|
14
15
|
import type { ChartState } from '../primitives/ChartPending'
|
|
15
16
|
import { maxTextWidth } from '../utils/measure-text'
|
|
@@ -24,8 +25,10 @@ type HeatmapTip = HeatmapCell & { anchor: { x: number; y: number } }
|
|
|
24
25
|
|
|
25
26
|
export type HeatmapProps<T> = BasaltProps & {
|
|
26
27
|
data: T[]
|
|
27
|
-
/**
|
|
28
|
-
|
|
28
|
+
/** Height in px, or one per size step (`{ base, sm?, md?, lg? }`). Used when neither
|
|
29
|
+
* `aspectRatio` nor `fill` is set. Default 240. `ChartFrameProps.height` owns the law —
|
|
30
|
+
* steps resolve off the MEASURED width. */
|
|
31
|
+
height?: number | ResponsiveChartHeight
|
|
29
32
|
/** height = Math.round(containerWidth / aspectRatio). Ignored when `fill` is set. */
|
|
30
33
|
aspectRatio?: number
|
|
31
34
|
/** Fill the parent flex/grid cell's measured height instead of a fixed/derived one. */
|
|
@@ -12,6 +12,7 @@ import type { BandFold, BandTooltipConfig } from '../hooks/useBandPlot'
|
|
|
12
12
|
import { probeAxisLabels } from '../layout/auto-margin'
|
|
13
13
|
import { AxisBottomDate, AxisLeftNumeric } from '../primitives/Axes'
|
|
14
14
|
import { ChartFrame, resolveLegend } from '../primitives/ChartFrame'
|
|
15
|
+
import type { ResponsiveChartHeight } from '../primitives/ChartFrame'
|
|
15
16
|
import {
|
|
16
17
|
ChartTooltipFloat,
|
|
17
18
|
TooltipBody,
|
|
@@ -79,8 +80,9 @@ export type MirroredBarsProps<T> = BasaltProps & {
|
|
|
79
80
|
/** Per-datum bar opacity — a real but qualified measurement (a short bucket, a partial interval)
|
|
80
81
|
* drawn dimmer. Default 1. */
|
|
81
82
|
getBarOpacity?: (d: T) => number
|
|
82
|
-
/**
|
|
83
|
-
|
|
83
|
+
/** Height in px, or one per size step (`{ base, sm?, md?, lg? }`). Default 240. Forwarded
|
|
84
|
+
* unchanged; `ChartFrameProps.height` owns the law — steps resolve off the MEASURED width. */
|
|
85
|
+
height?: number | ResponsiveChartHeight
|
|
84
86
|
fill?: boolean
|
|
85
87
|
formatX?: (key: string) => string
|
|
86
88
|
xTickValues?: (keys: readonly string[], plotWidth: number) => readonly string[]
|
|
@@ -6,6 +6,7 @@ import type { BasaltProps } from '../../common/props'
|
|
|
6
6
|
import type { CursorResolution } from '../cursor/resolve'
|
|
7
7
|
import type { CartesianTooltipConfig, AxisConfig, PlotContext } from '../primitives/CartesianChart'
|
|
8
8
|
import { CartesianChart } from '../primitives/CartesianChart'
|
|
9
|
+
import type { ResponsiveChartHeight } from '../primitives/ChartFrame'
|
|
9
10
|
import type { XZoneSpec } from '../primitives/XZoneRects'
|
|
10
11
|
import type { ZoneSpec } from '../primitives/ZoneRects'
|
|
11
12
|
import type { ChartState } from '../primitives/ChartPending'
|
|
@@ -15,8 +16,9 @@ import { VX } from '../../tokens'
|
|
|
15
16
|
|
|
16
17
|
export type MultiLineProps<T> = BasaltProps & {
|
|
17
18
|
data: T[]
|
|
18
|
-
/**
|
|
19
|
-
|
|
19
|
+
/** Height in px, or one per size step (`{ base, sm?, md?, lg? }`). Default 240. Forwarded
|
|
20
|
+
* unchanged; `ChartFrameProps.height` owns the law — steps resolve off the MEASURED width. */
|
|
21
|
+
height?: number | ResponsiveChartHeight
|
|
20
22
|
chartId: string
|
|
21
23
|
/** Extracts the x-axis category (date string) from a data point. */
|
|
22
24
|
getX: (d: T) => string
|
|
@@ -5,6 +5,7 @@ import type { BasaltProps } from '../../common/props'
|
|
|
5
5
|
import type { CursorResolution } from '../cursor/resolve'
|
|
6
6
|
import { CartesianChart } from '../primitives/CartesianChart'
|
|
7
7
|
import type { AxisConfig, PlotContext } from '../primitives/CartesianChart'
|
|
8
|
+
import type { ResponsiveChartHeight } from '../primitives/ChartFrame'
|
|
8
9
|
import type { ChartState } from '../primitives/ChartPending'
|
|
9
10
|
import { curveFor } from '../series'
|
|
10
11
|
import type { ChartLegendConfig, ChartSeries } from '../series'
|
|
@@ -12,8 +13,9 @@ import { isDev } from '../../common/is-dev'
|
|
|
12
13
|
|
|
13
14
|
export type StackedAreaProps<T> = BasaltProps & {
|
|
14
15
|
data: T[]
|
|
15
|
-
/**
|
|
16
|
-
|
|
16
|
+
/** Height in px, or one per size step (`{ base, sm?, md?, lg? }`). Default 240. Forwarded
|
|
17
|
+
* unchanged; `ChartFrameProps.height` owns the law — steps resolve off the MEASURED width. */
|
|
18
|
+
height?: number | ResponsiveChartHeight
|
|
17
19
|
chartId: string
|
|
18
20
|
getX: (d: T) => string
|
|
19
21
|
/** The stacked bands, bottom to top — the single source of truth for color, legend, and tooltip
|
|
@@ -7,6 +7,7 @@ import type { CursorResolution } from '../cursor/resolve'
|
|
|
7
7
|
import { AreaGradient, areaFillUrl } from '../primitives/AreaGradient'
|
|
8
8
|
import type { CartesianTooltipConfig, AxisConfig, PlotContext } from '../primitives/CartesianChart'
|
|
9
9
|
import { CartesianChart } from '../primitives/CartesianChart'
|
|
10
|
+
import type { ResponsiveChartHeight } from '../primitives/ChartFrame'
|
|
10
11
|
import type { XZoneSpec } from '../primitives/XZoneRects'
|
|
11
12
|
import type { ZoneSpec } from '../primitives/ZoneRects'
|
|
12
13
|
import type { ChartState } from '../primitives/ChartPending'
|
|
@@ -31,8 +32,9 @@ export type ZonedLineRefLine = {
|
|
|
31
32
|
|
|
32
33
|
export type ZonedLineProps<T> = BasaltProps & {
|
|
33
34
|
data: T[]
|
|
34
|
-
/**
|
|
35
|
-
|
|
35
|
+
/** Height in px, or one per size step (`{ base, sm?, md?, lg? }`). Default 240. Forwarded
|
|
36
|
+
* unchanged; `ChartFrameProps.height` owns the law — steps resolve off the MEASURED width. */
|
|
37
|
+
height?: number | ResponsiveChartHeight
|
|
36
38
|
chartId: string
|
|
37
39
|
/** Extracts the x-axis category (date string) from a data point. */
|
|
38
40
|
getX: (d: T) => string
|
|
@@ -17,6 +17,7 @@ import { fmtAxisDate } from '../utils/format'
|
|
|
17
17
|
import { autoXLabelRotate, smartTicks, smartTicksEvery, xLabelPxFor } from '../utils/ticks'
|
|
18
18
|
import { AxisBottomDate, AxisLeftNumeric, AxisRightNumeric } from './Axes'
|
|
19
19
|
import { ChartFrame, resolveLegend } from './ChartFrame'
|
|
20
|
+
import type { ResponsiveChartHeight } from './ChartFrame'
|
|
20
21
|
import { useChartTierMetrics } from './chart-tier'
|
|
21
22
|
import type { ChartState } from './ChartPending'
|
|
22
23
|
import { ChartTooltipFloat, TooltipBody, TooltipHeader, TooltipRow } from './ChartTooltip'
|
|
@@ -209,7 +210,9 @@ export type CartesianChartProps<T> = BasaltProps & {
|
|
|
209
210
|
/** X-range bands (time windows), drawn behind the marks. */
|
|
210
211
|
xZones?: XZoneSpec[]
|
|
211
212
|
refLines?: { value: number; color: string; dashed?: boolean; axis?: 'left' | 'right' }[]
|
|
212
|
-
|
|
213
|
+
/** Height in px, or one per size step (`{ base, sm?, md?, lg? }`). Default 240. Forwarded
|
|
214
|
+
* unchanged; `ChartFrameProps.height` owns the law — steps resolve off the MEASURED width. */
|
|
215
|
+
height?: number | ResponsiveChartHeight
|
|
213
216
|
aspectRatio?: number
|
|
214
217
|
fill?: boolean
|
|
215
218
|
legend?: ChartLegendConfig | false
|
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
import type { CSSProperties, ReactNode } from 'react'
|
|
2
2
|
import { useCallback, useState } from 'react'
|
|
3
3
|
import type { BasaltProps } from '../../common/props'
|
|
4
|
+
import { ignoredProp } from '../../common/errors'
|
|
5
|
+
import { useValidateProps } from '../../common/validate'
|
|
4
6
|
import { VX } from '../../tokens'
|
|
5
7
|
import { useChartSize } from '../hooks/useChartSize'
|
|
6
8
|
import { deriveLegend } from '../series'
|
|
7
9
|
import type { ChartLegendConfig, LegendPlacement, SeriesStyle } from '../series'
|
|
8
10
|
import {
|
|
9
|
-
legendEntryCap,
|
|
10
11
|
resolveChartTier,
|
|
11
12
|
resolveFrameHeight,
|
|
12
|
-
|
|
13
|
+
resolveLegendRollup,
|
|
13
14
|
resolvePlotRect,
|
|
14
15
|
} from './chart-frame-layout'
|
|
15
16
|
import type { ResponsiveChartHeight } from './chart-frame-layout'
|
|
@@ -20,7 +21,12 @@ import type { ChartState } from './ChartPending'
|
|
|
20
21
|
|
|
21
22
|
/** Re-exported for `ChartFrame.test.tsx` and any other consumer that previously reached these
|
|
22
23
|
* through `ChartFrame` — the layout math itself lives in `./chart-frame-layout` (pure, DOM-free). */
|
|
23
|
-
export {
|
|
24
|
+
export {
|
|
25
|
+
legendEntryCap,
|
|
26
|
+
resolveLegendRollup,
|
|
27
|
+
resolvePlotRect,
|
|
28
|
+
resolveChartTier,
|
|
29
|
+
} from './chart-frame-layout'
|
|
24
30
|
export type { ResponsiveChartHeight } from './chart-frame-layout'
|
|
25
31
|
|
|
26
32
|
const DEFAULT_HEIGHT = 240
|
|
@@ -32,15 +38,22 @@ export type ChartFrameLegend = {
|
|
|
32
38
|
/** Default 'bottom'. */
|
|
33
39
|
placement?: LegendPlacement
|
|
34
40
|
/**
|
|
35
|
-
*
|
|
41
|
+
* Rollup cap at high cardinality: the legend renders this many ENTRIES and folds the remainder
|
|
42
|
+
* into a `+N more` disclosure chip.
|
|
36
43
|
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
* (
|
|
44
|
+
* **It counts entries, not rows** — the name is inherited and now load-bearing across
|
|
45
|
+
* `ChartTierMetrics.legendMaxRows`, so it is not renamed in a patch. `maxRows: 3` on a 7-entry
|
|
46
|
+
* legend renders three entries, which is ONE row at any width they fit on. `ChartLegend` slices
|
|
47
|
+
* (`entries.slice(0, cap)`); nothing here measures rows.
|
|
48
|
+
*
|
|
49
|
+
* An explicit value WINS OUTRIGHT — over the phone tier's two-row default (`chartTierMetrics`)
|
|
50
|
+
* AND over a `fill` frame's measured fit (`legendEntryCap`). 1.30.0 fixed only the first half:
|
|
51
|
+
* the fit still ran `Math.min(fitted, caller)` on top, so meteo's 7-entry meteogram legend in a
|
|
52
|
+
* 150px docked row rendered 2 entries at `maxRows` 3, 6 AND 99 — five series drawn in colours
|
|
53
|
+
* nothing named. Both losers are DEFAULTS (the tier's, and `VX.minPlotHeight`); this is the one
|
|
54
|
+
* number a caller stated, so under `fill` the PLOT yields the height instead
|
|
55
|
+
* ({@link resolvePlotRect}'s `legendWins`). State a number too large for the box and the plot
|
|
56
|
+
* gets what is left — visible, and yours.
|
|
44
57
|
*/
|
|
45
58
|
maxRows?: number
|
|
46
59
|
/** Visually separate role: series | overlay | reference. */
|
|
@@ -62,7 +75,8 @@ export type ChartFrameProps = BasaltProps & {
|
|
|
62
75
|
series: readonly SeriesStyle[]
|
|
63
76
|
/**
|
|
64
77
|
* Height in pixels, or one per size step. Used when neither `aspectRatio` nor `fill` is set.
|
|
65
|
-
* Default 240.
|
|
78
|
+
* Default 240. Passing it WITH `fill` is a wiring mistake, not a fallback — `fill` wins and
|
|
79
|
+
* this is never read, which now warns once in dev (`common/errors.ts`' `ignoredProp`).
|
|
66
80
|
*
|
|
67
81
|
* `{ base: 180, md: 260 }` is the declarative escape from a page of fixed-height charts: the
|
|
68
82
|
* steps are compared against the frame's own MEASURED width, not the viewport, so a chart squeezed
|
|
@@ -70,7 +84,8 @@ export type ChartFrameProps = BasaltProps & {
|
|
|
70
84
|
* supported ({@link resolveFrameHeight}).
|
|
71
85
|
*/
|
|
72
86
|
height?: number | ResponsiveChartHeight
|
|
73
|
-
/** height = Math.round(containerWidth / aspectRatio). Ignored when `fill` is set
|
|
87
|
+
/** height = Math.round(containerWidth / aspectRatio). Ignored when `fill` is set — which warns
|
|
88
|
+
* once in dev, for the same reason `height` does. */
|
|
74
89
|
aspectRatio?: number
|
|
75
90
|
/** Fill the parent flex/grid cell's measured height instead of a fixed/derived one. */
|
|
76
91
|
fill?: boolean
|
|
@@ -215,6 +230,32 @@ export function ChartFrame({
|
|
|
215
230
|
})
|
|
216
231
|
}, [])
|
|
217
232
|
|
|
233
|
+
// The three sizing props are mutually exclusive and `fill` wins, silently — the type cannot say
|
|
234
|
+
// so without a union prop, which is a bigger change than a patch carries. Warn instead: a
|
|
235
|
+
// consumer passed `height` alongside `fill` for the life of a file and kept eight height
|
|
236
|
+
// constants (and a whole `compact` branch) that never moved a pixel, with nothing from tsc,
|
|
237
|
+
// oxlint or check-theme saying a word.
|
|
238
|
+
useValidateProps(
|
|
239
|
+
'ChartFrame',
|
|
240
|
+
() => [
|
|
241
|
+
fill && height !== undefined
|
|
242
|
+
? ignoredProp(
|
|
243
|
+
'ChartFrame',
|
|
244
|
+
'height',
|
|
245
|
+
"`fill` is set — a fill frame takes its parent cell's measured height. Pass one or the other.",
|
|
246
|
+
)
|
|
247
|
+
: null,
|
|
248
|
+
fill && aspectRatio !== undefined
|
|
249
|
+
? ignoredProp(
|
|
250
|
+
'ChartFrame',
|
|
251
|
+
'aspectRatio',
|
|
252
|
+
"`fill` is set — a fill frame takes its parent cell's measured height. Pass one or the other.",
|
|
253
|
+
)
|
|
254
|
+
: null,
|
|
255
|
+
],
|
|
256
|
+
[fill, height, aspectRatio],
|
|
257
|
+
)
|
|
258
|
+
|
|
218
259
|
const placement = legend === false ? 'bottom' : (legend.placement ?? 'bottom')
|
|
219
260
|
const vertical = placement === 'left' || placement === 'right'
|
|
220
261
|
// Every non-null state replaces the plot, and all three suppress the legend for one reason: a
|
|
@@ -232,38 +273,32 @@ export function ChartFrame({
|
|
|
232
273
|
const sideLegendWidth = legendVisible && vertical ? legendW : 0
|
|
233
274
|
const topBottomLegendHeight = legendVisible && !vertical ? legendH : 0
|
|
234
275
|
|
|
276
|
+
const legendItems = legend === false ? [] : deriveLegend(series)
|
|
277
|
+
const togglable = legend !== false && (legend.toggle ?? legendItems.length > 1)
|
|
278
|
+
|
|
279
|
+
// The cap, and who pays for it — one pure decision ({@link resolveLegendRollup} states the three
|
|
280
|
+
// cases and why the composition, not either half, is what shipped broken). A fixed-height frame
|
|
281
|
+
// grows around its legend; a `fill` one cannot, so its legend rolls up to the measured fit
|
|
282
|
+
// instead of eating the plot — unless the caller stated a number, which nothing trims and the
|
|
283
|
+
// plot pays for. Top/bottom only: a side legend costs width, not height.
|
|
284
|
+
const { maxRows, legendWins } = resolveLegendRollup({
|
|
285
|
+
...(legend !== false && legend.maxRows !== undefined && { statedMaxRows: legend.maxRows }),
|
|
286
|
+
tier,
|
|
287
|
+
fillBand: legendVisible && fill && !vertical,
|
|
288
|
+
items: legendItems,
|
|
289
|
+
containerW,
|
|
290
|
+
available: resolvedHeight - VX.minPlotHeight,
|
|
291
|
+
})
|
|
292
|
+
|
|
235
293
|
const plot = resolvePlotRect({
|
|
236
294
|
containerW,
|
|
237
295
|
resolvedHeight,
|
|
238
296
|
minWidth,
|
|
239
297
|
sideLegendWidth,
|
|
240
298
|
topBottomLegendHeight,
|
|
299
|
+
...(legendWins && { legendWins }),
|
|
241
300
|
})
|
|
242
301
|
|
|
243
|
-
const legendItems = legend === false ? [] : deriveLegend(series)
|
|
244
|
-
const togglable = legend !== false && (legend.toggle ?? legendItems.length > 1)
|
|
245
|
-
|
|
246
|
-
// A fixed-height frame grows around its legend; a `fill` one cannot, so its legend rolls up
|
|
247
|
-
// instead of eating the plot (see `legendEntryCap`). Top/bottom only — a side legend costs
|
|
248
|
-
// width, not height.
|
|
249
|
-
// The resolved cap — the caller's if they named one, else the tier's DEFAULT, never both
|
|
250
|
-
// `Math.min`'d (`resolveLegendMaxRows` owns that law and says why) — enters the fill measurement
|
|
251
|
-
// as the upper bound, so a phone-width `fill` frame can only ever roll up further, never less.
|
|
252
|
-
const callerMaxRows =
|
|
253
|
-
legend === false ? undefined : resolveLegendMaxRows({ callerMaxRows: legend.maxRows, tier })
|
|
254
|
-
|
|
255
|
-
const maxRows =
|
|
256
|
-
legend === false
|
|
257
|
-
? undefined
|
|
258
|
-
: fill && !vertical
|
|
259
|
-
? legendEntryCap({
|
|
260
|
-
items: legendItems,
|
|
261
|
-
containerW,
|
|
262
|
-
available: resolvedHeight - VX.minPlotHeight,
|
|
263
|
-
...(callerMaxRows !== undefined && { callerMaxRows }),
|
|
264
|
-
})
|
|
265
|
-
: callerMaxRows
|
|
266
|
-
|
|
267
302
|
const legendNode =
|
|
268
303
|
legend === false || resolvedState !== null ? null : (
|
|
269
304
|
<div ref={legendRef} style={legendWrapperStyle(vertical)}>
|
|
@@ -255,9 +255,11 @@ function LegendChild({ item }: { item: LegendEntry }) {
|
|
|
255
255
|
* Shared legend for all non-sparkline charts. `highlighted`/`onHighlight` are optional — omit for
|
|
256
256
|
* static legends without hover interactivity. `chartId` namespaces `split` clipPath ids so two
|
|
257
257
|
* legends on the same page never collide. `groups` renders series → hairline divider → overlay →
|
|
258
|
-
* reference bands instead of one flat smear. `maxRows` caps the number of rendered
|
|
259
|
-
* rolls the remainder into a `+N more` chip
|
|
260
|
-
*
|
|
258
|
+
* reference bands instead of one flat smear. `maxRows` caps the number of rendered ENTRIES and
|
|
259
|
+
* rolls the remainder into a `+N more` chip — it is an entry count, not a row count, in spite of
|
|
260
|
+
* the name (`entries.slice(0, cap)` below is the whole of it, and `ChartFrame` measures rows for a
|
|
261
|
+
* different purpose in `legendEntryCap`). The name is inherited and reaches the public
|
|
262
|
+
* `ChartTierMetrics.legendMaxRows`, so it stays until a minor can rename both.
|
|
261
263
|
*
|
|
262
264
|
* `onToggle` makes an entry a real toggle: clicking it hides the series in the plot, the tooltip,
|
|
263
265
|
* and the auto y-domain together (`ChartFrame` owns the `hidden` set — see `docs/CHARTS-SPEC.md`
|