insomni-plot 0.1.0-alpha.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 (242) hide show
  1. package/LICENSE.md +674 -0
  2. package/README.md +81 -0
  3. package/dist/core.d.mts +340 -0
  4. package/dist/core.mjs +1047 -0
  5. package/dist/index.d.mts +3426 -0
  6. package/dist/index.mjs +12762 -0
  7. package/dist/interactions-DEFL_F4E.mjs +5395 -0
  8. package/dist/range-presets-CzECsu3V.d.mts +1523 -0
  9. package/package.json +34 -0
  10. package/src/annotations.d.ts +121 -0
  11. package/src/annotations.ts +438 -0
  12. package/src/axis.d.ts +184 -0
  13. package/src/axis.test.ts +131 -0
  14. package/src/axis.ts +765 -0
  15. package/src/colorbar.d.ts +69 -0
  16. package/src/colorbar.ts +294 -0
  17. package/src/colors.d.ts +57 -0
  18. package/src/colors.test.ts +28 -0
  19. package/src/colors.ts +486 -0
  20. package/src/core.ts +299 -0
  21. package/src/format.d.ts +54 -0
  22. package/src/format.ts +138 -0
  23. package/src/grammar/accessibility.d.ts +147 -0
  24. package/src/grammar/accessibility.test.ts +199 -0
  25. package/src/grammar/accessibility.ts +443 -0
  26. package/src/grammar/aes.d.ts +35 -0
  27. package/src/grammar/aes.test.ts +75 -0
  28. package/src/grammar/aes.ts +120 -0
  29. package/src/grammar/annotations.d.ts +86 -0
  30. package/src/grammar/annotations.test.ts +68 -0
  31. package/src/grammar/annotations.ts +336 -0
  32. package/src/grammar/attach-brush.d.ts +44 -0
  33. package/src/grammar/attach-brush.test.ts +214 -0
  34. package/src/grammar/attach-brush.ts +111 -0
  35. package/src/grammar/attach-presets.d.ts +33 -0
  36. package/src/grammar/attach-presets.test.ts +106 -0
  37. package/src/grammar/attach-presets.ts +215 -0
  38. package/src/grammar/chart.d.ts +952 -0
  39. package/src/grammar/chart.test.ts +118 -0
  40. package/src/grammar/chart.ts +1172 -0
  41. package/src/grammar/color-utils.d.ts +29 -0
  42. package/src/grammar/color-utils.test.ts +53 -0
  43. package/src/grammar/color-utils.ts +66 -0
  44. package/src/grammar/constants.d.ts +45 -0
  45. package/src/grammar/constants.ts +61 -0
  46. package/src/grammar/coord.d.ts +183 -0
  47. package/src/grammar/coord.test.ts +355 -0
  48. package/src/grammar/coord.ts +619 -0
  49. package/src/grammar/data/pivot.d.ts +57 -0
  50. package/src/grammar/data/pivot.ts +107 -0
  51. package/src/grammar/emphasis-driver.d.ts +69 -0
  52. package/src/grammar/emphasis-driver.test.ts +199 -0
  53. package/src/grammar/emphasis-driver.ts +205 -0
  54. package/src/grammar/equality.d.ts +3 -0
  55. package/src/grammar/equality.ts +40 -0
  56. package/src/grammar/facet.d.ts +63 -0
  57. package/src/grammar/facet.test.ts +60 -0
  58. package/src/grammar/facet.ts +175 -0
  59. package/src/grammar/geoms/_categorical.d.ts +94 -0
  60. package/src/grammar/geoms/_categorical.ts +0 -0
  61. package/src/grammar/geoms/_distribution.d.ts +52 -0
  62. package/src/grammar/geoms/_distribution.ts +125 -0
  63. package/src/grammar/geoms/_mark.d.ts +69 -0
  64. package/src/grammar/geoms/_mark.ts +136 -0
  65. package/src/grammar/geoms/_shape.d.ts +41 -0
  66. package/src/grammar/geoms/_shape.ts +74 -0
  67. package/src/grammar/geoms/aggregate.d.ts +95 -0
  68. package/src/grammar/geoms/aggregate.test.ts +554 -0
  69. package/src/grammar/geoms/aggregate.ts +840 -0
  70. package/src/grammar/geoms/area.d.ts +32 -0
  71. package/src/grammar/geoms/area.test.ts +165 -0
  72. package/src/grammar/geoms/area.ts +578 -0
  73. package/src/grammar/geoms/band.d.ts +27 -0
  74. package/src/grammar/geoms/band.test.ts +57 -0
  75. package/src/grammar/geoms/band.ts +126 -0
  76. package/src/grammar/geoms/bar.d.ts +56 -0
  77. package/src/grammar/geoms/bar.test.ts +367 -0
  78. package/src/grammar/geoms/bar.ts +1054 -0
  79. package/src/grammar/geoms/boxplot.d.ts +129 -0
  80. package/src/grammar/geoms/boxplot.test.ts +299 -0
  81. package/src/grammar/geoms/boxplot.ts +834 -0
  82. package/src/grammar/geoms/connected-scatter.d.ts +27 -0
  83. package/src/grammar/geoms/connected-scatter.test.ts +157 -0
  84. package/src/grammar/geoms/connected-scatter.ts +63 -0
  85. package/src/grammar/geoms/emphasis.d.ts +76 -0
  86. package/src/grammar/geoms/emphasis.test.ts +135 -0
  87. package/src/grammar/geoms/emphasis.ts +162 -0
  88. package/src/grammar/geoms/histogram.d.ts +75 -0
  89. package/src/grammar/geoms/histogram.test.ts +262 -0
  90. package/src/grammar/geoms/histogram.ts +740 -0
  91. package/src/grammar/geoms/index.d.ts +20 -0
  92. package/src/grammar/geoms/index.ts +77 -0
  93. package/src/grammar/geoms/interval.d.ts +31 -0
  94. package/src/grammar/geoms/interval.test.ts +154 -0
  95. package/src/grammar/geoms/interval.ts +342 -0
  96. package/src/grammar/geoms/line.d.ts +38 -0
  97. package/src/grammar/geoms/line.test.ts +247 -0
  98. package/src/grammar/geoms/line.ts +659 -0
  99. package/src/grammar/geoms/point.d.ts +57 -0
  100. package/src/grammar/geoms/point.test.ts +163 -0
  101. package/src/grammar/geoms/point.ts +545 -0
  102. package/src/grammar/geoms/polar.test.ts +216 -0
  103. package/src/grammar/geoms/ribbon.d.ts +21 -0
  104. package/src/grammar/geoms/ribbon.test.ts +170 -0
  105. package/src/grammar/geoms/ribbon.ts +87 -0
  106. package/src/grammar/geoms/ridgeline.d.ts +89 -0
  107. package/src/grammar/geoms/ridgeline.test.ts +247 -0
  108. package/src/grammar/geoms/ridgeline.ts +1164 -0
  109. package/src/grammar/geoms/rolling.d.ts +43 -0
  110. package/src/grammar/geoms/rolling.test.ts +217 -0
  111. package/src/grammar/geoms/rolling.ts +387 -0
  112. package/src/grammar/geoms/rug.d.ts +28 -0
  113. package/src/grammar/geoms/rug.test.ts +126 -0
  114. package/src/grammar/geoms/rug.ts +214 -0
  115. package/src/grammar/geoms/rule.d.ts +23 -0
  116. package/src/grammar/geoms/rule.test.ts +69 -0
  117. package/src/grammar/geoms/rule.ts +212 -0
  118. package/src/grammar/geoms/smooth.d.ts +54 -0
  119. package/src/grammar/geoms/smooth.test.ts +78 -0
  120. package/src/grammar/geoms/smooth.ts +337 -0
  121. package/src/grammar/geoms/text.d.ts +29 -0
  122. package/src/grammar/geoms/text.test.ts +64 -0
  123. package/src/grammar/geoms/text.ts +234 -0
  124. package/src/grammar/geoms/tile.d.ts +61 -0
  125. package/src/grammar/geoms/tile.test.ts +157 -0
  126. package/src/grammar/geoms/tile.ts +621 -0
  127. package/src/grammar/geoms/types.d.ts +319 -0
  128. package/src/grammar/geoms/types.ts +362 -0
  129. package/src/grammar/geoms/violin.d.ts +85 -0
  130. package/src/grammar/geoms/violin.test.ts +187 -0
  131. package/src/grammar/geoms/violin.ts +672 -0
  132. package/src/grammar/index.d.ts +22 -0
  133. package/src/grammar/index.ts +269 -0
  134. package/src/grammar/interactions/_disposable.d.ts +5 -0
  135. package/src/grammar/interactions/_disposable.ts +23 -0
  136. package/src/grammar/interactions/_z.d.ts +4 -0
  137. package/src/grammar/interactions/_z.ts +16 -0
  138. package/src/grammar/interactions/brush-selection.test.ts +262 -0
  139. package/src/grammar/interactions/brush.d.ts +63 -0
  140. package/src/grammar/interactions/brush.test.ts +483 -0
  141. package/src/grammar/interactions/brush.ts +452 -0
  142. package/src/grammar/interactions/crosshair.d.ts +19 -0
  143. package/src/grammar/interactions/crosshair.test.ts +127 -0
  144. package/src/grammar/interactions/crosshair.ts +76 -0
  145. package/src/grammar/interactions/hit-layer.d.ts +64 -0
  146. package/src/grammar/interactions/hit-layer.ts +246 -0
  147. package/src/grammar/interactions/legend.d.ts +19 -0
  148. package/src/grammar/interactions/legend.ts +101 -0
  149. package/src/grammar/interactions/menu.d.ts +93 -0
  150. package/src/grammar/interactions/menu.test.ts +373 -0
  151. package/src/grammar/interactions/menu.ts +342 -0
  152. package/src/grammar/interactions/selection.d.ts +25 -0
  153. package/src/grammar/interactions/selection.test.ts +289 -0
  154. package/src/grammar/interactions/selection.ts +142 -0
  155. package/src/grammar/interactions/series-readout.d.ts +91 -0
  156. package/src/grammar/interactions/series-readout.test.ts +668 -0
  157. package/src/grammar/interactions/series-readout.ts +422 -0
  158. package/src/grammar/interactions/series-snap.d.ts +70 -0
  159. package/src/grammar/interactions/series-snap.test.ts +214 -0
  160. package/src/grammar/interactions/series-snap.ts +218 -0
  161. package/src/grammar/interactions/tooltip-axis.test.ts +176 -0
  162. package/src/grammar/interactions/tooltip-touch.browser.test.ts +49 -0
  163. package/src/grammar/interactions/tooltip-touch.test.ts +161 -0
  164. package/src/grammar/interactions/tooltip.d.ts +140 -0
  165. package/src/grammar/interactions/tooltip.test.ts +406 -0
  166. package/src/grammar/interactions/tooltip.ts +622 -0
  167. package/src/grammar/interactions/transitions.d.ts +34 -0
  168. package/src/grammar/interactions/transitions.test.ts +172 -0
  169. package/src/grammar/interactions/transitions.ts +160 -0
  170. package/src/grammar/layout.d.ts +68 -0
  171. package/src/grammar/layout.ts +186 -0
  172. package/src/grammar/legend-merge.test.ts +332 -0
  173. package/src/grammar/mount.d.ts +78 -0
  174. package/src/grammar/mount.test.ts +479 -0
  175. package/src/grammar/mount.ts +2112 -0
  176. package/src/grammar/palettes.d.ts +54 -0
  177. package/src/grammar/palettes.test.ts +80 -0
  178. package/src/grammar/palettes.ts +167 -0
  179. package/src/grammar/pan-zoom.test.ts +398 -0
  180. package/src/grammar/phylo.d.ts +65 -0
  181. package/src/grammar/phylo.test.ts +59 -0
  182. package/src/grammar/phylo.ts +112 -0
  183. package/src/grammar/pipeline.auto-ticks.test.ts +40 -0
  184. package/src/grammar/pipeline.d.ts +158 -0
  185. package/src/grammar/pipeline.test.ts +463 -0
  186. package/src/grammar/pipeline.ts +1233 -0
  187. package/src/grammar/profiling.d.ts +8 -0
  188. package/src/grammar/profiling.ts +24 -0
  189. package/src/grammar/scales.d.ts +188 -0
  190. package/src/grammar/scales.test.ts +181 -0
  191. package/src/grammar/scales.ts +800 -0
  192. package/src/grammar/svg.d.ts +3 -0
  193. package/src/grammar/svg.ts +39 -0
  194. package/src/grammar/theme.d.ts +261 -0
  195. package/src/grammar/theme.test.ts +105 -0
  196. package/src/grammar/theme.ts +490 -0
  197. package/src/heatmap/cpu.ts +109 -0
  198. package/src/heatmap/gpu.ts +565 -0
  199. package/src/heatmap/types.ts +177 -0
  200. package/src/heatmap.browser.test.ts +308 -0
  201. package/src/heatmap.test.ts +320 -0
  202. package/src/heatmap.ts +123 -0
  203. package/src/index.d.ts +1 -0
  204. package/src/index.ts +8 -0
  205. package/src/interactions.d.ts +48 -0
  206. package/src/interactions.test.ts +226 -0
  207. package/src/interactions.ts +394 -0
  208. package/src/layout/box.d.ts +48 -0
  209. package/src/layout/box.test.ts +107 -0
  210. package/src/layout/box.ts +143 -0
  211. package/src/legend.d.ts +115 -0
  212. package/src/legend.ts +422 -0
  213. package/src/marks/curve.d.ts +43 -0
  214. package/src/marks/curve.ts +244 -0
  215. package/src/marks/stack.d.ts +53 -0
  216. package/src/marks/stack.ts +184 -0
  217. package/src/marks.d.ts +273 -0
  218. package/src/marks.test.ts +541 -0
  219. package/src/marks.ts +1292 -0
  220. package/src/navigator.test.ts +174 -0
  221. package/src/navigator.ts +393 -0
  222. package/src/range-presets.d.ts +113 -0
  223. package/src/range-presets.test.ts +345 -0
  224. package/src/range-presets.ts +349 -0
  225. package/src/scales.d.ts +98 -0
  226. package/src/scales.test.ts +103 -0
  227. package/src/scales.ts +695 -0
  228. package/src/stats/index.d.ts +200 -0
  229. package/src/stats/index.test.ts +349 -0
  230. package/src/stats/index.ts +740 -0
  231. package/src/stats/regression.d.ts +38 -0
  232. package/src/stats/regression.test.ts +56 -0
  233. package/src/stats/regression.ts +396 -0
  234. package/src/stats/rolling-window.d.ts +55 -0
  235. package/src/stats/rolling-window.test.ts +237 -0
  236. package/src/stats/rolling-window.ts +256 -0
  237. package/src/test-setup.ts +19 -0
  238. package/src/viewport/axis-state.d.ts +72 -0
  239. package/src/viewport/axis-state.ts +476 -0
  240. package/src/viewport.d.ts +170 -0
  241. package/src/viewport.test.ts +363 -0
  242. package/src/viewport.ts +510 -0
@@ -0,0 +1,98 @@
1
+ export type NumericRange = readonly [number, number];
2
+ export type NumericDomain = readonly [number, number];
3
+ export type DateDomain = readonly [Date, Date];
4
+ export type TickFormatter<T> = (value: T) => string;
5
+ export interface ContinuousScale {
6
+ (value: number): number;
7
+ readonly domain: NumericDomain;
8
+ readonly range: NumericRange;
9
+ invert(value: number): number;
10
+ ticks(count?: number): readonly number[];
11
+ tickFormat(count?: number): TickFormatter<number>;
12
+ }
13
+ export interface TimeScale {
14
+ (value: Date): number;
15
+ readonly domain: DateDomain;
16
+ readonly range: NumericRange;
17
+ invert(value: number): Date;
18
+ ticks(count?: number): readonly Date[];
19
+ tickFormat(count?: number): TickFormatter<Date>;
20
+ }
21
+ export interface BandScale<Domain> {
22
+ (value: Domain): number;
23
+ readonly domain: readonly Domain[];
24
+ readonly range: NumericRange;
25
+ bandwidth(): number;
26
+ step(): number;
27
+ ticks(): readonly Domain[];
28
+ tickFormat(): TickFormatter<Domain>;
29
+ }
30
+ export type AxisScale<Value> = ContinuousScale | TimeScale | BandScale<Value>;
31
+ export interface BandScaleOptions {
32
+ padding?: number;
33
+ paddingInner?: number;
34
+ paddingOuter?: number;
35
+ align?: number;
36
+ }
37
+ /**
38
+ * Time interval units accepted by {@link timeTicks} / {@link timeTickFormat}
39
+ * and by axis `ticks: { interval, step }` specs. `quarter` is sugar for
40
+ * `month` with step `3 × step`.
41
+ */
42
+ export type TimeIntervalUnit = "millisecond" | "second" | "minute" | "hour" | "day" | "week" | "month" | "quarter" | "year";
43
+ export declare function clamp(value: number, min: number, max: number): number;
44
+ export declare function numericTicks(start: number, stop: number, count?: number): readonly number[];
45
+ /** Sign-preserving sqrt: f(-x) = -sqrt(x). Used by `sqrtScale` and viewport. */
46
+ export declare function signedSqrt(value: number): number;
47
+ /** Inverse of `signedSqrt`. */
48
+ export declare function signedSquare(value: number): number;
49
+ export declare function linearScale(domain: NumericDomain, range: NumericRange): ContinuousScale;
50
+ export declare function sqrtScale(domain: NumericDomain, range: NumericRange): ContinuousScale;
51
+ /** Sign-preserving log. Used by `logScale` and viewport transforms. */
52
+ export declare function logTransform(value: number): number;
53
+ /** Inverse of `logTransform`. */
54
+ export declare function logUntransform(value: number): number;
55
+ export declare function logScale(domain: NumericDomain, range: NumericRange): ContinuousScale;
56
+ export declare function timeScale(domain: DateDomain, range: NumericRange): TimeScale;
57
+ /**
58
+ * Generate UTC tick dates at an explicit interval (e.g. every month, every
59
+ * 3 weeks, every quarter). Use this when the default `timeScale.ticks(count)`
60
+ * heuristic picks the wrong granularity for a known cadence. `step` defaults
61
+ * to `1`. `quarter` is sugar for `month, step: 3`.
62
+ */
63
+ export declare function timeTicks(domain: DateDomain, unit: TimeIntervalUnit, step?: number): readonly Date[];
64
+ /**
65
+ * Tick formatter paired with {@link timeTicks}. The label shape is picked
66
+ * from the unit: years render `"2026"`, months `"Mar 2026"`, days `"Mar 14"`,
67
+ * etc. — matching the formatter used by {@link timeScale}.
68
+ */
69
+ export declare function timeTickFormat(unit: TimeIntervalUnit, step?: number): TickFormatter<Date>;
70
+ export declare function bandScale<Domain>(domain: readonly Domain[], range: NumericRange, options?: BandScaleOptions): BandScale<Domain>;
71
+ export interface GroupedBandScale<Outer, Inner> {
72
+ /** Position (start) of the inner band within `outer`. */
73
+ (outer: Outer, inner: Inner): number;
74
+ /** Inner bandwidth — the rendered width of one inner bar. */
75
+ bandwidth(): number;
76
+ /** Inner step — distance between adjacent inner band starts. */
77
+ step(): number;
78
+ /** Outer band scale this is nested inside. */
79
+ readonly outer: BandScale<Outer>;
80
+ /** Ordered keys of the inner band scale. */
81
+ readonly innerKeys: readonly Inner[];
82
+ }
83
+ export interface GroupedBandScaleOptions {
84
+ padding?: number;
85
+ paddingInner?: number;
86
+ paddingOuter?: number;
87
+ align?: number;
88
+ }
89
+ /**
90
+ * Build a nested band scale: each band of the `outer` scale is subdivided
91
+ * into one inner band per key. Returns a function `(outerKey, innerKey) →
92
+ * position` plus inner `bandwidth()` and `step()`.
93
+ *
94
+ * Use this for grouped bars / dot plots where each category has multiple
95
+ * series side by side. Outer scale handles category placement and
96
+ * spacing; inner band controls intra-group layout.
97
+ */
98
+ export declare function groupedBandScale<Outer, Inner>(outer: BandScale<Outer>, innerKeys: readonly Inner[], options?: GroupedBandScaleOptions): GroupedBandScale<Outer, Inner>;
@@ -0,0 +1,103 @@
1
+ import { describe, expect, test } from "vite-plus/test";
2
+
3
+ import {
4
+ bandScale,
5
+ linearScale,
6
+ logScale,
7
+ sqrtScale,
8
+ timeScale,
9
+ timeTickFormat,
10
+ timeTicks,
11
+ } from "./scales.ts";
12
+
13
+ describe("scales", () => {
14
+ test("linearScale maps values, inverts, and generates ticks", () => {
15
+ const scale = linearScale([0, 100], [0, 500]);
16
+
17
+ expect(scale(25)).toBe(125);
18
+ expect(scale.invert(250)).toBe(50);
19
+ expect(scale.ticks(5)).toEqual([0, 20, 40, 60, 80, 100]);
20
+ expect(scale.tickFormat(5)(40)).toBe("40");
21
+ });
22
+
23
+ test("sqrtScale uses signed roots so negative domains still work", () => {
24
+ const scale = sqrtScale([-9, 16], [0, 100]);
25
+
26
+ expect(scale(-4)).toBeCloseTo(14.285714, 5);
27
+ expect(scale.invert(scale(9))).toBeCloseTo(9, 5);
28
+ });
29
+
30
+ test("logScale rejects domains that cross zero", () => {
31
+ expect(() => logScale([-1, 10], [0, 100])).toThrow(/same sign/u);
32
+ });
33
+
34
+ test("logScale emits decade ticks for wide spans", () => {
35
+ const scale = logScale([1, 1000], [0, 300]);
36
+
37
+ expect(scale(10)).toBeCloseTo(100, 5);
38
+ expect(scale.invert(200)).toBeCloseTo(100, 5);
39
+ expect(scale.ticks(2)).toEqual([1, 10, 100, 1000]);
40
+ });
41
+
42
+ test("timeScale maps UTC dates and formats tick labels", () => {
43
+ const scale = timeScale(
44
+ [new Date("2026-01-01T00:00:00.000Z"), new Date("2026-01-01T03:00:00.000Z")],
45
+ [0, 300],
46
+ );
47
+
48
+ expect(scale(new Date("2026-01-01T01:30:00.000Z"))).toBe(150);
49
+ expect(scale.invert(200).toISOString()).toBe("2026-01-01T02:00:00.000Z");
50
+ expect(scale.ticks(3).map((value) => value.toISOString())).toEqual([
51
+ "2026-01-01T00:00:00.000Z",
52
+ "2026-01-01T01:00:00.000Z",
53
+ "2026-01-01T02:00:00.000Z",
54
+ "2026-01-01T03:00:00.000Z",
55
+ ]);
56
+ expect(scale.tickFormat(3)(new Date("2026-01-01T02:00:00.000Z"))).toBe("02:00");
57
+ });
58
+
59
+ test("timeTicks emits ticks at an explicit interval", () => {
60
+ const monthly = timeTicks(
61
+ [new Date("2026-01-15T00:00:00Z"), new Date("2026-06-10T00:00:00Z")],
62
+ "month",
63
+ );
64
+ expect(monthly.map((d) => d.toISOString())).toEqual([
65
+ "2026-02-01T00:00:00.000Z",
66
+ "2026-03-01T00:00:00.000Z",
67
+ "2026-04-01T00:00:00.000Z",
68
+ "2026-05-01T00:00:00.000Z",
69
+ "2026-06-01T00:00:00.000Z",
70
+ ]);
71
+ });
72
+
73
+ test("timeTicks 'quarter' is sugar for month/step 3", () => {
74
+ const quarters = timeTicks(
75
+ [new Date("2026-01-01T00:00:00Z"), new Date("2026-12-31T00:00:00Z")],
76
+ "quarter",
77
+ );
78
+ expect(quarters.map((d) => d.toISOString())).toEqual([
79
+ "2026-01-01T00:00:00.000Z",
80
+ "2026-04-01T00:00:00.000Z",
81
+ "2026-07-01T00:00:00.000Z",
82
+ "2026-10-01T00:00:00.000Z",
83
+ ]);
84
+ });
85
+
86
+ test("timeTickFormat picks label shape from the unit", () => {
87
+ expect(timeTickFormat("year")(new Date("2026-04-01T00:00:00Z"))).toBe("2026");
88
+ expect(timeTickFormat("month")(new Date("2026-04-01T00:00:00Z"))).toBe("Apr 2026");
89
+ expect(timeTickFormat("day")(new Date("2026-04-01T00:00:00Z"))).toBe("Apr 1");
90
+ expect(timeTickFormat("quarter")(new Date("2026-04-01T00:00:00Z"))).toBe("Apr 2026");
91
+ });
92
+
93
+ test("bandScale returns evenly spaced starts and bandwidth", () => {
94
+ const scale = bandScale(["a", "b", "c"], [0, 120]);
95
+
96
+ expect(scale("a")).toBe(0);
97
+ expect(scale("b")).toBe(40);
98
+ expect(scale("c")).toBe(80);
99
+ expect(scale.bandwidth()).toBe(40);
100
+ expect(scale.step()).toBe(40);
101
+ expect(scale.tickFormat()("b")).toBe("b");
102
+ });
103
+ });