nuxt-charts 3.0.0 → 3.1.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.
Files changed (49) hide show
  1. package/README.md +53 -6
  2. package/dist/module.json +1 -1
  3. package/dist/module.mjs +56 -3
  4. package/dist/runtime/assets/components.css +1 -1
  5. package/dist/runtime/assets/theme.css +1 -1
  6. package/dist/runtime/components/AreaChart.vue +1 -0
  7. package/dist/runtime/components/BarChart.vue +1 -0
  8. package/dist/runtime/components/DataChart.vue +91 -0
  9. package/dist/runtime/components/DataChart.vue.d.ts +27 -0
  10. package/dist/runtime/components/DottedMap.vue +261 -0
  11. package/dist/runtime/components/DottedMap.vue.d.ts +45 -0
  12. package/dist/runtime/components/LineChart.vue +1 -0
  13. package/dist/runtime/components/MetricChart.vue +322 -0
  14. package/dist/runtime/components/MetricChart.vue.d.ts +54 -0
  15. package/dist/runtime/components/TopoJSONMap.vue +273 -0
  16. package/dist/runtime/components/TopoJSONMap.vue.d.ts +45 -0
  17. package/dist/runtime/components/internal/CartesianFrame.vue +73 -4
  18. package/dist/runtime/components/internal/CartesianFrame.vue.d.ts +5 -0
  19. package/dist/runtime/components/internal/ChartAccessibility.vue.d.ts +1 -1
  20. package/dist/runtime/components/internal/CubeBarShape.vue.d.ts +1 -1
  21. package/dist/runtime/components/internal/MapCanvas.vue +297 -0
  22. package/dist/runtime/components/internal/MapCanvas.vue.d.ts +79 -0
  23. package/dist/runtime/components/internal/VariantBarShape.vue.d.ts +2 -2
  24. package/dist/runtime/data/world.json +182 -0
  25. package/dist/runtime/maps.d.ts +2 -0
  26. package/dist/runtime/maps.js +1 -0
  27. package/dist/runtime/spec/csv.d.ts +14 -0
  28. package/dist/runtime/spec/csv.js +13 -0
  29. package/dist/runtime/spec/index.d.ts +11 -0
  30. package/dist/runtime/spec/index.js +5 -0
  31. package/dist/runtime/spec/metric.d.ts +130 -0
  32. package/dist/runtime/spec/metric.js +63 -0
  33. package/dist/runtime/spec/resolve.d.ts +44 -0
  34. package/dist/runtime/spec/resolve.js +41 -0
  35. package/dist/runtime/spec/transform.d.ts +58 -0
  36. package/dist/runtime/spec/transform.js +199 -0
  37. package/dist/runtime/spec/types.d.ts +132 -0
  38. package/dist/runtime/spec/types.js +2 -0
  39. package/dist/runtime/types/charts.d.ts +131 -0
  40. package/dist/runtime/types/maps.d.ts +237 -0
  41. package/dist/runtime/types/maps.js +0 -0
  42. package/dist/runtime/types.d.ts +3 -0
  43. package/dist/runtime/utils/axis.d.ts +18 -0
  44. package/dist/runtime/utils/axis.js +44 -1
  45. package/dist/runtime/utils/dottedMap.d.ts +14 -0
  46. package/dist/runtime/utils/dottedMap.js +69 -0
  47. package/dist/runtime/utils/maps.d.ts +33 -0
  48. package/dist/runtime/utils/maps.js +77 -0
  49. package/package.json +23 -3
@@ -0,0 +1,132 @@
1
+ /**
2
+ * The chart specification: a fully serializable description of what to plot.
3
+ *
4
+ * This is the layer between "application data" and "a rendered chart". A spec
5
+ * says *what* to show — which fields, how to bucket them, how to aggregate,
6
+ * what to compare against — and never *how* to draw it. Everything here is
7
+ * plain JSON: no functions, no class instances, no `Date` objects. That is a
8
+ * deliberate constraint, not an accident of the current implementation.
9
+ *
10
+ * Keeping it JSON means a spec can cross a boundary: stored in a database,
11
+ * sent over the wire, written by hand in a config file, or produced by a model
12
+ * from a natural-language question. Formatting concerns that genuinely need a
13
+ * function (custom tick formatters, tooltip templates) stay on the rendering
14
+ * props, so the spec itself survives `JSON.stringify` / `JSON.parse` intact.
15
+ */
16
+ /** Chart forms a spec can request. Deliberately small for the first version. */
17
+ export type SpecChartType = "line" | "area" | "bar";
18
+ /**
19
+ * How rows sharing an x bucket are collapsed into one value.
20
+ *
21
+ * `count` ignores the y field entirely and counts rows, which is why the
22
+ * dimension's `y` is optional when counting.
23
+ */
24
+ export type SpecAggregate = "sum" | "average" | "count" | "min" | "max";
25
+ /** Calendar bucket width for a temporal x dimension. */
26
+ export type SpecInterval = "day" | "week" | "month";
27
+ /**
28
+ * A baseline to plot alongside the current period.
29
+ *
30
+ * `previous-period` shifts the window back by its own length (the 30 days
31
+ * before the visible 30 days); `previous-year` shifts it back one calendar
32
+ * year, which is the right comparison for seasonal data.
33
+ */
34
+ export type SpecCompare = "previous-period" | "previous-year";
35
+ /** Which row fields feed the axes. */
36
+ export interface SpecDimensions {
37
+ /** Field plotted on the category (x) axis. */
38
+ x: string;
39
+ /**
40
+ * Field aggregated onto the value (y) axis. Optional only for `count`,
41
+ * which measures rows rather than a field.
42
+ */
43
+ y?: string;
44
+ /**
45
+ * Field whose distinct values split the data into separate series.
46
+ *
47
+ * Each distinct value becomes its own line/area/bar group, aggregated
48
+ * independently. Cannot be combined with `compare` — both want to own the
49
+ * series dimension, and stacking them produces a chart nobody can read.
50
+ */
51
+ series?: string;
52
+ }
53
+ /** How raw rows are reshaped before they reach the renderer. */
54
+ export interface SpecTransform {
55
+ /**
56
+ * Bucket a temporal x dimension to this calendar width. Omit to treat the
57
+ * x values as opaque categories and group on exact equality.
58
+ */
59
+ interval?: SpecInterval;
60
+ /** How rows in a bucket are collapsed. Default `sum`. */
61
+ aggregate?: SpecAggregate;
62
+ /**
63
+ * Sort order of the resulting buckets. Temporal data defaults to `asc`
64
+ * (time reads left to right); categorical data defaults to `none`, which
65
+ * preserves first-seen order from the source rows.
66
+ */
67
+ sort?: "asc" | "desc" | "none";
68
+ /**
69
+ * Keep only the N largest buckets by value. Applied after aggregation, and
70
+ * meaningful mainly for categorical x dimensions.
71
+ */
72
+ limit?: number;
73
+ /**
74
+ * Emit buckets with no matching rows as zero instead of omitting them.
75
+ * Only possible with an `interval`, which defines what the missing buckets
76
+ * would have been. Default `true` — a gap in a time series is information,
77
+ * and silently closing it misrepresents the trend.
78
+ */
79
+ fillGaps?: boolean;
80
+ }
81
+ /**
82
+ * A complete, serializable chart description.
83
+ *
84
+ * @example
85
+ * ```ts
86
+ * const spec: ChartSpec = {
87
+ * type: "line",
88
+ * dimensions: { x: "createdAt", y: "amount" },
89
+ * transform: { interval: "month", aggregate: "sum" },
90
+ * compare: "previous-period",
91
+ * };
92
+ * ```
93
+ */
94
+ export interface ChartSpec {
95
+ type: SpecChartType;
96
+ dimensions: SpecDimensions;
97
+ transform?: SpecTransform;
98
+ /** Plot a shifted copy of the window as a second, muted series. */
99
+ compare?: SpecCompare;
100
+ /**
101
+ * Restrict rows to a closed date window before aggregating, as ISO date
102
+ * strings. Required for `compare`: without a window there is no "period" to
103
+ * step back from.
104
+ */
105
+ range?: {
106
+ from: string;
107
+ to: string;
108
+ };
109
+ /** Display label for the primary series. Defaults to the `y` field name. */
110
+ label?: string;
111
+ }
112
+ /**
113
+ * One aggregated row produced by the transform, ready for the renderer.
114
+ *
115
+ * The x bucket is carried under a dedicated `x` key so the series values can
116
+ * stay purely numeric. Typing the index signature as `string | number` instead
117
+ * would make every key non-numeric as far as the renderers' `NumericKeys<T>`
118
+ * constraint is concerned, and no series could be plotted.
119
+ *
120
+ * `undefined` is a deliberate member: with `fillGaps` off, a bucket a series
121
+ * has no rows for is a hole in the line rather than a zero.
122
+ */
123
+ export interface SpecDatum {
124
+ /** Bucket label for the x axis. */
125
+ x: string;
126
+ /** Aggregated values, keyed by series name. */
127
+ [series: string]: string | number | undefined;
128
+ }
129
+ /** Series key used for the shifted comparison series. */
130
+ export declare const COMPARE_KEY = "compare";
131
+ /** Series key used when a spec has neither a `series` split nor a compare. */
132
+ export declare const VALUE_KEY = "value";
@@ -0,0 +1,2 @@
1
+ export const COMPARE_KEY = "compare";
2
+ export const VALUE_KEY = "value";
@@ -13,6 +13,8 @@ import type { CurveType, DonutType, LegendPosition, Orientation } from "../enums
13
13
  import type { DitherVariant } from "../utils/dither.js";
14
14
  import type { StrokeGradientStop } from "../utils/gradient.js";
15
15
  import type { BackgroundVariant } from "../utils/background.js";
16
+ import type { ChartSpec, SpecAggregate, SpecChartType, SpecCompare, SpecInterval } from "../spec/types.js";
17
+ import type { MetricFormat, MetricRange } from "../spec/metric.js";
16
18
  import type { AreaFillVariant, BarVariant, DotVariant, LegendIndicatorVariant, RadarVariant, RadialVariant, StrokeVariant, TooltipRoundness, TooltipVariant } from "../utils/variants.js";
17
19
  import type { AxisConfig, AxisId, BulletLegendItemInterface, ChartPadding, ChartTheme, CrosshairConfig, MarkerConfig, YAxisConfig, axisFormatter } from "./shared.js";
18
20
  /** Keys whose declared value is numeric (nullable/optional numeric fields included). */
@@ -830,3 +832,132 @@ export interface SankeyChartProps<N extends SankeyInputNode = SankeyInputNode, L
830
832
  /** Animation duration in milliseconds. */
831
833
  duration?: number;
832
834
  }
835
+ /**
836
+ * Props for `<DataChart>`, the data-aware primitive.
837
+ *
838
+ * Unlike the other chart interfaces, this one takes *raw* rows plus a
839
+ * {@link ChartSpec} rather than pre-aggregated data and a `categories` map —
840
+ * the spec is what decides which fields become series and buckets.
841
+ */
842
+ export interface DataChartProps<T> extends ChartStyleProps {
843
+ /** Raw application rows. Aggregated according to `spec`. */
844
+ data: T[];
845
+ /** What to plot. Fully serializable — see {@link ChartSpec}. */
846
+ spec: ChartSpec;
847
+ /** Chart height in pixels. Default 260. */
848
+ height?: number;
849
+ /** Locale for axis bucket labels. Defaults to the browser's. */
850
+ locale?: string;
851
+ /** Hide the legend. */
852
+ hideLegend?: boolean;
853
+ /** Stack series instead of overlaying / grouping them. */
854
+ stacked?: boolean;
855
+ /** Per-chart appearance overrides, forwarded to the renderer. */
856
+ theme?: ChartTheme;
857
+ /**
858
+ * Formats y-axis ticks. A function, so it deliberately lives on the props
859
+ * rather than in the spec — keeping the spec itself JSON-serializable.
860
+ */
861
+ valueFormatter?: (value: number) => string;
862
+ }
863
+ /**
864
+ * Props for `<MetricChart>`, the analytics component.
865
+ *
866
+ * Where {@link DataChartProps} takes a spec and renders it, this one takes the
867
+ * *question* — which field, aggregated how, over what window, compared against
868
+ * what — and assembles the spec itself. The extra props over `DataChart` are
869
+ * all about the chrome a metric needs around the plot: a headline value, a
870
+ * delta, a range selector, and the export affordances.
871
+ */
872
+ export interface MetricChartProps<T> extends ChartStyleProps {
873
+ /** Raw application rows. Aggregated according to the props below. */
874
+ data: T[];
875
+ /** Metric name, rendered as the card's heading. */
876
+ title?: string;
877
+ /** Optional supporting line under the title. */
878
+ description?: string;
879
+ /** Field plotted on the x axis. Must hold dates for a temporal metric. */
880
+ x: string;
881
+ /** Field aggregated onto the y axis. Optional only for `count`. */
882
+ y?: string;
883
+ /**
884
+ * Field whose distinct values split the plot into separate series.
885
+ *
886
+ * Mutually exclusive with `compare`: both claim the series dimension, so
887
+ * asking for a split suppresses the comparison rather than stacking them.
888
+ */
889
+ series?: string;
890
+ /** How rows sharing a bucket are collapsed. Default `sum`. */
891
+ aggregate?: SpecAggregate;
892
+ /**
893
+ * Bucket width. Omit to let the selected range choose one that keeps the
894
+ * number of marks readable.
895
+ */
896
+ interval?: SpecInterval;
897
+ /** Plot a shifted baseline and compute the delta against it. */
898
+ compare?: SpecCompare;
899
+ /** Chart form. Default `line`. */
900
+ type?: SpecChartType;
901
+ /** Number rendering for the headline, axis and tooltip. Default `number`. */
902
+ format?: MetricFormat;
903
+ /** ISO 4217 code for the `currency` format. Default `EUR`. */
904
+ currency?: string;
905
+ /** Locale for every number and date. Defaults to the browser's. */
906
+ locale?: string;
907
+ /** Extra `Intl.NumberFormat` options, merged over the format's defaults. */
908
+ formatOptions?: Intl.NumberFormatOptions;
909
+ /**
910
+ * Full control over the headline and axis text, bypassing `format`.
911
+ *
912
+ * A function, so it stays on the props rather than in the spec.
913
+ */
914
+ valueFormatter?: (value: number) => string;
915
+ /**
916
+ * Treat a fall as the good outcome, so the delta colours invert.
917
+ *
918
+ * The right default for latency, error rates, churn and cost — anything
919
+ * where "down" is the win.
920
+ */
921
+ inverseSentiment?: boolean;
922
+ /**
923
+ * Selectable windows. Default 7D / 30D / 3M / 1Y.
924
+ *
925
+ * Pass an empty array to hide the selector and plot `range` alone.
926
+ */
927
+ ranges?: readonly MetricRange[];
928
+ /** Selected range key. Use `v-model:range` to control it. */
929
+ range?: string;
930
+ /**
931
+ * Explicit window, overriding the range selector's arithmetic.
932
+ *
933
+ * Use when the window comes from elsewhere on the page (a shared date
934
+ * picker) rather than from this component's own buttons.
935
+ */
936
+ window?: {
937
+ from: string;
938
+ to: string;
939
+ };
940
+ /**
941
+ * Date the windows count back from. Defaults to now.
942
+ *
943
+ * Pinning it makes a demo, a screenshot or a test render the same chart
944
+ * every time instead of drifting with the clock.
945
+ */
946
+ anchor?: string | number | Date;
947
+ /** Chart height in pixels. Default 220. */
948
+ height?: number;
949
+ /** Stack series instead of overlaying them. */
950
+ stacked?: boolean;
951
+ /** Hide the chart's own legend. */
952
+ hideLegend?: boolean;
953
+ /** Per-chart appearance overrides, forwarded to the renderer. */
954
+ theme?: ChartTheme;
955
+ /** Offer a toggle that swaps the chart for its data table. */
956
+ showTable?: boolean;
957
+ /** Offer a CSV download of the aggregated rows. */
958
+ exportable?: boolean;
959
+ /** Filename for the CSV download. Defaults to a slug of the title. */
960
+ exportFilename?: string;
961
+ /** Hide the headline value and delta, leaving the title and the plot. */
962
+ hideValue?: boolean;
963
+ }
@@ -0,0 +1,237 @@
1
+ import type { GeoProjection } from "d3-geo";
2
+ import type { Feature, FeatureCollection, Geometry } from "geojson";
3
+ import type { Topology, GeometryObject } from "topojson-specification";
4
+ import type { LegendPosition } from "../enums.js";
5
+ import type { ChartStyleProps } from "./charts.js";
6
+ import type { BulletLegendItemInterface, ChartPadding, ChartTheme, axisFormatter } from "./shared.js";
7
+ export type MapAccessor<T, V> = V | ((datum: T, index: number) => V);
8
+ export type MapProjectionName = "mercator" | "equalEarth" | "naturalEarth" | "equirectangular" | "orthographic" | "albersUsa";
9
+ /** Pass a projection name, a configured D3 projection, or a factory returning one. */
10
+ export type MapProjection = MapProjectionName | GeoProjection | (() => GeoProjection);
11
+ export type MapGeometry = FeatureCollection | Feature | Geometry | Topology<Record<string, GeometryObject>>;
12
+ export type MapRegionName = "world" | "europe" | "asia" | "oceania" | "usa";
13
+ export interface MapRegion {
14
+ lat: {
15
+ min: number;
16
+ max: number;
17
+ };
18
+ lng: {
19
+ min: number;
20
+ max: number;
21
+ };
22
+ }
23
+ export interface MapPoint {
24
+ id: string | number;
25
+ latitude: number;
26
+ longitude: number;
27
+ label?: string;
28
+ color?: string;
29
+ radius?: number;
30
+ [key: string]: unknown;
31
+ }
32
+ export interface MapLink<P extends MapPoint = MapPoint> {
33
+ source: string | number | P;
34
+ target: string | number | P;
35
+ color?: string;
36
+ width?: number;
37
+ label?: string;
38
+ [key: string]: unknown;
39
+ }
40
+ export interface MapArea {
41
+ id: string | number;
42
+ color?: string;
43
+ value?: number;
44
+ [key: string]: unknown;
45
+ }
46
+ export interface MapData<A extends MapArea = MapArea, P extends MapPoint = MapPoint, L extends MapLink<P> = MapLink<P>> {
47
+ areas?: A[];
48
+ points?: P[];
49
+ links?: L[];
50
+ }
51
+ /** Area accessors and tooltips receive joined row data and the original feature. */
52
+ export type MapFeature<A extends MapArea = MapArea> = A & {
53
+ properties: Record<string, unknown>;
54
+ feature: Feature;
55
+ };
56
+ export interface MapPin {
57
+ lat: number;
58
+ lng: number;
59
+ id?: string | number;
60
+ label?: string;
61
+ color?: string;
62
+ radius?: number;
63
+ svgOptions?: {
64
+ color?: string;
65
+ radius?: number;
66
+ strokeColor?: string;
67
+ strokeWidth?: number;
68
+ strokeOpacity?: number;
69
+ };
70
+ data?: Record<string, unknown>;
71
+ [key: string]: unknown;
72
+ }
73
+ export interface MapLegendItem {
74
+ color: string;
75
+ label: string;
76
+ }
77
+ export interface MapDot extends MapPin {
78
+ x: number;
79
+ y: number;
80
+ countryId?: string;
81
+ }
82
+ export interface PrecomputedMap {
83
+ width: number;
84
+ height: number;
85
+ points: Record<string, MapDot>;
86
+ region?: MapRegion;
87
+ grid?: "vertical" | "diagonal";
88
+ projection?: MapProjectionName;
89
+ countries?: string[];
90
+ /** Projection metadata used by getPin. */
91
+ scale?: number;
92
+ translate?: [number, number];
93
+ /** Legacy Mercator bounds from maps precomputed with nuxt-charts v2. */
94
+ X_MIN?: number;
95
+ Y_MAX?: number;
96
+ X_RANGE?: number;
97
+ Y_RANGE?: number;
98
+ ystep?: number;
99
+ }
100
+ export interface MapZoom {
101
+ x: number;
102
+ y: number;
103
+ k: number;
104
+ }
105
+ export interface MapMarkSlot<T> {
106
+ values: T;
107
+ x?: number;
108
+ y?: number;
109
+ path?: string;
110
+ color: string;
111
+ radius?: number;
112
+ scale: number;
113
+ projection: GeoProjection;
114
+ }
115
+ export interface MapOverlaySlot {
116
+ width: number;
117
+ height: number;
118
+ transform: MapZoom;
119
+ projection: GeoProjection;
120
+ }
121
+ export type MapTooltipSlot<A extends MapArea, P extends MapPoint, L extends MapLink<P>> = {
122
+ kind: "feature";
123
+ values: MapFeature<A>;
124
+ } | {
125
+ kind: "point";
126
+ values: P;
127
+ } | {
128
+ kind: "link";
129
+ values: L;
130
+ };
131
+ export type DottedMapTooltipSlot = {
132
+ kind: "pin";
133
+ values: MapPin;
134
+ } | {
135
+ kind: "dot";
136
+ values: MapDot;
137
+ };
138
+ export type MapInteractionKind = "feature" | "point" | "link" | "pin" | "dot";
139
+ export interface MapBaseProps extends ChartStyleProps {
140
+ height?: number | string;
141
+ width?: number | string;
142
+ projection?: MapProjection;
143
+ /** Fit the projection to the geometry. Default true. Disable for a preconfigured projection. */
144
+ fitView?: boolean;
145
+ fitViewPadding?: number;
146
+ padding?: number | Partial<ChartPadding>;
147
+ disableZoom?: boolean;
148
+ zoomFactor?: number;
149
+ zoomExtent?: [number, number];
150
+ /** Wheel zoom is opt-in so a map does not capture normal page scrolling. */
151
+ zoomOnScroll?: boolean;
152
+ zoomDuration?: number;
153
+ duration?: number;
154
+ showControls?: boolean;
155
+ hideTooltip?: boolean;
156
+ hideLegend?: boolean;
157
+ categories?: Record<string, BulletLegendItemInterface>;
158
+ legendPosition?: LegendPosition;
159
+ legendStyle?: string | Record<string, string>;
160
+ backgroundColor?: string;
161
+ theme?: ChartTheme;
162
+ showGraticule?: boolean;
163
+ showSphere?: boolean;
164
+ graticuleColor?: string;
165
+ sphereColor?: string;
166
+ }
167
+ export interface TopoJSONMapProps<A extends MapArea = MapArea, P extends MapPoint = MapPoint, L extends MapLink<P> = MapLink<P>> extends MapBaseProps {
168
+ /** Omit geometry to use the bundled world map. */
169
+ topoJson?: MapGeometry;
170
+ geoJson?: MapGeometry;
171
+ /** TopoJSON object name; defaults to countries, land, or the first object. */
172
+ mapFeatureKey?: string;
173
+ /** Property used to join areas to features. Defaults to feature.id. */
174
+ featureId?: string | ((feature: Feature) => string | number);
175
+ data?: MapData<A, P, L>;
176
+ areaColor?: MapAccessor<MapFeature<A>, string>;
177
+ areaCursor?: MapAccessor<MapFeature<A>, string>;
178
+ areaStroke?: MapAccessor<MapFeature<A>, string>;
179
+ areaStrokeWidth?: MapAccessor<MapFeature<A>, number>;
180
+ /** Numeric field/accessor for an automatically scaled choropleth. */
181
+ value?: keyof A | ((area: MapFeature<A>) => number | undefined);
182
+ colorScale?: (value: number) => string;
183
+ colorRange?: [string, string];
184
+ valueDomain?: [number, number];
185
+ pointColor?: MapAccessor<P, string>;
186
+ pointSize?: MapAccessor<P, number>;
187
+ pointRadius?: MapAccessor<P, number>;
188
+ pointStrokeColor?: MapAccessor<P, string>;
189
+ pointStrokeWidth?: MapAccessor<P, number>;
190
+ pointCursor?: MapAccessor<P, string>;
191
+ pointLabel?: MapAccessor<P, string>;
192
+ showPointLabels?: boolean;
193
+ linkColor?: MapAccessor<L, string>;
194
+ linkWidth?: MapAccessor<L, number>;
195
+ linkCursor?: MapAccessor<L, string>;
196
+ linkDasharray?: MapAccessor<L, string>;
197
+ /** Great-circle routes follow the globe and clip correctly at the date line. */
198
+ linkCurve?: "geodesic" | "straight" | "arc";
199
+ linkCurvature?: number;
200
+ mapFitToPoints?: boolean;
201
+ zoomOnClick?: boolean;
202
+ heatmapMode?: boolean;
203
+ heatmapModeBlurStdDeviation?: number;
204
+ heatmapModeZoomLevelThreshold?: number;
205
+ tooltipTitleFormatter?: (datum: MapFeature<A> | P | L) => string | number;
206
+ yFormatter?: axisFormatter;
207
+ }
208
+ /** Compatibility name for the v2 TopoJSONMap prop interface. */
209
+ export type MapsData<A extends MapArea = MapArea> = TopoJSONMapProps<A>;
210
+ export interface DottedMapProps extends MapBaseProps {
211
+ geoJson?: MapGeometry;
212
+ mapFeatureKey?: string;
213
+ regionName?: MapRegionName;
214
+ region?: MapRegion;
215
+ countries?: string[];
216
+ pins?: MapPin[];
217
+ mapHeight?: number;
218
+ mapWidth?: number;
219
+ grid?: "vertical" | "diagonal";
220
+ shape?: "circle" | "hexagon";
221
+ dotSize?: number;
222
+ color?: string;
223
+ countryColors?: Record<string, string>;
224
+ strokeColor?: string;
225
+ strokeWidth?: number;
226
+ strokeOpacity?: number;
227
+ avoidOuterPins?: boolean;
228
+ precomputedMap?: string | PrecomputedMap;
229
+ defaultZoom?: number;
230
+ showLegend?: boolean;
231
+ legend?: MapLegendItem[];
232
+ maxHeight?: number | string;
233
+ pinColor?: MapAccessor<MapPin, string>;
234
+ pinSize?: MapAccessor<MapPin, number>;
235
+ showPinLabels?: boolean;
236
+ tooltipTitleFormatter?: (datum: MapPin | MapDot) => string | number;
237
+ }
File without changes
@@ -6,6 +6,9 @@
6
6
  */
7
7
  export type * from "./types/shared";
8
8
  export type * from "./types/charts";
9
+ export type * from "./types/maps";
10
+ export type * from "./spec/types";
11
+ export type { TransformResult } from "./spec/transform.js";
9
12
  export type { AreaFillVariant, BarVariant, DotVariant, LegendIndicatorVariant, RadarVariant, RadialVariant, StrokeVariant, TooltipRoundness, TooltipVariant, } from "./utils/variants.js";
10
13
  export type { BackgroundVariant } from "./utils/background.js";
11
14
  export type { DitherVariant } from "./utils/dither.js";
@@ -29,10 +29,28 @@ export declare const AXIS_SLOT: {
29
29
  readonly defaultXHeight: 38;
30
30
  readonly titledXHeight: 52;
31
31
  };
32
+ /**
33
+ * Approximate rendered width of a tick label. A canvas measure would be exact
34
+ * but the axis slot is needed during setup, before the chart (or, in SSR, any
35
+ * DOM at all) exists — so width is estimated from glyph counts instead.
36
+ */
37
+ export declare function estimateTickLabelWidth(label: string, fontPx?: number): number;
32
38
  export declare function resolveYAxisWidth(options: {
33
39
  hasTitle: boolean;
34
40
  isCategoryAxis: boolean;
41
+ /** Formatted sample of the widest ticks the value axis is likely to show. */
42
+ sampleLabels?: readonly string[];
43
+ tickFontPx?: number;
44
+ /** Gap between the plot edge and the label, so it is not counted as space. */
45
+ tickMargin?: number;
35
46
  }): number;
47
+ /**
48
+ * The "nice" round tick values d3 (and so vccs) picks for a numeric domain.
49
+ * Mirrors d3-array's `ticks` step selection so the axis slot can be sized from
50
+ * the labels that will actually be drawn — the top tick usually sits above the
51
+ * data maximum, and is the widest.
52
+ */
53
+ export declare function niceTickValues(min: number, max: number, count?: number): number[];
36
54
  export declare function resolveXAxisHeight(options: {
37
55
  hasTitle: boolean;
38
56
  }): number;
@@ -30,10 +30,53 @@ export const AXIS_SLOT = {
30
30
  defaultXHeight: 38,
31
31
  titledXHeight: 52
32
32
  };
33
+ const MAX_NUMERIC_Y_WIDTH = 96;
34
+ const GLYPH_ADVANCE_RATIO = 0.6;
35
+ const DIGIT_ADVANCE_RATIO = 0.62;
36
+ const DEFAULT_TICK_FONT_PX = 12;
37
+ export function estimateTickLabelWidth(label, fontPx = DEFAULT_TICK_FONT_PX) {
38
+ let width = 0;
39
+ for (const char of label) {
40
+ width += /[0-9.,\s$€£¥%+-]/.test(char) ? DIGIT_ADVANCE_RATIO : GLYPH_ADVANCE_RATIO;
41
+ }
42
+ return width * fontPx;
43
+ }
33
44
  export function resolveYAxisWidth(options) {
34
45
  if (options.hasTitle) return AXIS_SLOT.titledYWidth;
35
46
  if (options.isCategoryAxis) return AXIS_SLOT.categoryYWidth;
36
- return AXIS_SLOT.numericYWidth;
47
+ const labels = options.sampleLabels;
48
+ if (!labels?.length) return AXIS_SLOT.numericYWidth;
49
+ let widest = 0;
50
+ for (const label of labels) {
51
+ widest = Math.max(widest, estimateTickLabelWidth(label, options.tickFontPx));
52
+ }
53
+ const needed = Math.ceil(widest + (options.tickMargin ?? 0));
54
+ return Math.min(Math.max(AXIS_SLOT.numericYWidth, needed), MAX_NUMERIC_Y_WIDTH);
55
+ }
56
+ export function niceTickValues(min, max, count = 5) {
57
+ if (!Number.isFinite(min) || !Number.isFinite(max)) return [];
58
+ if (min === max) return [min];
59
+ if (count <= 0) return [];
60
+ const step = tickIncrement(min, max, count);
61
+ if (!Number.isFinite(step) || step === 0) return [min, max];
62
+ const start = Math.ceil(min / step) * step;
63
+ const stop = Math.floor(max / step) * step;
64
+ const ticks = [];
65
+ for (let value = start, i = 0; value <= stop + step / 2; value += step, i++) {
66
+ ticks.push(start + i * step);
67
+ if (ticks.length > 100) break;
68
+ }
69
+ return ticks;
70
+ }
71
+ function tickIncrement(min, max, count) {
72
+ const rawStep = (max - min) / count;
73
+ const power = Math.floor(Math.log10(rawStep));
74
+ const magnitude = Math.pow(10, power);
75
+ const normalized = rawStep / magnitude;
76
+ if (normalized >= 7.5) return 10 * magnitude;
77
+ if (normalized >= 3) return 5 * magnitude;
78
+ if (normalized >= 1.5) return 2 * magnitude;
79
+ return magnitude;
37
80
  }
38
81
  export function resolveXAxisHeight(options) {
39
82
  return options.hasTitle ? AXIS_SLOT.titledXHeight : AXIS_SLOT.defaultXHeight;
@@ -0,0 +1,14 @@
1
+ import type { GeoProjection } from "d3-geo";
2
+ import type { FeatureCollection } from "geojson";
3
+ import type { DottedMapProps, MapDot, MapPin, PrecomputedMap, MapProjectionName } from "../types/maps.js";
4
+ export declare function filterMapCountries(collection: FeatureCollection, countries?: string[]): FeatureCollection;
5
+ export declare function generateMapDots(collection: FeatureCollection, projection: GeoProjection, width: number, height: number, options: Pick<DottedMapProps, "mapWidth" | "mapHeight" | "grid" | "region">): MapDot[];
6
+ /** Precompute a serializable dotted map, using the same grid as DottedMap. */
7
+ export declare function getMap(options?: Pick<DottedMapProps, "mapHeight" | "mapWidth" | "countries" | "region" | "grid" | "geoJson" | "mapFeatureKey"> & {
8
+ height?: number;
9
+ width?: number;
10
+ projection?: MapProjectionName;
11
+ geojsonWorld?: DottedMapProps["geoJson"];
12
+ }): PrecomputedMap;
13
+ /** Return the nearest grid position for a pin on a map produced by getMap. */
14
+ export declare function getPin(map: PrecomputedMap, pin: MapPin): MapDot;