@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.
- package/README.md +1 -0
- package/dist/action-bar.d.ts +1 -2
- package/dist/action-bar.js +7 -8
- package/dist/blocks/design-canvas-inspector.js +4 -1
- package/dist/blocks/file-explorer.js +1 -1
- package/dist/chat-message.js +4 -13
- package/dist/hooks/use-textarea-trigger-menu.d.ts +21 -3
- package/dist/hooks/use-textarea-trigger-menu.js +36 -40
- package/dist/hooks/use-user-ask.d.ts +1 -0
- package/dist/hooks/use-user-ask.js +5 -1
- package/dist/knob-contracts/audio-recorder-knobs.d.ts +1 -1
- package/dist/knob-contracts/audio-recorder-knobs.js +2 -1
- package/dist/knob-contracts/chart-knobs.d.ts +4 -0
- package/dist/knob-contracts/chart-knobs.js +24 -0
- package/dist/knob-contracts/choice-knobs.d.ts +1 -1
- package/dist/knob-contracts/choice-knobs.js +1 -0
- package/dist/knob-contracts/dockable-panel-knobs.d.ts +1 -1
- package/dist/knob-contracts/dockable-panel-knobs.js +2 -1
- package/dist/knob-contracts/emoji-picker-knobs.d.ts +1 -1
- package/dist/knob-contracts/emoji-picker-knobs.js +1 -0
- package/dist/knob-contracts/empty-knobs.d.ts +1 -1
- package/dist/knob-contracts/empty-knobs.js +2 -1
- package/dist/knob-contracts/icon-picker-knobs.d.ts +1 -1
- package/dist/knob-contracts/icon-picker-knobs.js +3 -1
- package/dist/knob-contracts/resizable-knobs.d.ts +1 -1
- package/dist/knob-contracts/resizable-knobs.js +2 -2
- package/dist/knob-contracts/sidebar-knobs.d.ts +1 -1
- package/dist/knob-contracts/sidebar-knobs.js +3 -1
- package/dist/knob-contracts/tabs-knobs.d.ts +1 -1
- package/dist/knob-contracts/tabs-knobs.js +2 -1
- package/dist/knob-contracts/tree-knobs.d.ts +1 -1
- package/dist/knob-contracts/tree-knobs.js +2 -1
- package/dist/lib/color.d.ts +2 -6
- package/dist/lib/color.js +45 -28
- package/dist/lib/contrast.d.ts +6 -0
- package/dist/lib/contrast.js +33 -1
- package/dist/lib/gradient.d.ts +17 -0
- package/dist/lib/gradient.js +69 -0
- package/dist/markdown-editor/drag-handle.js +73 -10
- package/dist/markdown-editor/drag-preview.d.ts +3 -0
- package/dist/markdown-editor/drag-preview.js +31 -0
- package/dist/markdown-editor/extensions.d.ts +2 -2
- package/dist/markdown-editor/extensions.js +25 -3
- package/dist/markdown-editor/suggestions.js +5 -5
- package/dist/markdown-editor/task-item.d.ts +3 -0
- package/dist/markdown-editor/task-item.js +50 -0
- package/dist/markdown-editor/toolbar.js +122 -63
- package/dist/markdown-editor.js +3 -3
- package/dist/skin.d.ts +0 -1
- package/dist/styles/index.css +3 -2
- package/dist/styles/recipes/action-bar.css +1 -1
- package/dist/styles/recipes/activity.css +1 -1
- package/dist/styles/recipes/alert.css +9 -10
- package/dist/styles/recipes/app-shell-header.css +2 -2
- package/dist/styles/recipes/audio-recorder.css +15 -4
- package/dist/styles/recipes/audio-visualizer.css +4 -4
- package/dist/styles/recipes/avatar.css +3 -3
- package/dist/styles/recipes/badge.css +2 -2
- package/dist/styles/recipes/button.css +2 -5
- package/dist/styles/recipes/card.css +0 -2
- package/dist/styles/recipes/chart.css +378 -0
- package/dist/styles/recipes/chat-composer-attachment.css +3 -3
- package/dist/styles/recipes/chat-composer.css +3 -3
- package/dist/styles/recipes/chat-layout.css +2 -2
- package/dist/styles/recipes/chat-message-social.css +1 -1
- package/dist/styles/recipes/chat-message.css +1 -1
- package/dist/styles/recipes/choice.css +9 -3
- package/dist/styles/recipes/code-diff.css +1 -1
- package/dist/styles/recipes/code.css +1 -1
- package/dist/styles/recipes/color-picker-details.css +17 -11
- package/dist/styles/recipes/color-picker-values.css +17 -7
- package/dist/styles/recipes/color-picker.css +88 -70
- package/dist/styles/recipes/context.css +2 -2
- package/dist/styles/recipes/dockable-panel.css +9 -2
- package/dist/styles/recipes/dynamic-notification-motion.css +1 -55
- package/dist/styles/recipes/dynamic-notification.css +0 -1
- package/dist/styles/recipes/emoji-picker.css +35 -19
- package/dist/styles/recipes/empty.css +19 -14
- package/dist/styles/recipes/field-parts.css +2 -2
- package/dist/styles/recipes/field.css +6 -4
- package/dist/styles/recipes/filter-bar.css +10 -10
- package/dist/styles/recipes/flow.css +5 -3
- package/dist/styles/recipes/gradient-editor.css +8 -0
- package/dist/styles/recipes/icon-picker.css +45 -10
- package/dist/styles/recipes/inline-attachment.css +4 -4
- package/dist/styles/recipes/inline-citation.css +2 -2
- package/dist/styles/recipes/item.css +4 -4
- package/dist/styles/recipes/markdown-editor.css +35 -24
- package/dist/styles/recipes/markdown.css +23 -27
- package/dist/styles/recipes/page-layout.css +13 -11
- package/dist/styles/recipes/popup-controls.css +2 -2
- package/dist/styles/recipes/popup-lists.css +3 -3
- package/dist/styles/recipes/popup.css +11 -9
- package/dist/styles/recipes/range.css +4 -5
- package/dist/styles/recipes/resizable.css +15 -10
- package/dist/styles/recipes/resize-handle.css +2 -2
- package/dist/styles/recipes/scroll-area.css +1 -1
- package/dist/styles/recipes/sidebar-menu.css +5 -15
- package/dist/styles/recipes/sidebar-mobile.css +4 -4
- package/dist/styles/recipes/sidebar-parts.css +26 -1
- package/dist/styles/recipes/sidebar.css +18 -31
- package/dist/styles/recipes/skeleton.css +10 -5
- package/dist/styles/recipes/stepper.css +1 -1
- package/dist/styles/recipes/switch.css +12 -11
- package/dist/styles/recipes/table-of-contents.css +6 -8
- package/dist/styles/recipes/table.css +4 -4
- package/dist/styles/recipes/tabs.css +10 -5
- package/dist/styles/recipes/task-list.css +3 -3
- package/dist/styles/recipes/tree.css +10 -3
- package/dist/styles/recipes/user-ask-details.css +5 -4
- package/dist/styles/recipes/user-ask.css +5 -5
- package/dist/styles/theme.css +158 -177
- package/dist/ui/chart-cartesian.d.ts +801 -0
- package/dist/ui/chart-cartesian.js +420 -0
- package/dist/ui/chart-colors.d.ts +7 -0
- package/dist/ui/chart-colors.js +27 -0
- package/dist/ui/chart-curves.d.ts +7 -0
- package/dist/ui/chart-curves.js +87 -0
- package/dist/ui/chart-marks.d.ts +68 -0
- package/dist/ui/chart-marks.js +227 -0
- package/dist/ui/chart-polar.d.ts +103 -0
- package/dist/ui/chart-polar.js +124 -0
- package/dist/ui/chart-series.d.ts +36 -0
- package/dist/ui/chart-series.js +71 -0
- package/dist/ui/chart.d.ts +32 -0
- package/dist/ui/chart.js +257 -0
- package/dist/ui/color-picker-area.d.ts +16 -0
- package/dist/ui/color-picker-area.js +175 -0
- package/dist/ui/color-picker-context.d.ts +27 -0
- package/dist/ui/color-picker-context.js +87 -0
- package/dist/ui/color-picker-contrast.d.ts +20 -0
- package/dist/ui/color-picker-contrast.js +129 -0
- package/dist/ui/color-picker-sliders.d.ts +15 -0
- package/dist/ui/color-picker-sliders.js +146 -0
- package/dist/ui/color-picker-swatches.d.ts +23 -0
- package/dist/ui/color-picker-swatches.js +94 -0
- package/dist/ui/color-picker-values.d.ts +59 -0
- package/dist/ui/color-picker-values.js +244 -0
- package/dist/ui/color-picker.d.ts +15 -110
- package/dist/ui/color-picker.js +30 -759
- package/dist/ui/emoji-picker.d.ts +10 -1
- package/dist/ui/emoji-picker.js +71 -14
- package/dist/ui/flow.js +1 -1
- package/dist/ui/gradient-editor-controls.d.ts +19 -0
- package/dist/ui/gradient-editor-controls.js +75 -0
- package/dist/ui/gradient-editor.d.ts +23 -22
- package/dist/ui/gradient-editor.js +77 -123
- package/dist/ui/icon-picker.d.ts +1 -1
- package/dist/ui/icon-picker.js +11 -6
- package/dist/ui/markdown-elements.d.ts +3 -1
- package/dist/ui/markdown-elements.js +62 -22
- package/dist/ui/markdown-heading.d.ts +9 -0
- package/dist/ui/markdown-heading.js +25 -0
- package/dist/ui/resizable.js +24 -9
- package/dist/ui/sidebar-provider.js +3 -12
- package/dist/ui/typography.d.ts +1 -0
- package/dist/ui/typography.js +12 -3
- package/dist/user-ask.js +4 -8
- package/package.json +7 -3
- package/dist/ui/emoji-icon-picker.d.ts +0 -13
- 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
|