@ctrl-ui/react 0.8.20 → 0.9.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 (161) hide show
  1. package/README.md +1 -0
  2. package/dist/action-bar.d.ts +1 -2
  3. package/dist/action-bar.js +7 -8
  4. package/dist/blocks/design-canvas-inspector.js +4 -1
  5. package/dist/blocks/file-explorer.js +1 -1
  6. package/dist/chat-message.js +4 -13
  7. package/dist/hooks/use-textarea-trigger-menu.d.ts +21 -3
  8. package/dist/hooks/use-textarea-trigger-menu.js +36 -40
  9. package/dist/hooks/use-user-ask.d.ts +1 -0
  10. package/dist/hooks/use-user-ask.js +5 -1
  11. package/dist/knob-contracts/audio-recorder-knobs.d.ts +1 -1
  12. package/dist/knob-contracts/audio-recorder-knobs.js +2 -1
  13. package/dist/knob-contracts/chart-knobs.d.ts +4 -0
  14. package/dist/knob-contracts/chart-knobs.js +24 -0
  15. package/dist/knob-contracts/choice-knobs.d.ts +1 -1
  16. package/dist/knob-contracts/choice-knobs.js +1 -0
  17. package/dist/knob-contracts/dockable-panel-knobs.d.ts +1 -1
  18. package/dist/knob-contracts/dockable-panel-knobs.js +2 -1
  19. package/dist/knob-contracts/emoji-picker-knobs.d.ts +1 -1
  20. package/dist/knob-contracts/emoji-picker-knobs.js +1 -0
  21. package/dist/knob-contracts/empty-knobs.d.ts +1 -1
  22. package/dist/knob-contracts/empty-knobs.js +2 -1
  23. package/dist/knob-contracts/icon-picker-knobs.d.ts +1 -1
  24. package/dist/knob-contracts/icon-picker-knobs.js +3 -1
  25. package/dist/knob-contracts/resizable-knobs.d.ts +1 -1
  26. package/dist/knob-contracts/resizable-knobs.js +2 -2
  27. package/dist/knob-contracts/sidebar-knobs.d.ts +1 -1
  28. package/dist/knob-contracts/sidebar-knobs.js +3 -1
  29. package/dist/knob-contracts/tabs-knobs.d.ts +1 -1
  30. package/dist/knob-contracts/tabs-knobs.js +2 -1
  31. package/dist/knob-contracts/tree-knobs.d.ts +1 -1
  32. package/dist/knob-contracts/tree-knobs.js +2 -1
  33. package/dist/lib/color.d.ts +2 -6
  34. package/dist/lib/color.js +45 -28
  35. package/dist/lib/contrast.d.ts +6 -0
  36. package/dist/lib/contrast.js +33 -1
  37. package/dist/lib/gradient.d.ts +17 -0
  38. package/dist/lib/gradient.js +69 -0
  39. package/dist/markdown-editor/drag-handle.js +73 -10
  40. package/dist/markdown-editor/drag-preview.d.ts +3 -0
  41. package/dist/markdown-editor/drag-preview.js +31 -0
  42. package/dist/markdown-editor/extensions.d.ts +2 -2
  43. package/dist/markdown-editor/extensions.js +25 -3
  44. package/dist/markdown-editor/suggestions.js +5 -5
  45. package/dist/markdown-editor/task-item.d.ts +3 -0
  46. package/dist/markdown-editor/task-item.js +50 -0
  47. package/dist/markdown-editor/toolbar.js +122 -63
  48. package/dist/markdown-editor.js +3 -3
  49. package/dist/skin.d.ts +0 -1
  50. package/dist/styles/index.css +3 -2
  51. package/dist/styles/recipes/action-bar.css +1 -1
  52. package/dist/styles/recipes/activity.css +1 -1
  53. package/dist/styles/recipes/alert.css +9 -10
  54. package/dist/styles/recipes/app-shell-header.css +2 -2
  55. package/dist/styles/recipes/audio-recorder.css +15 -4
  56. package/dist/styles/recipes/audio-visualizer.css +4 -4
  57. package/dist/styles/recipes/avatar.css +3 -3
  58. package/dist/styles/recipes/badge.css +2 -2
  59. package/dist/styles/recipes/button.css +2 -5
  60. package/dist/styles/recipes/card.css +0 -2
  61. package/dist/styles/recipes/chart.css +378 -0
  62. package/dist/styles/recipes/chat-composer-attachment.css +3 -3
  63. package/dist/styles/recipes/chat-composer.css +3 -3
  64. package/dist/styles/recipes/chat-layout.css +2 -2
  65. package/dist/styles/recipes/chat-message-social.css +1 -1
  66. package/dist/styles/recipes/chat-message.css +1 -1
  67. package/dist/styles/recipes/choice.css +9 -3
  68. package/dist/styles/recipes/code-diff.css +1 -1
  69. package/dist/styles/recipes/code.css +1 -1
  70. package/dist/styles/recipes/color-picker-details.css +17 -11
  71. package/dist/styles/recipes/color-picker-values.css +17 -7
  72. package/dist/styles/recipes/color-picker.css +88 -70
  73. package/dist/styles/recipes/context.css +2 -2
  74. package/dist/styles/recipes/dockable-panel.css +9 -2
  75. package/dist/styles/recipes/dynamic-notification-motion.css +1 -55
  76. package/dist/styles/recipes/dynamic-notification.css +0 -1
  77. package/dist/styles/recipes/emoji-picker.css +35 -19
  78. package/dist/styles/recipes/empty.css +19 -14
  79. package/dist/styles/recipes/field-parts.css +2 -2
  80. package/dist/styles/recipes/field.css +6 -4
  81. package/dist/styles/recipes/filter-bar.css +10 -10
  82. package/dist/styles/recipes/flow.css +5 -3
  83. package/dist/styles/recipes/gradient-editor.css +8 -0
  84. package/dist/styles/recipes/icon-picker.css +45 -10
  85. package/dist/styles/recipes/inline-attachment.css +4 -4
  86. package/dist/styles/recipes/inline-citation.css +2 -2
  87. package/dist/styles/recipes/item.css +4 -4
  88. package/dist/styles/recipes/markdown-editor.css +35 -24
  89. package/dist/styles/recipes/markdown.css +23 -27
  90. package/dist/styles/recipes/page-layout.css +13 -11
  91. package/dist/styles/recipes/popup-controls.css +2 -2
  92. package/dist/styles/recipes/popup-lists.css +3 -3
  93. package/dist/styles/recipes/popup.css +11 -9
  94. package/dist/styles/recipes/range.css +4 -5
  95. package/dist/styles/recipes/resizable.css +15 -10
  96. package/dist/styles/recipes/resize-handle.css +2 -2
  97. package/dist/styles/recipes/scroll-area.css +1 -1
  98. package/dist/styles/recipes/sidebar-menu.css +5 -15
  99. package/dist/styles/recipes/sidebar-mobile.css +4 -4
  100. package/dist/styles/recipes/sidebar-parts.css +26 -1
  101. package/dist/styles/recipes/sidebar.css +18 -31
  102. package/dist/styles/recipes/skeleton.css +10 -5
  103. package/dist/styles/recipes/stepper.css +1 -1
  104. package/dist/styles/recipes/switch.css +12 -11
  105. package/dist/styles/recipes/table-of-contents.css +6 -8
  106. package/dist/styles/recipes/table.css +4 -4
  107. package/dist/styles/recipes/tabs.css +10 -5
  108. package/dist/styles/recipes/task-list.css +3 -3
  109. package/dist/styles/recipes/tree.css +10 -3
  110. package/dist/styles/recipes/user-ask-details.css +5 -4
  111. package/dist/styles/recipes/user-ask.css +5 -5
  112. package/dist/styles/theme.css +158 -177
  113. package/dist/ui/chart-cartesian.d.ts +801 -0
  114. package/dist/ui/chart-cartesian.js +420 -0
  115. package/dist/ui/chart-colors.d.ts +7 -0
  116. package/dist/ui/chart-colors.js +27 -0
  117. package/dist/ui/chart-curves.d.ts +7 -0
  118. package/dist/ui/chart-curves.js +87 -0
  119. package/dist/ui/chart-marks.d.ts +68 -0
  120. package/dist/ui/chart-marks.js +227 -0
  121. package/dist/ui/chart-polar.d.ts +103 -0
  122. package/dist/ui/chart-polar.js +124 -0
  123. package/dist/ui/chart-series.d.ts +36 -0
  124. package/dist/ui/chart-series.js +71 -0
  125. package/dist/ui/chart.d.ts +32 -0
  126. package/dist/ui/chart.js +257 -0
  127. package/dist/ui/color-picker-area.d.ts +16 -0
  128. package/dist/ui/color-picker-area.js +175 -0
  129. package/dist/ui/color-picker-context.d.ts +27 -0
  130. package/dist/ui/color-picker-context.js +87 -0
  131. package/dist/ui/color-picker-contrast.d.ts +20 -0
  132. package/dist/ui/color-picker-contrast.js +129 -0
  133. package/dist/ui/color-picker-sliders.d.ts +15 -0
  134. package/dist/ui/color-picker-sliders.js +146 -0
  135. package/dist/ui/color-picker-swatches.d.ts +23 -0
  136. package/dist/ui/color-picker-swatches.js +94 -0
  137. package/dist/ui/color-picker-values.d.ts +59 -0
  138. package/dist/ui/color-picker-values.js +244 -0
  139. package/dist/ui/color-picker.d.ts +15 -110
  140. package/dist/ui/color-picker.js +30 -759
  141. package/dist/ui/emoji-picker.d.ts +10 -1
  142. package/dist/ui/emoji-picker.js +71 -14
  143. package/dist/ui/flow.js +1 -1
  144. package/dist/ui/gradient-editor-controls.d.ts +19 -0
  145. package/dist/ui/gradient-editor-controls.js +75 -0
  146. package/dist/ui/gradient-editor.d.ts +23 -22
  147. package/dist/ui/gradient-editor.js +77 -123
  148. package/dist/ui/icon-picker.d.ts +1 -1
  149. package/dist/ui/icon-picker.js +11 -6
  150. package/dist/ui/markdown-elements.d.ts +3 -1
  151. package/dist/ui/markdown-elements.js +62 -22
  152. package/dist/ui/markdown-heading.d.ts +9 -0
  153. package/dist/ui/markdown-heading.js +25 -0
  154. package/dist/ui/resizable.js +24 -9
  155. package/dist/ui/sidebar-provider.js +3 -12
  156. package/dist/ui/typography.d.ts +1 -0
  157. package/dist/ui/typography.js +12 -3
  158. package/dist/user-ask.js +4 -8
  159. package/package.json +7 -3
  160. package/dist/ui/emoji-icon-picker.d.ts +0 -13
  161. package/dist/ui/emoji-icon-picker.js +0 -50
@@ -0,0 +1,420 @@
1
+ import { chartColor, chartSeriesColor } from "./chart-colors.js";
2
+ import { chartCurve } from "./chart-curves.js";
3
+ import { chartBarFill, chartBarGradients, chartBars, chartFillPaint, chartGradients, chartOutline, chartPart, chartReferenceMarks, dimUnfocused, quietAxis, quietGrid, seriesTooltip } from "./chart-marks.js";
4
+ import { chartFormatters, chartSeriesRows, chartXScale, resolveChartSeries } from "./chart-series.js";
5
+ import { areaY, barX, barY, crosshair, defineChart, dot, group, lineY, stack, stackRowsY } from "@tanstack/charts";
6
+ import { decorative } from "@tanstack/charts/mark/decorative";
7
+ import { scaleBand } from "@tanstack/charts/scales/band";
8
+ import { scaleLinear } from "@tanstack/charts/scales/linear";
9
+ import { tooltip } from "@tanstack/charts/tooltip";
10
+ //#region src/ui/chart-cartesian.ts
11
+ function cartesianSetup(options, percent) {
12
+ const series = resolveChartSeries(options.series, options.fill ?? "gradient");
13
+ const rows = chartSeriesRows(options.data, options.x, series);
14
+ const formats = chartFormatters(options.locale);
15
+ const formatX = options.formatX ?? formats.x;
16
+ const formatValue = options.formatValue ?? formats.value;
17
+ return {
18
+ series,
19
+ rows,
20
+ formatX,
21
+ valueScale: {
22
+ scale: scaleLinear,
23
+ nice: true,
24
+ grid: options.grid === false ? false : quietGrid,
25
+ axis: quietAxis(options.formatTick ?? (percent ? formats.percent : formats.tick)),
26
+ ...options.yScale
27
+ },
28
+ tooltip: options.tooltip === false ? false : seriesTooltip(series.map((entry) => entry.label), formatX, formatValue),
29
+ lastX: rows.at(-1)?.x
30
+ };
31
+ }
32
+ function gradientColors(series) {
33
+ return series.filter((entry) => entry.fill === "gradient").map((entry) => entry.color);
34
+ }
35
+ const SERIES_FOCUS_POINT = {
36
+ id: "focus",
37
+ key: "id",
38
+ x: "x",
39
+ z: "series",
40
+ r: 4,
41
+ fillOpacity: 0,
42
+ strokeOpacity: 0
43
+ };
44
+ function areaChart(options) {
45
+ const stacked = options.stacked ?? false;
46
+ const { series, rows, formatX, valueScale, tooltip, lastX } = cartesianSetup(options, stacked === "percent");
47
+ const curve = chartCurve(options.curve ?? "smooth");
48
+ const stackedRows = stacked ? stackRowsY(rows, {
49
+ x: "x",
50
+ y: "value",
51
+ z: "series",
52
+ offset: stacked === "percent" ? "normalize" : void 0
53
+ }) : [];
54
+ const seriesMarks = series.flatMap((entry) => {
55
+ const areaParts = entry.fill === "hatched" ? ["area", "area-hatched"] : ["area"];
56
+ const fill = chartFillPaint(entry.color, entry.fill, "area");
57
+ const line = {
58
+ id: `line-${entry.key}`,
59
+ key: "id",
60
+ x: "x",
61
+ stroke: chartColor(entry.color),
62
+ strokeDasharray: entry.dashed ? "4 4" : void 0,
63
+ curve
64
+ };
65
+ if (stacked) {
66
+ const seriesRows = stackedRows.filter((row) => row.series === entry.label);
67
+ return [decorative(chartPart(areaY(seriesRows, {
68
+ id: `area-${entry.key}`,
69
+ key: "id",
70
+ x: "x",
71
+ y1: "y1",
72
+ y2: "y2",
73
+ fill,
74
+ curve
75
+ }), ...areaParts)), decorative(chartPart(lineY(seriesRows, {
76
+ ...line,
77
+ y: "y2"
78
+ }), "line"))];
79
+ }
80
+ const seriesRows = rows.filter((row) => row.series === entry.label);
81
+ return [decorative(chartPart(areaY(seriesRows, {
82
+ id: `area-${entry.key}`,
83
+ key: "id",
84
+ x: "x",
85
+ y: "value",
86
+ fill,
87
+ curve
88
+ }), ...areaParts)), decorative(chartPart(lineY(seriesRows, {
89
+ ...line,
90
+ y: "value"
91
+ }), "line"))];
92
+ });
93
+ const focusPoints = stacked ? dot(stackedRows, {
94
+ ...SERIES_FOCUS_POINT,
95
+ y: "y2"
96
+ }) : dot(rows, {
97
+ ...SERIES_FOCUS_POINT,
98
+ y: "value"
99
+ });
100
+ return defineChart({
101
+ marks: [
102
+ ...seriesMarks,
103
+ ...chartReferenceMarks(options.references ?? [], "y", lastX),
104
+ focusPoints,
105
+ crosshair({
106
+ x: true,
107
+ y: false,
108
+ strokeDasharray: "3 3"
109
+ })
110
+ ],
111
+ scales: {
112
+ x: {
113
+ ...chartXScale(rows, "point", options.xScale),
114
+ axis: quietAxis(formatX)
115
+ },
116
+ y: valueScale
117
+ },
118
+ gradients: chartGradients(gradientColors(series), "area"),
119
+ margin: { top: 8 },
120
+ focus: "group-x",
121
+ maxFocusDistance: Number.POSITIVE_INFINITY,
122
+ tooltip
123
+ });
124
+ }
125
+ function lineChart(options) {
126
+ const { series, rows, formatX, valueScale, tooltip, lastX } = cartesianSetup(options, false);
127
+ const curve = chartCurve(options.curve ?? "smooth");
128
+ const seriesMarks = series.flatMap((entry) => {
129
+ const seriesRows = rows.filter((row) => row.series === entry.label);
130
+ const line = decorative(chartPart(lineY(seriesRows, {
131
+ id: `line-${entry.key}`,
132
+ key: "id",
133
+ x: "x",
134
+ y: "value",
135
+ stroke: chartColor(entry.color),
136
+ strokeDasharray: entry.dashed ? "4 4" : void 0,
137
+ curve
138
+ }), "line"));
139
+ if (!options.dots) return [line];
140
+ return [line, decorative(chartPart(dot(seriesRows, {
141
+ id: `dot-${entry.key}`,
142
+ key: "id",
143
+ x: "x",
144
+ y: "value",
145
+ r: 3,
146
+ fill: chartColor(entry.color),
147
+ stroke: "var(--cui-chart-background)",
148
+ strokeWidth: 1.5
149
+ }), "dot"))];
150
+ });
151
+ return defineChart({
152
+ marks: [
153
+ ...seriesMarks,
154
+ ...chartReferenceMarks(options.references ?? [], "y", lastX),
155
+ dot(rows, {
156
+ ...SERIES_FOCUS_POINT,
157
+ y: "value"
158
+ }),
159
+ crosshair({
160
+ x: true,
161
+ y: false,
162
+ strokeDasharray: "3 3"
163
+ })
164
+ ],
165
+ scales: {
166
+ x: {
167
+ ...chartXScale(rows, "point", options.xScale),
168
+ axis: quietAxis(formatX)
169
+ },
170
+ y: valueScale
171
+ },
172
+ margin: { top: 8 },
173
+ focus: "group-x",
174
+ maxFocusDistance: Number.POSITIVE_INFINITY,
175
+ tooltip
176
+ });
177
+ }
178
+ function barChart(options) {
179
+ const layoutName = options.layout ?? "grouped";
180
+ const { rows, formatX, valueScale, tooltip } = cartesianSetup(options, layoutName === "percent");
181
+ const layout = {
182
+ grouped: group({ padding: .15 }),
183
+ stacked: void 0,
184
+ percent: stack({ offset: "normalize" })
185
+ }[layoutName];
186
+ const categoryScale = {
187
+ ...chartXScale(rows, "band", options.xScale),
188
+ axis: quietAxis(formatX)
189
+ };
190
+ const band = {
191
+ inset: -4,
192
+ radius: 6,
193
+ fillOpacity: .06
194
+ };
195
+ const shared = {
196
+ id: "bars",
197
+ key: "id",
198
+ z: "series",
199
+ stroke: (row) => chartOutline(row.color, row.fill),
200
+ strokeWidth: 1,
201
+ radius: { end: 6 },
202
+ states: dimUnfocused,
203
+ layout
204
+ };
205
+ if (options.orientation === "horizontal") return defineChart({
206
+ marks: [
207
+ crosshair({
208
+ x: false,
209
+ y: { band }
210
+ }),
211
+ chartBars(barX(rows, {
212
+ ...shared,
213
+ y: "x",
214
+ x: "value",
215
+ fill: (row) => chartBarFill(row, "x")
216
+ }), "x"),
217
+ ...chartReferenceMarks(options.references ?? [], "x", rows[0]?.x)
218
+ ],
219
+ scales: {
220
+ x: valueScale,
221
+ y: categoryScale
222
+ },
223
+ gradients: chartBarGradients(rows, "x"),
224
+ margin: { top: 8 },
225
+ focus: "group-y",
226
+ maxFocusDistance: Number.POSITIVE_INFINITY,
227
+ tooltip
228
+ });
229
+ return defineChart({
230
+ marks: [
231
+ crosshair({
232
+ x: { band },
233
+ y: false
234
+ }),
235
+ chartBars(barY(rows, {
236
+ ...shared,
237
+ x: "x",
238
+ y: "value",
239
+ fill: (row) => chartBarFill(row, "y")
240
+ }), "y"),
241
+ ...chartReferenceMarks(options.references ?? [], "y", rows.at(-1)?.x)
242
+ ],
243
+ scales: {
244
+ x: categoryScale,
245
+ y: valueScale
246
+ },
247
+ gradients: chartBarGradients(rows, "y"),
248
+ margin: { top: 8 },
249
+ focus: "group-x",
250
+ maxFocusDistance: Number.POSITIVE_INFINITY,
251
+ tooltip
252
+ });
253
+ }
254
+ function finiteNumber(value) {
255
+ return typeof value === "number" && Number.isFinite(value) ? value : void 0;
256
+ }
257
+ function scatterRows(options) {
258
+ return options.data.flatMap((datum, index) => {
259
+ const x = finiteNumber(datum[options.x]);
260
+ const y = finiteNumber(datum[options.y]);
261
+ if (x === void 0 || y === void 0) return [];
262
+ const size = options.size === void 0 ? 0 : finiteNumber(datum[options.size]) ?? 0;
263
+ const groupValue = options.group ? datum[options.group] : "";
264
+ const groupName = groupValue instanceof Date ? groupValue.toISOString() : String(groupValue ?? "");
265
+ return {
266
+ id: String(index),
267
+ x,
268
+ y,
269
+ size,
270
+ group: groupName
271
+ };
272
+ });
273
+ }
274
+ function scatterChart(options) {
275
+ const rows = scatterRows(options);
276
+ const groups = [...new Set(rows.map((row) => row.group))];
277
+ const maxSize = Math.max(1, ...rows.map((row) => row.size));
278
+ const formats = chartFormatters(options.locale);
279
+ const formatX = options.formatX ?? formats.value;
280
+ const formatValue = options.formatValue ?? formats.value;
281
+ const xLabel = options.xLabel ?? options.x;
282
+ const yLabel = options.yLabel ?? options.y;
283
+ const sizeKey = options.size;
284
+ const radius = sizeKey ? (row) => 3 + 11 * Math.sqrt(row.size / maxSize) : 4;
285
+ const dots = groups.map((groupName, groupIndex) => {
286
+ const color = chartColor(options.colors?.[groupName] ?? chartSeriesColor(groupIndex));
287
+ return chartPart(dot(rows.filter((row) => row.group === groupName), {
288
+ id: `dot-${groupName}`,
289
+ key: "id",
290
+ x: "x",
291
+ y: "y",
292
+ r: radius,
293
+ fill: color,
294
+ fillOpacity: .65,
295
+ stroke: color,
296
+ strokeWidth: 1,
297
+ states: dimUnfocused
298
+ }), "dot");
299
+ });
300
+ const rightmostX = rows.length > 0 ? Math.max(...rows.map((row) => row.x)) : void 0;
301
+ return defineChart({
302
+ marks: [...dots, ...chartReferenceMarks(options.references ?? [], "y", rightmostX)],
303
+ scales: {
304
+ x: {
305
+ scale: scaleLinear,
306
+ nice: true,
307
+ grid: quietGrid,
308
+ axis: quietAxis(formats.tick),
309
+ ...options.xScale
310
+ },
311
+ y: {
312
+ scale: scaleLinear,
313
+ nice: true,
314
+ grid: quietGrid,
315
+ axis: quietAxis(formats.tick),
316
+ ...options.yScale
317
+ }
318
+ },
319
+ margin: { top: 8 },
320
+ focus: "nearest",
321
+ tooltip: {
322
+ use: tooltip,
323
+ content: (points) => {
324
+ const point = points[0]?.datum;
325
+ if (!point) return { rows: [] };
326
+ const color = chartColor(options.colors?.[point.group] ?? chartSeriesColor(groups.indexOf(point.group)));
327
+ return {
328
+ title: point.group || void 0,
329
+ rows: [
330
+ {
331
+ label: xLabel,
332
+ value: formatX(point.x),
333
+ color
334
+ },
335
+ {
336
+ label: yLabel,
337
+ value: formatValue(point.y),
338
+ color
339
+ },
340
+ ...sizeKey ? [{
341
+ label: sizeKey,
342
+ value: formatValue(point.size),
343
+ color
344
+ }] : []
345
+ ]
346
+ };
347
+ }
348
+ }
349
+ });
350
+ }
351
+ function sparklineChart(options) {
352
+ const color = options.color ?? "blue";
353
+ const fill = options.fill ?? "gradient";
354
+ const type = options.type ?? "area";
355
+ const curve = chartCurve(options.curve ?? "smooth");
356
+ const rows = chartSeriesRows(options.data, options.x, resolveChartSeries([{
357
+ key: options.y,
358
+ color,
359
+ fill
360
+ }]));
361
+ const values = rows.flatMap((row) => row.value === null ? [] : row.value);
362
+ const min = values.length > 0 ? Math.min(...values) : 0;
363
+ const shared = {
364
+ guides: false,
365
+ margin: 2,
366
+ focus: false,
367
+ pointer: false,
368
+ keyboard: false,
369
+ tooltip: false
370
+ };
371
+ const valueScale = {
372
+ scale: scaleLinear,
373
+ nice: false
374
+ };
375
+ if (type === "bar") return defineChart({
376
+ ...shared,
377
+ marks: [chartBars(barY(rows, {
378
+ key: "id",
379
+ x: "x",
380
+ y: "value",
381
+ fill: (row) => chartBarFill(row, "y"),
382
+ radius: { end: 2 }
383
+ }), "y")],
384
+ scales: {
385
+ x: {
386
+ scale: () => scaleBand().padding(.2),
387
+ ...options.xScale
388
+ },
389
+ y: valueScale
390
+ },
391
+ gradients: chartBarGradients(rows, "y")
392
+ });
393
+ const line = chartPart(lineY(rows, {
394
+ key: "id",
395
+ x: "x",
396
+ y: "value",
397
+ stroke: chartColor(color),
398
+ curve
399
+ }), "line");
400
+ const areaParts = fill === "hatched" ? ["area", "area-hatched"] : ["area"];
401
+ const area = chartPart(areaY(rows, {
402
+ key: "id",
403
+ x: "x",
404
+ y1: min,
405
+ y2: "value",
406
+ fill: chartFillPaint(color, fill, "area"),
407
+ curve
408
+ }), ...areaParts);
409
+ return defineChart({
410
+ ...shared,
411
+ marks: type === "area" ? [area, line] : [line],
412
+ scales: {
413
+ x: chartXScale(rows, "point", options.xScale),
414
+ y: valueScale
415
+ },
416
+ gradients: type === "area" && fill === "gradient" ? chartGradients([color], "area") : []
417
+ });
418
+ }
419
+ //#endregion
420
+ export { areaChart, barChart, lineChart, scatterChart, sparklineChart };
@@ -0,0 +1,7 @@
1
+ //#region src/ui/chart-colors.d.ts
2
+ export declare const CHART_COLORS: readonly ["blue", "purple", "pink", "orange", "green", "yellow", "red", "neutral"];
3
+ export type ChartColor = (typeof CHART_COLORS)[number];
4
+ export declare const CHART_SERIES_COLORS: readonly ["blue", "purple", "pink", "orange", "green", "yellow"];
5
+ export declare function chartColor(color: ChartColor): string;
6
+ export declare function chartSeriesColor(index: number): ChartColor;
7
+ //#endregion
@@ -0,0 +1,27 @@
1
+ //#region src/ui/chart-colors.ts
2
+ const CHART_COLORS = [
3
+ "blue",
4
+ "purple",
5
+ "pink",
6
+ "orange",
7
+ "green",
8
+ "yellow",
9
+ "red",
10
+ "neutral"
11
+ ];
12
+ const CHART_SERIES_COLORS = [
13
+ "blue",
14
+ "purple",
15
+ "pink",
16
+ "orange",
17
+ "green",
18
+ "yellow"
19
+ ];
20
+ function chartColor(color) {
21
+ return `var(--chart-${color})`;
22
+ }
23
+ function chartSeriesColor(index) {
24
+ return CHART_SERIES_COLORS[index % CHART_SERIES_COLORS.length] ?? "blue";
25
+ }
26
+ //#endregion
27
+ export { CHART_COLORS, CHART_SERIES_COLORS, chartColor, chartSeriesColor };
@@ -0,0 +1,7 @@
1
+ import { ChartCurve } from "@tanstack/charts";
2
+ //#region src/ui/chart-curves.d.ts
3
+ export type ChartCurveName = "smooth" | "linear" | "step";
4
+ export declare const smoothCurve: ChartCurve;
5
+ export declare const stepCurve: ChartCurve;
6
+ export declare function chartCurve(name: ChartCurveName): ChartCurve | undefined;
7
+ //#endregion
@@ -0,0 +1,87 @@
1
+ //#region src/ui/chart-curves.ts
2
+ function monotoneTangents(points) {
3
+ const tangents = points.map(() => 0);
4
+ const last = points.length - 1;
5
+ for (let index = 1; index < last; index += 1) {
6
+ const [x0, y0] = points[index - 1] ?? [0, 0];
7
+ const [x1, y1] = points[index] ?? [0, 0];
8
+ const [x2, y2] = points[index + 1] ?? [0, 0];
9
+ const h0 = x1 - x0;
10
+ const h1 = x2 - x1;
11
+ const s0 = h0 ? (y1 - y0) / h0 : 0;
12
+ const s1 = h1 ? (y2 - y1) / h1 : 0;
13
+ const weightedSlope = (s0 * h1 + s1 * h0) / (h0 + h1);
14
+ tangents[index] = (Math.sign(s0) + Math.sign(s1)) * Math.min(Math.abs(s0), Math.abs(s1), .5 * Math.abs(weightedSlope)) || 0;
15
+ }
16
+ if (last > 0) {
17
+ tangents[0] = endTangent(points[0], points[1], tangents[1] ?? 0);
18
+ tangents[last] = endTangent(points[last - 1], points[last], tangents[last - 1] ?? 0);
19
+ }
20
+ return tangents;
21
+ }
22
+ function endTangent(from, to, neighbourTangent) {
23
+ if (!(from && to)) return neighbourTangent;
24
+ const h = to[0] - from[0];
25
+ return h ? (3 * (to[1] - from[1]) / h - neighbourTangent) / 2 : neighbourTangent;
26
+ }
27
+ function monotoneSegments(points) {
28
+ if (points.length < 3) return points.slice(1).map(([x, y]) => `L${x},${y}`);
29
+ const tangents = monotoneTangents(points);
30
+ return points.slice(1).map(([x1, y1], index) => {
31
+ const [x0, y0] = points[index] ?? [x1, y1];
32
+ const t0 = tangents[index] ?? 0;
33
+ const t1 = tangents[index + 1] ?? 0;
34
+ const dx = (x1 - x0) / 3;
35
+ return `C${x0 + dx},${y0 + dx * t0} ${x1 - dx},${y1 - dx * t1} ${x1},${y1}`;
36
+ });
37
+ }
38
+ function reversedMonotoneSegments(points) {
39
+ if (points.length < 3) return points.slice(0, -1).map(([x, y]) => `L${x},${y}`).reverse();
40
+ const tangents = monotoneTangents(points);
41
+ return points.slice(1).map(([x1, y1], index) => {
42
+ const [x0, y0] = points[index] ?? [x1, y1];
43
+ const t0 = tangents[index] ?? 0;
44
+ const t1 = tangents[index + 1] ?? 0;
45
+ const dx = (x1 - x0) / 3;
46
+ return `C${x1 - dx},${y1 - dx * t1} ${x0 + dx},${y0 + dx * t0} ${x0},${y0}`;
47
+ }).reverse();
48
+ }
49
+ function closedArea(top, bottom, topSegments, bottomSegments) {
50
+ const [firstX, firstY] = top[0] ?? [0, 0];
51
+ const [lastX, lastY] = bottom.at(-1) ?? top.at(-1) ?? [0, 0];
52
+ return `M${firstX},${firstY}${topSegments.join("")}L${lastX},${lastY}${bottomSegments.join("")}Z`;
53
+ }
54
+ const smoothCurve = {
55
+ line(points) {
56
+ if (points.length === 0) return "";
57
+ const [x, y] = points[0] ?? [0, 0];
58
+ return `M${x},${y}${monotoneSegments(points).join("")}`;
59
+ },
60
+ area(top, bottom) {
61
+ if (top.length === 0) return "";
62
+ return closedArea(top, bottom, monotoneSegments(top), reversedMonotoneSegments(bottom));
63
+ }
64
+ };
65
+ function stepSegments(points) {
66
+ return points.slice(1).map(([x, y]) => `H${x}V${y}`);
67
+ }
68
+ function reversedStepSegments(points) {
69
+ return points.slice(0, -1).map(([, y], index) => `V${y}H${points[index]?.[0] ?? 0}`).reverse();
70
+ }
71
+ const stepCurve = {
72
+ line(points) {
73
+ if (points.length === 0) return "";
74
+ const [x, y] = points[0] ?? [0, 0];
75
+ return `M${x},${y}${stepSegments(points).join("")}`;
76
+ },
77
+ area(top, bottom) {
78
+ if (top.length === 0) return "";
79
+ return closedArea(top, bottom, stepSegments(top), reversedStepSegments(bottom));
80
+ }
81
+ };
82
+ function chartCurve(name) {
83
+ if (name === "smooth") return smoothCurve;
84
+ if (name === "step") return stepCurve;
85
+ }
86
+ //#endregion
87
+ export { chartCurve, smoothCurve, stepCurve };
@@ -0,0 +1,68 @@
1
+ import { ChartColor } from "./chart-colors.js";
2
+ import { ChartFill, ChartSeriesRow, ChartXValue } from "./chart-series.js";
3
+ import { ChartLinearGradient, ChartTooltipContentContext } from "@tanstack/charts";
4
+ import { decorative } from "@tanstack/charts/mark/decorative";
5
+ //#region src/ui/chart-marks.d.ts
6
+ type AnyChartMark = Parameters<typeof decorative>[0];
7
+ export type ChartPart = "area" | "area-hatched" | "line" | "dot" | "reference";
8
+ export declare function chartPart<TMark extends AnyChartMark>(mark: TMark, ...parts: ChartPart[]): TMark;
9
+ export declare function chartBars<TMark extends AnyChartMark>(mark: TMark, axis: "x" | "y"): TMark;
10
+ export type ChartGradientKind = "area" | "bar-y" | "bar-y-negative" | "bar-x" | "bar-x-negative" | "arc";
11
+ export declare function chartGradient(color: ChartColor, kind: ChartGradientKind): ChartLinearGradient;
12
+ export declare function chartGradients(colors: Iterable<ChartColor>, kind: ChartGradientKind): ChartLinearGradient[];
13
+ export declare function chartHatch(color: ChartColor): string;
14
+ export declare function chartFillPaint(color: ChartColor, fill: ChartFill, kind: ChartGradientKind): string;
15
+ type ChartBarPaintRow = {
16
+ color: ChartColor;
17
+ fill: ChartFill;
18
+ value: number | null;
19
+ };
20
+ export declare function chartBarFill(row: ChartBarPaintRow, axis: "x" | "y"): string;
21
+ export declare function chartBarGradients(rows: readonly ChartBarPaintRow[], axis: "x" | "y"): ChartLinearGradient[];
22
+ export declare function chartOutline(color: ChartColor, fill: ChartFill): string;
23
+ export declare function quietAxis<TValue>(format: (value: TValue) => string): {
24
+ readonly line: false;
25
+ readonly ticks: {
26
+ readonly line: false;
27
+ readonly padding: 8;
28
+ readonly format: (value: TValue) => string;
29
+ };
30
+ };
31
+ export declare const quietGrid: {
32
+ readonly strokeDasharray: "3 3";
33
+ };
34
+ export declare const dimUnfocused: readonly [{
35
+ readonly when: {
36
+ readonly focus: "unmatched";
37
+ };
38
+ readonly style: {
39
+ readonly opacity: 0.4;
40
+ };
41
+ }];
42
+ export type ChartReference = {
43
+ value: number;
44
+ label?: string;
45
+ color?: ChartColor;
46
+ };
47
+ export declare function chartReferenceMarks(references: readonly ChartReference[], axis: "x" | "y", anchor: ChartXValue | undefined): ((import("@tanstack/charts").CartesianChartMark<ChartReference, number, NonNullable<ChartXValue>, number, NonNullable<ChartXValue>, import("@tanstack/charts/types/core").MarkScaleBindings<undefined, undefined>> & {
48
+ readonly __decorativeMark: import("@tanstack/charts").CartesianChartMark<ChartReference, number, NonNullable<ChartXValue>, number, NonNullable<ChartXValue>, import("@tanstack/charts/types/core").MarkScaleBindings<undefined, undefined>> | import("@tanstack/charts").CartesianChartMark<ChartReference, NonNullable<ChartXValue>, number, NonNullable<ChartXValue>, number, import("@tanstack/charts/types/core").MarkScaleBindings<undefined, undefined>>;
49
+ }) | (import("@tanstack/charts").CartesianChartMark<ChartReference, NonNullable<ChartXValue>, number, NonNullable<ChartXValue>, number, import("@tanstack/charts/types/core").MarkScaleBindings<undefined, undefined>> & {
50
+ readonly __decorativeMark: import("@tanstack/charts").CartesianChartMark<ChartReference, number, NonNullable<ChartXValue>, number, NonNullable<ChartXValue>, import("@tanstack/charts/types/core").MarkScaleBindings<undefined, undefined>> | import("@tanstack/charts").CartesianChartMark<ChartReference, NonNullable<ChartXValue>, number, NonNullable<ChartXValue>, number, import("@tanstack/charts/types/core").MarkScaleBindings<undefined, undefined>>;
51
+ }))[];
52
+ type SeriesPoint = {
53
+ key: string;
54
+ datum: ChartSeriesRow;
55
+ };
56
+ export declare function seriesTooltip(seriesLabels: readonly string[], formatX: (value: ChartXValue) => string, formatValue: (value: number) => string): {
57
+ use: import("@tanstack/charts").ChartTooltipExtension;
58
+ content: (points: readonly SeriesPoint[], context: ChartTooltipContentContext) => {
59
+ title: string | undefined;
60
+ rows: {
61
+ label: string;
62
+ value: string;
63
+ color: string;
64
+ active: boolean;
65
+ }[];
66
+ };
67
+ };
68
+ //#endregion