@rokkit/chart 1.3.6 → 1.3.8

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 (93) hide show
  1. package/dist/AnimatedPlot.svelte +26 -8
  2. package/dist/AnimatedPlot.svelte.d.ts +5 -0
  3. package/dist/Chart.svelte +1 -1
  4. package/dist/FacetPlot.svelte +1 -1
  5. package/dist/Plot/Axis.svelte +21 -10
  6. package/dist/Plot/Grid.svelte +32 -13
  7. package/dist/Plot/Grid.svelte.d.ts +6 -1
  8. package/dist/Plot/Root.svelte +64 -46
  9. package/dist/Plot/Root.svelte.d.ts +27 -2
  10. package/dist/Plot/Timeline.svelte +39 -13
  11. package/dist/Plot/index.d.ts +4 -4
  12. package/dist/Plot/index.js +4 -4
  13. package/dist/Plot.svelte +128 -164
  14. package/dist/Plot.svelte.d.ts +18 -2
  15. package/dist/PlotState.svelte.d.ts +23 -0
  16. package/dist/PlotState.svelte.js +155 -16
  17. package/dist/PlotSurface.svelte +162 -0
  18. package/dist/PlotSurface.svelte.d.ts +25 -0
  19. package/dist/charts/AreaChart.svelte +37 -4
  20. package/dist/charts/AreaChart.svelte.d.ts +12 -2
  21. package/dist/charts/BarChart.svelte +32 -3
  22. package/dist/charts/BarChart.svelte.d.ts +10 -1
  23. package/dist/charts/BoxPlot.svelte +4 -2
  24. package/dist/charts/BoxPlot.svelte.d.ts +1 -0
  25. package/dist/charts/BubbleChart.svelte +34 -3
  26. package/dist/charts/BubbleChart.svelte.d.ts +11 -1
  27. package/dist/charts/LineChart.svelte +35 -4
  28. package/dist/charts/LineChart.svelte.d.ts +11 -2
  29. package/dist/charts/PieChart.svelte +1 -1
  30. package/dist/charts/ScatterPlot.svelte +32 -3
  31. package/dist/charts/ScatterPlot.svelte.d.ts +10 -1
  32. package/dist/charts/ViolinPlot.svelte +4 -2
  33. package/dist/charts/ViolinPlot.svelte.d.ts +1 -0
  34. package/dist/examples/BarChartExample.svelte +1 -1
  35. package/dist/geoms/Arc.svelte +3 -2
  36. package/dist/geoms/Area.svelte +20 -3
  37. package/dist/geoms/Bar.svelte +57 -13
  38. package/dist/geoms/Box.svelte +131 -76
  39. package/dist/geoms/Box.svelte.d.ts +7 -0
  40. package/dist/geoms/Candlestick.svelte +17 -11
  41. package/dist/geoms/Highlight.svelte +105 -0
  42. package/dist/geoms/Highlight.svelte.d.ts +10 -0
  43. package/dist/geoms/Jitter.svelte +82 -0
  44. package/dist/geoms/Jitter.svelte.d.ts +15 -0
  45. package/dist/geoms/Line.svelte +26 -9
  46. package/dist/geoms/Point.svelte +37 -11
  47. package/dist/geoms/Rule.svelte +113 -0
  48. package/dist/geoms/Rule.svelte.d.ts +16 -0
  49. package/dist/geoms/Trend.svelte +66 -0
  50. package/dist/geoms/Trend.svelte.d.ts +12 -0
  51. package/dist/geoms/Violin.svelte +30 -6
  52. package/dist/geoms/Violin.svelte.d.ts +5 -0
  53. package/dist/geoms/Waterfall.svelte +16 -9
  54. package/dist/geoms/lib/areas.d.ts +6 -2
  55. package/dist/geoms/lib/areas.js +17 -11
  56. package/dist/geoms/lib/bars.d.ts +18 -3
  57. package/dist/geoms/lib/bars.js +70 -96
  58. package/dist/index.d.ts +30 -4
  59. package/dist/index.js +31 -5
  60. package/dist/lib/brewing/ViolinBrewer.svelte.d.ts +1 -1
  61. package/dist/lib/brewing/brewer.svelte.d.ts +1 -0
  62. package/dist/lib/brewing/marks/arcs.js +7 -1
  63. package/dist/lib/brewing/marks/boxes.d.ts +11 -2
  64. package/dist/lib/brewing/marks/boxes.js +32 -11
  65. package/dist/lib/brewing/marks/lines.d.ts +6 -2
  66. package/dist/lib/brewing/marks/lines.js +19 -12
  67. package/dist/lib/brewing/marks/swarm.d.ts +32 -0
  68. package/dist/lib/brewing/marks/swarm.js +125 -0
  69. package/dist/lib/brewing/marks/violins.d.ts +4 -20
  70. package/dist/lib/brewing/marks/violins.js +41 -25
  71. package/dist/lib/brewing/stats.d.ts +3 -1
  72. package/dist/lib/brewing/stats.js +28 -11
  73. package/dist/lib/highlight.d.ts +10 -0
  74. package/dist/lib/highlight.js +43 -0
  75. package/dist/lib/plot/scales.js +12 -7
  76. package/dist/lib/plot/types.d.ts +9 -1
  77. package/dist/lib/plot/types.js +3 -1
  78. package/dist/lib/scale.d.ts +10 -0
  79. package/dist/lib/scale.js +14 -0
  80. package/dist/lib/select.d.ts +22 -0
  81. package/dist/lib/select.js +22 -0
  82. package/dist/lib/trend.d.ts +27 -0
  83. package/dist/lib/trend.js +113 -0
  84. package/dist/patterns/README.md +1 -1
  85. package/package.json +1 -1
  86. package/dist/Plot/Area.svelte +0 -54
  87. package/dist/Plot/Area.svelte.d.ts +0 -14
  88. package/dist/Plot/Bar.svelte +0 -69
  89. package/dist/Plot/Bar.svelte.d.ts +0 -11
  90. package/dist/Plot/Line.svelte +0 -54
  91. package/dist/Plot/Line.svelte.d.ts +0 -13
  92. package/dist/Plot/Point.svelte +0 -58
  93. package/dist/Plot/Point.svelte.d.ts +0 -13
@@ -34,6 +34,11 @@
34
34
  tween?: boolean
35
35
  sorted?: boolean
36
36
  dynamicDomain?: boolean
37
+ /** Chart direction for standard (non-race) animations — flips via the same geom
38
+ * place() mechanism as static charts. `flip` is sugar for 'horizontal'. Ignored for
39
+ * the bar-chart race, which is inherently horizontal. */
40
+ orientation?: 'vertical' | 'horizontal'
41
+ flip?: boolean
37
42
  label?: boolean | string | ((data: Row) => string)
38
43
  children?: Snippet
39
44
  }
@@ -53,16 +58,22 @@
53
58
  helpers = {},
54
59
  width = 600,
55
60
  height = 400,
56
- mode = 'light',
61
+ mode = undefined,
57
62
  grid = true,
58
63
  legend = false,
59
64
  tween = true,
60
65
  sorted = false,
61
66
  dynamicDomain = false,
67
+ orientation = undefined,
68
+ flip = false,
62
69
  label = false,
63
70
  children
64
71
  }: Props = $props()
65
72
 
73
+ // Standard-animation direction (non-race). Flows to the frameSpec so the geoms flip via the
74
+ // same place() path as static charts; the frame tween interpolates values orientation-agnostically.
75
+ const resolvedOrientation = $derived(flip ? 'horizontal' : orientation)
76
+
66
77
  // Effective geom list: explicit array takes precedence; otherwise build from shorthand props
67
78
  const effectiveGeoms = $derived(
68
79
  geoms.length > 0
@@ -348,23 +359,30 @@
348
359
  }))
349
360
  )
350
361
 
351
- const xDomainForFrame = $derived(
362
+ // The race's value-axis domain (the caller's x = the measured value), dynamic or static.
363
+ const valueDomainForFrame = $derived(
352
364
  isHorizontalRace && dynamicDomain ? $xDomainTween : staticDomains.xDomain
353
365
  )
354
366
  const frameSpec = $derived<PlotSpec>({
355
367
  data: $displayTween,
356
- x,
357
- y: isHorizontalRace ? '_rank' : y,
368
+ // Race uses the STANDARD convention so it flips through the same place() path as every
369
+ // chart: x = the continuous `_rank` (category/position axis), y = the caller's value.
370
+ // `continuousCategory` keeps the rank scale linear so fractional ranks tween smoothly.
371
+ x: isHorizontalRace ? '_rank' : x,
372
+ y: isHorizontalRace ? x : y,
358
373
  color,
359
374
  geoms: isHorizontalRace ? raceGeoms : prepared.geoms,
360
- xDomain: xDomainForFrame,
361
- yDomain: isHorizontalRace ? [0, entityCount - 1] : staticDomains.yDomain,
362
- orientation: isHorizontalRace ? 'horizontal' : undefined
375
+ // Pad the rank axis half a slot each end so the first/last bar's thickness doesn't spill
376
+ // past the axis (a continuous-category bar centres on the rank with a step-derived height).
377
+ xDomain: isHorizontalRace ? [-0.5, entityCount - 0.5] : staticDomains.xDomain,
378
+ yDomain: isHorizontalRace ? valueDomainForFrame : staticDomains.yDomain,
379
+ orientation: isHorizontalRace ? 'horizontal' : resolvedOrientation,
380
+ continuousCategory: isHorizontalRace
363
381
  })
364
382
  </script>
365
383
 
366
384
  <div data-plot-animated>
367
- <PlotChart spec={frameSpec} {helpers} {width} {height} {mode} {grid} {legend}>
385
+ <PlotChart spec={frameSpec} {helpers} {width} {height} {mode} {grid} {legend} animate={false}>
368
386
  {@render children?.()}
369
387
  </PlotChart>
370
388
 
@@ -27,6 +27,11 @@ type Props = {
27
27
  tween?: boolean;
28
28
  sorted?: boolean;
29
29
  dynamicDomain?: boolean;
30
+ /** Chart direction for standard (non-race) animations — flips via the same geom
31
+ * place() mechanism as static charts. `flip` is sugar for 'horizontal'. Ignored for
32
+ * the bar-chart race, which is inherently horizontal. */
33
+ orientation?: 'vertical' | 'horizontal';
34
+ flip?: boolean;
30
35
  label?: boolean | string | ((data: Row) => string);
31
36
  children?: Snippet;
32
37
  };
package/dist/Chart.svelte CHANGED
@@ -45,7 +45,7 @@
45
45
  symbol = undefined,
46
46
  width = 600,
47
47
  height = 400,
48
- mode = 'light',
48
+ mode = undefined,
49
49
  children
50
50
  }: Props = $props()
51
51
 
@@ -47,7 +47,7 @@
47
47
  panelHeight,
48
48
  width = 900,
49
49
  height = 300,
50
- mode = 'light',
50
+ mode = undefined,
51
51
  grid = true,
52
52
  legend = false,
53
53
  children
@@ -59,16 +59,21 @@
59
59
  const xTicks = $derived(buildTicks(state.xScale))
60
60
  const yTicks = $derived(buildTicks(state.yScale))
61
61
 
62
- const xTransform = $derived(`translate(0, ${state.xAxisY ?? state.innerHeight})`)
63
- const yTransform = $derived(`translate(${state.yAxisX ?? 0}, 0)`)
62
+ // When flipped, the x-channel (category) axis stands up on the left and the value (y)
63
+ // axis lies along the bottom — each axis type renders on the opposite screen orientation.
64
+ const flipped = $derived(state.isFlipped)
65
+ const renderAsX = $derived((type === 'x') !== flipped)
66
+ const ticks = $derived(type === 'x' ? xTicks : yTicks)
67
+ const hAxisY = $derived(flipped ? state.innerHeight : (state.xAxisY ?? state.innerHeight))
68
+ const vAxisX = $derived(flipped ? 0 : (state.yAxisX ?? 0))
64
69
  </script>
65
70
 
66
- {#if type === 'x'}
67
- <g class="axis x-axis" transform={xTransform} data-plot-axis="x">
71
+ {#if renderAsX}
72
+ <g class="axis x-axis" transform={`translate(0, ${hAxisY})`} data-plot-axis="x">
68
73
  {#if showLine}
69
74
  <line x1="0" y1="0" x2={state.innerWidth} y2="0" data-plot-axis-line />
70
75
  {/if}
71
- {#each xTicks as tick (tick.minor ? `minor-${tick.pos}` : tick.value)}
76
+ {#each ticks as tick (tick.minor ? `minor-${tick.pos}` : tick.value)}
72
77
  <g transform="translate({tick.pos}, 0)" data-plot-tick>
73
78
  {#if showTicks}
74
79
  <line
@@ -96,11 +101,11 @@
96
101
  {/if}
97
102
  </g>
98
103
  {:else}
99
- <g class="axis y-axis" transform={yTransform} data-plot-axis="y">
104
+ <g class="axis y-axis" transform={`translate(${vAxisX}, 0)`} data-plot-axis="y">
100
105
  {#if showLine}
101
106
  <line x1="0" y1="0" x2="0" y2={state.innerHeight} data-plot-axis-line />
102
107
  {/if}
103
- {#each yTicks as tick (tick.minor ? `minor-${tick.pos}` : tick.value)}
108
+ {#each ticks as tick (tick.minor ? `minor-${tick.pos}` : tick.value)}
104
109
  <g transform="translate(0, {tick.pos})" data-plot-tick>
105
110
  {#if showTicks}
106
111
  <line
@@ -130,16 +135,22 @@
130
135
  {/if}
131
136
 
132
137
  <style>
138
+ /* Readable, theme-adaptive axis colours (fall back to currentColor for a base-only
139
+ consumer that doesn't provide the --ink token vocabulary). currentColor alone was a
140
+ mode-hardcoded surface tone that went faint on a dark theme paper. */
133
141
  .axis {
134
142
  font-size: 11px;
135
- fill: currentColor;
136
- stroke: currentColor;
143
+ fill: var(--ink-mute, currentColor);
144
+ stroke: var(--ink-mute, currentColor);
137
145
  }
138
146
  .axis-label {
139
147
  font-size: 13px;
140
148
  font-weight: 500;
149
+ fill: var(--ink, currentColor);
141
150
  }
142
151
  [data-plot-axis-line] {
143
- stroke: currentColor;
152
+ stroke: var(--paper-edge, currentColor);
153
+ /* Decoration only — never intercept clicks meant for data marks beneath it. */
154
+ pointer-events: none;
144
155
  }
145
156
  </style>
@@ -2,26 +2,44 @@
2
2
  import { getContext } from 'svelte'
3
3
  import type { PlotState } from '../PlotState.svelte.js'
4
4
 
5
+ type Props = {
6
+ lines?: 'auto' | 'x' | 'y' | 'both'
7
+ xTicks?: number
8
+ yTicks?: number
9
+ }
10
+ let { lines = 'auto', xTicks = 6, yTicks = 6 }: Props = $props()
11
+
5
12
  const state = getContext<PlotState>('plot-state')
6
13
 
7
- const xGridLines = $derived.by(() => {
8
- const s = state.xScale
9
- if (!s || typeof s.bandwidth !== 'function') return []
10
- return s.domain().map((val: unknown) => ({ pos: (s(val) ?? 0) + s.bandwidth() / 2 }))
11
- })
14
+ const isBand = (s: unknown) =>
15
+ !!s && typeof (s as { bandwidth?: unknown }).bandwidth === 'function'
12
16
 
13
- const yGridLines = $derived.by(() => {
17
+ const showX = $derived(
18
+ lines === 'x' || lines === 'both' || (lines === 'auto' && isBand(state.xScale))
19
+ )
20
+ const showY = $derived(lines === 'auto' || lines === 'y' || lines === 'both')
21
+
22
+ const yLines = $derived.by(() => {
14
23
  const s = state.yScale
15
- if (!s || typeof s.ticks !== 'function') return []
16
- return s.ticks(6).map((val: number) => ({ pos: s(val) }))
24
+ if (!showY || !s || typeof s.ticks !== 'function') return []
25
+ return s.ticks(yTicks).map((val: number) => ({ pos: s(val) }))
26
+ })
27
+
28
+ const xLines = $derived.by(() => {
29
+ const s = state.xScale
30
+ if (!showX || !s) return []
31
+ if (isBand(s))
32
+ return s.domain().map((val: unknown) => ({ pos: (s(val) ?? 0) + s.bandwidth() / 2 }))
33
+ if (typeof s.ticks !== 'function') return []
34
+ return s.ticks(xTicks).map((val: number) => ({ pos: s(val) }))
17
35
  })
18
36
  </script>
19
37
 
20
38
  <g class="grid" data-plot-grid>
21
- {#each yGridLines as line (line.pos)}
22
- <line x1="0" y1={line.pos} x2={state.innerWidth} y2={line.pos} data-plot-grid-line />
39
+ {#each yLines as line (line.pos)}
40
+ <line x1="0" y1={line.pos} x2={state.innerWidth} y2={line.pos} data-plot-grid-line="y" />
23
41
  {/each}
24
- {#each xGridLines as line (line.pos)}
42
+ {#each xLines as line (line.pos)}
25
43
  <line x1={line.pos} y1="0" x2={line.pos} y2={state.innerHeight} data-plot-grid-line="x" />
26
44
  {/each}
27
45
  </g>
@@ -29,7 +47,8 @@
29
47
  <style>
30
48
  [data-plot-grid-line] {
31
49
  stroke: var(--chart-grid-color, currentColor);
32
- opacity: 0.15;
33
- stroke-dasharray: 2 4;
50
+ stroke-width: var(--chart-grid-width, 1);
51
+ stroke-dasharray: var(--chart-grid-dash, 2 4);
52
+ opacity: var(--chart-grid-opacity, 0.15);
34
53
  }
35
54
  </style>
@@ -1,3 +1,8 @@
1
- declare const Grid: import("svelte").Component<Record<string, never>, {}, "">;
1
+ type Props = {
2
+ lines?: 'auto' | 'x' | 'y' | 'both';
3
+ xTicks?: number;
4
+ yTicks?: number;
5
+ };
6
+ declare const Grid: import("svelte").Component<Props, {}, "">;
2
7
  type Grid = ReturnType<typeof Grid>;
3
8
  export default Grid;
@@ -1,13 +1,13 @@
1
1
  <script lang="ts">
2
- import { setContext, untrack } from 'svelte'
3
2
  import type { Snippet } from 'svelte'
4
- import { PlotState } from '../PlotState.svelte.js'
5
3
  import { defaultPreset } from '../lib/preset.js'
4
+ import PlotSurface from '../PlotSurface.svelte'
6
5
 
7
6
  type Margin = { top?: number; right?: number; bottom?: number; left?: number }
7
+ type Row = Record<string, unknown>
8
8
 
9
9
  type Props = {
10
- data?: Record<string, unknown>[]
10
+ data?: Row[]
11
11
  x?: string
12
12
  y?: string
13
13
  color?: string
@@ -15,6 +15,30 @@
15
15
  height?: number
16
16
  margin?: Margin
17
17
  mode?: 'light' | 'dark'
18
+ /** Chart direction. 'horizontal' stands the category axis up and runs the value
19
+ * axis sideways — x/y channels are unchanged. `flip` is sugar for 'horizontal'. */
20
+ orientation?: 'vertical' | 'horizontal'
21
+ flip?: boolean
22
+ /** Order the category (band) axis by aggregated value instead of by label — bars sorted
23
+ * by size (histogram-style). 'desc' (largest first) | 'asc'. */
24
+ sort?: 'asc' | 'desc'
25
+ /** Explicit scale domain overrides. */
26
+ xDomain?: unknown[]
27
+ yDomain?: unknown[]
28
+ /** Continuous colour scale controls (sequential/diverging). */
29
+ colorScale?: unknown
30
+ colorScheme?: string
31
+ colorDomain?: unknown[]
32
+ colorMidpoint?: number
33
+ /** Enable d3-zoom pan/zoom. */
34
+ zoom?: boolean
35
+ /** Opt-in click-to-select; fires onselect(detail) and syncs bind:selected (row refs). */
36
+ selectable?: boolean
37
+ onselect?: (detail: unknown) => void
38
+ selected?: Row[]
39
+ /** Animate marks on data/flip changes. Enabled one frame after mount so the
40
+ * initial layout paints un-animated. Default `true`. */
41
+ animate?: boolean
18
42
  children?: Snippet
19
43
  }
20
44
 
@@ -26,52 +50,46 @@
26
50
  width = 600,
27
51
  height = 400,
28
52
  margin = undefined,
29
- mode = 'light',
53
+ mode = undefined,
54
+ orientation = undefined,
55
+ flip = false,
56
+ sort = undefined,
57
+ xDomain = undefined,
58
+ yDomain = undefined,
59
+ colorScale = undefined,
60
+ colorScheme = undefined,
61
+ colorDomain = undefined,
62
+ colorMidpoint = undefined,
63
+ zoom = false,
64
+ selectable = false,
65
+ onselect = undefined,
66
+ selected = $bindable([]),
67
+ animate = true,
30
68
  children
31
69
  }: Props = $props()
32
70
 
33
- const plotState = untrack(
34
- () =>
35
- new PlotState({
36
- data,
37
- channels: { x, y, color },
38
- width,
39
- height,
40
- margin,
41
- mode,
42
- chartPreset: defaultPreset
43
- })
44
- )
45
-
46
- $effect(() => {
47
- plotState.update({
48
- data,
49
- channels: { x, y, color },
50
- width,
51
- height,
52
- margin,
53
- mode,
54
- chartPreset: defaultPreset
55
- })
71
+ // The composable root is just PlotSurface (the shared shell) fed a config from these props.
72
+ const config = $derived({
73
+ data,
74
+ channels: { x, y, color },
75
+ width,
76
+ height,
77
+ margin,
78
+ mode,
79
+ orientation: flip ? 'horizontal' : orientation,
80
+ sort,
81
+ xDomain,
82
+ yDomain,
83
+ colorScale,
84
+ colorScheme,
85
+ colorDomain,
86
+ colorMidpoint,
87
+ onselect,
88
+ selectable,
89
+ chartPreset: defaultPreset
56
90
  })
57
-
58
- setContext('plot-state', plotState)
59
-
60
- const svgWidth = $derived(plotState.innerWidth + (margin?.left ?? 50) + (margin?.right ?? 30))
61
- const svgHeight = $derived(plotState.innerHeight + (margin?.top ?? 20) + (margin?.bottom ?? 40))
62
- const marginLeft = $derived(margin?.left ?? 50)
63
- const marginTop = $derived(margin?.top ?? 20)
64
91
  </script>
65
92
 
66
- <svg
67
- {width}
68
- {height}
69
- viewBox="0 0 {svgWidth} {svgHeight}"
70
- role="img"
71
- aria-label="Chart visualization"
72
- data-plot-root
73
- >
74
- <g transform="translate({marginLeft}, {marginTop})" data-plot-canvas>
75
- {@render children?.()}
76
- </g>
77
- </svg>
93
+ <PlotSurface {config} {animate} {zoom} bind:selected>
94
+ {@render children?.()}
95
+ </PlotSurface>
@@ -5,8 +5,9 @@ type Margin = {
5
5
  bottom?: number;
6
6
  left?: number;
7
7
  };
8
+ type Row = Record<string, unknown>;
8
9
  type Props = {
9
- data?: Record<string, unknown>[];
10
+ data?: Row[];
10
11
  x?: string;
11
12
  y?: string;
12
13
  color?: string;
@@ -14,8 +15,32 @@ type Props = {
14
15
  height?: number;
15
16
  margin?: Margin;
16
17
  mode?: 'light' | 'dark';
18
+ /** Chart direction. 'horizontal' stands the category axis up and runs the value
19
+ * axis sideways — x/y channels are unchanged. `flip` is sugar for 'horizontal'. */
20
+ orientation?: 'vertical' | 'horizontal';
21
+ flip?: boolean;
22
+ /** Order the category (band) axis by aggregated value instead of by label — bars sorted
23
+ * by size (histogram-style). 'desc' (largest first) | 'asc'. */
24
+ sort?: 'asc' | 'desc';
25
+ /** Explicit scale domain overrides. */
26
+ xDomain?: unknown[];
27
+ yDomain?: unknown[];
28
+ /** Continuous colour scale controls (sequential/diverging). */
29
+ colorScale?: unknown;
30
+ colorScheme?: string;
31
+ colorDomain?: unknown[];
32
+ colorMidpoint?: number;
33
+ /** Enable d3-zoom pan/zoom. */
34
+ zoom?: boolean;
35
+ /** Opt-in click-to-select; fires onselect(detail) and syncs bind:selected (row refs). */
36
+ selectable?: boolean;
37
+ onselect?: (detail: unknown) => void;
38
+ selected?: Row[];
39
+ /** Animate marks on data/flip changes. Enabled one frame after mount so the
40
+ * initial layout paints un-animated. Default `true`. */
41
+ animate?: boolean;
17
42
  children?: Snippet;
18
43
  };
19
- declare const Root: import("svelte").Component<Props, {}, "">;
44
+ declare const Root: import("svelte").Component<Props, {}, "selected">;
20
45
  type Root = ReturnType<typeof Root>;
21
46
  export default Root;
@@ -24,8 +24,6 @@
24
24
  const safeIndex = $derived(
25
25
  frameKeys.length === 0 ? 0 : Math.min(currentIndex, frameKeys.length - 1)
26
26
  )
27
-
28
- const SPEEDS = [0.5, 1, 1.5, 2, 4]
29
27
  </script>
30
28
 
31
29
  <div class="timeline" data-plot-timeline>
@@ -56,17 +54,20 @@
56
54
  data-plot-timeline-scrub
57
55
  />
58
56
 
59
- <!-- Speed selector -->
60
- <select
61
- aria-label="Playback speed"
62
- value={speed}
63
- onchange={(e) => onspeed?.(Number(e.currentTarget.value))}
64
- data-plot-timeline-speed
65
- >
66
- {#each SPEEDS as s (s)}
67
- <option value={s}>{s}×</option>
68
- {/each}
69
- </select>
57
+ <!-- Speed (× multiplier) — themed numeric input -->
58
+ <div class="speed">
59
+ <input
60
+ type="number"
61
+ min="0.25"
62
+ max="8"
63
+ step="0.25"
64
+ value={speed}
65
+ aria-label="Playback speed"
66
+ onchange={(e) => onspeed?.(Math.max(0.25, Number(e.currentTarget.value) || 1))}
67
+ data-plot-timeline-speed
68
+ />
69
+ <span class="speed-unit" aria-hidden="true">×</span>
70
+ </div>
70
71
  </div>
71
72
 
72
73
  <style>
@@ -76,6 +77,7 @@
76
77
  gap: 8px;
77
78
  padding: 8px 0;
78
79
  font-size: 12px;
80
+ color: var(--ink-mute, currentColor);
79
81
  }
80
82
  .play-pause {
81
83
  font-size: 16px;
@@ -83,12 +85,36 @@
83
85
  background: none;
84
86
  border: none;
85
87
  padding: 0;
88
+ color: var(--ink, currentColor);
86
89
  }
87
90
  .scrub {
88
91
  flex: 1;
92
+ accent-color: var(--accent, currentColor);
89
93
  }
90
94
  .frame-label {
91
95
  min-width: 4ch;
92
96
  text-align: right;
97
+ color: var(--ink, currentColor);
98
+ }
99
+ .speed {
100
+ display: inline-flex;
101
+ align-items: center;
102
+ gap: 2px;
103
+ }
104
+ .speed input {
105
+ width: 3.5em;
106
+ font: inherit;
107
+ color: var(--ink, currentColor);
108
+ background: var(--paper, transparent);
109
+ border: 1px solid var(--paper-edge, currentColor);
110
+ border-radius: 4px;
111
+ padding: 2px 4px;
112
+ }
113
+ .speed input:focus-visible {
114
+ outline: 2px solid var(--accent, currentColor);
115
+ outline-offset: 1px;
116
+ }
117
+ .speed-unit {
118
+ color: var(--ink-mute, currentColor);
93
119
  }
94
120
  </style>
@@ -1,9 +1,9 @@
1
1
  export { default as Root } from "./Root.svelte";
2
2
  export { default as Axis } from "./Axis.svelte";
3
- export { default as Bar } from "./Bar.svelte";
3
+ export { default as Bar } from "../geoms/Bar.svelte";
4
4
  export { default as Legend } from "./Legend.svelte";
5
5
  export { default as Grid } from "./Grid.svelte";
6
- export { default as Line } from "./Line.svelte";
7
- export { default as Area } from "./Area.svelte";
8
- export { default as Point } from "./Point.svelte";
6
+ export { default as Line } from "../geoms/Line.svelte";
7
+ export { default as Area } from "../geoms/Area.svelte";
8
+ export { default as Point } from "../geoms/Point.svelte";
9
9
  export { default as Arc } from "./Arc.svelte";
@@ -1,9 +1,9 @@
1
1
  export { default as Root } from './Root.svelte'
2
2
  export { default as Axis } from './Axis.svelte'
3
- export { default as Bar } from './Bar.svelte'
3
+ export { default as Bar } from '../geoms/Bar.svelte'
4
4
  export { default as Legend } from './Legend.svelte'
5
5
  export { default as Grid } from './Grid.svelte'
6
- export { default as Line } from './Line.svelte'
7
- export { default as Area } from './Area.svelte'
8
- export { default as Point } from './Point.svelte'
6
+ export { default as Line } from '../geoms/Line.svelte'
7
+ export { default as Area } from '../geoms/Area.svelte'
8
+ export { default as Point } from '../geoms/Point.svelte'
9
9
  export { default as Arc } from './Arc.svelte'