@matthieumordrel/chart-studio 0.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 (160) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +371 -0
  3. package/dist/core/chart-capabilities.d.ts +60 -0
  4. package/dist/core/chart-capabilities.d.ts.map +1 -0
  5. package/dist/core/chart-capabilities.js +55 -0
  6. package/dist/core/chart-capabilities.js.map +1 -0
  7. package/dist/core/colors.d.ts +25 -0
  8. package/dist/core/colors.d.ts.map +1 -0
  9. package/dist/core/colors.js +55 -0
  10. package/dist/core/colors.js.map +1 -0
  11. package/dist/core/config-utils.d.ts +43 -0
  12. package/dist/core/config-utils.d.ts.map +1 -0
  13. package/dist/core/config-utils.js +81 -0
  14. package/dist/core/config-utils.js.map +1 -0
  15. package/dist/core/date-utils.d.ts +29 -0
  16. package/dist/core/date-utils.d.ts.map +1 -0
  17. package/dist/core/date-utils.js +59 -0
  18. package/dist/core/date-utils.js.map +1 -0
  19. package/dist/core/define-chart-schema.d.ts +105 -0
  20. package/dist/core/define-chart-schema.d.ts.map +1 -0
  21. package/dist/core/define-chart-schema.js +45 -0
  22. package/dist/core/define-chart-schema.js.map +1 -0
  23. package/dist/core/formatting.d.ts +47 -0
  24. package/dist/core/formatting.d.ts.map +1 -0
  25. package/dist/core/formatting.js +397 -0
  26. package/dist/core/formatting.js.map +1 -0
  27. package/dist/core/index.d.ts +17 -0
  28. package/dist/core/index.d.ts.map +1 -0
  29. package/dist/core/index.js +13 -0
  30. package/dist/core/index.js.map +1 -0
  31. package/dist/core/infer-columns.d.ts +6 -0
  32. package/dist/core/infer-columns.d.ts.map +1 -0
  33. package/dist/core/infer-columns.js +513 -0
  34. package/dist/core/infer-columns.js.map +1 -0
  35. package/dist/core/metric-utils.d.ts +43 -0
  36. package/dist/core/metric-utils.d.ts.map +1 -0
  37. package/dist/core/metric-utils.js +142 -0
  38. package/dist/core/metric-utils.js.map +1 -0
  39. package/dist/core/pipeline-data-points.d.ts +23 -0
  40. package/dist/core/pipeline-data-points.d.ts.map +1 -0
  41. package/dist/core/pipeline-data-points.js +236 -0
  42. package/dist/core/pipeline-data-points.js.map +1 -0
  43. package/dist/core/pipeline-helpers.d.ts +38 -0
  44. package/dist/core/pipeline-helpers.d.ts.map +1 -0
  45. package/dist/core/pipeline-helpers.js +98 -0
  46. package/dist/core/pipeline-helpers.js.map +1 -0
  47. package/dist/core/pipeline.d.ts +70 -0
  48. package/dist/core/pipeline.d.ts.map +1 -0
  49. package/dist/core/pipeline.js +157 -0
  50. package/dist/core/pipeline.js.map +1 -0
  51. package/dist/core/types.d.ts +1109 -0
  52. package/dist/core/types.d.ts.map +1 -0
  53. package/dist/core/types.js +15 -0
  54. package/dist/core/types.js.map +1 -0
  55. package/dist/core/use-chart-options.d.ts +66 -0
  56. package/dist/core/use-chart-options.d.ts.map +1 -0
  57. package/dist/core/use-chart-options.js +5 -0
  58. package/dist/core/use-chart-options.js.map +1 -0
  59. package/dist/core/use-chart-resolvers.d.ts +14 -0
  60. package/dist/core/use-chart-resolvers.d.ts.map +1 -0
  61. package/dist/core/use-chart-resolvers.js +42 -0
  62. package/dist/core/use-chart-resolvers.js.map +1 -0
  63. package/dist/core/use-chart.d.ts +47 -0
  64. package/dist/core/use-chart.d.ts.map +1 -0
  65. package/dist/core/use-chart.js +266 -0
  66. package/dist/core/use-chart.js.map +1 -0
  67. package/dist/index.d.ts +36 -0
  68. package/dist/index.d.ts.map +1 -0
  69. package/dist/index.js +36 -0
  70. package/dist/index.js.map +1 -0
  71. package/dist/ui/chart-axis-ticks.d.ts +35 -0
  72. package/dist/ui/chart-axis-ticks.d.ts.map +1 -0
  73. package/dist/ui/chart-axis-ticks.js +80 -0
  74. package/dist/ui/chart-axis-ticks.js.map +1 -0
  75. package/dist/ui/chart-canvas.d.ts +22 -0
  76. package/dist/ui/chart-canvas.d.ts.map +1 -0
  77. package/dist/ui/chart-canvas.js +338 -0
  78. package/dist/ui/chart-canvas.js.map +1 -0
  79. package/dist/ui/chart-context.d.ts +89 -0
  80. package/dist/ui/chart-context.d.ts.map +1 -0
  81. package/dist/ui/chart-context.js +129 -0
  82. package/dist/ui/chart-context.js.map +1 -0
  83. package/dist/ui/chart-date-range-badge.d.ts +14 -0
  84. package/dist/ui/chart-date-range-badge.d.ts.map +1 -0
  85. package/dist/ui/chart-date-range-badge.js +31 -0
  86. package/dist/ui/chart-date-range-badge.js.map +1 -0
  87. package/dist/ui/chart-date-range-panel.d.ts +25 -0
  88. package/dist/ui/chart-date-range-panel.d.ts.map +1 -0
  89. package/dist/ui/chart-date-range-panel.js +126 -0
  90. package/dist/ui/chart-date-range-panel.js.map +1 -0
  91. package/dist/ui/chart-date-range.d.ts +14 -0
  92. package/dist/ui/chart-date-range.d.ts.map +1 -0
  93. package/dist/ui/chart-date-range.js +38 -0
  94. package/dist/ui/chart-date-range.js.map +1 -0
  95. package/dist/ui/chart-debug.d.ts +10 -0
  96. package/dist/ui/chart-debug.d.ts.map +1 -0
  97. package/dist/ui/chart-debug.js +127 -0
  98. package/dist/ui/chart-debug.js.map +1 -0
  99. package/dist/ui/chart-dropdown.d.ts +35 -0
  100. package/dist/ui/chart-dropdown.d.ts.map +1 -0
  101. package/dist/ui/chart-dropdown.js +77 -0
  102. package/dist/ui/chart-dropdown.js.map +1 -0
  103. package/dist/ui/chart-filters-panel.d.ts +19 -0
  104. package/dist/ui/chart-filters-panel.d.ts.map +1 -0
  105. package/dist/ui/chart-filters-panel.js +47 -0
  106. package/dist/ui/chart-filters-panel.js.map +1 -0
  107. package/dist/ui/chart-filters.d.ts +12 -0
  108. package/dist/ui/chart-filters.d.ts.map +1 -0
  109. package/dist/ui/chart-filters.js +27 -0
  110. package/dist/ui/chart-filters.js.map +1 -0
  111. package/dist/ui/chart-group-by-selector.d.ts +8 -0
  112. package/dist/ui/chart-group-by-selector.d.ts.map +1 -0
  113. package/dist/ui/chart-group-by-selector.js +20 -0
  114. package/dist/ui/chart-group-by-selector.js.map +1 -0
  115. package/dist/ui/chart-metric-panel.d.ts +18 -0
  116. package/dist/ui/chart-metric-panel.d.ts.map +1 -0
  117. package/dist/ui/chart-metric-panel.js +119 -0
  118. package/dist/ui/chart-metric-panel.js.map +1 -0
  119. package/dist/ui/chart-metric-selector.d.ts +10 -0
  120. package/dist/ui/chart-metric-selector.d.ts.map +1 -0
  121. package/dist/ui/chart-metric-selector.js +28 -0
  122. package/dist/ui/chart-metric-selector.js.map +1 -0
  123. package/dist/ui/chart-select.d.ts +25 -0
  124. package/dist/ui/chart-select.d.ts.map +1 -0
  125. package/dist/ui/chart-select.js +36 -0
  126. package/dist/ui/chart-select.js.map +1 -0
  127. package/dist/ui/chart-source-switcher.d.ts +16 -0
  128. package/dist/ui/chart-source-switcher.d.ts.map +1 -0
  129. package/dist/ui/chart-source-switcher.js +32 -0
  130. package/dist/ui/chart-source-switcher.js.map +1 -0
  131. package/dist/ui/chart-time-bucket-selector.d.ts +9 -0
  132. package/dist/ui/chart-time-bucket-selector.d.ts.map +1 -0
  133. package/dist/ui/chart-time-bucket-selector.js +26 -0
  134. package/dist/ui/chart-time-bucket-selector.js.map +1 -0
  135. package/dist/ui/chart-toolbar-overflow.d.ts +29 -0
  136. package/dist/ui/chart-toolbar-overflow.d.ts.map +1 -0
  137. package/dist/ui/chart-toolbar-overflow.js +110 -0
  138. package/dist/ui/chart-toolbar-overflow.js.map +1 -0
  139. package/dist/ui/chart-toolbar.d.ts +45 -0
  140. package/dist/ui/chart-toolbar.d.ts.map +1 -0
  141. package/dist/ui/chart-toolbar.js +45 -0
  142. package/dist/ui/chart-toolbar.js.map +1 -0
  143. package/dist/ui/chart-type-selector.d.ts +8 -0
  144. package/dist/ui/chart-type-selector.d.ts.map +1 -0
  145. package/dist/ui/chart-type-selector.js +23 -0
  146. package/dist/ui/chart-type-selector.js.map +1 -0
  147. package/dist/ui/chart-x-axis-selector.d.ts +8 -0
  148. package/dist/ui/chart-x-axis-selector.d.ts.map +1 -0
  149. package/dist/ui/chart-x-axis-selector.js +15 -0
  150. package/dist/ui/chart-x-axis-selector.js.map +1 -0
  151. package/dist/ui/index.d.ts +25 -0
  152. package/dist/ui/index.d.ts.map +1 -0
  153. package/dist/ui/index.js +24 -0
  154. package/dist/ui/index.js.map +1 -0
  155. package/dist/ui/theme.css +62 -0
  156. package/dist/ui/toolbar-types.d.ts +43 -0
  157. package/dist/ui/toolbar-types.d.ts.map +1 -0
  158. package/dist/ui/toolbar-types.js +51 -0
  159. package/dist/ui/toolbar-types.js.map +1 -0
  160. package/package.json +76 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Matthieu Mordrel
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,371 @@
1
+ # chart-studio
2
+
3
+ Composable charting for React with two adoption paths:
4
+
5
+ - use the **headless core** if you want chart state, filtering, grouping, and transformed data
6
+ - use the **optional UI layer** if you also want ready-made controls and a Recharts canvas
7
+
8
+ Package: `@matthieumordrel/chart-studio`
9
+
10
+ ## Start Here
11
+
12
+ Choose the path that matches your app:
13
+
14
+ ### 1. Headless core
15
+
16
+ Use this if you already have your own design system or chart renderer.
17
+
18
+ You get:
19
+
20
+ - `useChart`
21
+ - optional `schema` via `defineChartSchema`
22
+ - transformed chart data
23
+ - filtering, grouping, metrics, and time bucketing logic
24
+
25
+ Install:
26
+
27
+ ```bash
28
+ bun add @matthieumordrel/chart-studio react
29
+ ```
30
+
31
+ Import from:
32
+
33
+ ```tsx
34
+ import { useChart } from '@matthieumordrel/chart-studio'
35
+ ```
36
+
37
+ ### 2. Ready-made UI
38
+
39
+ Use this if you want the package to render the controls and chart for you.
40
+
41
+ You get:
42
+
43
+ - everything from the headless core
44
+ - `<Chart>`
45
+ - `<ChartToolbar>`
46
+ - `<ChartCanvas>`
47
+ - granular UI controls from `@matthieumordrel/chart-studio/ui`
48
+
49
+ Install:
50
+
51
+ ```bash
52
+ bun add @matthieumordrel/chart-studio react recharts lucide-react
53
+ ```
54
+
55
+ Then import the package theme once in your app stylesheet:
56
+
57
+ ```css
58
+ @import 'tailwindcss';
59
+ @import '@matthieumordrel/chart-studio/ui/theme.css';
60
+ ```
61
+
62
+ Import from:
63
+
64
+ ```tsx
65
+ import { useChart } from '@matthieumordrel/chart-studio'
66
+ import { Chart, ChartToolbar, ChartCanvas } from '@matthieumordrel/chart-studio/ui'
67
+ ```
68
+
69
+ ## Smallest Working Example (Single Source)
70
+
71
+ ```tsx
72
+ import { useChart } from '@matthieumordrel/chart-studio'
73
+ import { Chart, ChartToolbar, ChartCanvas } from '@matthieumordrel/chart-studio/ui'
74
+ import { data } from './data.json'
75
+
76
+ export function JobsChart() {
77
+ const chart = useChart({ data })
78
+
79
+ return (
80
+ <Chart chart={chart}>
81
+ <ChartToolbar />
82
+ <ChartCanvas height={320} />
83
+ </Chart>
84
+ )
85
+ }
86
+ ```
87
+
88
+ ## How It Works
89
+
90
+ 1. Pass your raw data to `useChart()`.
91
+ 2. Add an optional `schema` with `defineChartSchema<Row>()(...)` when you need labels, type overrides, derived columns, or control restrictions (allowed metrics, groupings, chart types, etc.).
92
+ 3. Either render your own UI from the returned state, or use the components from `@matthieumordrel/chart-studio/ui`.
93
+
94
+ ## Column Types
95
+
96
+ | Type | What it is for |
97
+ | ---------- | --------------------------------------- |
98
+ | `date` | time-series X-axis |
99
+ | `category` | categorical X-axis, grouping, filtering |
100
+ | `boolean` | grouping, filtering |
101
+ | `number` | metrics such as sum, avg, min, max |
102
+
103
+ ## Declarative Schema and Control Restrictions
104
+
105
+ If you want to expose only a subset of groupings, metrics, chart types, or axes, use `defineChartSchema<Row>()()` with the control sections:
106
+
107
+ ```tsx
108
+ import { defineChartSchema, useChart } from '@matthieumordrel/chart-studio'
109
+
110
+ type Row = { periodEnd: string; segment: string; revenue: number; netIncome: number }
111
+
112
+ const schema = defineChartSchema<Row>()({
113
+ columns: {
114
+ periodEnd: { type: 'date', label: 'Period End' },
115
+ segment: { type: 'category' },
116
+ revenue: { type: 'number' },
117
+ netIncome: { type: 'number' }
118
+ },
119
+ xAxis: { allowed: ['periodEnd'] },
120
+ groupBy: { allowed: ['segment'] },
121
+ metric: {
122
+ allowed: [
123
+ { kind: 'count' },
124
+ { kind: 'aggregate', columnId: 'revenue', aggregate: ['sum', 'avg'] },
125
+ { kind: 'aggregate', columnId: 'netIncome', aggregate: 'sum' }
126
+ ]
127
+ },
128
+ chartType: { allowed: ['bar', 'line'] },
129
+ timeBucket: { allowed: ['year', 'quarter', 'month'] }
130
+ })
131
+
132
+ const chart = useChart({ data, schema })
133
+ ```
134
+
135
+ Why this pattern:
136
+
137
+ - `columns` defines types, labels, and formats for raw fields; use `false` to exclude a column from the chart
138
+ - Derived columns use `{ kind: 'derived', type, label?, accessor, format? }` for computed values from each row
139
+ - `xAxis`, `groupBy`, `metric`, `chartType`, `timeBucket` restrict the allowed options
140
+ - invalid column IDs and config keys are rejected at compile time
141
+ - metric restrictions preserve the order you declare, so the first allowed metric becomes the default
142
+
143
+ ## Headless Example
144
+
145
+ If you want to render your own UI or your own charting library, use only the core state:
146
+
147
+ ```tsx
148
+ import { defineChartSchema, useChart } from '@matthieumordrel/chart-studio'
149
+
150
+ type Job = {
151
+ dateAdded: string
152
+ ownerName: string
153
+ salary: number
154
+ }
155
+
156
+ const jobSchema = defineChartSchema<Job>()({
157
+ columns: {
158
+ dateAdded: { type: 'date', label: 'Date Added' },
159
+ ownerName: { type: 'category', label: 'Consultant' },
160
+ salary: { type: 'number', label: 'Salary' }
161
+ }
162
+ })
163
+
164
+ export function JobsChartHeadless({ data }: { data: Job[] }) {
165
+ const chart = useChart({ data, schema: jobSchema })
166
+
167
+ return (
168
+ <div>
169
+ <div>Chart type: {chart.chartType}</div>
170
+ <div>Rows: {chart.transformedData.length}</div>
171
+ <pre>{JSON.stringify(chart.transformedData, null, 2)}</pre>
172
+ </div>
173
+ )
174
+ }
175
+ ```
176
+
177
+ ## Styling Requirements
178
+
179
+ The headless core has no styling requirements.
180
+
181
+ The `ui` layer is Tailwind-based and uses semantic classes such as:
182
+
183
+ - `bg-background`
184
+ - `text-foreground`
185
+ - `border-border`
186
+ - `bg-popover`
187
+ - `text-muted-foreground`
188
+
189
+ For those classes to render correctly, Tailwind needs real values behind tokens like `background`, `foreground`, `border`, and `popover`.
190
+
191
+ You can use `ui` in two ways:
192
+
193
+ ### 1. Recommended: import the built-in theme
194
+
195
+ This is the easiest setup:
196
+
197
+ ```css
198
+ @import 'tailwindcss';
199
+ @import '@matthieumordrel/chart-studio/ui/theme.css';
200
+ ```
201
+
202
+ This does three things for you:
203
+
204
+ - Tailwind utilities for the package components
205
+ - automatic scanning of the package UI classes
206
+ - default fallback values for all semantic UI tokens
207
+ - built-in light and dark default themes
208
+
209
+ If your app already defines matching shadcn-style variables, those values take over automatically. If not, the built-in defaults are used.
210
+
211
+ The shipped theme supports dark mode through either:
212
+
213
+ - `.dark`
214
+ - `[data-theme="dark"]`
215
+
216
+ ### 2. Advanced: define everything yourself
217
+
218
+ If you do not want to import `@matthieumordrel/chart-studio/ui/theme.css`, you can provide all the required semantic tokens yourself in your app theme.
219
+
220
+ If neither of those is true, use the headless core and render your own controls.
221
+
222
+ ### Minimum UI theme contract
223
+
224
+ You do not need shadcn itself to use `@matthieumordrel/chart-studio/ui`.
225
+
226
+ If you import `@matthieumordrel/chart-studio/ui/theme.css`, every token below gets a built-in fallback automatically.
227
+
228
+ If your app already defines some of these variables, your values override the defaults for those specific tokens only. Missing ones still fall back to the package defaults.
229
+
230
+ These are the tokens currently expected by the UI layer:
231
+
232
+ | Token | Purpose |
233
+ | -------------------- | -------------------------------------- |
234
+ | `background` | control backgrounds and input surfaces |
235
+ | `foreground` | primary text |
236
+ | `muted` | subtle backgrounds and hover states |
237
+ | `muted-foreground` | secondary text and icons |
238
+ | `border` | outlines and separators |
239
+ | `popover` | dropdowns and floating panels |
240
+ | `popover-foreground` | popover text color |
241
+ | `primary` | selected and active states |
242
+ | `primary-foreground` | text on filled primary surfaces |
243
+ | `ring` | focus-visible ring color |
244
+
245
+ Minimal example:
246
+
247
+ ```css
248
+ :root {
249
+ --background: 0 0% 100%;
250
+ --foreground: 222.2 84% 4.9%;
251
+ --muted: 210 40% 96.1%;
252
+ --muted-foreground: 215.4 16.3% 46.9%;
253
+ --border: 214.3 31.8% 91.4%;
254
+ --popover: 0 0% 100%;
255
+ --popover-foreground: 222.2 84% 4.9%;
256
+ --primary: 222.2 47.4% 11.2%;
257
+ --primary-foreground: 210 40% 98%;
258
+ --ring: 221.2 83.2% 53.3%;
259
+ }
260
+ ```
261
+
262
+ How this works in practice:
263
+
264
+ - import `ui/theme.css` and do nothing else: the package uses its own defaults
265
+ - toggle dark mode with either `.dark` or `[data-theme="dark"]`: the package uses its built-in dark defaults
266
+ - import `ui/theme.css` and define only a few variables: your values win for those variables, defaults cover the rest
267
+ - skip `ui/theme.css`: you must define the whole token contract yourself
268
+
269
+ That makes the package usable out of the box while still being easy to theme.
270
+
271
+ ### Optional chart color tokens
272
+
273
+ Chart series colors also support shadcn-style chart variables:
274
+
275
+ | Token | Purpose |
276
+ | --------- | ------------------- |
277
+ | `chart-1` | first series color |
278
+ | `chart-2` | second series color |
279
+ | `chart-3` | third series color |
280
+ | `chart-4` | fourth series color |
281
+ | `chart-5` | fifth series color |
282
+
283
+ These are also optional when you import `ui/theme.css`.
284
+
285
+ If your app defines `--chart-1` through `--chart-5`, those colors are used automatically.
286
+
287
+ If they are not defined, `chart-studio` falls back to a built-in OKLCH palette, with separate light and dark defaults. That is why you may see blue, rose, cyan, or other fallback colors in charts when your app does not provide chart variables.
288
+
289
+ Minimal example:
290
+
291
+ ```css
292
+ :root {
293
+ --chart-1: 221.2 83.2% 53.3%;
294
+ --chart-2: 262.1 83.3% 57.8%;
295
+ --chart-3: 24.6 95% 53.1%;
296
+ --chart-4: 142.1 76.2% 36.3%;
297
+ --chart-5: 346.8 77.2% 49.8%;
298
+ }
299
+ ```
300
+
301
+ ## Compatibility
302
+
303
+ - `react`: `>=18.2.0 <20`
304
+ - `recharts`: `>=2.15.4 <3` for the UI layer
305
+ - `lucide-react`: `>=0.577.0 <1` for the UI layer
306
+
307
+ ## Common Questions
308
+
309
+ ### Which import path should I use?
310
+
311
+ - Use `@matthieumordrel/chart-studio` for the headless core.
312
+ - Use `@matthieumordrel/chart-studio/ui` for the optional UI components.
313
+
314
+ ### Do I need Recharts?
315
+
316
+ Only for the UI layer. The headless core works without it.
317
+
318
+ ### Do I need Tailwind?
319
+
320
+ Only for the UI layer. The headless core does not require it.
321
+
322
+ ### Can I use multiple datasets?
323
+
324
+ Yes:
325
+
326
+ ```tsx
327
+ import { defineChartSchema, useChart } from '@matthieumordrel/chart-studio'
328
+
329
+ const chart = useChart({
330
+ sources: [
331
+ {
332
+ id: 'jobs',
333
+ label: 'Jobs',
334
+ data: jobs,
335
+ schema: defineChartSchema<Job>()({
336
+ columns: { dateAdded: { type: 'date', label: 'Date Added' } }
337
+ })
338
+ },
339
+ { id: 'candidates', label: 'Candidates', data: candidates }
340
+ ]
341
+ })
342
+ ```
343
+
344
+ ### What chart types are available?
345
+
346
+ - date X-axis: `bar`, `line`, `area`
347
+ - category or boolean X-axis: `bar`, `pie`, `donut`
348
+ - `pie` and `donut` do not support `groupBy`
349
+
350
+ ## Troubleshooting
351
+
352
+ ### The UI looks mostly unstyled
353
+
354
+ If the components render but look plain, compressed, or layout incorrectly, the most common cause is that the package theme file is not imported.
355
+
356
+ Start with:
357
+
358
+ ```css
359
+ @import 'tailwindcss';
360
+ @import '@matthieumordrel/chart-studio/ui/theme.css';
361
+ ```
362
+
363
+ If you are importing the package source directly in a local playground or monorepo, make sure Tailwind is scanning those source files too.
364
+
365
+ If your app already uses shadcn-style tokens, also make sure tokens such as `background`, `foreground`, `muted`, `border`, `popover`, `primary`, `ring`, and optionally `chart-1` through `chart-5` are defined in your theme.
366
+
367
+ ## Release
368
+
369
+ - `bun run release:check`
370
+ - `bun run release:publish -- --tag=latest`
371
+ - `npm publish` runs `prepublishOnly`, which calls `bun run release:check`
@@ -0,0 +1,60 @@
1
+ import type { ChartType } from './types.js';
2
+ /**
3
+ * X-axis kinds understood by chart-studio when validating chart types.
4
+ */
5
+ export type ChartAxisType = 'date' | 'category' | 'boolean';
6
+ /**
7
+ * Declarative chart-type capability matrix.
8
+ *
9
+ * Centralizing these rules keeps UI controls and state validation aligned as
10
+ * more chart types are added later.
11
+ */
12
+ export type ChartTypeCapabilities = {
13
+ supportedXAxisTypes: readonly ChartAxisType[];
14
+ supportsGrouping: boolean;
15
+ supportsTimeBucketing: boolean;
16
+ };
17
+ /**
18
+ * Capabilities for each supported chart type.
19
+ */
20
+ export declare const CHART_TYPE_CONFIG: {
21
+ readonly bar: {
22
+ readonly supportedXAxisTypes: readonly ["date", "category", "boolean"];
23
+ readonly supportsGrouping: true;
24
+ readonly supportsTimeBucketing: true;
25
+ };
26
+ readonly line: {
27
+ readonly supportedXAxisTypes: readonly ["date"];
28
+ readonly supportsGrouping: true;
29
+ readonly supportsTimeBucketing: true;
30
+ };
31
+ readonly area: {
32
+ readonly supportedXAxisTypes: readonly ["date"];
33
+ readonly supportsGrouping: true;
34
+ readonly supportsTimeBucketing: true;
35
+ };
36
+ readonly pie: {
37
+ readonly supportedXAxisTypes: readonly ["category", "boolean"];
38
+ readonly supportsGrouping: false;
39
+ readonly supportsTimeBucketing: false;
40
+ };
41
+ readonly donut: {
42
+ readonly supportedXAxisTypes: readonly ["category", "boolean"];
43
+ readonly supportsGrouping: false;
44
+ readonly supportsTimeBucketing: false;
45
+ };
46
+ };
47
+ type ChartTypeAvailability = {
48
+ hasGroupBy: boolean;
49
+ xAxisType: ChartAxisType | null;
50
+ };
51
+ /**
52
+ * Whether a chart type can represent the current chart state.
53
+ */
54
+ export declare function isChartTypeAvailable(chartType: ChartType, availability: ChartTypeAvailability): boolean;
55
+ /**
56
+ * Chart types valid for the current axis + feature combination.
57
+ */
58
+ export declare function getAvailableChartTypes(availability: ChartTypeAvailability): ChartType[];
59
+ export {};
60
+ //# sourceMappingURL=chart-capabilities.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chart-capabilities.d.ts","sourceRoot":"","sources":["../../src/core/chart-capabilities.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,YAAY,CAAA;AAGzC;;GAEG;AACH,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG,UAAU,GAAG,SAAS,CAAA;AAE3D;;;;;GAKG;AACH,MAAM,MAAM,qBAAqB,GAAG;IAClC,mBAAmB,EAAE,SAAS,aAAa,EAAE,CAAA;IAC7C,gBAAgB,EAAE,OAAO,CAAA;IACzB,qBAAqB,EAAE,OAAO,CAAA;CAC/B,CAAA;AAED;;GAEG;AACH,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;CA0B+B,CAAA;AAE7D,KAAK,qBAAqB,GAAG;IAC3B,UAAU,EAAE,OAAO,CAAA;IACnB,SAAS,EAAE,aAAa,GAAG,IAAI,CAAA;CAChC,CAAA;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAClC,SAAS,EAAE,SAAS,EACpB,YAAY,EAAE,qBAAqB,GAClC,OAAO,CAeT;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CAAC,YAAY,EAAE,qBAAqB,GAAG,SAAS,EAAE,CAEvF"}
@@ -0,0 +1,55 @@
1
+ import { CHART_TYPE_ORDER } from './config-utils.js';
2
+ /**
3
+ * Capabilities for each supported chart type.
4
+ */
5
+ export const CHART_TYPE_CONFIG = {
6
+ bar: {
7
+ supportedXAxisTypes: ['date', 'category', 'boolean'],
8
+ supportsGrouping: true,
9
+ supportsTimeBucketing: true,
10
+ },
11
+ line: {
12
+ supportedXAxisTypes: ['date'],
13
+ supportsGrouping: true,
14
+ supportsTimeBucketing: true,
15
+ },
16
+ area: {
17
+ supportedXAxisTypes: ['date'],
18
+ supportsGrouping: true,
19
+ supportsTimeBucketing: true,
20
+ },
21
+ pie: {
22
+ supportedXAxisTypes: ['category', 'boolean'],
23
+ supportsGrouping: false,
24
+ supportsTimeBucketing: false,
25
+ },
26
+ donut: {
27
+ supportedXAxisTypes: ['category', 'boolean'],
28
+ supportsGrouping: false,
29
+ supportsTimeBucketing: false,
30
+ },
31
+ };
32
+ /**
33
+ * Whether a chart type can represent the current chart state.
34
+ */
35
+ export function isChartTypeAvailable(chartType, availability) {
36
+ const { hasGroupBy, xAxisType } = availability;
37
+ if (xAxisType === null)
38
+ return false;
39
+ const capabilities = CHART_TYPE_CONFIG[chartType];
40
+ const supportedXAxisTypes = capabilities.supportedXAxisTypes;
41
+ if (!supportedXAxisTypes.includes(xAxisType)) {
42
+ return false;
43
+ }
44
+ if (hasGroupBy && !capabilities.supportsGrouping) {
45
+ return false;
46
+ }
47
+ return true;
48
+ }
49
+ /**
50
+ * Chart types valid for the current axis + feature combination.
51
+ */
52
+ export function getAvailableChartTypes(availability) {
53
+ return CHART_TYPE_ORDER.filter((chartType) => isChartTypeAvailable(chartType, availability));
54
+ }
55
+ //# sourceMappingURL=chart-capabilities.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chart-capabilities.js","sourceRoot":"","sources":["../../src/core/chart-capabilities.ts"],"names":[],"mappings":"AACA,OAAO,EAAC,gBAAgB,EAAC,MAAM,mBAAmB,CAAA;AAmBlD;;GAEG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG;IAC/B,GAAG,EAAE;QACH,mBAAmB,EAAE,CAAC,MAAM,EAAE,UAAU,EAAE,SAAS,CAAC;QACpD,gBAAgB,EAAE,IAAI;QACtB,qBAAqB,EAAE,IAAI;KAC5B;IACD,IAAI,EAAE;QACJ,mBAAmB,EAAE,CAAC,MAAM,CAAC;QAC7B,gBAAgB,EAAE,IAAI;QACtB,qBAAqB,EAAE,IAAI;KAC5B;IACD,IAAI,EAAE;QACJ,mBAAmB,EAAE,CAAC,MAAM,CAAC;QAC7B,gBAAgB,EAAE,IAAI;QACtB,qBAAqB,EAAE,IAAI;KAC5B;IACD,GAAG,EAAE;QACH,mBAAmB,EAAE,CAAC,UAAU,EAAE,SAAS,CAAC;QAC5C,gBAAgB,EAAE,KAAK;QACvB,qBAAqB,EAAE,KAAK;KAC7B;IACD,KAAK,EAAE;QACL,mBAAmB,EAAE,CAAC,UAAU,EAAE,SAAS,CAAC;QAC5C,gBAAgB,EAAE,KAAK;QACvB,qBAAqB,EAAE,KAAK;KAC7B;CAC0D,CAAA;AAO7D;;GAEG;AACH,MAAM,UAAU,oBAAoB,CAClC,SAAoB,EACpB,YAAmC;IAEnC,MAAM,EAAC,UAAU,EAAE,SAAS,EAAC,GAAG,YAAY,CAAA;IAC5C,IAAI,SAAS,KAAK,IAAI;QAAE,OAAO,KAAK,CAAA;IAEpC,MAAM,YAAY,GAAG,iBAAiB,CAAC,SAAS,CAAC,CAAA;IACjD,MAAM,mBAAmB,GAA6B,YAAY,CAAC,mBAAmB,CAAA;IACtF,IAAI,CAAC,mBAAmB,CAAC,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC;QAC7C,OAAO,KAAK,CAAA;IACd,CAAC;IAED,IAAI,UAAU,IAAI,CAAC,YAAY,CAAC,gBAAgB,EAAE,CAAC;QACjD,OAAO,KAAK,CAAA;IACd,CAAC;IAED,OAAO,IAAI,CAAA;AACb,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,sBAAsB,CAAC,YAAmC;IACxE,OAAO,gBAAgB,CAAC,MAAM,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,oBAAoB,CAAC,SAAS,EAAE,YAAY,CAAC,CAAC,CAAA;AAC9F,CAAC"}
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Color assignment for chart series.
3
+ *
4
+ * Uses shadcn chart CSS variables (`--chart-1` through `--chart-5`) by default.
5
+ * Falls back to a built-in OKLCH palette when CSS variables are not available.
6
+ */
7
+ /**
8
+ * Get a color for the Nth series.
9
+ * Cycles through the palette when index exceeds palette length.
10
+ *
11
+ * @param index - Zero-based series index
12
+ * @param useShadcn - Whether to use shadcn CSS variables (default: true)
13
+ * @returns CSS color value
14
+ */
15
+ export declare function getSeriesColor(index: number, useShadcn?: boolean): string;
16
+ /**
17
+ * Generate a color map for a list of group labels.
18
+ * Assigns colors deterministically based on array order.
19
+ *
20
+ * @param groups - Array of group labels
21
+ * @param useShadcn - Whether to use shadcn CSS variables (default: true)
22
+ * @returns Map from group label to CSS color
23
+ */
24
+ export declare function buildColorMap(groups: string[], useShadcn?: boolean): Map<string, string>;
25
+ //# sourceMappingURL=colors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"colors.d.ts","sourceRoot":"","sources":["../../src/core/colors.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAyBH;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,SAAS,UAAO,GAAG,MAAM,CAGtE;AAED;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE,SAAS,UAAO,GAAG,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAMrF"}
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Color assignment for chart series.
3
+ *
4
+ * Uses shadcn chart CSS variables (`--chart-1` through `--chart-5`) by default.
5
+ * Falls back to a built-in OKLCH palette when CSS variables are not available.
6
+ */
7
+ /** Fallback palette using OKLCH for perceptually uniform colors. */
8
+ const FALLBACK_COLORS = [
9
+ 'oklch(0.65 0.15 250)', // blue
10
+ 'oklch(0.65 0.15 350)', // rose
11
+ 'oklch(0.65 0.15 200)', // cyan
12
+ 'oklch(0.65 0.15 70)', // amber
13
+ 'oklch(0.65 0.15 150)', // teal
14
+ 'oklch(0.65 0.15 30)', // orange
15
+ 'oklch(0.65 0.15 300)', // purple
16
+ 'oklch(0.65 0.15 120)', // green
17
+ 'oklch(0.65 0.12 170)', // mint
18
+ 'oklch(0.65 0.12 220)', // slate blue
19
+ ];
20
+ /** Shadcn chart CSS variables (5 colors) with safe fallbacks. */
21
+ const SHADCN_CHART_COLORS = [
22
+ `hsl(var(--chart-1, var(--cs-chart-1, 245 72% 57%)))`,
23
+ `hsl(var(--chart-2, var(--cs-chart-2, 271 72% 55%)))`,
24
+ `hsl(var(--chart-3, var(--cs-chart-3, 330 68% 54%)))`,
25
+ `hsl(var(--chart-4, var(--cs-chart-4, 170 65% 38%)))`,
26
+ `hsl(var(--chart-5, var(--cs-chart-5, 30 90% 54%)))`,
27
+ ];
28
+ /**
29
+ * Get a color for the Nth series.
30
+ * Cycles through the palette when index exceeds palette length.
31
+ *
32
+ * @param index - Zero-based series index
33
+ * @param useShadcn - Whether to use shadcn CSS variables (default: true)
34
+ * @returns CSS color value
35
+ */
36
+ export function getSeriesColor(index, useShadcn = true) {
37
+ const palette = useShadcn ? SHADCN_CHART_COLORS : FALLBACK_COLORS;
38
+ return palette[index % palette.length];
39
+ }
40
+ /**
41
+ * Generate a color map for a list of group labels.
42
+ * Assigns colors deterministically based on array order.
43
+ *
44
+ * @param groups - Array of group labels
45
+ * @param useShadcn - Whether to use shadcn CSS variables (default: true)
46
+ * @returns Map from group label to CSS color
47
+ */
48
+ export function buildColorMap(groups, useShadcn = true) {
49
+ const map = new Map();
50
+ for (let i = 0; i < groups.length; i++) {
51
+ map.set(groups[i], getSeriesColor(i, useShadcn));
52
+ }
53
+ return map;
54
+ }
55
+ //# sourceMappingURL=colors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"colors.js","sourceRoot":"","sources":["../../src/core/colors.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,oEAAoE;AACpE,MAAM,eAAe,GAAG;IACtB,sBAAsB,EAAE,OAAO;IAC/B,sBAAsB,EAAE,OAAO;IAC/B,sBAAsB,EAAE,OAAO;IAC/B,qBAAqB,EAAE,QAAQ;IAC/B,sBAAsB,EAAE,OAAO;IAC/B,qBAAqB,EAAE,SAAS;IAChC,sBAAsB,EAAE,SAAS;IACjC,sBAAsB,EAAE,QAAQ;IAChC,sBAAsB,EAAE,OAAO;IAC/B,sBAAsB,EAAE,aAAa;CAC7B,CAAA;AAEV,iEAAiE;AACjE,MAAM,mBAAmB,GAAG;IAC1B,qDAAqD;IACrD,qDAAqD;IACrD,qDAAqD;IACrD,qDAAqD;IACrD,oDAAoD;CAC5C,CAAA;AAEV;;;;;;;GAOG;AACH,MAAM,UAAU,cAAc,CAAC,KAAa,EAAE,SAAS,GAAG,IAAI;IAC5D,MAAM,OAAO,GAAG,SAAS,CAAC,CAAC,CAAC,mBAAmB,CAAC,CAAC,CAAC,eAAe,CAAA;IACjE,OAAO,OAAO,CAAC,KAAK,GAAG,OAAO,CAAC,MAAM,CAAE,CAAA;AACzC,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,aAAa,CAAC,MAAgB,EAAE,SAAS,GAAG,IAAI;IAC9D,MAAM,GAAG,GAAG,IAAI,GAAG,EAAkB,CAAA;IACrC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACvC,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAE,EAAE,cAAc,CAAC,CAAC,EAAE,SAAS,CAAC,CAAC,CAAA;IACnD,CAAC;IACD,OAAO,GAAG,CAAA;AACZ,CAAC"}
@@ -0,0 +1,43 @@
1
+ import type { ChartType, TimeBucket } from './types.js';
2
+ type IdOption<TId extends string> = {
3
+ id: TId;
4
+ };
5
+ type IdControlConfig<TId extends string> = {
6
+ allowed?: readonly TId[];
7
+ hidden?: readonly TId[];
8
+ default?: TId;
9
+ };
10
+ type ValueControlConfig<TValue extends string> = {
11
+ allowed?: readonly TValue[];
12
+ hidden?: readonly TValue[];
13
+ default?: TValue;
14
+ };
15
+ /**
16
+ * Restrict an ID-keyed option list with `allowed`/`hidden`.
17
+ *
18
+ * When `fallbackToBaseIfEmpty` is enabled, required controls keep their base
19
+ * runtime options even if the config does not match the active source.
20
+ */
21
+ export declare function restrictConfiguredIdOptions<TId extends string, TOption extends IdOption<TId>>(options: readonly TOption[], config: IdControlConfig<TId> | undefined, fallbackToBaseIfEmpty?: boolean): TOption[];
22
+ /**
23
+ * Resolve one ID-based selection against the current option list.
24
+ */
25
+ export declare function resolveConfiguredIdSelection<TId extends string>(currentValue: TId | null, options: readonly IdOption<TId>[], configuredDefault: TId | undefined, fallbackValue: TId | null, preferFirstAvailable?: boolean): TId | null;
26
+ /**
27
+ * Restrict a primitive option list with `allowed`/`hidden`.
28
+ */
29
+ export declare function restrictConfiguredValues<TValue extends string>(values: readonly TValue[], config: ValueControlConfig<TValue> | undefined, fallbackToBaseIfEmpty?: boolean): TValue[];
30
+ /**
31
+ * Resolve one primitive selection against the current option list.
32
+ */
33
+ export declare function resolveConfiguredValue<TValue extends string>(currentValue: TValue, values: readonly TValue[], configuredDefault: TValue | undefined): TValue;
34
+ /**
35
+ * Ordered time buckets exposed by the headless API.
36
+ */
37
+ export declare const TIME_BUCKET_ORDER: readonly TimeBucket[];
38
+ /**
39
+ * Ordered chart types exposed by the headless API.
40
+ */
41
+ export declare const CHART_TYPE_ORDER: readonly ChartType[];
42
+ export {};
43
+ //# sourceMappingURL=config-utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config-utils.d.ts","sourceRoot":"","sources":["../../src/core/config-utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,SAAS,EACT,UAAU,EACX,MAAM,YAAY,CAAA;AAEnB,KAAK,QAAQ,CAAC,GAAG,SAAS,MAAM,IAAI;IAClC,EAAE,EAAE,GAAG,CAAA;CACR,CAAA;AAED,KAAK,eAAe,CAAC,GAAG,SAAS,MAAM,IAAI;IACzC,OAAO,CAAC,EAAE,SAAS,GAAG,EAAE,CAAA;IACxB,MAAM,CAAC,EAAE,SAAS,GAAG,EAAE,CAAA;IACvB,OAAO,CAAC,EAAE,GAAG,CAAA;CACd,CAAA;AAED,KAAK,kBAAkB,CAAC,MAAM,SAAS,MAAM,IAAI;IAC/C,OAAO,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;IAC3B,MAAM,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;IAC1B,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB,CAAA;AA+CD;;;;;GAKG;AACH,wBAAgB,2BAA2B,CAAC,GAAG,SAAS,MAAM,EAAE,OAAO,SAAS,QAAQ,CAAC,GAAG,CAAC,EAC3F,OAAO,EAAE,SAAS,OAAO,EAAE,EAC3B,MAAM,EAAE,eAAe,CAAC,GAAG,CAAC,GAAG,SAAS,EACxC,qBAAqB,UAAQ,GAC5B,OAAO,EAAE,CAEX;AAED;;GAEG;AACH,wBAAgB,4BAA4B,CAAC,GAAG,SAAS,MAAM,EAC7D,YAAY,EAAE,GAAG,GAAG,IAAI,EACxB,OAAO,EAAE,SAAS,QAAQ,CAAC,GAAG,CAAC,EAAE,EACjC,iBAAiB,EAAE,GAAG,GAAG,SAAS,EAClC,aAAa,EAAE,GAAG,GAAG,IAAI,EACzB,oBAAoB,UAAO,GAC1B,GAAG,GAAG,IAAI,CAcZ;AAED;;GAEG;AACH,wBAAgB,wBAAwB,CAAC,MAAM,SAAS,MAAM,EAC5D,MAAM,EAAE,SAAS,MAAM,EAAE,EACzB,MAAM,EAAE,kBAAkB,CAAC,MAAM,CAAC,GAAG,SAAS,EAC9C,qBAAqB,UAAQ,GAC5B,MAAM,EAAE,CAEV;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CAAC,MAAM,SAAS,MAAM,EAC1D,YAAY,EAAE,MAAM,EACpB,MAAM,EAAE,SAAS,MAAM,EAAE,EACzB,iBAAiB,EAAE,MAAM,GAAG,SAAS,GACpC,MAAM,CAUR;AAED;;GAEG;AACH,eAAO,MAAM,iBAAiB,EAAE,SAAS,UAAU,EAAgD,CAAA;AAEnG;;GAEG;AACH,eAAO,MAAM,gBAAgB,EAAE,SAAS,SAAS,EAA4C,CAAA"}