@voila.dev/chart 4.0.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/README.md +85 -0
- package/dist/brush/brush-layer.d.ts +16 -0
- package/dist/brush/brush-layer.d.ts.map +1 -0
- package/dist/brush/brush-layer.js +137 -0
- package/dist/brush/brush-layer.js.map +7 -0
- package/dist/brush/index.d.ts +10 -0
- package/dist/brush/index.d.ts.map +1 -0
- package/dist/brush/index.js +8 -0
- package/dist/brush/index.js.map +7 -0
- package/dist/canvas/canvas-renderer.d.ts +11 -0
- package/dist/canvas/canvas-renderer.d.ts.map +1 -0
- package/dist/canvas/canvas-renderer.js +49 -0
- package/dist/canvas/canvas-renderer.js.map +7 -0
- package/dist/canvas/index.d.ts +2 -0
- package/dist/canvas/index.d.ts.map +1 -0
- package/dist/canvas/index.js +5 -0
- package/dist/canvas/index.js.map +7 -0
- package/dist/canvas/paint-scene.d.ts +4 -0
- package/dist/canvas/paint-scene.d.ts.map +1 -0
- package/dist/canvas/paint-scene.js +130 -0
- package/dist/canvas/paint-scene.js.map +7 -0
- package/dist/canvas/resolve-color.d.ts +18 -0
- package/dist/canvas/resolve-color.d.ts.map +1 -0
- package/dist/canvas/resolve-color.js +52 -0
- package/dist/canvas/resolve-color.js.map +7 -0
- package/dist/contour/density.d.ts +20 -0
- package/dist/contour/density.d.ts.map +1 -0
- package/dist/contour/density.js +59 -0
- package/dist/contour/density.js.map +7 -0
- package/dist/contour/index.d.ts +2 -0
- package/dist/contour/index.d.ts.map +1 -0
- package/dist/contour/index.js +5 -0
- package/dist/contour/index.js.map +7 -0
- package/dist/core/channel.d.ts +11 -0
- package/dist/core/channel.d.ts.map +1 -0
- package/dist/core/channel.js +44 -0
- package/dist/core/channel.js.map +7 -0
- package/dist/core/compile.d.ts +22 -0
- package/dist/core/compile.d.ts.map +1 -0
- package/dist/core/compile.js +295 -0
- package/dist/core/compile.js.map +7 -0
- package/dist/core/define-chart.d.ts +96 -0
- package/dist/core/define-chart.d.ts.map +1 -0
- package/dist/core/define-chart.js +7 -0
- package/dist/core/define-chart.js.map +7 -0
- package/dist/core/facet.d.ts +7 -0
- package/dist/core/facet.d.ts.map +1 -0
- package/dist/core/facet.js +134 -0
- package/dist/core/facet.js.map +7 -0
- package/dist/core/format.d.ts +37 -0
- package/dist/core/format.d.ts.map +1 -0
- package/dist/core/format.js +108 -0
- package/dist/core/format.js.map +7 -0
- package/dist/core/guides/axes.d.ts +25 -0
- package/dist/core/guides/axes.d.ts.map +1 -0
- package/dist/core/guides/axes.js +204 -0
- package/dist/core/guides/axes.js.map +7 -0
- package/dist/core/marks/area.d.ts +23 -0
- package/dist/core/marks/area.d.ts.map +1 -0
- package/dist/core/marks/area.js +187 -0
- package/dist/core/marks/area.js.map +7 -0
- package/dist/core/marks/bar.d.ts +26 -0
- package/dist/core/marks/bar.d.ts.map +1 -0
- package/dist/core/marks/bar.js +200 -0
- package/dist/core/marks/bar.js.map +7 -0
- package/dist/core/marks/cell.d.ts +16 -0
- package/dist/core/marks/cell.d.ts.map +1 -0
- package/dist/core/marks/cell.js +98 -0
- package/dist/core/marks/cell.js.map +7 -0
- package/dist/core/marks/dot.d.ts +17 -0
- package/dist/core/marks/dot.d.ts.map +1 -0
- package/dist/core/marks/dot.js +125 -0
- package/dist/core/marks/dot.js.map +7 -0
- package/dist/core/marks/funnel-layout.d.ts +37 -0
- package/dist/core/marks/funnel-layout.d.ts.map +1 -0
- package/dist/core/marks/funnel-layout.js +61 -0
- package/dist/core/marks/funnel-layout.js.map +7 -0
- package/dist/core/marks/funnel.d.ts +19 -0
- package/dist/core/marks/funnel.d.ts.map +1 -0
- package/dist/core/marks/funnel.js +143 -0
- package/dist/core/marks/funnel.js.map +7 -0
- package/dist/core/marks/line.d.ts +17 -0
- package/dist/core/marks/line.d.ts.map +1 -0
- package/dist/core/marks/line.js +150 -0
- package/dist/core/marks/line.js.map +7 -0
- package/dist/core/marks/polar.d.ts +61 -0
- package/dist/core/marks/polar.d.ts.map +1 -0
- package/dist/core/marks/polar.js +436 -0
- package/dist/core/marks/polar.js.map +7 -0
- package/dist/core/marks/rect.d.ts +22 -0
- package/dist/core/marks/rect.d.ts.map +1 -0
- package/dist/core/marks/rect.js +119 -0
- package/dist/core/marks/rect.js.map +7 -0
- package/dist/core/marks/rule.d.ts +17 -0
- package/dist/core/marks/rule.d.ts.map +1 -0
- package/dist/core/marks/rule.js +73 -0
- package/dist/core/marks/rule.js.map +7 -0
- package/dist/core/marks/shared.d.ts +41 -0
- package/dist/core/marks/shared.d.ts.map +1 -0
- package/dist/core/marks/shared.js +74 -0
- package/dist/core/marks/shared.js.map +7 -0
- package/dist/core/marks/stack.d.ts +13 -0
- package/dist/core/marks/stack.d.ts.map +1 -0
- package/dist/core/marks/stack.js +28 -0
- package/dist/core/marks/stack.js.map +7 -0
- package/dist/core/marks/stats/difference.d.ts +20 -0
- package/dist/core/marks/stats/difference.d.ts.map +1 -0
- package/dist/core/marks/stats/difference.js +168 -0
- package/dist/core/marks/stats/difference.js.map +7 -0
- package/dist/core/marks/stats/distribution.d.ts +41 -0
- package/dist/core/marks/stats/distribution.d.ts.map +1 -0
- package/dist/core/marks/stats/distribution.js +354 -0
- package/dist/core/marks/stats/distribution.js.map +7 -0
- package/dist/core/marks/stats/dodge.d.ts +16 -0
- package/dist/core/marks/stats/dodge.d.ts.map +1 -0
- package/dist/core/marks/stats/dodge.js +104 -0
- package/dist/core/marks/stats/dodge.js.map +7 -0
- package/dist/core/marks/stats/hexbin.d.ts +26 -0
- package/dist/core/marks/stats/hexbin.d.ts.map +1 -0
- package/dist/core/marks/stats/hexbin.js +113 -0
- package/dist/core/marks/stats/hexbin.js.map +7 -0
- package/dist/core/marks/stats/histogram.d.ts +27 -0
- package/dist/core/marks/stats/histogram.d.ts.map +1 -0
- package/dist/core/marks/stats/histogram.js +70 -0
- package/dist/core/marks/stats/histogram.js.map +7 -0
- package/dist/core/marks/stats/regression.d.ts +13 -0
- package/dist/core/marks/stats/regression.d.ts.map +1 -0
- package/dist/core/marks/stats/regression.js +80 -0
- package/dist/core/marks/stats/regression.js.map +7 -0
- package/dist/core/marks/stats/statistics.d.ts +34 -0
- package/dist/core/marks/stats/statistics.d.ts.map +1 -0
- package/dist/core/marks/stats/statistics.js +111 -0
- package/dist/core/marks/stats/statistics.js.map +7 -0
- package/dist/core/marks/stats/waffle.d.ts +18 -0
- package/dist/core/marks/stats/waffle.d.ts.map +1 -0
- package/dist/core/marks/stats/waffle.js +117 -0
- package/dist/core/marks/stats/waffle.js.map +7 -0
- package/dist/core/marks/text.d.ts +19 -0
- package/dist/core/marks/text.d.ts.map +1 -0
- package/dist/core/marks/text.js +60 -0
- package/dist/core/marks/text.js.map +7 -0
- package/dist/core/motion/tween.d.ts +6 -0
- package/dist/core/motion/tween.d.ts.map +1 -0
- package/dist/core/motion/tween.js +98 -0
- package/dist/core/motion/tween.js.map +7 -0
- package/dist/core/nearest.d.ts +25 -0
- package/dist/core/nearest.d.ts.map +1 -0
- package/dist/core/nearest.js +151 -0
- package/dist/core/nearest.js.map +7 -0
- package/dist/core/paths.d.ts +65 -0
- package/dist/core/paths.d.ts.map +1 -0
- package/dist/core/paths.js +192 -0
- package/dist/core/paths.js.map +7 -0
- package/dist/core/scales/color.d.ts +11 -0
- package/dist/core/scales/color.d.ts.map +1 -0
- package/dist/core/scales/color.js +35 -0
- package/dist/core/scales/color.js.map +7 -0
- package/dist/core/scales/continuous.d.ts +18 -0
- package/dist/core/scales/continuous.d.ts.map +1 -0
- package/dist/core/scales/continuous.js +82 -0
- package/dist/core/scales/continuous.js.map +7 -0
- package/dist/core/scales/discrete.d.ts +22 -0
- package/dist/core/scales/discrete.d.ts.map +1 -0
- package/dist/core/scales/discrete.js +103 -0
- package/dist/core/scales/discrete.js.map +7 -0
- package/dist/core/scales/resolve.d.ts +18 -0
- package/dist/core/scales/resolve.d.ts.map +1 -0
- package/dist/core/scales/resolve.js +112 -0
- package/dist/core/scales/resolve.js.map +7 -0
- package/dist/core/scales/time.d.ts +4 -0
- package/dist/core/scales/time.d.ts.map +1 -0
- package/dist/core/scales/time.js +140 -0
- package/dist/core/scales/time.js.map +7 -0
- package/dist/core/text.d.ts +3 -0
- package/dist/core/text.d.ts.map +1 -0
- package/dist/core/text.js +29 -0
- package/dist/core/text.js.map +7 -0
- package/dist/core/theme.d.ts +8 -0
- package/dist/core/theme.d.ts.map +1 -0
- package/dist/core/theme.js +21 -0
- package/dist/core/theme.js.map +7 -0
- package/dist/core/ticks.d.ts +27 -0
- package/dist/core/ticks.d.ts.map +1 -0
- package/dist/core/ticks.js +76 -0
- package/dist/core/ticks.js.map +7 -0
- package/dist/core/types.d.ts +305 -0
- package/dist/core/types.d.ts.map +1 -0
- package/dist/core/types.js +1 -0
- package/dist/core/types.js.map +7 -0
- package/dist/force/force.d.ts +30 -0
- package/dist/force/force.d.ts.map +1 -0
- package/dist/force/force.js +143 -0
- package/dist/force/force.js.map +7 -0
- package/dist/force/index.d.ts +2 -0
- package/dist/force/index.d.ts.map +1 -0
- package/dist/force/index.js +7 -0
- package/dist/force/index.js.map +7 -0
- package/dist/geo/geo.d.ts +59 -0
- package/dist/geo/geo.d.ts.map +1 -0
- package/dist/geo/geo.js +231 -0
- package/dist/geo/geo.js.map +7 -0
- package/dist/geo/index.d.ts +2 -0
- package/dist/geo/index.d.ts.map +1 -0
- package/dist/geo/index.js +13 -0
- package/dist/geo/index.js.map +7 -0
- package/dist/hierarchy/hierarchy.d.ts +24 -0
- package/dist/hierarchy/hierarchy.d.ts.map +1 -0
- package/dist/hierarchy/hierarchy.js +279 -0
- package/dist/hierarchy/hierarchy.js.map +7 -0
- package/dist/hierarchy/index.d.ts +2 -0
- package/dist/hierarchy/index.d.ts.map +1 -0
- package/dist/hierarchy/index.js +11 -0
- package/dist/hierarchy/index.js.map +7 -0
- package/dist/index.d.ts +29 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +96 -0
- package/dist/index.js.map +7 -0
- package/dist/react/brush-contract.d.ts +23 -0
- package/dist/react/brush-contract.d.ts.map +1 -0
- package/dist/react/brush-contract.js +7 -0
- package/dist/react/brush-contract.js.map +7 -0
- package/dist/react/chart-data-table.d.ts +18 -0
- package/dist/react/chart-data-table.d.ts.map +1 -0
- package/dist/react/chart-data-table.js +55 -0
- package/dist/react/chart-data-table.js.map +7 -0
- package/dist/react/chart-legend.d.ts +17 -0
- package/dist/react/chart-legend.d.ts.map +1 -0
- package/dist/react/chart-legend.js +126 -0
- package/dist/react/chart-legend.js.map +7 -0
- package/dist/react/chart-tooltip.d.ts +18 -0
- package/dist/react/chart-tooltip.d.ts.map +1 -0
- package/dist/react/chart-tooltip.js +95 -0
- package/dist/react/chart-tooltip.js.map +7 -0
- package/dist/react/chart.d.ts +45 -0
- package/dist/react/chart.d.ts.map +1 -0
- package/dist/react/chart.js +383 -0
- package/dist/react/chart.js.map +7 -0
- package/dist/react/focus-overlay.d.ts +14 -0
- package/dist/react/focus-overlay.d.ts.map +1 -0
- package/dist/react/focus-overlay.js +147 -0
- package/dist/react/focus-overlay.js.map +7 -0
- package/dist/react/index.d.ts +7 -0
- package/dist/react/index.d.ts.map +1 -0
- package/dist/react/index.js +11 -0
- package/dist/react/index.js.map +7 -0
- package/dist/react/messages.d.ts +16 -0
- package/dist/react/messages.d.ts.map +1 -0
- package/dist/react/messages.js +33 -0
- package/dist/react/messages.js.map +7 -0
- package/dist/react/renderer.d.ts +14 -0
- package/dist/react/renderer.d.ts.map +1 -0
- package/dist/react/renderer.js +1 -0
- package/dist/react/renderer.js.map +7 -0
- package/dist/react/sr-only.d.ts +4 -0
- package/dist/react/sr-only.d.ts.map +1 -0
- package/dist/react/sr-only.js +15 -0
- package/dist/react/sr-only.js.map +7 -0
- package/dist/react/svg-renderer.d.ts +4 -0
- package/dist/react/svg-renderer.d.ts.map +1 -0
- package/dist/react/svg-renderer.js +20 -0
- package/dist/react/svg-renderer.js.map +7 -0
- package/dist/react/svg-scene.d.ts +8 -0
- package/dist/react/svg-scene.d.ts.map +1 -0
- package/dist/react/svg-scene.js +121 -0
- package/dist/react/svg-scene.js.map +7 -0
- package/dist/react/use-animated-scene.d.ts +10 -0
- package/dist/react/use-animated-scene.d.ts.map +1 -0
- package/dist/react/use-animated-scene.js +41 -0
- package/dist/react/use-animated-scene.js.map +7 -0
- package/dist/react/use-chart-focus.d.ts +29 -0
- package/dist/react/use-chart-focus.d.ts.map +1 -0
- package/dist/react/use-chart-focus.js +180 -0
- package/dist/react/use-chart-focus.js.map +7 -0
- package/dist/react/use-chart-width.d.ts +12 -0
- package/dist/react/use-chart-width.d.ts.map +1 -0
- package/dist/react/use-chart-width.js +23 -0
- package/dist/react/use-chart-width.js.map +7 -0
- package/dist/react/use-chart-zoom.d.ts +13 -0
- package/dist/react/use-chart-zoom.d.ts.map +1 -0
- package/dist/react/use-chart-zoom.js +52 -0
- package/dist/react/use-chart-zoom.js.map +7 -0
- package/dist/react/use-text-measurer.d.ts +8 -0
- package/dist/react/use-text-measurer.d.ts.map +1 -0
- package/dist/react/use-text-measurer.js +44 -0
- package/dist/react/use-text-measurer.js.map +7 -0
- package/dist/sankey/index.d.ts +2 -0
- package/dist/sankey/index.d.ts.map +1 -0
- package/dist/sankey/index.js +7 -0
- package/dist/sankey/index.js.map +7 -0
- package/dist/sankey/sankey.d.ts +29 -0
- package/dist/sankey/sankey.d.ts.map +1 -0
- package/dist/sankey/sankey.js +127 -0
- package/dist/sankey/sankey.js.map +7 -0
- package/dist/voronoi/index.d.ts +2 -0
- package/dist/voronoi/index.d.ts.map +1 -0
- package/dist/voronoi/index.js +5 -0
- package/dist/voronoi/index.js.map +7 -0
- package/dist/voronoi/voronoi.d.ts +16 -0
- package/dist/voronoi/voronoi.d.ts.map +1 -0
- package/dist/voronoi/voronoi.js +62 -0
- package/dist/voronoi/voronoi.js.map +7 -0
- package/package.json +74 -0
- package/src/brush/brush-layer.tsx +174 -0
- package/src/brush/index.ts +14 -0
- package/src/canvas/canvas-renderer.tsx +56 -0
- package/src/canvas/index.ts +1 -0
- package/src/canvas/paint-scene.ts +152 -0
- package/src/canvas/resolve-color.ts +65 -0
- package/src/contour/density.ts +90 -0
- package/src/contour/index.ts +1 -0
- package/src/core/channel.ts +53 -0
- package/src/core/compile.ts +436 -0
- package/src/core/define-chart.ts +128 -0
- package/src/core/facet.ts +189 -0
- package/src/core/format.ts +163 -0
- package/src/core/guides/axes.ts +283 -0
- package/src/core/marks/area.ts +238 -0
- package/src/core/marks/bar.ts +278 -0
- package/src/core/marks/cell.ts +123 -0
- package/src/core/marks/dot.ts +157 -0
- package/src/core/marks/funnel-layout.ts +113 -0
- package/src/core/marks/funnel.ts +172 -0
- package/src/core/marks/line.ts +206 -0
- package/src/core/marks/polar.ts +568 -0
- package/src/core/marks/rect.ts +156 -0
- package/src/core/marks/rule.ts +104 -0
- package/src/core/marks/shared.ts +134 -0
- package/src/core/marks/stack.ts +42 -0
- package/src/core/marks/stats/difference.ts +205 -0
- package/src/core/marks/stats/distribution.ts +425 -0
- package/src/core/marks/stats/dodge.ts +136 -0
- package/src/core/marks/stats/hexbin.ts +152 -0
- package/src/core/marks/stats/histogram.ts +108 -0
- package/src/core/marks/stats/regression.ts +108 -0
- package/src/core/marks/stats/statistics.ts +153 -0
- package/src/core/marks/stats/waffle.ts +141 -0
- package/src/core/marks/text.ts +97 -0
- package/src/core/motion/tween.ts +131 -0
- package/src/core/nearest.ts +223 -0
- package/src/core/paths.ts +323 -0
- package/src/core/scales/color.ts +51 -0
- package/src/core/scales/continuous.ts +111 -0
- package/src/core/scales/discrete.ts +156 -0
- package/src/core/scales/resolve.ts +158 -0
- package/src/core/scales/time.ts +180 -0
- package/src/core/text.ts +42 -0
- package/src/core/theme.ts +26 -0
- package/src/core/ticks.ts +118 -0
- package/src/core/types.ts +358 -0
- package/src/force/force.ts +200 -0
- package/src/force/index.ts +6 -0
- package/src/geo/geo.ts +346 -0
- package/src/geo/index.ts +10 -0
- package/src/hierarchy/hierarchy.ts +377 -0
- package/src/hierarchy/index.ts +6 -0
- package/src/index.ts +70 -0
- package/src/react/brush-contract.ts +35 -0
- package/src/react/chart-data-table.tsx +98 -0
- package/src/react/chart-legend.tsx +130 -0
- package/src/react/chart-tooltip.tsx +111 -0
- package/src/react/chart.tsx +489 -0
- package/src/react/focus-overlay.tsx +164 -0
- package/src/react/index.ts +9 -0
- package/src/react/messages.ts +49 -0
- package/src/react/renderer.ts +15 -0
- package/src/react/sr-only.ts +14 -0
- package/src/react/svg-renderer.tsx +18 -0
- package/src/react/svg-scene.tsx +146 -0
- package/src/react/use-animated-scene.ts +57 -0
- package/src/react/use-chart-focus.ts +245 -0
- package/src/react/use-chart-width.ts +27 -0
- package/src/react/use-chart-zoom.ts +67 -0
- package/src/react/use-text-measurer.ts +49 -0
- package/src/sankey/index.ts +6 -0
- package/src/sankey/sankey.ts +179 -0
- package/src/voronoi/index.ts +1 -0
- package/src/voronoi/voronoi.ts +84 -0
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import type { ChartFocusOrder, ChartMargin, ChartMark, ChartPositionScaleKind, ChartProjection, ChartScene, ChartTextMeasurer, ChartTheme, ChartValue } from "./types.js";
|
|
2
|
+
export interface ChartAxisOptions {
|
|
3
|
+
/** Axis title, data-table column heading and tooltip heading. */
|
|
4
|
+
readonly label?: string;
|
|
5
|
+
/** `false` hides the axis; the scale still places the marks. */
|
|
6
|
+
readonly axis?: boolean;
|
|
7
|
+
readonly grid?: boolean;
|
|
8
|
+
/** A tick count to aim for, or the exact values to tick. */
|
|
9
|
+
readonly ticks?: number | ReadonlyArray<ChartValue>;
|
|
10
|
+
readonly tickFormat?: (value: ChartValue) => string;
|
|
11
|
+
/** How the tooltip, live region and data table write a value of this channel. */
|
|
12
|
+
readonly format?: (value: ChartValue) => string;
|
|
13
|
+
}
|
|
14
|
+
export interface ChartPositionScaleOptions extends ChartAxisOptions {
|
|
15
|
+
readonly type?: ChartPositionScaleKind;
|
|
16
|
+
readonly domain?: ReadonlyArray<ChartValue>;
|
|
17
|
+
/** Widen a continuous domain to round ticks. On by default for y. */
|
|
18
|
+
readonly nice?: boolean;
|
|
19
|
+
/** Keep zero on a continuous domain even when no mark asks for it. */
|
|
20
|
+
readonly zero?: boolean;
|
|
21
|
+
readonly reverse?: boolean;
|
|
22
|
+
readonly paddingInner?: number;
|
|
23
|
+
readonly paddingOuter?: number;
|
|
24
|
+
}
|
|
25
|
+
export interface ChartColorOptions {
|
|
26
|
+
readonly type?: "ordinal" | "sequential";
|
|
27
|
+
readonly domain?: ReadonlyArray<ChartValue>;
|
|
28
|
+
/** Palette for an ordinal scale, `[from, to]` for a sequential one. */
|
|
29
|
+
readonly range?: ReadonlyArray<string>;
|
|
30
|
+
readonly legend?: boolean;
|
|
31
|
+
/** Display names of the series, by value. */
|
|
32
|
+
readonly labels?: Readonly<Record<string, string>>;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Small multiples: the same chart once per value, on a grid, every cell on
|
|
36
|
+
* the same scales so the cells compare at a glance.
|
|
37
|
+
*/
|
|
38
|
+
export interface ChartFacetOptions {
|
|
39
|
+
readonly values: ReadonlyArray<ChartValue>;
|
|
40
|
+
/** The marks of one cell. */
|
|
41
|
+
readonly marks: (value: ChartValue, index: number) => ReadonlyArray<ChartMark | false | null | undefined>;
|
|
42
|
+
readonly label?: (value: ChartValue) => string;
|
|
43
|
+
/** Cells per row. Defaults to three, or fewer when there are fewer values. */
|
|
44
|
+
readonly columns?: number;
|
|
45
|
+
/** Pixels between two cells. */
|
|
46
|
+
readonly gap?: number;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* What `facet(…)` returns: the options, and the compiler that lays the cells
|
|
50
|
+
* out. Carried by the spec rather than imported by the core, so a chart
|
|
51
|
+
* without facets never ships the facet code.
|
|
52
|
+
*/
|
|
53
|
+
export interface ChartFacet {
|
|
54
|
+
readonly options: ChartFacetOptions;
|
|
55
|
+
readonly compile: (spec: ChartSpec, options: ChartFacetCompileOptions) => ChartScene;
|
|
56
|
+
}
|
|
57
|
+
/** The compile options a facet compiler is handed, plus the per-cell compiler. */
|
|
58
|
+
export interface ChartFacetCompileOptions {
|
|
59
|
+
readonly width: number;
|
|
60
|
+
readonly height: number;
|
|
61
|
+
readonly measureText?: ChartTextMeasurer;
|
|
62
|
+
readonly hiddenSeries?: ReadonlySet<string>;
|
|
63
|
+
readonly xDomain?: readonly [number, number];
|
|
64
|
+
readonly compileCell: (spec: ChartSpec, options: Omit<ChartFacetCompileOptions, "compileCell">) => ChartScene;
|
|
65
|
+
}
|
|
66
|
+
export interface ChartSpec {
|
|
67
|
+
/** Falsy entries are skipped, so a mark can be switched on with `&&`. Unused with `facet`. */
|
|
68
|
+
readonly marks?: ReadonlyArray<ChartMark | false | null | undefined>;
|
|
69
|
+
readonly x?: ChartPositionScaleOptions;
|
|
70
|
+
readonly y?: ChartPositionScaleOptions;
|
|
71
|
+
readonly color?: ChartColorOptions;
|
|
72
|
+
/** Overrides the margins the axes would reserve, side by side. */
|
|
73
|
+
readonly margin?: Partial<ChartMargin>;
|
|
74
|
+
readonly focus?: ChartFocusOrder;
|
|
75
|
+
/** Small multiples, from `facet(…)`. With it, `marks` is ignored: each cell takes its own. */
|
|
76
|
+
readonly facet?: ChartFacet;
|
|
77
|
+
/** For maps: how longitude and latitude become pixels. */
|
|
78
|
+
readonly projection?: ChartProjection;
|
|
79
|
+
readonly locale?: string;
|
|
80
|
+
readonly theme?: Partial<ChartTheme>;
|
|
81
|
+
}
|
|
82
|
+
export interface ChartBuildContext {
|
|
83
|
+
readonly width: number;
|
|
84
|
+
readonly height: number;
|
|
85
|
+
}
|
|
86
|
+
export interface ChartDefinition {
|
|
87
|
+
readonly build: (context: ChartBuildContext) => ChartSpec;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* A chart as data. Pass the spec when it does not depend on the size, or a
|
|
91
|
+
* builder when it does (fewer ticks on a phone, a different layout below some
|
|
92
|
+
* width). Build it in `useMemo`: the definition's identity is what tells the
|
|
93
|
+
* renderer the data changed.
|
|
94
|
+
*/
|
|
95
|
+
export declare function defineChart(spec: ChartSpec | ((context: ChartBuildContext) => ChartSpec)): ChartDefinition;
|
|
96
|
+
//# sourceMappingURL=define-chart.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"define-chart.d.ts","sourceRoot":"","sources":["../../src/core/define-chart.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACX,eAAe,EACf,WAAW,EACX,SAAS,EACT,sBAAsB,EACtB,eAAe,EACf,UAAU,EACV,iBAAiB,EACjB,UAAU,EACV,UAAU,EACV,MAAM,iBAAiB,CAAC;AAEzB,MAAM,WAAW,gBAAgB;IAChC,iEAAiE;IACjE,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,gEAAgE;IAChE,QAAQ,CAAC,IAAI,CAAC,EAAE,OAAO,CAAC;IACxB,QAAQ,CAAC,IAAI,CAAC,EAAE,OAAO,CAAC;IACxB,4DAA4D;IAC5D,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,aAAa,CAAC,UAAU,CAAC,CAAC;IACpD,QAAQ,CAAC,UAAU,CAAC,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,MAAM,CAAC;IACpD,iFAAiF;IACjF,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,MAAM,CAAC;CAChD;AAED,MAAM,WAAW,yBAA0B,SAAQ,gBAAgB;IAClE,QAAQ,CAAC,IAAI,CAAC,EAAE,sBAAsB,CAAC;IACvC,QAAQ,CAAC,MAAM,CAAC,EAAE,aAAa,CAAC,UAAU,CAAC,CAAC;IAC5C,qEAAqE;IACrE,QAAQ,CAAC,IAAI,CAAC,EAAE,OAAO,CAAC;IACxB,sEAAsE;IACtE,QAAQ,CAAC,IAAI,CAAC,EAAE,OAAO,CAAC;IACxB,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC;IAC3B,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;CAC/B;AAED,MAAM,WAAW,iBAAiB;IACjC,QAAQ,CAAC,IAAI,CAAC,EAAE,SAAS,GAAG,YAAY,CAAC;IACzC,QAAQ,CAAC,MAAM,CAAC,EAAE,aAAa,CAAC,UAAU,CAAC,CAAC;IAC5C,uEAAuE;IACvE,QAAQ,CAAC,KAAK,CAAC,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;IACvC,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC;IAC1B,6CAA6C;IAC7C,QAAQ,CAAC,MAAM,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;CACnD;AAED;;;GAGG;AACH,MAAM,WAAW,iBAAiB;IACjC,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAC,UAAU,CAAC,CAAC;IAC3C,6BAA6B;IAC7B,QAAQ,CAAC,KAAK,EAAE,CACf,KAAK,EAAE,UAAU,EACjB,KAAK,EAAE,MAAM,KACT,aAAa,CAAC,SAAS,GAAG,KAAK,GAAG,IAAI,GAAG,SAAS,CAAC,CAAC;IACzD,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,MAAM,CAAC;IAC/C,8EAA8E;IAC9E,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,gCAAgC;IAChC,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;;;GAIG;AACH,MAAM,WAAW,UAAU;IAC1B,QAAQ,CAAC,OAAO,EAAE,iBAAiB,CAAC;IACpC,QAAQ,CAAC,OAAO,EAAE,CACjB,IAAI,EAAE,SAAS,EACf,OAAO,EAAE,wBAAwB,KAC7B,UAAU,CAAC;CAChB;AAED,kFAAkF;AAClF,MAAM,WAAW,wBAAwB;IACxC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,WAAW,CAAC,EAAE,iBAAiB,CAAC;IACzC,QAAQ,CAAC,YAAY,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IAC5C,QAAQ,CAAC,OAAO,CAAC,EAAE,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7C,QAAQ,CAAC,WAAW,EAAE,CACrB,IAAI,EAAE,SAAS,EACf,OAAO,EAAE,IAAI,CAAC,wBAAwB,EAAE,aAAa,CAAC,KAClD,UAAU,CAAC;CAChB;AAED,MAAM,WAAW,SAAS;IACzB,8FAA8F;IAC9F,QAAQ,CAAC,KAAK,CAAC,EAAE,aAAa,CAAC,SAAS,GAAG,KAAK,GAAG,IAAI,GAAG,SAAS,CAAC,CAAC;IACrE,QAAQ,CAAC,CAAC,CAAC,EAAE,yBAAyB,CAAC;IACvC,QAAQ,CAAC,CAAC,CAAC,EAAE,yBAAyB,CAAC;IACvC,QAAQ,CAAC,KAAK,CAAC,EAAE,iBAAiB,CAAC;IACnC,kEAAkE;IAClE,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC,WAAW,CAAC,CAAC;IACvC,QAAQ,CAAC,KAAK,CAAC,EAAE,eAAe,CAAC;IACjC,8FAA8F;IAC9F,QAAQ,CAAC,KAAK,CAAC,EAAE,UAAU,CAAC;IAC5B,0DAA0D;IAC1D,QAAQ,CAAC,UAAU,CAAC,EAAE,eAAe,CAAC;IACtC,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC,UAAU,CAAC,CAAC;CACrC;AAED,MAAM,WAAW,iBAAiB;IACjC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,eAAe;IAC/B,QAAQ,CAAC,KAAK,EAAE,CAAC,OAAO,EAAE,iBAAiB,KAAK,SAAS,CAAC;CAC1D;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CAC1B,IAAI,EAAE,SAAS,GAAG,CAAC,CAAC,OAAO,EAAE,iBAAiB,KAAK,SAAS,CAAC,GAC3D,eAAe,CAEjB"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/core/define-chart.ts"],
|
|
4
|
+
"sourcesContent": ["import type {\n\tChartFocusOrder,\n\tChartMargin,\n\tChartMark,\n\tChartPositionScaleKind,\n\tChartProjection,\n\tChartScene,\n\tChartTextMeasurer,\n\tChartTheme,\n\tChartValue,\n} from \"#/core/types.ts\";\n\nexport interface ChartAxisOptions {\n\t/** Axis title, data-table column heading and tooltip heading. */\n\treadonly label?: string;\n\t/** `false` hides the axis; the scale still places the marks. */\n\treadonly axis?: boolean;\n\treadonly grid?: boolean;\n\t/** A tick count to aim for, or the exact values to tick. */\n\treadonly ticks?: number | ReadonlyArray<ChartValue>;\n\treadonly tickFormat?: (value: ChartValue) => string;\n\t/** How the tooltip, live region and data table write a value of this channel. */\n\treadonly format?: (value: ChartValue) => string;\n}\n\nexport interface ChartPositionScaleOptions extends ChartAxisOptions {\n\treadonly type?: ChartPositionScaleKind;\n\treadonly domain?: ReadonlyArray<ChartValue>;\n\t/** Widen a continuous domain to round ticks. On by default for y. */\n\treadonly nice?: boolean;\n\t/** Keep zero on a continuous domain even when no mark asks for it. */\n\treadonly zero?: boolean;\n\treadonly reverse?: boolean;\n\treadonly paddingInner?: number;\n\treadonly paddingOuter?: number;\n}\n\nexport interface ChartColorOptions {\n\treadonly type?: \"ordinal\" | \"sequential\";\n\treadonly domain?: ReadonlyArray<ChartValue>;\n\t/** Palette for an ordinal scale, `[from, to]` for a sequential one. */\n\treadonly range?: ReadonlyArray<string>;\n\treadonly legend?: boolean;\n\t/** Display names of the series, by value. */\n\treadonly labels?: Readonly<Record<string, string>>;\n}\n\n/**\n * Small multiples: the same chart once per value, on a grid, every cell on\n * the same scales so the cells compare at a glance.\n */\nexport interface ChartFacetOptions {\n\treadonly values: ReadonlyArray<ChartValue>;\n\t/** The marks of one cell. */\n\treadonly marks: (\n\t\tvalue: ChartValue,\n\t\tindex: number,\n\t) => ReadonlyArray<ChartMark | false | null | undefined>;\n\treadonly label?: (value: ChartValue) => string;\n\t/** Cells per row. Defaults to three, or fewer when there are fewer values. */\n\treadonly columns?: number;\n\t/** Pixels between two cells. */\n\treadonly gap?: number;\n}\n\n/**\n * What `facet(\u2026)` returns: the options, and the compiler that lays the cells\n * out. Carried by the spec rather than imported by the core, so a chart\n * without facets never ships the facet code.\n */\nexport interface ChartFacet {\n\treadonly options: ChartFacetOptions;\n\treadonly compile: (\n\t\tspec: ChartSpec,\n\t\toptions: ChartFacetCompileOptions,\n\t) => ChartScene;\n}\n\n/** The compile options a facet compiler is handed, plus the per-cell compiler. */\nexport interface ChartFacetCompileOptions {\n\treadonly width: number;\n\treadonly height: number;\n\treadonly measureText?: ChartTextMeasurer;\n\treadonly hiddenSeries?: ReadonlySet<string>;\n\treadonly xDomain?: readonly [number, number];\n\treadonly compileCell: (\n\t\tspec: ChartSpec,\n\t\toptions: Omit<ChartFacetCompileOptions, \"compileCell\">,\n\t) => ChartScene;\n}\n\nexport interface ChartSpec {\n\t/** Falsy entries are skipped, so a mark can be switched on with `&&`. Unused with `facet`. */\n\treadonly marks?: ReadonlyArray<ChartMark | false | null | undefined>;\n\treadonly x?: ChartPositionScaleOptions;\n\treadonly y?: ChartPositionScaleOptions;\n\treadonly color?: ChartColorOptions;\n\t/** Overrides the margins the axes would reserve, side by side. */\n\treadonly margin?: Partial<ChartMargin>;\n\treadonly focus?: ChartFocusOrder;\n\t/** Small multiples, from `facet(\u2026)`. With it, `marks` is ignored: each cell takes its own. */\n\treadonly facet?: ChartFacet;\n\t/** For maps: how longitude and latitude become pixels. */\n\treadonly projection?: ChartProjection;\n\treadonly locale?: string;\n\treadonly theme?: Partial<ChartTheme>;\n}\n\nexport interface ChartBuildContext {\n\treadonly width: number;\n\treadonly height: number;\n}\n\nexport interface ChartDefinition {\n\treadonly build: (context: ChartBuildContext) => ChartSpec;\n}\n\n/**\n * A chart as data. Pass the spec when it does not depend on the size, or a\n * builder when it does (fewer ticks on a phone, a different layout below some\n * width). Build it in `useMemo`: the definition's identity is what tells the\n * renderer the data changed.\n */\nexport function defineChart(\n\tspec: ChartSpec | ((context: ChartBuildContext) => ChartSpec),\n): ChartDefinition {\n\treturn { build: typeof spec === \"function\" ? spec : () => spec };\n}\n"],
|
|
5
|
+
"mappings": "AA2HO,SAAS,YACf,MACkB;AAClB,SAAO,EAAE,OAAO,OAAO,SAAS,aAAa,OAAO,MAAM,KAAK;AAChE;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { ChartFacet, ChartFacetOptions } from "./define-chart.js";
|
|
2
|
+
/**
|
|
3
|
+
* Small multiples: the same chart once per value, on a grid, every cell on
|
|
4
|
+
* the same scales. Pass the result as the definition's `facet`.
|
|
5
|
+
*/
|
|
6
|
+
export declare function facet(options: ChartFacetOptions): ChartFacet;
|
|
7
|
+
//# sourceMappingURL=facet.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"facet.d.ts","sourceRoot":"","sources":["../../src/core/facet.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACX,UAAU,EAEV,iBAAiB,EAEjB,MAAM,wBAAwB,CAAC;AA4KhC;;;GAGG;AACH,wBAAgB,KAAK,CAAC,OAAO,EAAE,iBAAiB,GAAG,UAAU,CAM5D"}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import { channelsOf, isMark } from "./compile.js";
|
|
2
|
+
import { formatValue } from "./format.js";
|
|
3
|
+
import { categoryKey } from "./scales/discrete.js";
|
|
4
|
+
import { planScale } from "./scales/resolve.js";
|
|
5
|
+
import { DEFAULT_THEME } from "./theme.js";
|
|
6
|
+
const DEFAULT_COLUMNS = 3;
|
|
7
|
+
const DEFAULT_GAP = 16;
|
|
8
|
+
const TITLE_GAP = 6;
|
|
9
|
+
function shifted(hit, dx, dy) {
|
|
10
|
+
if (hit === void 0) {
|
|
11
|
+
return void 0;
|
|
12
|
+
}
|
|
13
|
+
return hit.kind === "rect" ? {
|
|
14
|
+
kind: "rect",
|
|
15
|
+
rect: { ...hit.rect, x: hit.rect.x + dx, y: hit.rect.y + dy }
|
|
16
|
+
} : { ...hit, cx: hit.cx + dx, cy: hit.cy + dy };
|
|
17
|
+
}
|
|
18
|
+
function compileFacets(spec, facet2, options) {
|
|
19
|
+
const { compileCell, ...cellOptions } = options;
|
|
20
|
+
const { width, height } = options;
|
|
21
|
+
const theme = { ...DEFAULT_THEME, ...spec.theme };
|
|
22
|
+
const values = facet2.values;
|
|
23
|
+
const columns = Math.max(
|
|
24
|
+
1,
|
|
25
|
+
Math.min(facet2.columns ?? DEFAULT_COLUMNS, values.length)
|
|
26
|
+
);
|
|
27
|
+
const rows = Math.max(1, Math.ceil(values.length / columns));
|
|
28
|
+
const gap = facet2.gap ?? DEFAULT_GAP;
|
|
29
|
+
const title = theme.fontSize + TITLE_GAP;
|
|
30
|
+
const cellWidth = (width - gap * (columns - 1)) / columns;
|
|
31
|
+
const cellHeight = (height - gap * (rows - 1)) / rows;
|
|
32
|
+
const label = (value) => facet2.label?.(value) ?? formatValue(value, spec.locale);
|
|
33
|
+
const cellMarks = values.map(
|
|
34
|
+
(value, index) => facet2.marks(value, index).filter(isMark)
|
|
35
|
+
);
|
|
36
|
+
const everyMark = cellMarks.flat();
|
|
37
|
+
const cartesian = everyMark.filter((mark) => mark.coordinate === "cartesian");
|
|
38
|
+
const sharedX = planScale(channelsOf(cartesian, "x"), spec.x, "x");
|
|
39
|
+
const sharedY = planScale(channelsOf(cartesian, "y"), spec.y, "y");
|
|
40
|
+
const colors = channelsOf(everyMark, "color").flatMap(
|
|
41
|
+
(channel) => channel.values
|
|
42
|
+
);
|
|
43
|
+
const numericColors = colors.length > 0 && colors.every((value) => typeof value === "number");
|
|
44
|
+
const colorDomain = spec.color?.domain ?? (colors.length === 0 ? void 0 : numericColors ? [Math.min(...colors), Math.max(...colors)] : [
|
|
45
|
+
...new Map(
|
|
46
|
+
colors.map((value) => [categoryKey(value), value])
|
|
47
|
+
).values()
|
|
48
|
+
]);
|
|
49
|
+
const nodes = [];
|
|
50
|
+
const points = [];
|
|
51
|
+
const legend = [];
|
|
52
|
+
const cells = [];
|
|
53
|
+
let first;
|
|
54
|
+
for (const [index, value] of values.entries()) {
|
|
55
|
+
const key = categoryKey(value);
|
|
56
|
+
const x = index % columns * (cellWidth + gap);
|
|
57
|
+
const y = Math.floor(index / columns) * (cellHeight + gap);
|
|
58
|
+
const cell = compileCell(
|
|
59
|
+
{
|
|
60
|
+
...spec,
|
|
61
|
+
facet: void 0,
|
|
62
|
+
marks: cellMarks[index],
|
|
63
|
+
x: sharedX ? { ...spec.x, domain: sharedX.domain, nice: false } : spec.x,
|
|
64
|
+
y: sharedY ? { ...spec.y, domain: sharedY.domain, nice: false } : spec.y,
|
|
65
|
+
color: colorDomain ? { ...spec.color, domain: colorDomain } : spec.color
|
|
66
|
+
},
|
|
67
|
+
{ ...cellOptions, width: cellWidth, height: cellHeight - title }
|
|
68
|
+
);
|
|
69
|
+
first ??= cell;
|
|
70
|
+
const dy = y + title;
|
|
71
|
+
nodes.push(
|
|
72
|
+
{
|
|
73
|
+
kind: "text",
|
|
74
|
+
key: `facet-title:${key}`,
|
|
75
|
+
role: "facet-title",
|
|
76
|
+
x: x + cell.plot.x,
|
|
77
|
+
y,
|
|
78
|
+
text: label(value),
|
|
79
|
+
paint: {
|
|
80
|
+
fill: theme.foreground,
|
|
81
|
+
fontSize: theme.fontSize,
|
|
82
|
+
fontWeight: 600,
|
|
83
|
+
baseline: "top"
|
|
84
|
+
}
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
kind: "group",
|
|
88
|
+
key: `facet:${key}`,
|
|
89
|
+
translate: { x, y: dy },
|
|
90
|
+
children: cell.nodes
|
|
91
|
+
}
|
|
92
|
+
);
|
|
93
|
+
for (const point of cell.points) {
|
|
94
|
+
points.push({
|
|
95
|
+
...point,
|
|
96
|
+
key: `${key}|${point.key}`,
|
|
97
|
+
x: point.x + x,
|
|
98
|
+
y: point.y + dy,
|
|
99
|
+
hit: shifted(point.hit, x, dy),
|
|
100
|
+
facet: key,
|
|
101
|
+
title: `${label(value)} \xB7 ${point.title}`
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
legend.push(...cell.legend);
|
|
105
|
+
cells.push({
|
|
106
|
+
key,
|
|
107
|
+
label: label(value),
|
|
108
|
+
plot: { ...cell.plot, x: cell.plot.x + x, y: cell.plot.y + dy }
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
const distinctLegend = [
|
|
112
|
+
...new Map(legend.map((item) => [item.key, item])).values()
|
|
113
|
+
];
|
|
114
|
+
return {
|
|
115
|
+
...first ?? compileCell({ ...spec, facet: void 0, marks: [] }, cellOptions),
|
|
116
|
+
width,
|
|
117
|
+
height,
|
|
118
|
+
plot: { x: 0, y: 0, width, height },
|
|
119
|
+
nodes,
|
|
120
|
+
points,
|
|
121
|
+
legend: distinctLegend,
|
|
122
|
+
cells
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
function facet(options) {
|
|
126
|
+
return {
|
|
127
|
+
options,
|
|
128
|
+
compile: (spec, compileOptions) => compileFacets(spec, options, compileOptions)
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
export {
|
|
132
|
+
facet
|
|
133
|
+
};
|
|
134
|
+
//# sourceMappingURL=facet.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/core/facet.ts"],
|
|
4
|
+
"sourcesContent": ["import { channelsOf, isMark } from \"#/core/compile.ts\";\nimport type {\n\tChartFacet,\n\tChartFacetCompileOptions,\n\tChartFacetOptions,\n\tChartSpec,\n} from \"#/core/define-chart.ts\";\nimport { formatValue } from \"#/core/format.ts\";\nimport { categoryKey } from \"#/core/scales/discrete.ts\";\nimport { planScale } from \"#/core/scales/resolve.ts\";\nimport { DEFAULT_THEME } from \"#/core/theme.ts\";\nimport type {\n\tChartHitShape,\n\tChartLegendItem,\n\tChartMark,\n\tChartPoint,\n\tChartScene,\n\tSceneNode,\n} from \"#/core/types.ts\";\n\nconst DEFAULT_COLUMNS = 3;\nconst DEFAULT_GAP = 16;\nconst TITLE_GAP = 6;\n\nfunction shifted(\n\thit: ChartHitShape | undefined,\n\tdx: number,\n\tdy: number,\n): ChartHitShape | undefined {\n\tif (hit === undefined) {\n\t\treturn undefined;\n\t}\n\treturn hit.kind === \"rect\"\n\t\t? {\n\t\t\t\tkind: \"rect\",\n\t\t\t\trect: { ...hit.rect, x: hit.rect.x + dx, y: hit.rect.y + dy },\n\t\t\t}\n\t\t: { ...hit, cx: hit.cx + dx, cy: hit.cy + dy };\n}\n\n/**\n * Small multiples. Every cell is compiled as a chart of its own, on x, y and\n * colour domains planned over all the cells together, then moved to its\n * place in the grid under a title. Points carry their cell, so the keyboard\n * walks one cell's values and the tooltip names the cell.\n */\nfunction compileFacets(\n\tspec: ChartSpec,\n\tfacet: ChartFacetOptions,\n\toptions: ChartFacetCompileOptions,\n): ChartScene {\n\tconst { compileCell, ...cellOptions } = options;\n\tconst { width, height } = options;\n\tconst theme = { ...DEFAULT_THEME, ...spec.theme };\n\tconst values = facet.values;\n\tconst columns = Math.max(\n\t\t1,\n\t\tMath.min(facet.columns ?? DEFAULT_COLUMNS, values.length),\n\t);\n\tconst rows = Math.max(1, Math.ceil(values.length / columns));\n\tconst gap = facet.gap ?? DEFAULT_GAP;\n\tconst title = theme.fontSize + TITLE_GAP;\n\tconst cellWidth = (width - gap * (columns - 1)) / columns;\n\tconst cellHeight = (height - gap * (rows - 1)) / rows;\n\tconst label = (value: (typeof values)[number]) =>\n\t\tfacet.label?.(value) ?? formatValue(value, spec.locale);\n\n\tconst cellMarks: ChartMark[][] = values.map((value, index) =>\n\t\tfacet.marks(value, index).filter(isMark),\n\t);\n\tconst everyMark = cellMarks.flat();\n\tconst cartesian = everyMark.filter((mark) => mark.coordinate === \"cartesian\");\n\tconst sharedX = planScale(channelsOf(cartesian, \"x\"), spec.x, \"x\");\n\tconst sharedY = planScale(channelsOf(cartesian, \"y\"), spec.y, \"y\");\n\tconst colors = channelsOf(everyMark, \"color\").flatMap(\n\t\t(channel) => channel.values,\n\t);\n\tconst numericColors =\n\t\tcolors.length > 0 && colors.every((value) => typeof value === \"number\");\n\tconst colorDomain =\n\t\tspec.color?.domain ??\n\t\t(colors.length === 0\n\t\t\t? undefined\n\t\t\t: numericColors\n\t\t\t\t? [Math.min(...(colors as number[])), Math.max(...(colors as number[]))]\n\t\t\t\t: [\n\t\t\t\t\t\t...new Map(\n\t\t\t\t\t\t\tcolors.map((value) => [categoryKey(value), value]),\n\t\t\t\t\t\t).values(),\n\t\t\t\t\t]);\n\n\tconst nodes: SceneNode[] = [];\n\tconst points: ChartPoint[] = [];\n\tconst legend: ChartLegendItem[] = [];\n\tconst cells: Array<{ key: string; label: string; plot: ChartScene[\"plot\"] }> =\n\t\t[];\n\tlet first: ChartScene | undefined;\n\n\tfor (const [index, value] of values.entries()) {\n\t\tconst key = categoryKey(value);\n\t\tconst x = (index % columns) * (cellWidth + gap);\n\t\tconst y = Math.floor(index / columns) * (cellHeight + gap);\n\t\tconst cell = compileCell(\n\t\t\t{\n\t\t\t\t...spec,\n\t\t\t\tfacet: undefined,\n\t\t\t\tmarks: cellMarks[index],\n\t\t\t\tx: sharedX\n\t\t\t\t\t? { ...spec.x, domain: sharedX.domain, nice: false }\n\t\t\t\t\t: spec.x,\n\t\t\t\ty: sharedY\n\t\t\t\t\t? { ...spec.y, domain: sharedY.domain, nice: false }\n\t\t\t\t\t: spec.y,\n\t\t\t\tcolor: colorDomain\n\t\t\t\t\t? { ...spec.color, domain: colorDomain }\n\t\t\t\t\t: spec.color,\n\t\t\t},\n\t\t\t{ ...cellOptions, width: cellWidth, height: cellHeight - title },\n\t\t);\n\t\tfirst ??= cell;\n\t\tconst dy = y + title;\n\t\tnodes.push(\n\t\t\t{\n\t\t\t\tkind: \"text\",\n\t\t\t\tkey: `facet-title:${key}`,\n\t\t\t\trole: \"facet-title\",\n\t\t\t\tx: x + cell.plot.x,\n\t\t\t\ty,\n\t\t\t\ttext: label(value),\n\t\t\t\tpaint: {\n\t\t\t\t\tfill: theme.foreground,\n\t\t\t\t\tfontSize: theme.fontSize,\n\t\t\t\t\tfontWeight: 600,\n\t\t\t\t\tbaseline: \"top\",\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tkind: \"group\",\n\t\t\t\tkey: `facet:${key}`,\n\t\t\t\ttranslate: { x, y: dy },\n\t\t\t\tchildren: cell.nodes,\n\t\t\t},\n\t\t);\n\t\tfor (const point of cell.points) {\n\t\t\tpoints.push({\n\t\t\t\t...point,\n\t\t\t\tkey: `${key}|${point.key}`,\n\t\t\t\tx: point.x + x,\n\t\t\t\ty: point.y + dy,\n\t\t\t\thit: shifted(point.hit, x, dy),\n\t\t\t\tfacet: key,\n\t\t\t\ttitle: `${label(value)} \u00B7 ${point.title}`,\n\t\t\t});\n\t\t}\n\t\tlegend.push(...cell.legend);\n\t\tcells.push({\n\t\t\tkey,\n\t\t\tlabel: label(value),\n\t\t\tplot: { ...cell.plot, x: cell.plot.x + x, y: cell.plot.y + dy },\n\t\t});\n\t}\n\n\tconst distinctLegend = [\n\t\t...new Map(legend.map((item) => [item.key, item])).values(),\n\t];\n\treturn {\n\t\t...(first ??\n\t\t\tcompileCell({ ...spec, facet: undefined, marks: [] }, cellOptions)),\n\t\twidth,\n\t\theight,\n\t\tplot: { x: 0, y: 0, width, height },\n\t\tnodes,\n\t\tpoints,\n\t\tlegend: distinctLegend,\n\t\tcells,\n\t};\n}\n\n/**\n * Small multiples: the same chart once per value, on a grid, every cell on\n * the same scales. Pass the result as the definition's `facet`.\n */\nexport function facet(options: ChartFacetOptions): ChartFacet {\n\treturn {\n\t\toptions,\n\t\tcompile: (spec, compileOptions) =>\n\t\t\tcompileFacets(spec, options, compileOptions),\n\t};\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["facet"]
|
|
7
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Number formatting for axes, labels and tooltips. The whole app writes its
|
|
3
|
+
* numbers in French, so that is the default here too — pass an explicit locale
|
|
4
|
+
* when a chart lives on a page that does not.
|
|
5
|
+
*/
|
|
6
|
+
export interface FormatNumberOptions {
|
|
7
|
+
readonly locale?: string;
|
|
8
|
+
readonly maximumFractionDigits?: number;
|
|
9
|
+
readonly minimumFractionDigits?: number;
|
|
10
|
+
}
|
|
11
|
+
export declare function formatNumber(value: number, options?: FormatNumberOptions): string;
|
|
12
|
+
/** "12 k", "3,4 M" — for axis ticks, where width is the scarce resource. */
|
|
13
|
+
export declare function formatCompactNumber(value: number, locale?: string): string;
|
|
14
|
+
export declare function formatPercentage(fraction: number, locale?: string): string;
|
|
15
|
+
/**
|
|
16
|
+
* What a value axis uses when the caller has no opinion: plain digits until the
|
|
17
|
+
* numbers get long, compact notation after that, and at most one decimal so a
|
|
18
|
+
* tick step of 0.5 still reads correctly.
|
|
19
|
+
*/
|
|
20
|
+
export declare function formatTickValue(value: number, locale?: string): string;
|
|
21
|
+
/** Renders an unknown datum field as a label without throwing on `null`. */
|
|
22
|
+
export declare function formatLabel(value: unknown): string;
|
|
23
|
+
/**
|
|
24
|
+
* A value as a tooltip, the live region or the data table says it: dates in
|
|
25
|
+
* full, numbers grouped, strings as they are.
|
|
26
|
+
*/
|
|
27
|
+
export declare function formatValue(value: unknown, locale?: string): string;
|
|
28
|
+
export declare function dateFormat(locale: string, options: Intl.DateTimeFormatOptions): Intl.DateTimeFormat;
|
|
29
|
+
/** A date written in full, for a tooltip or a table cell: "3 mars 2026", with the time when there is one. */
|
|
30
|
+
export declare function formatDate(value: Date, locale: string): string;
|
|
31
|
+
/**
|
|
32
|
+
* How to write the dates of one channel in full, judged on all of them: a
|
|
33
|
+
* series of month buckets reads "mars 2026", of years "2026", anything finer
|
|
34
|
+
* the full date.
|
|
35
|
+
*/
|
|
36
|
+
export declare function dateValueFormatter(values: ReadonlyArray<unknown>, locale: string): ((value: Date) => string) | undefined;
|
|
37
|
+
//# sourceMappingURL=format.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"format.d.ts","sourceRoot":"","sources":["../../src/core/format.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAsBH,MAAM,WAAW,mBAAmB;IACnC,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,qBAAqB,CAAC,EAAE,MAAM,CAAC;IACxC,QAAQ,CAAC,qBAAqB,CAAC,EAAE,MAAM,CAAC;CACxC;AAED,wBAAgB,YAAY,CAC3B,KAAK,EAAE,MAAM,EACb,OAAO,GAAE,mBAAwB,GAC/B,MAAM,CAGR;AAED,4EAA4E;AAC5E,wBAAgB,mBAAmB,CAClC,KAAK,EAAE,MAAM,EACb,MAAM,GAAE,MAAuB,GAC7B,MAAM,CAKR;AAED,wBAAgB,gBAAgB,CAC/B,QAAQ,EAAE,MAAM,EAChB,MAAM,GAAE,MAAuB,GAC7B,MAAM,CAKR;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAC9B,KAAK,EAAE,MAAM,EACb,MAAM,GAAE,MAAuB,GAC7B,MAAM,CAKR;AAED,4EAA4E;AAC5E,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAQlD;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAC1B,KAAK,EAAE,OAAO,EACd,MAAM,GAAE,MAAuB,GAC7B,MAAM,CAQR;AAID,wBAAgB,UAAU,CACzB,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,IAAI,CAAC,qBAAqB,GACjC,IAAI,CAAC,cAAc,CAYrB;AAID,6GAA6G;AAC7G,wBAAgB,UAAU,CAAC,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAS9D;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CACjC,MAAM,EAAE,aAAa,CAAC,OAAO,CAAC,EAC9B,MAAM,EAAE,MAAM,GACZ,CAAC,CAAC,KAAK,EAAE,IAAI,KAAK,MAAM,CAAC,GAAG,SAAS,CAiBvC"}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
const DEFAULT_LOCALE = "fr-FR";
|
|
2
|
+
const COMPACT_THRESHOLD = 1e4;
|
|
3
|
+
const formatterCache = /* @__PURE__ */ new Map();
|
|
4
|
+
function formatterFor(locale, options) {
|
|
5
|
+
const cacheKey = `${locale}:${JSON.stringify(options)}`;
|
|
6
|
+
const cached = formatterCache.get(cacheKey);
|
|
7
|
+
if (cached !== void 0) {
|
|
8
|
+
return cached;
|
|
9
|
+
}
|
|
10
|
+
const created = new Intl.NumberFormat(locale, options);
|
|
11
|
+
formatterCache.set(cacheKey, created);
|
|
12
|
+
return created;
|
|
13
|
+
}
|
|
14
|
+
function formatNumber(value, options = {}) {
|
|
15
|
+
const { locale = DEFAULT_LOCALE, ...digits } = options;
|
|
16
|
+
return formatterFor(locale, digits).format(value);
|
|
17
|
+
}
|
|
18
|
+
function formatCompactNumber(value, locale = DEFAULT_LOCALE) {
|
|
19
|
+
return formatterFor(locale, {
|
|
20
|
+
notation: "compact",
|
|
21
|
+
maximumFractionDigits: 1
|
|
22
|
+
}).format(value);
|
|
23
|
+
}
|
|
24
|
+
function formatPercentage(fraction, locale = DEFAULT_LOCALE) {
|
|
25
|
+
return formatterFor(locale, {
|
|
26
|
+
style: "percent",
|
|
27
|
+
maximumFractionDigits: 1
|
|
28
|
+
}).format(fraction);
|
|
29
|
+
}
|
|
30
|
+
function formatTickValue(value, locale = DEFAULT_LOCALE) {
|
|
31
|
+
if (Math.abs(value) >= COMPACT_THRESHOLD) {
|
|
32
|
+
return formatCompactNumber(value, locale);
|
|
33
|
+
}
|
|
34
|
+
return formatNumber(value, { locale, maximumFractionDigits: 2 });
|
|
35
|
+
}
|
|
36
|
+
function formatLabel(value) {
|
|
37
|
+
if (value === null || value === void 0) {
|
|
38
|
+
return "";
|
|
39
|
+
}
|
|
40
|
+
if (typeof value === "number") {
|
|
41
|
+
return formatTickValue(value);
|
|
42
|
+
}
|
|
43
|
+
return String(value);
|
|
44
|
+
}
|
|
45
|
+
function formatValue(value, locale = DEFAULT_LOCALE) {
|
|
46
|
+
if (value instanceof Date) {
|
|
47
|
+
return formatDate(value, locale);
|
|
48
|
+
}
|
|
49
|
+
if (typeof value === "number") {
|
|
50
|
+
return formatNumber(value, { locale, maximumFractionDigits: 2 });
|
|
51
|
+
}
|
|
52
|
+
return formatLabel(value);
|
|
53
|
+
}
|
|
54
|
+
const formatters = /* @__PURE__ */ new Map();
|
|
55
|
+
function dateFormat(locale, options) {
|
|
56
|
+
const key = `${locale}:${JSON.stringify(options)}`;
|
|
57
|
+
const cached = formatters.get(key);
|
|
58
|
+
if (cached !== void 0) {
|
|
59
|
+
return cached;
|
|
60
|
+
}
|
|
61
|
+
const created = new Intl.DateTimeFormat(locale, {
|
|
62
|
+
...options,
|
|
63
|
+
timeZone: "UTC"
|
|
64
|
+
});
|
|
65
|
+
formatters.set(key, created);
|
|
66
|
+
return created;
|
|
67
|
+
}
|
|
68
|
+
const MS_PER_DAY = 864e5;
|
|
69
|
+
function formatDate(value, locale) {
|
|
70
|
+
const time = value.getTime();
|
|
71
|
+
const hasTime = time % MS_PER_DAY !== 0;
|
|
72
|
+
return dateFormat(locale, {
|
|
73
|
+
day: "numeric",
|
|
74
|
+
month: "long",
|
|
75
|
+
year: "numeric",
|
|
76
|
+
...hasTime ? { hour: "2-digit", minute: "2-digit" } : {}
|
|
77
|
+
}).format(value);
|
|
78
|
+
}
|
|
79
|
+
function dateValueFormatter(values, locale) {
|
|
80
|
+
const dates = values.filter((value) => value instanceof Date);
|
|
81
|
+
if (dates.length === 0) {
|
|
82
|
+
return void 0;
|
|
83
|
+
}
|
|
84
|
+
const firstOfMonth = dates.every(
|
|
85
|
+
(date) => date.getTime() % MS_PER_DAY === 0 && date.getUTCDate() === 1
|
|
86
|
+
);
|
|
87
|
+
if (firstOfMonth && dates.every((date) => date.getUTCMonth() === 0)) {
|
|
88
|
+
const year = dateFormat(locale, { year: "numeric" });
|
|
89
|
+
return (value) => year.format(value);
|
|
90
|
+
}
|
|
91
|
+
if (firstOfMonth) {
|
|
92
|
+
const month = dateFormat(locale, { month: "long", year: "numeric" });
|
|
93
|
+
return (value) => month.format(value);
|
|
94
|
+
}
|
|
95
|
+
return void 0;
|
|
96
|
+
}
|
|
97
|
+
export {
|
|
98
|
+
dateFormat,
|
|
99
|
+
dateValueFormatter,
|
|
100
|
+
formatCompactNumber,
|
|
101
|
+
formatDate,
|
|
102
|
+
formatLabel,
|
|
103
|
+
formatNumber,
|
|
104
|
+
formatPercentage,
|
|
105
|
+
formatTickValue,
|
|
106
|
+
formatValue
|
|
107
|
+
};
|
|
108
|
+
//# sourceMappingURL=format.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/core/format.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * Number formatting for axes, labels and tooltips. The whole app writes its\n * numbers in French, so that is the default here too \u2014 pass an explicit locale\n * when a chart lives on a page that does not.\n */\n\nconst DEFAULT_LOCALE = \"fr-FR\";\n/** Above this magnitude an axis tick reads better as \"12 k\" than as \"12 000\". */\nconst COMPACT_THRESHOLD = 10_000;\n\nconst formatterCache = new Map<string, Intl.NumberFormat>();\n\nfunction formatterFor(\n\tlocale: string,\n\toptions: Intl.NumberFormatOptions,\n): Intl.NumberFormat {\n\tconst cacheKey = `${locale}:${JSON.stringify(options)}`;\n\tconst cached = formatterCache.get(cacheKey);\n\tif (cached !== undefined) {\n\t\treturn cached;\n\t}\n\tconst created = new Intl.NumberFormat(locale, options);\n\tformatterCache.set(cacheKey, created);\n\treturn created;\n}\n\nexport interface FormatNumberOptions {\n\treadonly locale?: string;\n\treadonly maximumFractionDigits?: number;\n\treadonly minimumFractionDigits?: number;\n}\n\nexport function formatNumber(\n\tvalue: number,\n\toptions: FormatNumberOptions = {},\n): string {\n\tconst { locale = DEFAULT_LOCALE, ...digits } = options;\n\treturn formatterFor(locale, digits).format(value);\n}\n\n/** \"12 k\", \"3,4 M\" \u2014 for axis ticks, where width is the scarce resource. */\nexport function formatCompactNumber(\n\tvalue: number,\n\tlocale: string = DEFAULT_LOCALE,\n): string {\n\treturn formatterFor(locale, {\n\t\tnotation: \"compact\",\n\t\tmaximumFractionDigits: 1,\n\t}).format(value);\n}\n\nexport function formatPercentage(\n\tfraction: number,\n\tlocale: string = DEFAULT_LOCALE,\n): string {\n\treturn formatterFor(locale, {\n\t\tstyle: \"percent\",\n\t\tmaximumFractionDigits: 1,\n\t}).format(fraction);\n}\n\n/**\n * What a value axis uses when the caller has no opinion: plain digits until the\n * numbers get long, compact notation after that, and at most one decimal so a\n * tick step of 0.5 still reads correctly.\n */\nexport function formatTickValue(\n\tvalue: number,\n\tlocale: string = DEFAULT_LOCALE,\n): string {\n\tif (Math.abs(value) >= COMPACT_THRESHOLD) {\n\t\treturn formatCompactNumber(value, locale);\n\t}\n\treturn formatNumber(value, { locale, maximumFractionDigits: 2 });\n}\n\n/** Renders an unknown datum field as a label without throwing on `null`. */\nexport function formatLabel(value: unknown): string {\n\tif (value === null || value === undefined) {\n\t\treturn \"\";\n\t}\n\tif (typeof value === \"number\") {\n\t\treturn formatTickValue(value);\n\t}\n\treturn String(value);\n}\n\n/**\n * A value as a tooltip, the live region or the data table says it: dates in\n * full, numbers grouped, strings as they are.\n */\nexport function formatValue(\n\tvalue: unknown,\n\tlocale: string = DEFAULT_LOCALE,\n): string {\n\tif (value instanceof Date) {\n\t\treturn formatDate(value, locale);\n\t}\n\tif (typeof value === \"number\") {\n\t\treturn formatNumber(value, { locale, maximumFractionDigits: 2 });\n\t}\n\treturn formatLabel(value);\n}\n\nconst formatters = new Map<string, Intl.DateTimeFormat>();\n\nexport function dateFormat(\n\tlocale: string,\n\toptions: Intl.DateTimeFormatOptions,\n): Intl.DateTimeFormat {\n\tconst key = `${locale}:${JSON.stringify(options)}`;\n\tconst cached = formatters.get(key);\n\tif (cached !== undefined) {\n\t\treturn cached;\n\t}\n\tconst created = new Intl.DateTimeFormat(locale, {\n\t\t...options,\n\t\ttimeZone: \"UTC\",\n\t});\n\tformatters.set(key, created);\n\treturn created;\n}\n\nconst MS_PER_DAY = 86_400_000;\n\n/** A date written in full, for a tooltip or a table cell: \"3 mars 2026\", with the time when there is one. */\nexport function formatDate(value: Date, locale: string): string {\n\tconst time = value.getTime();\n\tconst hasTime = time % MS_PER_DAY !== 0;\n\treturn dateFormat(locale, {\n\t\tday: \"numeric\",\n\t\tmonth: \"long\",\n\t\tyear: \"numeric\",\n\t\t...(hasTime ? { hour: \"2-digit\", minute: \"2-digit\" } : {}),\n\t}).format(value);\n}\n\n/**\n * How to write the dates of one channel in full, judged on all of them: a\n * series of month buckets reads \"mars 2026\", of years \"2026\", anything finer\n * the full date.\n */\nexport function dateValueFormatter(\n\tvalues: ReadonlyArray<unknown>,\n\tlocale: string,\n): ((value: Date) => string) | undefined {\n\tconst dates = values.filter((value): value is Date => value instanceof Date);\n\tif (dates.length === 0) {\n\t\treturn undefined;\n\t}\n\tconst firstOfMonth = dates.every(\n\t\t(date) => date.getTime() % MS_PER_DAY === 0 && date.getUTCDate() === 1,\n\t);\n\tif (firstOfMonth && dates.every((date) => date.getUTCMonth() === 0)) {\n\t\tconst year = dateFormat(locale, { year: \"numeric\" });\n\t\treturn (value) => year.format(value);\n\t}\n\tif (firstOfMonth) {\n\t\tconst month = dateFormat(locale, { month: \"long\", year: \"numeric\" });\n\t\treturn (value) => month.format(value);\n\t}\n\treturn undefined;\n}\n"],
|
|
5
|
+
"mappings": "AAMA,MAAM,iBAAiB;AAEvB,MAAM,oBAAoB;AAE1B,MAAM,iBAAiB,oBAAI,IAA+B;AAE1D,SAAS,aACR,QACA,SACoB;AACpB,QAAM,WAAW,GAAG,MAAM,IAAI,KAAK,UAAU,OAAO,CAAC;AACrD,QAAM,SAAS,eAAe,IAAI,QAAQ;AAC1C,MAAI,WAAW,QAAW;AACzB,WAAO;AAAA,EACR;AACA,QAAM,UAAU,IAAI,KAAK,aAAa,QAAQ,OAAO;AACrD,iBAAe,IAAI,UAAU,OAAO;AACpC,SAAO;AACR;AAQO,SAAS,aACf,OACA,UAA+B,CAAC,GACvB;AACT,QAAM,EAAE,SAAS,gBAAgB,GAAG,OAAO,IAAI;AAC/C,SAAO,aAAa,QAAQ,MAAM,EAAE,OAAO,KAAK;AACjD;AAGO,SAAS,oBACf,OACA,SAAiB,gBACR;AACT,SAAO,aAAa,QAAQ;AAAA,IAC3B,UAAU;AAAA,IACV,uBAAuB;AAAA,EACxB,CAAC,EAAE,OAAO,KAAK;AAChB;AAEO,SAAS,iBACf,UACA,SAAiB,gBACR;AACT,SAAO,aAAa,QAAQ;AAAA,IAC3B,OAAO;AAAA,IACP,uBAAuB;AAAA,EACxB,CAAC,EAAE,OAAO,QAAQ;AACnB;AAOO,SAAS,gBACf,OACA,SAAiB,gBACR;AACT,MAAI,KAAK,IAAI,KAAK,KAAK,mBAAmB;AACzC,WAAO,oBAAoB,OAAO,MAAM;AAAA,EACzC;AACA,SAAO,aAAa,OAAO,EAAE,QAAQ,uBAAuB,EAAE,CAAC;AAChE;AAGO,SAAS,YAAY,OAAwB;AACnD,MAAI,UAAU,QAAQ,UAAU,QAAW;AAC1C,WAAO;AAAA,EACR;AACA,MAAI,OAAO,UAAU,UAAU;AAC9B,WAAO,gBAAgB,KAAK;AAAA,EAC7B;AACA,SAAO,OAAO,KAAK;AACpB;AAMO,SAAS,YACf,OACA,SAAiB,gBACR;AACT,MAAI,iBAAiB,MAAM;AAC1B,WAAO,WAAW,OAAO,MAAM;AAAA,EAChC;AACA,MAAI,OAAO,UAAU,UAAU;AAC9B,WAAO,aAAa,OAAO,EAAE,QAAQ,uBAAuB,EAAE,CAAC;AAAA,EAChE;AACA,SAAO,YAAY,KAAK;AACzB;AAEA,MAAM,aAAa,oBAAI,IAAiC;AAEjD,SAAS,WACf,QACA,SACsB;AACtB,QAAM,MAAM,GAAG,MAAM,IAAI,KAAK,UAAU,OAAO,CAAC;AAChD,QAAM,SAAS,WAAW,IAAI,GAAG;AACjC,MAAI,WAAW,QAAW;AACzB,WAAO;AAAA,EACR;AACA,QAAM,UAAU,IAAI,KAAK,eAAe,QAAQ;AAAA,IAC/C,GAAG;AAAA,IACH,UAAU;AAAA,EACX,CAAC;AACD,aAAW,IAAI,KAAK,OAAO;AAC3B,SAAO;AACR;AAEA,MAAM,aAAa;AAGZ,SAAS,WAAW,OAAa,QAAwB;AAC/D,QAAM,OAAO,MAAM,QAAQ;AAC3B,QAAM,UAAU,OAAO,eAAe;AACtC,SAAO,WAAW,QAAQ;AAAA,IACzB,KAAK;AAAA,IACL,OAAO;AAAA,IACP,MAAM;AAAA,IACN,GAAI,UAAU,EAAE,MAAM,WAAW,QAAQ,UAAU,IAAI,CAAC;AAAA,EACzD,CAAC,EAAE,OAAO,KAAK;AAChB;AAOO,SAAS,mBACf,QACA,QACwC;AACxC,QAAM,QAAQ,OAAO,OAAO,CAAC,UAAyB,iBAAiB,IAAI;AAC3E,MAAI,MAAM,WAAW,GAAG;AACvB,WAAO;AAAA,EACR;AACA,QAAM,eAAe,MAAM;AAAA,IAC1B,CAAC,SAAS,KAAK,QAAQ,IAAI,eAAe,KAAK,KAAK,WAAW,MAAM;AAAA,EACtE;AACA,MAAI,gBAAgB,MAAM,MAAM,CAAC,SAAS,KAAK,YAAY,MAAM,CAAC,GAAG;AACpE,UAAM,OAAO,WAAW,QAAQ,EAAE,MAAM,UAAU,CAAC;AACnD,WAAO,CAAC,UAAU,KAAK,OAAO,KAAK;AAAA,EACpC;AACA,MAAI,cAAc;AACjB,UAAM,QAAQ,WAAW,QAAQ,EAAE,OAAO,QAAQ,MAAM,UAAU,CAAC;AACnE,WAAO,CAAC,UAAU,MAAM,OAAO,KAAK;AAAA,EACrC;AACA,SAAO;AACR;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { ChartAxisOptions } from "../define-chart.js";
|
|
2
|
+
import type { ChartPositionScale, ChartRect, ChartTextMeasurer, ChartTheme, ChartValue, SceneNode } from "../types.js";
|
|
3
|
+
export interface AxisMeasureInput {
|
|
4
|
+
readonly scale: ChartPositionScale;
|
|
5
|
+
readonly options: ChartAxisOptions | undefined;
|
|
6
|
+
readonly title: string | undefined;
|
|
7
|
+
readonly theme: ChartTheme;
|
|
8
|
+
readonly measureText: ChartTextMeasurer;
|
|
9
|
+
}
|
|
10
|
+
export declare function axisTicks(scale: ChartPositionScale, options: ChartAxisOptions | undefined): ReadonlyArray<ChartValue>;
|
|
11
|
+
export declare function tickLabel(scale: ChartPositionScale, options: ChartAxisOptions | undefined, value: ChartValue): string;
|
|
12
|
+
/** Cuts a label down to `maxWidth`, ending on an ellipsis. */
|
|
13
|
+
export declare function truncate(text: string, maxWidth: number, fontSize: number, measureText: ChartTextMeasurer): string;
|
|
14
|
+
/** Room the left axis needs: its widest tick label, the gap, and the title. */
|
|
15
|
+
export declare function leftMargin(input: AxisMeasureInput, chartWidth: number): number;
|
|
16
|
+
export declare function bottomMargin(input: AxisMeasureInput): number;
|
|
17
|
+
/** Half the last x label hangs past the plot's right edge; leave it room. */
|
|
18
|
+
export declare function rightMargin(input: AxisMeasureInput): number;
|
|
19
|
+
export interface AxisNodes {
|
|
20
|
+
readonly grid: ReadonlyArray<SceneNode>;
|
|
21
|
+
readonly labels: ReadonlyArray<SceneNode>;
|
|
22
|
+
}
|
|
23
|
+
export declare function bottomAxis(input: AxisMeasureInput, plot: ChartRect, grid: boolean): AxisNodes;
|
|
24
|
+
export declare function leftAxis(input: AxisMeasureInput, plot: ChartRect, grid: boolean, chartWidth: number): AxisNodes;
|
|
25
|
+
//# sourceMappingURL=axes.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"axes.d.ts","sourceRoot":"","sources":["../../../src/core/guides/axes.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC/D,OAAO,KAAK,EACX,kBAAkB,EAClB,SAAS,EACT,iBAAiB,EACjB,UAAU,EACV,UAAU,EACV,SAAS,EACT,MAAM,iBAAiB,CAAC;AAgBzB,MAAM,WAAW,gBAAgB;IAChC,QAAQ,CAAC,KAAK,EAAE,kBAAkB,CAAC;IACnC,QAAQ,CAAC,OAAO,EAAE,gBAAgB,GAAG,SAAS,CAAC;IAC/C,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,CAAC;IACnC,QAAQ,CAAC,KAAK,EAAE,UAAU,CAAC;IAC3B,QAAQ,CAAC,WAAW,EAAE,iBAAiB,CAAC;CACxC;AAED,wBAAgB,SAAS,CACxB,KAAK,EAAE,kBAAkB,EACzB,OAAO,EAAE,gBAAgB,GAAG,SAAS,GACnC,aAAa,CAAC,UAAU,CAAC,CAM3B;AAED,wBAAgB,SAAS,CACxB,KAAK,EAAE,kBAAkB,EACzB,OAAO,EAAE,gBAAgB,GAAG,SAAS,EACrC,KAAK,EAAE,UAAU,GACf,MAAM,CAER;AAED,8DAA8D;AAC9D,wBAAgB,QAAQ,CACvB,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,EAChB,WAAW,EAAE,iBAAiB,GAC5B,MAAM,CASR;AAMD,+EAA+E;AAC/E,wBAAgB,UAAU,CACzB,KAAK,EAAE,gBAAgB,EACvB,UAAU,EAAE,MAAM,GAChB,MAAM,CAgBR;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,gBAAgB,GAAG,MAAM,CAW5D;AAED,6EAA6E;AAC7E,wBAAgB,WAAW,CAAC,KAAK,EAAE,gBAAgB,GAAG,MAAM,CAa3D;AAED,MAAM,WAAW,SAAS;IACzB,QAAQ,CAAC,IAAI,EAAE,aAAa,CAAC,SAAS,CAAC,CAAC;IACxC,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAC,SAAS,CAAC,CAAC;CAC1C;AAsBD,wBAAgB,UAAU,CACzB,KAAK,EAAE,gBAAgB,EACvB,IAAI,EAAE,SAAS,EACf,IAAI,EAAE,OAAO,GACX,SAAS,CAwDX;AAED,wBAAgB,QAAQ,CACvB,KAAK,EAAE,gBAAgB,EACvB,IAAI,EAAE,SAAS,EACf,IAAI,EAAE,OAAO,EACb,UAAU,EAAE,MAAM,GAChB,SAAS,CAmEX"}
|