@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,227 @@
1
+ import { chartColor } from "./chart-colors.js";
2
+ import { ruleX, ruleY, text } from "@tanstack/charts";
3
+ import { decorative } from "@tanstack/charts/mark/decorative";
4
+ import { tooltip } from "@tanstack/charts/tooltip";
5
+ //#region src/ui/chart-marks.ts
6
+ function withSceneNodes(mark, transform) {
7
+ return {
8
+ ...mark,
9
+ initialize(context) {
10
+ const initialized = mark.initialize(context);
11
+ const { resolveLayout } = initialized;
12
+ return {
13
+ ...initialized,
14
+ render: (renderContext) => {
15
+ const scene = initialized.render(renderContext);
16
+ return {
17
+ ...scene,
18
+ nodes: transform(scene.nodes, renderContext)
19
+ };
20
+ },
21
+ resolveLayout: resolveLayout ? (layoutContext) => {
22
+ const layout = resolveLayout(layoutContext);
23
+ return {
24
+ ...layout,
25
+ render: (renderContext) => {
26
+ const scene = layout.render(renderContext);
27
+ return {
28
+ ...scene,
29
+ nodes: transform(scene.nodes, renderContext)
30
+ };
31
+ }
32
+ };
33
+ } : void 0
34
+ };
35
+ }
36
+ };
37
+ }
38
+ function withClass(node, className) {
39
+ return {
40
+ ...node,
41
+ className: node.className ? `${node.className} ${className}` : className
42
+ };
43
+ }
44
+ function chartPart(mark, ...parts) {
45
+ const partClass = parts.map((part) => `cui-chart-${part}`).join(" ");
46
+ return withSceneNodes(mark, (nodes) => nodes.map((node) => withClass(node, partClass)));
47
+ }
48
+ const BASELINE_TOLERANCE = .5;
49
+ function withNegativeBars(nodes, axis, baseline) {
50
+ return nodes.map((node) => {
51
+ if (node.kind === "group") return {
52
+ ...node,
53
+ children: withNegativeBars(node.children, axis, baseline)
54
+ };
55
+ if (node.kind !== "rect") return node;
56
+ return (axis === "y" ? node.y >= baseline - BASELINE_TOLERANCE : node.x + node.width <= baseline + BASELINE_TOLERANCE) ? withClass(node, "cui-chart-bar-negative") : node;
57
+ });
58
+ }
59
+ function chartBars(mark, axis) {
60
+ return withSceneNodes(mark, (nodes, renderContext) => {
61
+ const valueScale = renderContext.scales[axis];
62
+ return (valueScale ? withNegativeBars(nodes, axis, valueScale.map(0)) : [...nodes]).map((node) => withClass(node, `cui-chart-bar-${axis}`));
63
+ });
64
+ }
65
+ const BAR_STOPS = [[0, 1], [1, .55]];
66
+ const GRADIENT_GEOMETRY = {
67
+ area: {
68
+ x1: 0,
69
+ y1: 0,
70
+ x2: 0,
71
+ y2: 1,
72
+ stops: [
73
+ [0, .5],
74
+ [.6, .12],
75
+ [1, 0]
76
+ ]
77
+ },
78
+ "bar-y": {
79
+ x1: 0,
80
+ y1: 0,
81
+ x2: 0,
82
+ y2: 1,
83
+ stops: BAR_STOPS
84
+ },
85
+ "bar-y-negative": {
86
+ x1: 0,
87
+ y1: 1,
88
+ x2: 0,
89
+ y2: 0,
90
+ stops: BAR_STOPS
91
+ },
92
+ "bar-x": {
93
+ x1: 1,
94
+ y1: 0,
95
+ x2: 0,
96
+ y2: 0,
97
+ stops: BAR_STOPS
98
+ },
99
+ "bar-x-negative": {
100
+ x1: 0,
101
+ y1: 0,
102
+ x2: 1,
103
+ y2: 0,
104
+ stops: BAR_STOPS
105
+ },
106
+ arc: {
107
+ x1: 0,
108
+ y1: 0,
109
+ x2: 1,
110
+ y2: 1,
111
+ stops: [[0, 1], [
112
+ 1,
113
+ 1,
114
+ .1
115
+ ]]
116
+ }
117
+ };
118
+ function chartGradient(color, kind) {
119
+ const { stops, ...geometry } = GRADIENT_GEOMETRY[kind];
120
+ return {
121
+ ...geometry,
122
+ id: `cui-${kind}-${color}`,
123
+ stops: stops.map(([offset, opacity, lightnessLift]) => ({
124
+ offset,
125
+ opacity,
126
+ color: lightnessLift ? `oklch(from ${chartColor(color)} calc(l + ${lightnessLift}) c h)` : chartColor(color)
127
+ }))
128
+ };
129
+ }
130
+ function chartGradients(colors, kind) {
131
+ return [...new Set(colors)].map((color) => chartGradient(color, kind));
132
+ }
133
+ function chartHatch(color) {
134
+ return `var(--_cui-chart-hatch-${color}, ${chartColor(color)})`;
135
+ }
136
+ function chartFillPaint(color, fill, kind) {
137
+ if (fill === "gradient") return `url(#cui-${kind}-${color})`;
138
+ if (fill === "hatched") return chartHatch(color);
139
+ return kind === "area" ? `oklch(from ${chartColor(color)} l c h / 0.2)` : chartColor(color);
140
+ }
141
+ function barGradientKind(axis, value) {
142
+ return value !== null && value < 0 ? `bar-${axis}-negative` : `bar-${axis}`;
143
+ }
144
+ function chartBarFill(row, axis) {
145
+ return chartFillPaint(row.color, row.fill, barGradientKind(axis, row.value));
146
+ }
147
+ function chartBarGradients(rows, axis) {
148
+ const gradients = /* @__PURE__ */ new Map();
149
+ for (const row of rows) {
150
+ if (row.fill !== "gradient") continue;
151
+ const gradient = chartGradient(row.color, barGradientKind(axis, row.value));
152
+ gradients.set(gradient.id, gradient);
153
+ }
154
+ return [...gradients.values()];
155
+ }
156
+ function chartOutline(color, fill) {
157
+ return fill === "hatched" ? chartColor(color) : "none";
158
+ }
159
+ function quietAxis(format) {
160
+ return {
161
+ line: false,
162
+ ticks: {
163
+ line: false,
164
+ padding: 8,
165
+ format
166
+ }
167
+ };
168
+ }
169
+ const quietGrid = { strokeDasharray: "3 3" };
170
+ const dimUnfocused = [{
171
+ when: { focus: "unmatched" },
172
+ style: { opacity: .4 }
173
+ }];
174
+ function chartReferenceMarks(references, axis, anchor) {
175
+ if (references.length === 0) return [];
176
+ const paint = {
177
+ strokeDasharray: "4 4",
178
+ strokeOpacity: .7,
179
+ stroke: (reference) => chartColor(reference.color ?? "neutral")
180
+ };
181
+ const labelled = references.filter((reference) => reference.label);
182
+ const labelPaint = {
183
+ text: "label",
184
+ fill: (reference) => chartColor(reference.color ?? "neutral"),
185
+ fontSize: 11
186
+ };
187
+ const rules = axis === "y" ? ruleY(references, {
188
+ y: "value",
189
+ ...paint
190
+ }) : ruleX(references, {
191
+ x: "value",
192
+ ...paint
193
+ });
194
+ const marks = [decorative(chartPart(rules, "reference"))];
195
+ if (labelled.length === 0 || anchor === void 0) return marks;
196
+ const labels = axis === "y" ? text(labelled, {
197
+ ...labelPaint,
198
+ x: () => anchor,
199
+ y: "value",
200
+ anchor: "end",
201
+ dy: -6
202
+ }) : text(labelled, {
203
+ ...labelPaint,
204
+ x: "value",
205
+ y: () => anchor,
206
+ anchor: "start",
207
+ dx: 6
208
+ });
209
+ return [...marks, decorative(labels)];
210
+ }
211
+ function seriesTooltip(seriesLabels, formatX, formatValue) {
212
+ const seriesRank = (point) => seriesLabels.indexOf(point.datum.series);
213
+ return {
214
+ use: tooltip,
215
+ content: (points, context) => ({
216
+ title: points[0] ? formatX(points[0].datum.x) : void 0,
217
+ rows: [...points].sort((left, right) => seriesRank(left) - seriesRank(right)).flatMap((point) => point.datum.value === null ? [] : {
218
+ label: point.datum.series,
219
+ value: formatValue(point.datum.value),
220
+ color: chartColor(point.datum.color),
221
+ active: point.key === context.primaryPoint?.key
222
+ })
223
+ })
224
+ };
225
+ }
226
+ //#endregion
227
+ export { chartBarFill, chartBarGradients, chartBars, chartFillPaint, chartGradient, chartGradients, chartHatch, chartOutline, chartPart, chartReferenceMarks, dimUnfocused, quietAxis, quietGrid, seriesTooltip };
@@ -0,0 +1,103 @@
1
+ import { ChartColor } from "./chart-colors.js";
2
+ import { ChartFill, ChartValueKey, ChartXKey } from "./chart-series.js";
3
+ //#region src/ui/chart-polar.d.ts
4
+ export type ChartSliceOptions<TDatum> = {
5
+ data: readonly TDatum[];
6
+ label: ChartXKey<TDatum>;
7
+ value: ChartValueKey<TDatum>;
8
+ colors?: Partial<Record<string, ChartColor>>;
9
+ fill?: ChartFill;
10
+ fills?: Partial<Record<string, ChartFill>>;
11
+ formatValue?: (value: number) => string;
12
+ locale?: Intl.LocalesArgument;
13
+ };
14
+ export type ChartSlice = {
15
+ id: string;
16
+ label: string;
17
+ value: number;
18
+ color: ChartColor;
19
+ fill: ChartFill;
20
+ };
21
+ export declare function resolveChartSlices<TDatum>(options: ChartSliceOptions<TDatum>): ChartSlice[];
22
+ export type DonutChartOptions<TDatum> = ChartSliceOptions<TDatum> & {
23
+ innerRadius?: number;
24
+ };
25
+ export declare function donutChart<TDatum>(options: DonutChartOptions<TDatum>): Omit<import("@tanstack/charts").StaticChartDefinition<import("@tanstack/charts/polar").PieDatum<ChartSlice>, number, number, string>, keyof import("@tanstack/charts").ChartDefinitionOptions<unknown, import("@tanstack/charts").ChartValue, import("@tanstack/charts").ChartValue, string> | keyof import("@tanstack/charts/types/core").StoredChartSpec> & Omit<{
26
+ readonly marks: readonly [import("@tanstack/charts").ChartMark<import("@tanstack/charts/polar").PieDatum<ChartSlice>, number, number, never, never, "x", "y">];
27
+ readonly scales: {
28
+ x: null;
29
+ y: null;
30
+ };
31
+ readonly gradients: import("@tanstack/charts").ChartLinearGradient[];
32
+ readonly tooltip: {
33
+ readonly use: import("@tanstack/charts").ChartTooltipExtension;
34
+ readonly content: (points: readonly import("@tanstack/charts").ChartPoint<NoInfer<import("@tanstack/charts/polar").PieDatum<ChartSlice>>, number, number>[], context: import("@tanstack/charts").ChartTooltipContentContext) => {
35
+ rows: {
36
+ label: string;
37
+ value: string;
38
+ color: string;
39
+ active: boolean;
40
+ }[];
41
+ };
42
+ };
43
+ }, keyof import("@tanstack/charts").ChartDefinitionOptions<unknown, import("@tanstack/charts").ChartValue, import("@tanstack/charts").ChartValue, string>> & Pick<import("@tanstack/charts").StaticChartDefinition<import("@tanstack/charts/polar").PieDatum<ChartSlice>, number, number, string>, never> & Pick<{
44
+ readonly marks: readonly [import("@tanstack/charts").ChartMark<import("@tanstack/charts/polar").PieDatum<ChartSlice>, number, number, never, never, "x", "y">];
45
+ readonly scales: {
46
+ x: null;
47
+ y: null;
48
+ };
49
+ readonly gradients: import("@tanstack/charts").ChartLinearGradient[];
50
+ readonly tooltip: {
51
+ readonly use: import("@tanstack/charts").ChartTooltipExtension;
52
+ readonly content: (points: readonly import("@tanstack/charts").ChartPoint<NoInfer<import("@tanstack/charts/polar").PieDatum<ChartSlice>>, number, number>[], context: import("@tanstack/charts").ChartTooltipContentContext) => {
53
+ rows: {
54
+ label: string;
55
+ value: string;
56
+ color: string;
57
+ active: boolean;
58
+ }[];
59
+ };
60
+ };
61
+ }, "tooltip">;
62
+ export type RadialBarChartOptions<TDatum> = ChartSliceOptions<TDatum> & {
63
+ max?: number;
64
+ track?: "hatched" | "solid" | "none";
65
+ };
66
+ export declare function radialBarChart<TDatum>(options: RadialBarChartOptions<TDatum>): Omit<import("@tanstack/charts").StaticChartDefinition<ChartSlice, number, string, string>, keyof import("@tanstack/charts").ChartDefinitionOptions<unknown, import("@tanstack/charts").ChartValue, import("@tanstack/charts").ChartValue, string> | keyof import("@tanstack/charts/types/core").StoredChartSpec> & Omit<{
67
+ readonly marks: readonly [...import("@tanstack/charts/types/core").DecorativeChartMark<import("@tanstack/charts").ChartMark<ChartSlice, number, string, never, never, "x", "y">>[], import("@tanstack/charts").ChartMark<ChartSlice, number, string, never, never, "x", "y">];
68
+ readonly scales: {
69
+ x: null;
70
+ y: null;
71
+ };
72
+ readonly gradients: import("@tanstack/charts").ChartLinearGradient[];
73
+ readonly tooltip: {
74
+ readonly use: import("@tanstack/charts").ChartTooltipExtension;
75
+ readonly content: (points: readonly import("@tanstack/charts").ChartPoint<NoInfer<ChartSlice>, number, string>[], context: import("@tanstack/charts").ChartTooltipContentContext) => {
76
+ rows: {
77
+ label: string;
78
+ value: string;
79
+ color: string;
80
+ active: boolean;
81
+ }[];
82
+ };
83
+ };
84
+ }, keyof import("@tanstack/charts").ChartDefinitionOptions<unknown, import("@tanstack/charts").ChartValue, import("@tanstack/charts").ChartValue, string>> & Pick<import("@tanstack/charts").StaticChartDefinition<ChartSlice, number, string, string>, never> & Pick<{
85
+ readonly marks: readonly [...import("@tanstack/charts/types/core").DecorativeChartMark<import("@tanstack/charts").ChartMark<ChartSlice, number, string, never, never, "x", "y">>[], import("@tanstack/charts").ChartMark<ChartSlice, number, string, never, never, "x", "y">];
86
+ readonly scales: {
87
+ x: null;
88
+ y: null;
89
+ };
90
+ readonly gradients: import("@tanstack/charts").ChartLinearGradient[];
91
+ readonly tooltip: {
92
+ readonly use: import("@tanstack/charts").ChartTooltipExtension;
93
+ readonly content: (points: readonly import("@tanstack/charts").ChartPoint<NoInfer<ChartSlice>, number, string>[], context: import("@tanstack/charts").ChartTooltipContentContext) => {
94
+ rows: {
95
+ label: string;
96
+ value: string;
97
+ color: string;
98
+ active: boolean;
99
+ }[];
100
+ };
101
+ };
102
+ }, "tooltip">;
103
+ //#endregion
@@ -0,0 +1,124 @@
1
+ import { chartColor, chartSeriesColor } from "./chart-colors.js";
2
+ import { chartFillPaint, chartGradients, chartHatch, chartOutline, dimUnfocused } from "./chart-marks.js";
3
+ import { chartFormatters } from "./chart-series.js";
4
+ import { defineChart } from "@tanstack/charts";
5
+ import { decorative } from "@tanstack/charts/mark/decorative";
6
+ import { scaleBand } from "@tanstack/charts/scales/band";
7
+ import { scaleLinear } from "@tanstack/charts/scales/linear";
8
+ import { tooltip } from "@tanstack/charts/tooltip";
9
+ import { pie, polar, radialArc, radialBarAngle } from "@tanstack/charts/polar";
10
+ //#region src/ui/chart-polar.ts
11
+ function resolveChartSlices(options) {
12
+ return options.data.flatMap((datum, index) => {
13
+ const label = datum[options.label];
14
+ const value = datum[options.value];
15
+ if (typeof label !== "string" || typeof value !== "number" || !Number.isFinite(value) || value < 0) return [];
16
+ return {
17
+ id: label,
18
+ label,
19
+ value,
20
+ color: options.colors?.[label] ?? chartSeriesColor(index),
21
+ fill: options.fills?.[label] ?? options.fill ?? "gradient"
22
+ };
23
+ });
24
+ }
25
+ function donutChart(options) {
26
+ const slices = resolveChartSlices(options);
27
+ const innerRadius = options.innerRadius ?? .62;
28
+ const formats = chartFormatters(options.locale);
29
+ const formatValue = options.formatValue ?? formats.value;
30
+ return defineChart({
31
+ marks: [polar({
32
+ marks: [radialArc(pie(slices, {
33
+ value: "value",
34
+ gapAngle: .025
35
+ }), {
36
+ key: "label",
37
+ innerRadius: ({ radius }) => radius * innerRadius,
38
+ cornerRadius: 4,
39
+ fill: (slice) => chartFillPaint(slice.color, slice.fill, "arc"),
40
+ stroke: (slice) => chartOutline(slice.color, slice.fill),
41
+ strokeWidth: 1,
42
+ className: "cui-chart-arc",
43
+ states: dimUnfocused
44
+ })],
45
+ scales: {
46
+ angle: null,
47
+ radius: null
48
+ },
49
+ inset: 4
50
+ })],
51
+ scales: {
52
+ x: null,
53
+ y: null
54
+ },
55
+ gradients: chartGradients(slices.filter((slice) => slice.fill === "gradient").map((slice) => slice.color), "arc"),
56
+ tooltip: {
57
+ use: tooltip,
58
+ content: (points, context) => ({ rows: points.map((point) => ({
59
+ label: point.datum.label,
60
+ value: `${formatValue(point.datum.value)} · ${formats.percent(point.datum.fraction)}`,
61
+ color: chartColor(point.datum.color),
62
+ active: point.key === context.primaryPoint?.key
63
+ })) })
64
+ }
65
+ });
66
+ }
67
+ function radialBarChart(options) {
68
+ const rows = resolveChartSlices(options);
69
+ const max = options.max ?? Math.max(1, ...rows.map((row) => row.value));
70
+ const track = options.track ?? "hatched";
71
+ const formatValue = options.formatValue ?? chartFormatters(options.locale).value;
72
+ const ringScales = () => ({
73
+ angle: { scale: scaleLinear().domain([0, max]) },
74
+ radius: {
75
+ scale: () => scaleBand().padding(.3),
76
+ range: [({ radius }) => radius * .4, ({ radius }) => radius]
77
+ }
78
+ });
79
+ const trackMarks = track === "none" ? [] : [decorative(polar({
80
+ className: "cui-chart-radial-track",
81
+ marks: [radialBarAngle(rows, {
82
+ id: "track",
83
+ key: "id",
84
+ radius: "label",
85
+ angle: () => max,
86
+ cornerRadius: "full",
87
+ fill: track === "hatched" ? chartHatch("neutral") : "oklch(from var(--chart-neutral) l c h / 0.15)"
88
+ })],
89
+ scales: ringScales()
90
+ }))];
91
+ return defineChart({
92
+ marks: [...trackMarks, polar({
93
+ marks: [radialBarAngle(rows, {
94
+ id: "value",
95
+ key: "id",
96
+ radius: "label",
97
+ angle: "value",
98
+ cornerRadius: "full",
99
+ fill: (row) => chartFillPaint(row.color, row.fill, "arc"),
100
+ stroke: (row) => chartOutline(row.color, row.fill),
101
+ strokeWidth: 1,
102
+ className: "cui-chart-radial-bar",
103
+ states: dimUnfocused
104
+ })],
105
+ scales: ringScales()
106
+ })],
107
+ scales: {
108
+ x: null,
109
+ y: null
110
+ },
111
+ gradients: chartGradients(rows.filter((row) => row.fill === "gradient").map((row) => row.color), "arc"),
112
+ tooltip: {
113
+ use: tooltip,
114
+ content: (points, context) => ({ rows: points.map((point) => ({
115
+ label: point.datum.label,
116
+ value: `${formatValue(point.datum.value)} / ${formatValue(max)}`,
117
+ color: chartColor(point.datum.color),
118
+ active: point.key === context.primaryPoint?.key
119
+ })) })
120
+ }
121
+ });
122
+ }
123
+ //#endregion
124
+ export { donutChart, radialBarChart, resolveChartSlices };
@@ -0,0 +1,36 @@
1
+ import { ChartColor } from "./chart-colors.js";
2
+ import { ChartPositionScaleOptions } from "@tanstack/charts";
3
+ //#region src/ui/chart-series.d.ts
4
+ export type ChartFill = "gradient" | "hatched" | "solid";
5
+ export type ChartXValue = string | number | Date;
6
+ export type ChartXKey<TDatum> = { [K in keyof TDatum]-?: TDatum[K] extends ChartXValue ? K : never; }[keyof TDatum] & keyof TDatum & string;
7
+ export type ChartValueKey<TDatum> = { [K in keyof TDatum]-?: TDatum[K] extends number | null | undefined ? K : never; }[keyof TDatum] & keyof TDatum & string;
8
+ export type ChartSeries<TDatum> = {
9
+ key: ChartValueKey<TDatum>;
10
+ label?: string;
11
+ color?: ChartColor;
12
+ fill?: ChartFill;
13
+ dashed?: boolean;
14
+ };
15
+ export type ResolvedChartSeries<TDatum> = Required<ChartSeries<TDatum>>;
16
+ export type ChartSeriesRow = {
17
+ id: string;
18
+ x: ChartXValue;
19
+ value: number | null;
20
+ series: string;
21
+ color: ChartColor;
22
+ fill: ChartFill;
23
+ };
24
+ export declare function resolveChartSeries<TDatum>(series: readonly ChartSeries<TDatum>[], fill?: ChartFill): ResolvedChartSeries<TDatum>[];
25
+ export declare function chartSeriesRows<TDatum>(data: readonly TDatum[], x: ChartXKey<TDatum>, series: readonly ResolvedChartSeries<TDatum>[]): ChartSeriesRow[];
26
+ export type ChartFormatters = {
27
+ x: (value: ChartXValue) => string;
28
+ value: (value: number) => string;
29
+ tick: (value: number) => string;
30
+ percent: (value: number) => string;
31
+ };
32
+ export declare function chartFormatters(locale?: Intl.LocalesArgument): ChartFormatters;
33
+ export declare function chartXScale(rows: readonly {
34
+ x: ChartXValue;
35
+ }[], kind: "point" | "band", override?: Partial<ChartPositionScaleOptions>): ChartPositionScaleOptions;
36
+ //#endregion
@@ -0,0 +1,71 @@
1
+ import { chartSeriesColor } from "./chart-colors.js";
2
+ import { scaleBand } from "@tanstack/charts/scales/band";
3
+ import { scaleLinear } from "@tanstack/charts/scales/linear";
4
+ import { scalePoint } from "@tanstack/charts/scales/point";
5
+ //#region src/ui/chart-series.ts
6
+ function resolveChartSeries(series, fill = "gradient") {
7
+ return series.map((entry, index) => ({
8
+ key: entry.key,
9
+ label: entry.label ?? entry.key,
10
+ color: entry.color ?? chartSeriesColor(index),
11
+ fill: entry.fill ?? fill,
12
+ dashed: entry.dashed ?? false
13
+ }));
14
+ }
15
+ function isChartXValue(value) {
16
+ return typeof value === "string" || typeof value === "number" && Number.isFinite(value) || value instanceof Date;
17
+ }
18
+ function chartSeriesRows(data, x, series) {
19
+ return series.flatMap((entry) => data.flatMap((datum) => {
20
+ const xValue = datum[x];
21
+ if (!isChartXValue(xValue)) return [];
22
+ const value = datum[entry.key];
23
+ return {
24
+ id: `${entry.key}:${xValue instanceof Date ? xValue.toISOString() : String(xValue)}`,
25
+ x: xValue,
26
+ value: typeof value === "number" && Number.isFinite(value) ? value : null,
27
+ series: entry.label,
28
+ color: entry.color,
29
+ fill: entry.fill
30
+ };
31
+ }));
32
+ }
33
+ function chartFormatters(locale = "en-US") {
34
+ const plainNumber = new Intl.NumberFormat(locale);
35
+ const compactNumber = new Intl.NumberFormat(locale, { notation: "compact" });
36
+ const percentNumber = new Intl.NumberFormat(locale, { style: "percent" });
37
+ const shortDay = new Intl.DateTimeFormat(locale, {
38
+ month: "short",
39
+ day: "numeric",
40
+ timeZone: "UTC"
41
+ });
42
+ return {
43
+ x: (value) => {
44
+ if (value instanceof Date) return shortDay.format(value);
45
+ if (typeof value === "number") return plainNumber.format(value);
46
+ return value;
47
+ },
48
+ value: (value) => plainNumber.format(value),
49
+ tick: (value) => compactNumber.format(value),
50
+ percent: (value) => percentNumber.format(value)
51
+ };
52
+ }
53
+ function chartXScale(rows, kind, override) {
54
+ if (override?.scale) return {
55
+ ...override,
56
+ scale: override.scale
57
+ };
58
+ const first = rows[0]?.x;
59
+ if (first instanceof Date) throw new Error("Chart presets need an xScale for Date x values, for example { scale: scaleUtc } from d3-scale.");
60
+ if (typeof first === "number") return {
61
+ scale: scaleLinear,
62
+ nice: true,
63
+ ...override
64
+ };
65
+ return {
66
+ scale: kind === "band" ? () => scaleBand().padding(.3) : scalePoint,
67
+ ...override
68
+ };
69
+ }
70
+ //#endregion
71
+ export { chartFormatters, chartSeriesRows, chartXScale, resolveChartSeries };
@@ -0,0 +1,32 @@
1
+ import { ChartKnobStyle } from "../knob-contracts/chart-knobs.js";
2
+ import { ChartColor } from "./chart-colors.js";
3
+ import { ChartFill } from "./chart-series.js";
4
+ import { CSSProperties, ComponentProps, ReactNode } from "react";
5
+ import { ChartTooltipContent, ChartValue } from "@tanstack/charts";
6
+ import { ChartProps } from "@tanstack/charts/react/tooltip";
7
+ //#region src/ui/chart.d.ts
8
+ type ChartStyle = CSSProperties & ChartKnobStyle;
9
+ export type ChartRootProps = Omit<ComponentProps<"div">, "style"> & {
10
+ style?: ChartStyle;
11
+ };
12
+ export declare function Chart({ className, style, ...props }: ChartRootProps): import("react").JSX.Element;
13
+ export type ChartPlotProps<TDatum, TXValue extends ChartValue = ChartValue, TYValue extends ChartValue = ChartValue> = Omit<ChartProps<TDatum, TXValue, TYValue>, "style" | "className"> & {
14
+ className?: string;
15
+ style?: ChartStyle;
16
+ children?: ReactNode;
17
+ };
18
+ export declare function ChartPlot<TDatum, TXValue extends ChartValue = ChartValue, TYValue extends ChartValue = ChartValue>({ className, style, children, ...props }: ChartPlotProps<TDatum, TXValue, TYValue>): import("react").JSX.Element;
19
+ export declare function ChartCenter({ className, ...props }: ComponentProps<"div">): import("react").JSX.Element;
20
+ export declare function ChartLegend({ className, ...props }: ComponentProps<"ul">): import("react").JSX.Element;
21
+ export type ChartLegendItemProps = Omit<ComponentProps<"li">, "color"> & {
22
+ color: ChartColor;
23
+ fill?: ChartFill;
24
+ hidden?: boolean;
25
+ onHiddenChange?: (hidden: boolean) => void;
26
+ };
27
+ export declare function ChartLegendItem({ color, fill, hidden, onHiddenChange, children, className, style, ...props }: ChartLegendItemProps): import("react").JSX.Element;
28
+ export type ChartTooltipBodyProps = Omit<ComponentProps<"div">, "content"> & {
29
+ content: string | ChartTooltipContent;
30
+ };
31
+ export declare function ChartTooltipBody({ content, className, ...props }: ChartTooltipBodyProps): import("react").JSX.Element;
32
+ //#endregion