@voila.dev/chart 6.0.125 → 6.0.126

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.
@@ -1 +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"}
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;AAOD,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"}
@@ -14,6 +14,9 @@ function axisTicks(scale, options) {
14
14
  function tickLabel(scale, options, value) {
15
15
  return options?.tickFormat?.(value) ?? scale.format(value);
16
16
  }
17
+ function tickKey(value) {
18
+ return value instanceof Date ? String(value.getTime()) : String(value);
19
+ }
17
20
  function truncate(text, maxWidth, fontSize, measureText) {
18
21
  if (measureText(text, fontSize) <= maxWidth) {
19
22
  return text;
@@ -78,7 +81,7 @@ function bottomAxis(input, plot, grid) {
78
81
  const positions = ticks.map((tick) => scale.center(tick));
79
82
  const gridNodes = grid ? ticks.map((tick, index) => ({
80
83
  kind: "line",
81
- key: `grid-x:${tickLabel(scale, options, tick)}`,
84
+ key: `grid-x:${tickKey(tick)}`,
82
85
  role: "grid",
83
86
  x1: positions[index],
84
87
  y1: plot.y,
@@ -95,7 +98,7 @@ function bottomAxis(input, plot, grid) {
95
98
  const labels = thinned(ticks, widths, positions).map(
96
99
  (index) => ({
97
100
  kind: "text",
98
- key: `tick-x:${texts[index]}`,
101
+ key: `tick-x:${tickKey(ticks[index])}`,
99
102
  role: "axis",
100
103
  x: positions[index],
101
104
  y: baseline,
@@ -133,7 +136,7 @@ function leftAxis(input, plot, grid, chartWidth) {
133
136
  const positions = ticks.map((tick) => scale.center(tick));
134
137
  const gridNodes = grid ? ticks.map((tick, index) => ({
135
138
  kind: "line",
136
- key: `grid-y:${tickLabel(scale, options, tick)}`,
139
+ key: `grid-y:${tickKey(tick)}`,
137
140
  role: "grid",
138
141
  x1: plot.x,
139
142
  y1: positions[index],
@@ -158,7 +161,7 @@ function leftAxis(input, plot, grid, chartWidth) {
158
161
  const labels = thinned(ticks, heights, positions).map(
159
162
  (index) => ({
160
163
  kind: "text",
161
- key: `tick-y:${texts[index]}`,
164
+ key: `tick-y:${tickKey(ticks[index])}`,
162
165
  role: "axis",
163
166
  x: plot.x - TICK_GAP,
164
167
  y: positions[index],
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/core/guides/axes.ts"],
4
- "sourcesContent": ["import type { ChartAxisOptions } from \"#/core/define-chart.ts\";\nimport type {\n\tChartPositionScale,\n\tChartRect,\n\tChartTextMeasurer,\n\tChartTheme,\n\tChartValue,\n\tSceneNode,\n} from \"#/core/types.ts\";\n\n/**\n * The axes as scene nodes: grid lines behind the marks, tick labels and\n * titles in front. They are measured before the plot exists, so the margins\n * fit the widest label instead of a guessed constant.\n */\n\nconst TICK_GAP = 8;\nconst TITLE_GAP = 6;\nconst LABEL_SPACING = 8;\nconst EDGE = 8;\n/** A category label on a horizontal bar chart never takes more of the width than this. */\nconst MAX_CATEGORY_SHARE = 0.35;\nconst ELLIPSIS = \"\u2026\";\n\nexport interface AxisMeasureInput {\n\treadonly scale: ChartPositionScale;\n\treadonly options: ChartAxisOptions | undefined;\n\treadonly title: string | undefined;\n\treadonly theme: ChartTheme;\n\treadonly measureText: ChartTextMeasurer;\n}\n\nexport function axisTicks(\n\tscale: ChartPositionScale,\n\toptions: ChartAxisOptions | undefined,\n): ReadonlyArray<ChartValue> {\n\tconst ticks = options?.ticks;\n\tif (Array.isArray(ticks)) {\n\t\treturn ticks;\n\t}\n\treturn scale.ticks(typeof ticks === \"number\" ? ticks : undefined);\n}\n\nexport function tickLabel(\n\tscale: ChartPositionScale,\n\toptions: ChartAxisOptions | undefined,\n\tvalue: ChartValue,\n): string {\n\treturn options?.tickFormat?.(value) ?? scale.format(value);\n}\n\n/** Cuts a label down to `maxWidth`, ending on an ellipsis. */\nexport function truncate(\n\ttext: string,\n\tmaxWidth: number,\n\tfontSize: number,\n\tmeasureText: ChartTextMeasurer,\n): string {\n\tif (measureText(text, fontSize) <= maxWidth) {\n\t\treturn text;\n\t}\n\tlet kept = text;\n\twhile (kept.length > 1 && measureText(kept + ELLIPSIS, fontSize) > maxWidth) {\n\t\tkept = kept.slice(0, -1);\n\t}\n\treturn kept + ELLIPSIS;\n}\n\nfunction hidden(options: ChartAxisOptions | undefined): boolean {\n\treturn options?.axis === false;\n}\n\n/** Room the left axis needs: its widest tick label, the gap, and the title. */\nexport function leftMargin(\n\tinput: AxisMeasureInput,\n\tchartWidth: number,\n): number {\n\tconst { scale, options, title, theme, measureText } = input;\n\tif (hidden(options)) {\n\t\treturn EDGE;\n\t}\n\tconst discrete = scale.kind === \"band\" || scale.kind === \"point\";\n\tconst widest = Math.max(\n\t\t0,\n\t\t...axisTicks(scale, options).map((tick) =>\n\t\t\tmeasureText(tickLabel(scale, options, tick), theme.fontSize),\n\t\t),\n\t);\n\tconst labels = discrete\n\t\t? Math.min(widest, chartWidth * MAX_CATEGORY_SHARE)\n\t\t: widest;\n\treturn EDGE + labels + TICK_GAP + (title ? theme.fontSize + TITLE_GAP : 0);\n}\n\nexport function bottomMargin(input: AxisMeasureInput): number {\n\tconst { options, title, theme } = input;\n\tif (hidden(options)) {\n\t\treturn EDGE;\n\t}\n\treturn (\n\t\tTICK_GAP +\n\t\ttheme.fontSize +\n\t\tEDGE / 2 +\n\t\t(title ? theme.fontSize + TITLE_GAP : 0)\n\t);\n}\n\n/** Half the last x label hangs past the plot's right edge; leave it room. */\nexport function rightMargin(input: AxisMeasureInput): number {\n\tconst { scale, options, theme, measureText } = input;\n\tif (hidden(options) || scale.kind === \"band\" || scale.kind === \"point\") {\n\t\treturn EDGE;\n\t}\n\tconst ticks = axisTicks(scale, options);\n\tconst last = ticks[ticks.length - 1];\n\tif (last === undefined) {\n\t\treturn EDGE;\n\t}\n\tconst half = measureText(tickLabel(scale, options, last), theme.fontSize) / 2;\n\tconst overhang = half - (scale.range[1] - scale.map(last));\n\treturn Math.max(EDGE, overhang + 2);\n}\n\nexport interface AxisNodes {\n\treadonly grid: ReadonlyArray<SceneNode>;\n\treadonly labels: ReadonlyArray<SceneNode>;\n}\n\n/**\n * Every `step`-th label, so neighbours never overlap. The step is the same\n * across the axis: thinning by skipping single labels reads as noise.\n */\nfunction thinned(\n\tticks: ReadonlyArray<ChartValue>,\n\twidths: ReadonlyArray<number>,\n\tpositions: ReadonlyArray<number>,\n): ReadonlyArray<number> {\n\tif (ticks.length < 2) {\n\t\treturn ticks.map((_tick, index) => index);\n\t}\n\tconst spacing = Math.abs(positions[1] - positions[0]) || 1;\n\tconst widest = Math.max(...widths);\n\tconst step = Math.max(1, Math.ceil((widest + LABEL_SPACING) / spacing));\n\treturn ticks\n\t\t.map((_tick, index) => index)\n\t\t.filter((index) => index % step === 0);\n}\n\nexport function bottomAxis(\n\tinput: AxisMeasureInput,\n\tplot: ChartRect,\n\tgrid: boolean,\n): AxisNodes {\n\tconst { scale, options, title, theme, measureText } = input;\n\tconst ticks = axisTicks(scale, options);\n\tconst positions = ticks.map((tick) => scale.center(tick));\n\tconst gridNodes: SceneNode[] = grid\n\t\t? ticks.map((tick, index) => ({\n\t\t\t\tkind: \"line\",\n\t\t\t\tkey: `grid-x:${tickLabel(scale, options, tick)}`,\n\t\t\t\trole: \"grid\",\n\t\t\t\tx1: positions[index],\n\t\t\t\ty1: plot.y,\n\t\t\t\tx2: positions[index],\n\t\t\t\ty2: plot.y + plot.height,\n\t\t\t\tpaint: { stroke: theme.grid, strokeWidth: 1 },\n\t\t\t}))\n\t\t: [];\n\tif (hidden(options)) {\n\t\treturn { grid: gridNodes, labels: [] };\n\t}\n\tconst texts = ticks.map((tick) => tickLabel(scale, options, tick));\n\tconst widths = texts.map((text) => measureText(text, theme.fontSize));\n\tconst baseline = plot.y + plot.height + TICK_GAP;\n\tconst labels: SceneNode[] = thinned(ticks, widths, positions).map(\n\t\t(index) => ({\n\t\t\tkind: \"text\",\n\t\t\tkey: `tick-x:${texts[index]}`,\n\t\t\trole: \"axis\",\n\t\t\tx: positions[index],\n\t\t\ty: baseline,\n\t\t\ttext: texts[index],\n\t\t\tpaint: {\n\t\t\t\tfill: theme.muted,\n\t\t\t\tfontSize: theme.fontSize,\n\t\t\t\ttextAnchor: \"middle\",\n\t\t\t\tbaseline: \"top\",\n\t\t\t},\n\t\t}),\n\t);\n\tif (title) {\n\t\tlabels.push({\n\t\t\tkind: \"text\",\n\t\t\tkey: \"title-x\",\n\t\t\trole: \"axis-title\",\n\t\t\tx: plot.x + plot.width / 2,\n\t\t\ty: baseline + theme.fontSize + TITLE_GAP,\n\t\t\ttext: title,\n\t\t\tpaint: {\n\t\t\t\tfill: theme.muted,\n\t\t\t\tfontSize: theme.fontSize,\n\t\t\t\tfontWeight: 500,\n\t\t\t\ttextAnchor: \"middle\",\n\t\t\t\tbaseline: \"top\",\n\t\t\t},\n\t\t});\n\t}\n\treturn { grid: gridNodes, labels };\n}\n\nexport function leftAxis(\n\tinput: AxisMeasureInput,\n\tplot: ChartRect,\n\tgrid: boolean,\n\tchartWidth: number,\n): AxisNodes {\n\tconst { scale, options, title, theme, measureText } = input;\n\tconst ticks = axisTicks(scale, options);\n\tconst positions = ticks.map((tick) => scale.center(tick));\n\tconst gridNodes: SceneNode[] = grid\n\t\t? ticks.map((tick, index) => ({\n\t\t\t\tkind: \"line\",\n\t\t\t\tkey: `grid-y:${tickLabel(scale, options, tick)}`,\n\t\t\t\trole: \"grid\",\n\t\t\t\tx1: plot.x,\n\t\t\t\ty1: positions[index],\n\t\t\t\tx2: plot.x + plot.width,\n\t\t\t\ty2: positions[index],\n\t\t\t\tpaint: { stroke: theme.grid, strokeWidth: 1 },\n\t\t\t}))\n\t\t: [];\n\tif (hidden(options)) {\n\t\treturn { grid: gridNodes, labels: [] };\n\t}\n\tconst discrete = scale.kind === \"band\" || scale.kind === \"point\";\n\tconst maxWidth = discrete\n\t\t? chartWidth * MAX_CATEGORY_SHARE\n\t\t: Number.POSITIVE_INFINITY;\n\tconst texts = ticks.map((tick) =>\n\t\ttruncate(\n\t\t\ttickLabel(scale, options, tick),\n\t\t\tmaxWidth,\n\t\t\ttheme.fontSize,\n\t\t\tmeasureText,\n\t\t),\n\t);\n\tconst heights = texts.map(() => theme.fontSize);\n\tconst labels: SceneNode[] = thinned(ticks, heights, positions).map(\n\t\t(index) => ({\n\t\t\tkind: \"text\",\n\t\t\tkey: `tick-y:${texts[index]}`,\n\t\t\trole: \"axis\",\n\t\t\tx: plot.x - TICK_GAP,\n\t\t\ty: positions[index],\n\t\t\ttext: texts[index],\n\t\t\tpaint: {\n\t\t\t\tfill: theme.muted,\n\t\t\t\tfontSize: theme.fontSize,\n\t\t\t\ttextAnchor: \"end\",\n\t\t\t\tbaseline: \"middle\",\n\t\t\t},\n\t\t}),\n\t);\n\tif (title) {\n\t\tlabels.push({\n\t\t\tkind: \"text\",\n\t\t\tkey: \"title-y\",\n\t\t\trole: \"axis-title\",\n\t\t\tx: EDGE / 2 + theme.fontSize / 2,\n\t\t\ty: plot.y + plot.height / 2,\n\t\t\ttext: title,\n\t\t\trotate: -90,\n\t\t\tpaint: {\n\t\t\t\tfill: theme.muted,\n\t\t\t\tfontSize: theme.fontSize,\n\t\t\t\tfontWeight: 500,\n\t\t\t\ttextAnchor: \"middle\",\n\t\t\t\tbaseline: \"middle\",\n\t\t\t},\n\t\t});\n\t}\n\treturn { grid: gridNodes, labels };\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import type { ChartAxisOptions } from \"#/core/define-chart.ts\";\nimport type {\n\tChartPositionScale,\n\tChartRect,\n\tChartTextMeasurer,\n\tChartTheme,\n\tChartValue,\n\tSceneNode,\n} from \"#/core/types.ts\";\n\n/**\n * The axes as scene nodes: grid lines behind the marks, tick labels and\n * titles in front. They are measured before the plot exists, so the margins\n * fit the widest label instead of a guessed constant.\n */\n\nconst TICK_GAP = 8;\nconst TITLE_GAP = 6;\nconst LABEL_SPACING = 8;\nconst EDGE = 8;\n/** A category label on a horizontal bar chart never takes more of the width than this. */\nconst MAX_CATEGORY_SHARE = 0.35;\nconst ELLIPSIS = \"\u2026\";\n\nexport interface AxisMeasureInput {\n\treadonly scale: ChartPositionScale;\n\treadonly options: ChartAxisOptions | undefined;\n\treadonly title: string | undefined;\n\treadonly theme: ChartTheme;\n\treadonly measureText: ChartTextMeasurer;\n}\n\nexport function axisTicks(\n\tscale: ChartPositionScale,\n\toptions: ChartAxisOptions | undefined,\n): ReadonlyArray<ChartValue> {\n\tconst ticks = options?.ticks;\n\tif (Array.isArray(ticks)) {\n\t\treturn ticks;\n\t}\n\treturn scale.ticks(typeof ticks === \"number\" ? ticks : undefined);\n}\n\nexport function tickLabel(\n\tscale: ChartPositionScale,\n\toptions: ChartAxisOptions | undefined,\n\tvalue: ChartValue,\n): string {\n\treturn options?.tickFormat?.(value) ?? scale.format(value);\n}\n\n/** Two ticks can share a label (\"11:00\" on two days), never a value. */\nfunction tickKey(value: ChartValue): string {\n\treturn value instanceof Date ? String(value.getTime()) : String(value);\n}\n\n/** Cuts a label down to `maxWidth`, ending on an ellipsis. */\nexport function truncate(\n\ttext: string,\n\tmaxWidth: number,\n\tfontSize: number,\n\tmeasureText: ChartTextMeasurer,\n): string {\n\tif (measureText(text, fontSize) <= maxWidth) {\n\t\treturn text;\n\t}\n\tlet kept = text;\n\twhile (kept.length > 1 && measureText(kept + ELLIPSIS, fontSize) > maxWidth) {\n\t\tkept = kept.slice(0, -1);\n\t}\n\treturn kept + ELLIPSIS;\n}\n\nfunction hidden(options: ChartAxisOptions | undefined): boolean {\n\treturn options?.axis === false;\n}\n\n/** Room the left axis needs: its widest tick label, the gap, and the title. */\nexport function leftMargin(\n\tinput: AxisMeasureInput,\n\tchartWidth: number,\n): number {\n\tconst { scale, options, title, theme, measureText } = input;\n\tif (hidden(options)) {\n\t\treturn EDGE;\n\t}\n\tconst discrete = scale.kind === \"band\" || scale.kind === \"point\";\n\tconst widest = Math.max(\n\t\t0,\n\t\t...axisTicks(scale, options).map((tick) =>\n\t\t\tmeasureText(tickLabel(scale, options, tick), theme.fontSize),\n\t\t),\n\t);\n\tconst labels = discrete\n\t\t? Math.min(widest, chartWidth * MAX_CATEGORY_SHARE)\n\t\t: widest;\n\treturn EDGE + labels + TICK_GAP + (title ? theme.fontSize + TITLE_GAP : 0);\n}\n\nexport function bottomMargin(input: AxisMeasureInput): number {\n\tconst { options, title, theme } = input;\n\tif (hidden(options)) {\n\t\treturn EDGE;\n\t}\n\treturn (\n\t\tTICK_GAP +\n\t\ttheme.fontSize +\n\t\tEDGE / 2 +\n\t\t(title ? theme.fontSize + TITLE_GAP : 0)\n\t);\n}\n\n/** Half the last x label hangs past the plot's right edge; leave it room. */\nexport function rightMargin(input: AxisMeasureInput): number {\n\tconst { scale, options, theme, measureText } = input;\n\tif (hidden(options) || scale.kind === \"band\" || scale.kind === \"point\") {\n\t\treturn EDGE;\n\t}\n\tconst ticks = axisTicks(scale, options);\n\tconst last = ticks[ticks.length - 1];\n\tif (last === undefined) {\n\t\treturn EDGE;\n\t}\n\tconst half = measureText(tickLabel(scale, options, last), theme.fontSize) / 2;\n\tconst overhang = half - (scale.range[1] - scale.map(last));\n\treturn Math.max(EDGE, overhang + 2);\n}\n\nexport interface AxisNodes {\n\treadonly grid: ReadonlyArray<SceneNode>;\n\treadonly labels: ReadonlyArray<SceneNode>;\n}\n\n/**\n * Every `step`-th label, so neighbours never overlap. The step is the same\n * across the axis: thinning by skipping single labels reads as noise.\n */\nfunction thinned(\n\tticks: ReadonlyArray<ChartValue>,\n\twidths: ReadonlyArray<number>,\n\tpositions: ReadonlyArray<number>,\n): ReadonlyArray<number> {\n\tif (ticks.length < 2) {\n\t\treturn ticks.map((_tick, index) => index);\n\t}\n\tconst spacing = Math.abs(positions[1] - positions[0]) || 1;\n\tconst widest = Math.max(...widths);\n\tconst step = Math.max(1, Math.ceil((widest + LABEL_SPACING) / spacing));\n\treturn ticks\n\t\t.map((_tick, index) => index)\n\t\t.filter((index) => index % step === 0);\n}\n\nexport function bottomAxis(\n\tinput: AxisMeasureInput,\n\tplot: ChartRect,\n\tgrid: boolean,\n): AxisNodes {\n\tconst { scale, options, title, theme, measureText } = input;\n\tconst ticks = axisTicks(scale, options);\n\tconst positions = ticks.map((tick) => scale.center(tick));\n\tconst gridNodes: SceneNode[] = grid\n\t\t? ticks.map((tick, index) => ({\n\t\t\t\tkind: \"line\",\n\t\t\t\tkey: `grid-x:${tickKey(tick)}`,\n\t\t\t\trole: \"grid\",\n\t\t\t\tx1: positions[index],\n\t\t\t\ty1: plot.y,\n\t\t\t\tx2: positions[index],\n\t\t\t\ty2: plot.y + plot.height,\n\t\t\t\tpaint: { stroke: theme.grid, strokeWidth: 1 },\n\t\t\t}))\n\t\t: [];\n\tif (hidden(options)) {\n\t\treturn { grid: gridNodes, labels: [] };\n\t}\n\tconst texts = ticks.map((tick) => tickLabel(scale, options, tick));\n\tconst widths = texts.map((text) => measureText(text, theme.fontSize));\n\tconst baseline = plot.y + plot.height + TICK_GAP;\n\tconst labels: SceneNode[] = thinned(ticks, widths, positions).map(\n\t\t(index) => ({\n\t\t\tkind: \"text\",\n\t\t\tkey: `tick-x:${tickKey(ticks[index])}`,\n\t\t\trole: \"axis\",\n\t\t\tx: positions[index],\n\t\t\ty: baseline,\n\t\t\ttext: texts[index],\n\t\t\tpaint: {\n\t\t\t\tfill: theme.muted,\n\t\t\t\tfontSize: theme.fontSize,\n\t\t\t\ttextAnchor: \"middle\",\n\t\t\t\tbaseline: \"top\",\n\t\t\t},\n\t\t}),\n\t);\n\tif (title) {\n\t\tlabels.push({\n\t\t\tkind: \"text\",\n\t\t\tkey: \"title-x\",\n\t\t\trole: \"axis-title\",\n\t\t\tx: plot.x + plot.width / 2,\n\t\t\ty: baseline + theme.fontSize + TITLE_GAP,\n\t\t\ttext: title,\n\t\t\tpaint: {\n\t\t\t\tfill: theme.muted,\n\t\t\t\tfontSize: theme.fontSize,\n\t\t\t\tfontWeight: 500,\n\t\t\t\ttextAnchor: \"middle\",\n\t\t\t\tbaseline: \"top\",\n\t\t\t},\n\t\t});\n\t}\n\treturn { grid: gridNodes, labels };\n}\n\nexport function leftAxis(\n\tinput: AxisMeasureInput,\n\tplot: ChartRect,\n\tgrid: boolean,\n\tchartWidth: number,\n): AxisNodes {\n\tconst { scale, options, title, theme, measureText } = input;\n\tconst ticks = axisTicks(scale, options);\n\tconst positions = ticks.map((tick) => scale.center(tick));\n\tconst gridNodes: SceneNode[] = grid\n\t\t? ticks.map((tick, index) => ({\n\t\t\t\tkind: \"line\",\n\t\t\t\tkey: `grid-y:${tickKey(tick)}`,\n\t\t\t\trole: \"grid\",\n\t\t\t\tx1: plot.x,\n\t\t\t\ty1: positions[index],\n\t\t\t\tx2: plot.x + plot.width,\n\t\t\t\ty2: positions[index],\n\t\t\t\tpaint: { stroke: theme.grid, strokeWidth: 1 },\n\t\t\t}))\n\t\t: [];\n\tif (hidden(options)) {\n\t\treturn { grid: gridNodes, labels: [] };\n\t}\n\tconst discrete = scale.kind === \"band\" || scale.kind === \"point\";\n\tconst maxWidth = discrete\n\t\t? chartWidth * MAX_CATEGORY_SHARE\n\t\t: Number.POSITIVE_INFINITY;\n\tconst texts = ticks.map((tick) =>\n\t\ttruncate(\n\t\t\ttickLabel(scale, options, tick),\n\t\t\tmaxWidth,\n\t\t\ttheme.fontSize,\n\t\t\tmeasureText,\n\t\t),\n\t);\n\tconst heights = texts.map(() => theme.fontSize);\n\tconst labels: SceneNode[] = thinned(ticks, heights, positions).map(\n\t\t(index) => ({\n\t\t\tkind: \"text\",\n\t\t\tkey: `tick-y:${tickKey(ticks[index])}`,\n\t\t\trole: \"axis\",\n\t\t\tx: plot.x - TICK_GAP,\n\t\t\ty: positions[index],\n\t\t\ttext: texts[index],\n\t\t\tpaint: {\n\t\t\t\tfill: theme.muted,\n\t\t\t\tfontSize: theme.fontSize,\n\t\t\t\ttextAnchor: \"end\",\n\t\t\t\tbaseline: \"middle\",\n\t\t\t},\n\t\t}),\n\t);\n\tif (title) {\n\t\tlabels.push({\n\t\t\tkind: \"text\",\n\t\t\tkey: \"title-y\",\n\t\t\trole: \"axis-title\",\n\t\t\tx: EDGE / 2 + theme.fontSize / 2,\n\t\t\ty: plot.y + plot.height / 2,\n\t\t\ttext: title,\n\t\t\trotate: -90,\n\t\t\tpaint: {\n\t\t\t\tfill: theme.muted,\n\t\t\t\tfontSize: theme.fontSize,\n\t\t\t\tfontWeight: 500,\n\t\t\t\ttextAnchor: \"middle\",\n\t\t\t\tbaseline: \"middle\",\n\t\t\t},\n\t\t});\n\t}\n\treturn { grid: gridNodes, labels };\n}\n"],
5
+ "mappings": "AAgBA,MAAM,WAAW;AACjB,MAAM,YAAY;AAClB,MAAM,gBAAgB;AACtB,MAAM,OAAO;AAEb,MAAM,qBAAqB;AAC3B,MAAM,WAAW;AAUV,SAAS,UACf,OACA,SAC4B;AAC5B,QAAM,QAAQ,SAAS;AACvB,MAAI,MAAM,QAAQ,KAAK,GAAG;AACzB,WAAO;AAAA,EACR;AACA,SAAO,MAAM,MAAM,OAAO,UAAU,WAAW,QAAQ,MAAS;AACjE;AAEO,SAAS,UACf,OACA,SACA,OACS;AACT,SAAO,SAAS,aAAa,KAAK,KAAK,MAAM,OAAO,KAAK;AAC1D;AAGA,SAAS,QAAQ,OAA2B;AAC3C,SAAO,iBAAiB,OAAO,OAAO,MAAM,QAAQ,CAAC,IAAI,OAAO,KAAK;AACtE;AAGO,SAAS,SACf,MACA,UACA,UACA,aACS;AACT,MAAI,YAAY,MAAM,QAAQ,KAAK,UAAU;AAC5C,WAAO;AAAA,EACR;AACA,MAAI,OAAO;AACX,SAAO,KAAK,SAAS,KAAK,YAAY,OAAO,UAAU,QAAQ,IAAI,UAAU;AAC5E,WAAO,KAAK,MAAM,GAAG,EAAE;AAAA,EACxB;AACA,SAAO,OAAO;AACf;AAEA,SAAS,OAAO,SAAgD;AAC/D,SAAO,SAAS,SAAS;AAC1B;AAGO,SAAS,WACf,OACA,YACS;AACT,QAAM,EAAE,OAAO,SAAS,OAAO,OAAO,YAAY,IAAI;AACtD,MAAI,OAAO,OAAO,GAAG;AACpB,WAAO;AAAA,EACR;AACA,QAAM,WAAW,MAAM,SAAS,UAAU,MAAM,SAAS;AACzD,QAAM,SAAS,KAAK;AAAA,IACnB;AAAA,IACA,GAAG,UAAU,OAAO,OAAO,EAAE;AAAA,MAAI,CAAC,SACjC,YAAY,UAAU,OAAO,SAAS,IAAI,GAAG,MAAM,QAAQ;AAAA,IAC5D;AAAA,EACD;AACA,QAAM,SAAS,WACZ,KAAK,IAAI,QAAQ,aAAa,kBAAkB,IAChD;AACH,SAAO,OAAO,SAAS,YAAY,QAAQ,MAAM,WAAW,YAAY;AACzE;AAEO,SAAS,aAAa,OAAiC;AAC7D,QAAM,EAAE,SAAS,OAAO,MAAM,IAAI;AAClC,MAAI,OAAO,OAAO,GAAG;AACpB,WAAO;AAAA,EACR;AACA,SACC,WACA,MAAM,WACN,OAAO,KACN,QAAQ,MAAM,WAAW,YAAY;AAExC;AAGO,SAAS,YAAY,OAAiC;AAC5D,QAAM,EAAE,OAAO,SAAS,OAAO,YAAY,IAAI;AAC/C,MAAI,OAAO,OAAO,KAAK,MAAM,SAAS,UAAU,MAAM,SAAS,SAAS;AACvE,WAAO;AAAA,EACR;AACA,QAAM,QAAQ,UAAU,OAAO,OAAO;AACtC,QAAM,OAAO,MAAM,MAAM,SAAS,CAAC;AACnC,MAAI,SAAS,QAAW;AACvB,WAAO;AAAA,EACR;AACA,QAAM,OAAO,YAAY,UAAU,OAAO,SAAS,IAAI,GAAG,MAAM,QAAQ,IAAI;AAC5E,QAAM,WAAW,QAAQ,MAAM,MAAM,CAAC,IAAI,MAAM,IAAI,IAAI;AACxD,SAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AACnC;AAWA,SAAS,QACR,OACA,QACA,WACwB;AACxB,MAAI,MAAM,SAAS,GAAG;AACrB,WAAO,MAAM,IAAI,CAAC,OAAO,UAAU,KAAK;AAAA,EACzC;AACA,QAAM,UAAU,KAAK,IAAI,UAAU,CAAC,IAAI,UAAU,CAAC,CAAC,KAAK;AACzD,QAAM,SAAS,KAAK,IAAI,GAAG,MAAM;AACjC,QAAM,OAAO,KAAK,IAAI,GAAG,KAAK,MAAM,SAAS,iBAAiB,OAAO,CAAC;AACtE,SAAO,MACL,IAAI,CAAC,OAAO,UAAU,KAAK,EAC3B,OAAO,CAAC,UAAU,QAAQ,SAAS,CAAC;AACvC;AAEO,SAAS,WACf,OACA,MACA,MACY;AACZ,QAAM,EAAE,OAAO,SAAS,OAAO,OAAO,YAAY,IAAI;AACtD,QAAM,QAAQ,UAAU,OAAO,OAAO;AACtC,QAAM,YAAY,MAAM,IAAI,CAAC,SAAS,MAAM,OAAO,IAAI,CAAC;AACxD,QAAM,YAAyB,OAC5B,MAAM,IAAI,CAAC,MAAM,WAAW;AAAA,IAC5B,MAAM;AAAA,IACN,KAAK,UAAU,QAAQ,IAAI,CAAC;AAAA,IAC5B,MAAM;AAAA,IACN,IAAI,UAAU,KAAK;AAAA,IACnB,IAAI,KAAK;AAAA,IACT,IAAI,UAAU,KAAK;AAAA,IACnB,IAAI,KAAK,IAAI,KAAK;AAAA,IAClB,OAAO,EAAE,QAAQ,MAAM,MAAM,aAAa,EAAE;AAAA,EAC7C,EAAE,IACD,CAAC;AACJ,MAAI,OAAO,OAAO,GAAG;AACpB,WAAO,EAAE,MAAM,WAAW,QAAQ,CAAC,EAAE;AAAA,EACtC;AACA,QAAM,QAAQ,MAAM,IAAI,CAAC,SAAS,UAAU,OAAO,SAAS,IAAI,CAAC;AACjE,QAAM,SAAS,MAAM,IAAI,CAAC,SAAS,YAAY,MAAM,MAAM,QAAQ,CAAC;AACpE,QAAM,WAAW,KAAK,IAAI,KAAK,SAAS;AACxC,QAAM,SAAsB,QAAQ,OAAO,QAAQ,SAAS,EAAE;AAAA,IAC7D,CAAC,WAAW;AAAA,MACX,MAAM;AAAA,MACN,KAAK,UAAU,QAAQ,MAAM,KAAK,CAAC,CAAC;AAAA,MACpC,MAAM;AAAA,MACN,GAAG,UAAU,KAAK;AAAA,MAClB,GAAG;AAAA,MACH,MAAM,MAAM,KAAK;AAAA,MACjB,OAAO;AAAA,QACN,MAAM,MAAM;AAAA,QACZ,UAAU,MAAM;AAAA,QAChB,YAAY;AAAA,QACZ,UAAU;AAAA,MACX;AAAA,IACD;AAAA,EACD;AACA,MAAI,OAAO;AACV,WAAO,KAAK;AAAA,MACX,MAAM;AAAA,MACN,KAAK;AAAA,MACL,MAAM;AAAA,MACN,GAAG,KAAK,IAAI,KAAK,QAAQ;AAAA,MACzB,GAAG,WAAW,MAAM,WAAW;AAAA,MAC/B,MAAM;AAAA,MACN,OAAO;AAAA,QACN,MAAM,MAAM;AAAA,QACZ,UAAU,MAAM;AAAA,QAChB,YAAY;AAAA,QACZ,YAAY;AAAA,QACZ,UAAU;AAAA,MACX;AAAA,IACD,CAAC;AAAA,EACF;AACA,SAAO,EAAE,MAAM,WAAW,OAAO;AAClC;AAEO,SAAS,SACf,OACA,MACA,MACA,YACY;AACZ,QAAM,EAAE,OAAO,SAAS,OAAO,OAAO,YAAY,IAAI;AACtD,QAAM,QAAQ,UAAU,OAAO,OAAO;AACtC,QAAM,YAAY,MAAM,IAAI,CAAC,SAAS,MAAM,OAAO,IAAI,CAAC;AACxD,QAAM,YAAyB,OAC5B,MAAM,IAAI,CAAC,MAAM,WAAW;AAAA,IAC5B,MAAM;AAAA,IACN,KAAK,UAAU,QAAQ,IAAI,CAAC;AAAA,IAC5B,MAAM;AAAA,IACN,IAAI,KAAK;AAAA,IACT,IAAI,UAAU,KAAK;AAAA,IACnB,IAAI,KAAK,IAAI,KAAK;AAAA,IAClB,IAAI,UAAU,KAAK;AAAA,IACnB,OAAO,EAAE,QAAQ,MAAM,MAAM,aAAa,EAAE;AAAA,EAC7C,EAAE,IACD,CAAC;AACJ,MAAI,OAAO,OAAO,GAAG;AACpB,WAAO,EAAE,MAAM,WAAW,QAAQ,CAAC,EAAE;AAAA,EACtC;AACA,QAAM,WAAW,MAAM,SAAS,UAAU,MAAM,SAAS;AACzD,QAAM,WAAW,WACd,aAAa,qBACb,OAAO;AACV,QAAM,QAAQ,MAAM;AAAA,IAAI,CAAC,SACxB;AAAA,MACC,UAAU,OAAO,SAAS,IAAI;AAAA,MAC9B;AAAA,MACA,MAAM;AAAA,MACN;AAAA,IACD;AAAA,EACD;AACA,QAAM,UAAU,MAAM,IAAI,MAAM,MAAM,QAAQ;AAC9C,QAAM,SAAsB,QAAQ,OAAO,SAAS,SAAS,EAAE;AAAA,IAC9D,CAAC,WAAW;AAAA,MACX,MAAM;AAAA,MACN,KAAK,UAAU,QAAQ,MAAM,KAAK,CAAC,CAAC;AAAA,MACpC,MAAM;AAAA,MACN,GAAG,KAAK,IAAI;AAAA,MACZ,GAAG,UAAU,KAAK;AAAA,MAClB,MAAM,MAAM,KAAK;AAAA,MACjB,OAAO;AAAA,QACN,MAAM,MAAM;AAAA,QACZ,UAAU,MAAM;AAAA,QAChB,YAAY;AAAA,QACZ,UAAU;AAAA,MACX;AAAA,IACD;AAAA,EACD;AACA,MAAI,OAAO;AACV,WAAO,KAAK;AAAA,MACX,MAAM;AAAA,MACN,KAAK;AAAA,MACL,MAAM;AAAA,MACN,GAAG,OAAO,IAAI,MAAM,WAAW;AAAA,MAC/B,GAAG,KAAK,IAAI,KAAK,SAAS;AAAA,MAC1B,MAAM;AAAA,MACN,QAAQ;AAAA,MACR,OAAO;AAAA,QACN,MAAM,MAAM;AAAA,QACZ,UAAU,MAAM;AAAA,QAChB,YAAY;AAAA,QACZ,YAAY;AAAA,QACZ,UAAU;AAAA,MACX;AAAA,IACD,CAAC;AAAA,EACF;AACA,SAAO,EAAE,MAAM,WAAW,OAAO;AAClC;",
6
6
  "names": []
7
7
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@voila.dev/chart",
3
- "version": "6.0.125",
3
+ "version": "6.0.126",
4
4
  "type": "module",
5
5
  "description": "Charts as data: a definition of marks, channels and scales, drawn to SVG or Canvas, keyboard and screen-reader accessible. The core has no dependencies; the heavy layouts (geo, hierarchy, sankey…) sit behind their own subpaths.",
6
6
  "license": "MIT",
@@ -64,7 +64,7 @@
64
64
  },
65
65
  "sideEffects": false,
66
66
  "dependencies": {
67
- "@voila.dev/motion": "6.0.125",
67
+ "@voila.dev/motion": "6.0.126",
68
68
  "d3-contour": "4.0.2",
69
69
  "d3-delaunay": "6.0.4",
70
70
  "d3-force": "3.0.0",
@@ -49,6 +49,11 @@ export function tickLabel(
49
49
  return options?.tickFormat?.(value) ?? scale.format(value);
50
50
  }
51
51
 
52
+ /** Two ticks can share a label ("11:00" on two days), never a value. */
53
+ function tickKey(value: ChartValue): string {
54
+ return value instanceof Date ? String(value.getTime()) : String(value);
55
+ }
56
+
52
57
  /** Cuts a label down to `maxWidth`, ending on an ellipsis. */
53
58
  export function truncate(
54
59
  text: string,
@@ -157,7 +162,7 @@ export function bottomAxis(
157
162
  const gridNodes: SceneNode[] = grid
158
163
  ? ticks.map((tick, index) => ({
159
164
  kind: "line",
160
- key: `grid-x:${tickLabel(scale, options, tick)}`,
165
+ key: `grid-x:${tickKey(tick)}`,
161
166
  role: "grid",
162
167
  x1: positions[index],
163
168
  y1: plot.y,
@@ -175,7 +180,7 @@ export function bottomAxis(
175
180
  const labels: SceneNode[] = thinned(ticks, widths, positions).map(
176
181
  (index) => ({
177
182
  kind: "text",
178
- key: `tick-x:${texts[index]}`,
183
+ key: `tick-x:${tickKey(ticks[index])}`,
179
184
  role: "axis",
180
185
  x: positions[index],
181
186
  y: baseline,
@@ -220,7 +225,7 @@ export function leftAxis(
220
225
  const gridNodes: SceneNode[] = grid
221
226
  ? ticks.map((tick, index) => ({
222
227
  kind: "line",
223
- key: `grid-y:${tickLabel(scale, options, tick)}`,
228
+ key: `grid-y:${tickKey(tick)}`,
224
229
  role: "grid",
225
230
  x1: plot.x,
226
231
  y1: positions[index],
@@ -248,7 +253,7 @@ export function leftAxis(
248
253
  const labels: SceneNode[] = thinned(ticks, heights, positions).map(
249
254
  (index) => ({
250
255
  kind: "text",
251
- key: `tick-y:${texts[index]}`,
256
+ key: `tick-y:${tickKey(ticks[index])}`,
252
257
  role: "axis",
253
258
  x: plot.x - TICK_GAP,
254
259
  y: positions[index],