@idyeah/diya 0.0.0 → 1.0.1
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/CHANGELOG.md +332 -0
- package/NOTICE +29 -0
- package/README.md +596 -5
- package/dist/DiyaProvider.d.ts +79 -0
- package/dist/DiyaProvider.js +77 -0
- package/dist/Icon.d.ts +10 -0
- package/dist/Icon.js +27 -0
- package/dist/accordion/Accordion.d.ts +34 -0
- package/dist/accordion/Accordion.js +15 -0
- package/dist/alert/Alert.d.ts +49 -0
- package/dist/alert/Alert.js +11 -0
- package/dist/alert/messages.d.ts +9 -0
- package/dist/alert/messages.js +2 -0
- package/dist/api.json +7277 -0
- package/dist/api.schema.json +214 -0
- package/dist/application-patterns/Chatbot.d.ts +114 -0
- package/dist/application-patterns/Chatbot.js +110 -0
- package/dist/application-patterns/SideNavigation.d.ts +74 -0
- package/dist/application-patterns/SideNavigation.js +31 -0
- package/dist/application-patterns/chatbot-messages.d.ts +20 -0
- package/dist/application-patterns/chatbot-messages.js +2 -0
- package/dist/application-patterns/side-navigation-messages.d.ts +5 -0
- package/dist/application-patterns/side-navigation-messages.js +2 -0
- package/dist/avatar/Avatar.d.ts +32 -0
- package/dist/avatar/Avatar.js +19 -0
- package/dist/avatar/messages.d.ts +7 -0
- package/dist/avatar/messages.js +2 -0
- package/dist/badge/Badge.d.ts +42 -0
- package/dist/badge/Badge.js +16 -0
- package/dist/badge/badge-utils.d.ts +6 -0
- package/dist/badge/badge-utils.js +24 -0
- package/dist/badge/messages.d.ts +4 -0
- package/dist/badge/messages.js +2 -0
- package/dist/breadcrumbs/Breadcrumbs.d.ts +34 -0
- package/dist/breadcrumbs/Breadcrumbs.js +19 -0
- package/dist/breadcrumbs/messages.d.ts +5 -0
- package/dist/breadcrumbs/messages.js +2 -0
- package/dist/button/Button.d.ts +46 -0
- package/dist/button/Button.js +44 -0
- package/dist/calendar/Calendar.d.ts +55 -0
- package/dist/calendar/Calendar.js +61 -0
- package/dist/calendar/messages.d.ts +8 -0
- package/dist/calendar/messages.js +2 -0
- package/dist/calendar/month.d.ts +61 -0
- package/dist/calendar/month.js +134 -0
- package/dist/card/Card.d.ts +46 -0
- package/dist/card/Card.js +8 -0
- package/dist/choice/Choice.d.ts +72 -0
- package/dist/choice/Choice.js +68 -0
- package/dist/choice/messages.d.ts +4 -0
- package/dist/choice/messages.js +2 -0
- package/dist/code-block/CodeBlock.d.ts +20 -0
- package/dist/code-block/CodeBlock.js +49 -0
- package/dist/code-block/messages.d.ts +8 -0
- package/dist/code-block/messages.js +2 -0
- package/dist/combobox/Combobox.d.ts +60 -0
- package/dist/combobox/Combobox.js +160 -0
- package/dist/combobox/messages.d.ts +9 -0
- package/dist/combobox/messages.js +2 -0
- package/dist/combobox/useAnchoredPopover.d.ts +5 -0
- package/dist/combobox/useAnchoredPopover.js +100 -0
- package/dist/data-viz/DataViz.d.ts +140 -0
- package/dist/data-viz/DataViz.js +469 -0
- package/dist/data-viz/index.d.ts +2 -0
- package/dist/data-viz/index.js +1 -0
- package/dist/data-viz/messages.d.ts +11 -0
- package/dist/data-viz/messages.js +2 -0
- package/dist/date-picker/DatePicker.d.ts +57 -0
- package/dist/date-picker/DatePicker.js +146 -0
- package/dist/date-picker/messages.d.ts +13 -0
- package/dist/date-picker/messages.js +2 -0
- package/dist/dialog/Dialog.d.ts +84 -0
- package/dist/dialog/Dialog.js +107 -0
- package/dist/dialog/messages.d.ts +9 -0
- package/dist/dialog/messages.js +2 -0
- package/dist/fonts/DIYASans-Italic.woff2 +0 -0
- package/dist/fonts/DIYASans.woff2 +0 -0
- package/dist/fonts/OFL.txt +94 -0
- package/dist/index.d.ts +50 -0
- package/dist/index.js +46 -0
- package/dist/internal/brand.d.ts +33 -0
- package/dist/internal/brand.js +57 -0
- package/dist/internal/cx.d.ts +2 -0
- package/dist/internal/cx.js +4 -0
- package/dist/internal/link.d.ts +14 -0
- package/dist/internal/link.js +7 -0
- package/dist/internal/slot.d.ts +15 -0
- package/dist/internal/slot.js +67 -0
- package/dist/layout/Inline.d.ts +4 -0
- package/dist/layout/Inline.js +7 -0
- package/dist/layout/Stack.d.ts +4 -0
- package/dist/layout/Stack.js +7 -0
- package/dist/layout/layout.d.ts +32 -0
- package/dist/layout/layout.js +12 -0
- package/dist/link/TextLink.d.ts +23 -0
- package/dist/link/TextLink.js +17 -0
- package/dist/list/List.d.ts +48 -0
- package/dist/list/List.js +17 -0
- package/dist/list/messages.d.ts +4 -0
- package/dist/list/messages.js +2 -0
- package/dist/menu/Menu.d.ts +49 -0
- package/dist/menu/Menu.js +54 -0
- package/dist/messages-defaults.d.ts +201 -0
- package/dist/messages-defaults.js +61 -0
- package/dist/messages.d.ts +12 -0
- package/dist/messages.js +1 -0
- package/dist/metric/Metric.d.ts +42 -0
- package/dist/metric/Metric.js +11 -0
- package/dist/metric/messages.d.ts +9 -0
- package/dist/metric/messages.js +2 -0
- package/dist/overlay/FloatingSurface.d.ts +29 -0
- package/dist/overlay/FloatingSurface.js +254 -0
- package/dist/page-header/PageHeader.d.ts +38 -0
- package/dist/page-header/PageHeader.js +10 -0
- package/dist/pagination/Pagination.d.ts +44 -0
- package/dist/pagination/Pagination.js +26 -0
- package/dist/pagination/messages.d.ts +11 -0
- package/dist/pagination/messages.js +2 -0
- package/dist/popover/Popover.d.ts +23 -0
- package/dist/popover/Popover.js +8 -0
- package/dist/progress/Progress.d.ts +33 -0
- package/dist/progress/Progress.js +14 -0
- package/dist/progress/messages.d.ts +4 -0
- package/dist/progress/messages.js +2 -0
- package/dist/prose/Prose.d.ts +14 -0
- package/dist/prose/Prose.js +7 -0
- package/dist/rating/Rating.d.ts +32 -0
- package/dist/rating/Rating.js +27 -0
- package/dist/rating/messages.d.ts +6 -0
- package/dist/rating/messages.js +2 -0
- package/dist/search/Search.d.ts +11 -0
- package/dist/search/Search.js +9 -0
- package/dist/select/Select.d.ts +65 -0
- package/dist/select/Select.js +19 -0
- package/dist/select/messages.d.ts +4 -0
- package/dist/select/messages.js +2 -0
- package/dist/site-header/SiteHeader.d.ts +88 -0
- package/dist/site-header/SiteHeader.js +211 -0
- package/dist/site-header/messages.d.ts +8 -0
- package/dist/site-header/messages.js +2 -0
- package/dist/slider/Slider.d.ts +54 -0
- package/dist/slider/Slider.js +89 -0
- package/dist/slider/messages.d.ts +5 -0
- package/dist/slider/messages.js +2 -0
- package/dist/styles.css +3386 -0
- package/dist/switch/Switch.d.ts +47 -0
- package/dist/switch/Switch.js +18 -0
- package/dist/table/Table.d.ts +99 -0
- package/dist/table/Table.js +158 -0
- package/dist/table/messages.d.ts +23 -0
- package/dist/table/messages.js +2 -0
- package/dist/tabs/Tabs.d.ts +49 -0
- package/dist/tabs/Tabs.js +26 -0
- package/dist/tag/Tag.d.ts +37 -0
- package/dist/tag/Tag.js +20 -0
- package/dist/tag/messages.d.ts +4 -0
- package/dist/tag/messages.js +2 -0
- package/dist/text-area/TextArea.d.ts +47 -0
- package/dist/text-area/TextArea.js +19 -0
- package/dist/text-input/TextInput.d.ts +53 -0
- package/dist/text-input/TextInput.js +35 -0
- package/dist/text-input/messages.d.ts +4 -0
- package/dist/text-input/messages.js +3 -0
- package/dist/text-input/native-field.d.ts +15 -0
- package/dist/text-input/native-field.js +56 -0
- package/dist/toast/Toast.d.ts +24 -0
- package/dist/toast/Toast.js +79 -0
- package/dist/toast/messages.d.ts +4 -0
- package/dist/toast/messages.js +2 -0
- package/dist/tooltip/Tooltip.d.ts +22 -0
- package/dist/tooltip/Tooltip.js +92 -0
- package/dist/workflow/ContentState.d.ts +44 -0
- package/dist/workflow/ContentState.js +11 -0
- package/dist/workflow/Drawer.d.ts +48 -0
- package/dist/workflow/Drawer.js +51 -0
- package/dist/workflow/FileUpload.d.ts +67 -0
- package/dist/workflow/FileUpload.js +47 -0
- package/dist/workflow/Form.d.ts +52 -0
- package/dist/workflow/Form.js +16 -0
- package/dist/workflow/Wizard.d.ts +76 -0
- package/dist/workflow/Wizard.js +31 -0
- package/dist/workflow/drawer-messages.d.ts +4 -0
- package/dist/workflow/drawer-messages.js +2 -0
- package/dist/workflow/file-upload-messages.d.ts +11 -0
- package/dist/workflow/file-upload-messages.js +2 -0
- package/dist/workflow/form-messages.d.ts +5 -0
- package/dist/workflow/form-messages.js +2 -0
- package/dist/workflow/wizard-messages.d.ts +9 -0
- package/dist/workflow/wizard-messages.js +2 -0
- package/package.json +82 -7
|
@@ -0,0 +1,469 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { forwardRef, useCallback, useEffect, useId, useRef, useState } from 'react';
|
|
4
|
+
import * as echarts from 'echarts/core';
|
|
5
|
+
import { LineChart, BarChart, PieChart, TreemapChart } from 'echarts/charts';
|
|
6
|
+
import { GridComponent, TooltipComponent, LegendComponent, AriaComponent } from 'echarts/components';
|
|
7
|
+
import { SVGRenderer } from 'echarts/renderers';
|
|
8
|
+
import { useDiyaAppearance, useDiyaLocale, useDiyaMessages } from '../DiyaProvider.js';
|
|
9
|
+
import { Button } from '../button/Button.js';
|
|
10
|
+
import { badgeForeground } from '../badge/badge-utils.js';
|
|
11
|
+
import { BRAND_GROUNDS } from '../internal/brand.js';
|
|
12
|
+
import { cx } from '../internal/cx.js';
|
|
13
|
+
import { chartMessages } from './messages.js';
|
|
14
|
+
echarts.use([
|
|
15
|
+
LineChart,
|
|
16
|
+
BarChart,
|
|
17
|
+
PieChart,
|
|
18
|
+
TreemapChart,
|
|
19
|
+
GridComponent,
|
|
20
|
+
TooltipComponent,
|
|
21
|
+
LegendComponent,
|
|
22
|
+
AriaComponent,
|
|
23
|
+
SVGRenderer,
|
|
24
|
+
]);
|
|
25
|
+
function resolveData(items) {
|
|
26
|
+
return items.flatMap((item) => {
|
|
27
|
+
const children = item.children ? resolveData(item.children) : undefined;
|
|
28
|
+
const value = item.value ?? children?.reduce((sum, child) => sum + child.value, 0);
|
|
29
|
+
return value !== undefined && Number.isFinite(value) && value >= 0
|
|
30
|
+
? [{ ...item, value, children }]
|
|
31
|
+
: [];
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
function dataRows(items, parent = '') {
|
|
35
|
+
return items.flatMap((item) => {
|
|
36
|
+
const name = parent ? parent + ' / ' + item.name : item.name;
|
|
37
|
+
return item.children?.length ? dataRows(item.children, name) : [{ name, value: item.value }];
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
const compactLegendGap = 12;
|
|
41
|
+
function compactCircularLayout(width, height, names, fontFamily) {
|
|
42
|
+
// Match ECharts' legend marker, label spacing, padding and wrapped row gaps.
|
|
43
|
+
const availableWidth = Math.max(1, width - 10);
|
|
44
|
+
let rowWidth = 0, rowHeight = 0, legendHeight = 10;
|
|
45
|
+
for (const name of new Set(names)) {
|
|
46
|
+
const text = echarts.format.getTextRect(name, `13px ${fontFamily}`);
|
|
47
|
+
const itemWidth = 15 + text.width;
|
|
48
|
+
const itemHeight = Math.max(10, text.height);
|
|
49
|
+
if (rowWidth && rowWidth + compactLegendGap + itemWidth > availableWidth) {
|
|
50
|
+
legendHeight += rowHeight + compactLegendGap;
|
|
51
|
+
rowWidth = 0;
|
|
52
|
+
rowHeight = 0;
|
|
53
|
+
}
|
|
54
|
+
rowWidth += (rowWidth ? compactLegendGap : 0) + itemWidth;
|
|
55
|
+
rowHeight = Math.max(rowHeight, itemHeight);
|
|
56
|
+
}
|
|
57
|
+
legendHeight += rowHeight;
|
|
58
|
+
const plotHeight = Math.max(8, height - legendHeight - 12);
|
|
59
|
+
return {
|
|
60
|
+
centerY: (plotHeight + 8) / 2,
|
|
61
|
+
outerRadius: Math.max(1, Math.min(Math.min(width, height) * .32, (plotHeight - 8) / 2)),
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
/** DIYA's chart colors, "Vivid": bright system blue, orange, pink, green, violet, teal, indigo and red, ordered so neighbours stay apart. Bright hues sit below 3:1 on white, so every chart also ships its legend, tooltips and a data table. */
|
|
65
|
+
export const chartPalettes = {
|
|
66
|
+
light: ['#007aff', '#ff9500', '#ff2d55', '#34c759', '#af52de', '#30b0c7', '#5856d6', '#ff3b30'],
|
|
67
|
+
dark: ['#0a84ff', '#ff9f0a', '#ff375f', '#30d158', '#bf5af2', '#40c8e0', '#7d7aff', '#ffd60a'],
|
|
68
|
+
};
|
|
69
|
+
const channel = (value) => { value /= 255; return value <= 0.04045 ? value / 12.92 : ((value + 0.055) / 1.055) ** 2.4; };
|
|
70
|
+
const luminance = ([r, g, b]) => 0.2126 * channel(r) + 0.7152 * channel(g) + 0.0722 * channel(b);
|
|
71
|
+
const contrast = (a, b) => { const [x, y] = [luminance(a), luminance(b)].sort((m, n) => n - m); return (x + 0.05) / (y + 0.05); };
|
|
72
|
+
/** CIE Lab, for telling whether two series colors read as the same. */
|
|
73
|
+
function lab(rgb) {
|
|
74
|
+
const [x, y, z] = [[0.4124, 0.3576, 0.1805], [0.2126, 0.7152, 0.0722], [0.0193, 0.1192, 0.9505]].map((row, i) => row.reduce((sum, weight, j) => sum + weight * channel(rgb[j]), 0) / [0.9505, 1, 1.089][i]);
|
|
75
|
+
const f = (t) => (t > 0.008856 ? Math.cbrt(t) : 7.787 * t + 16 / 116);
|
|
76
|
+
return [116 * f(y) - 16, 500 * (f(x) - f(y)), 200 * (f(y) - f(z))];
|
|
77
|
+
}
|
|
78
|
+
const distance = (a, b) => Math.hypot(...lab(a).map((value, i) => value - lab(b)[i]));
|
|
79
|
+
/** Mixes `b` into `a` by `amount` (0 to 1), in sRGB. */
|
|
80
|
+
const mixColor = (a, b, amount) => a.map((value, i) => value + (b[i] - value) * amount);
|
|
81
|
+
const hex = ([r, g, b]) => '#' + [r, g, b].map((value) => Math.round(value).toString(16).padStart(2, '0')).join('');
|
|
82
|
+
/** Reads plain `#rgb`, `#rrggbb` and `rgb()`/`rgba()` values, as computed styles report them. */
|
|
83
|
+
function parseColor(value) {
|
|
84
|
+
const text = value.trim();
|
|
85
|
+
const short = text.match(/^#([\da-f])([\da-f])([\da-f])$/i);
|
|
86
|
+
if (short)
|
|
87
|
+
return [1, 2, 3].map((i) => parseInt(short[i] + short[i], 16));
|
|
88
|
+
const long = text.match(/^#([\da-f]{2})([\da-f]{2})([\da-f]{2})(?:[\da-f]{2})?$/i);
|
|
89
|
+
if (long)
|
|
90
|
+
return [1, 2, 3].map((i) => parseInt(long[i], 16));
|
|
91
|
+
const rgb = text.match(/^rgba?\(\s*([\d.]+)[\s,]+([\d.]+)[\s,]+([\d.]+)\s*(?:[,/]\s*([\d.]+%?))?\s*\)$/i);
|
|
92
|
+
if (!rgb || rgb[4] === '0' || rgb[4] === '0%')
|
|
93
|
+
return undefined;
|
|
94
|
+
return [Number(rgb[1]), Number(rgb[2]), Number(rgb[3])];
|
|
95
|
+
}
|
|
96
|
+
/** Resolves DIYA's color tokens where the chart sits, as sRGB; a canvas paints any color syntax the browser knows. */
|
|
97
|
+
function readColors(node) {
|
|
98
|
+
if (typeof getComputedStyle !== 'function')
|
|
99
|
+
return {};
|
|
100
|
+
let context = null;
|
|
101
|
+
try {
|
|
102
|
+
const canvas = document.createElement('canvas');
|
|
103
|
+
canvas.width = canvas.height = 1;
|
|
104
|
+
context = canvas.getContext('2d', { willReadFrequently: true });
|
|
105
|
+
}
|
|
106
|
+
catch { /* No canvas, such as in a test DOM; plain values still parse. */ }
|
|
107
|
+
const probe = document.createElement('span');
|
|
108
|
+
probe.style.display = 'none';
|
|
109
|
+
node.appendChild(probe);
|
|
110
|
+
const read = (token) => {
|
|
111
|
+
probe.style.color = '';
|
|
112
|
+
probe.style.color = `var(${token})`;
|
|
113
|
+
const value = getComputedStyle(probe).color;
|
|
114
|
+
if (!value || value.includes('var('))
|
|
115
|
+
return undefined;
|
|
116
|
+
if (!context)
|
|
117
|
+
return parseColor(value);
|
|
118
|
+
context.clearRect(0, 0, 1, 1);
|
|
119
|
+
context.fillStyle = '#000';
|
|
120
|
+
context.fillStyle = value;
|
|
121
|
+
context.fillRect(0, 0, 1, 1);
|
|
122
|
+
const [r, g, b, a] = context.getImageData(0, 0, 1, 1).data;
|
|
123
|
+
return a > 0 ? [r, g, b] : parseColor(value);
|
|
124
|
+
};
|
|
125
|
+
const text = read('--diya-color-text');
|
|
126
|
+
const colors = {
|
|
127
|
+
text,
|
|
128
|
+
muted: read('--diya-color-muted'),
|
|
129
|
+
surface: read('--diya-color-raised'),
|
|
130
|
+
};
|
|
131
|
+
probe.remove();
|
|
132
|
+
return colors;
|
|
133
|
+
}
|
|
134
|
+
/** A line, area, bar, combo, pie, donut or treemap chart. Data and formatting belong to the caller; rendering never fetches a service. */
|
|
135
|
+
export const Chart = forwardRef(function Chart({ type, label, categories = [], series = [], data = [], variant = 'standard', legend = true, grid = true, tooltip = true, valueDomain, height = 300, palette, centerLabel, formatValue: formatValueProp, className, ...props }, ref) {
|
|
136
|
+
const t = useDiyaMessages('chart', chartMessages);
|
|
137
|
+
const locale = useDiyaLocale();
|
|
138
|
+
const localeFormatter = useCallback((value) => value.toLocaleString(locale), [locale]);
|
|
139
|
+
const formatValue = formatValueProp ?? localeFormatter;
|
|
140
|
+
const host = useRef(null);
|
|
141
|
+
const chart = useRef(null);
|
|
142
|
+
const [chartWidth, setChartWidth] = useState(0);
|
|
143
|
+
// The provider resolves the theme (including 'system') and the brand's accent tokens for it.
|
|
144
|
+
const { theme, glass, fontFamily, brand: brandTokens } = useDiyaAppearance();
|
|
145
|
+
// Layout is measured when options are set. A web font that arrives later
|
|
146
|
+
// changes those measurements, so options are set again once it loads.
|
|
147
|
+
const [fontEpoch, setFontEpoch] = useState(0);
|
|
148
|
+
useEffect(() => {
|
|
149
|
+
const fonts = typeof document === 'undefined' ? undefined : document.fonts;
|
|
150
|
+
if (!fonts)
|
|
151
|
+
return;
|
|
152
|
+
let active = true;
|
|
153
|
+
const families = new Set(fontFamily.split(',').map((name) => name.trim().replace(/^['"]|['"]$/g, '')));
|
|
154
|
+
const refresh = () => { if (active)
|
|
155
|
+
setFontEpoch((epoch) => epoch + 1); };
|
|
156
|
+
// Only this chart's own fonts matter; other faces loading elsewhere do not.
|
|
157
|
+
const onLoaded = (event) => {
|
|
158
|
+
const faces = event.fontfaces ?? [];
|
|
159
|
+
if (faces.some((face) => families.has(face.family.replace(/^['"]|['"]$/g, ''))))
|
|
160
|
+
refresh();
|
|
161
|
+
};
|
|
162
|
+
fonts.addEventListener?.('loadingdone', onLoaded);
|
|
163
|
+
const spec = `13px ${fontFamily}`;
|
|
164
|
+
let ready = false;
|
|
165
|
+
try {
|
|
166
|
+
ready = fonts.check?.(spec) ?? false;
|
|
167
|
+
}
|
|
168
|
+
catch { /* An unparsable family is treated as not loaded. */ }
|
|
169
|
+
if (!ready)
|
|
170
|
+
void fonts.load?.(spec).then(refresh, () => { });
|
|
171
|
+
return () => {
|
|
172
|
+
active = false;
|
|
173
|
+
fonts.removeEventListener?.('loadingdone', onLoaded);
|
|
174
|
+
};
|
|
175
|
+
}, [fontFamily]);
|
|
176
|
+
const light = theme === 'light';
|
|
177
|
+
const circular = type === 'pie' || type === 'donut';
|
|
178
|
+
const cartesian = !circular && type !== 'treemap';
|
|
179
|
+
const showLegend = legend && type !== 'treemap';
|
|
180
|
+
const compactCircular = circular && showLegend && height < 240;
|
|
181
|
+
const safeData = resolveData(data);
|
|
182
|
+
const compactLayout = compactCircular && chartWidth > 0
|
|
183
|
+
? compactCircularLayout(chartWidth, height, safeData.map(item => item.name), fontFamily)
|
|
184
|
+
: null;
|
|
185
|
+
const circularCenter = compactLayout?.centerY ?? (height * (compactCircular ? .36 : showLegend ? .44 : .5));
|
|
186
|
+
const compactInnerRadius = compactLayout && compactLayout.outerRadius * (variant === 'thin' ? 56 / 64 : 46 / 64);
|
|
187
|
+
const total = type === 'donut' ? formatValue(safeData.reduce((sum, item) => sum + item.value, 0)) : '';
|
|
188
|
+
const defaultTotalSize = Math.max(14, Math.min(30, height * (compactCircular ? .09 : .1)));
|
|
189
|
+
const totalWidth = compactInnerRadius && type === 'donut'
|
|
190
|
+
? echarts.format.getTextRect(total, `${defaultTotalSize}px ${fontFamily}`).width
|
|
191
|
+
: 0;
|
|
192
|
+
const totalSize = compactInnerRadius && totalWidth
|
|
193
|
+
? Math.max(14, Math.min(defaultTotalSize, Math.floor(defaultTotalSize * (compactInnerRadius * 2 - 8) / totalWidth)))
|
|
194
|
+
: defaultTotalSize;
|
|
195
|
+
const circularRadius = compactLayout
|
|
196
|
+
? type === 'pie' ? compactLayout.outerRadius : [compactInnerRadius, compactLayout.outerRadius]
|
|
197
|
+
: type === 'pie'
|
|
198
|
+
? compactCircular ? '64%' : '76%'
|
|
199
|
+
: variant === 'thin'
|
|
200
|
+
? compactCircular ? ['56%', '64%'] : ['70%', '80%']
|
|
201
|
+
: compactCircular ? ['46%', '64%'] : ['58%', '80%'];
|
|
202
|
+
const empty = cartesian
|
|
203
|
+
? !categories.length ||
|
|
204
|
+
!series.some((item) => item.values.some((value) => value !== null && Number.isFinite(value)))
|
|
205
|
+
: !safeData.some((item) => item.value > 0);
|
|
206
|
+
useEffect(() => {
|
|
207
|
+
const node = host.current;
|
|
208
|
+
if (!node || empty)
|
|
209
|
+
return;
|
|
210
|
+
const instance = echarts.init(node, undefined, { renderer: 'svg' });
|
|
211
|
+
chart.current = instance;
|
|
212
|
+
const resize = () => {
|
|
213
|
+
instance.resize();
|
|
214
|
+
setChartWidth(node.clientWidth);
|
|
215
|
+
};
|
|
216
|
+
setChartWidth(node.clientWidth);
|
|
217
|
+
const observer = typeof ResizeObserver === 'function' ? new ResizeObserver(resize) : null;
|
|
218
|
+
observer?.observe(node);
|
|
219
|
+
if (!observer)
|
|
220
|
+
window.addEventListener('resize', resize);
|
|
221
|
+
return () => {
|
|
222
|
+
observer?.disconnect();
|
|
223
|
+
window.removeEventListener('resize', resize);
|
|
224
|
+
chart.current = null;
|
|
225
|
+
instance.dispose();
|
|
226
|
+
};
|
|
227
|
+
}, [empty]);
|
|
228
|
+
useEffect(() => {
|
|
229
|
+
if (!chart.current || !host.current)
|
|
230
|
+
return;
|
|
231
|
+
// Surface and text come from the tokens where the chart sits, so a caller's overrides hold.
|
|
232
|
+
const tokens = readColors(host.current);
|
|
233
|
+
const onLight = light;
|
|
234
|
+
const grounds = BRAND_GROUNDS[onLight ? 'light' : 'dark'];
|
|
235
|
+
const surface = tokens.surface ?? parseColor(grounds.raised);
|
|
236
|
+
const ink = tokens.text ?? parseColor(grounds.text);
|
|
237
|
+
const text = hex(tokens.muted ?? (onLight ? [84, 84, 84] : [190, 190, 190]));
|
|
238
|
+
const line = `rgba(${ink.join(',')},${onLight ? 0.08 : 0.1})`;
|
|
239
|
+
// A colored brand leads series 1; a brand too pale for the surface falls back to its readable ink.
|
|
240
|
+
// A neutral brand (DIYA's default ink, or any gray) steps aside and the palette leads.
|
|
241
|
+
const accent = parseColor(brandTokens.accent);
|
|
242
|
+
const brand = accent && contrast(accent, surface) >= 3 ? accent : parseColor(brandTokens.accentInk) ?? accent ?? ink;
|
|
243
|
+
const neutral = Math.max(...brand) - Math.min(...brand) < 16;
|
|
244
|
+
// A palette color that reads as the brand's twin moves to the back, so the first series stay distinct.
|
|
245
|
+
const rest = chartPalettes[onLight ? 'light' : 'dark'];
|
|
246
|
+
const twin = (color) => distance(parseColor(color), brand) < 25;
|
|
247
|
+
const colors = palette?.length ? palette : neutral ? rest : [hex(brand), ...rest.filter((color) => !twin(color)), ...rest.filter(twin)];
|
|
248
|
+
const horizontal = type === 'bar' && variant === 'horizontal';
|
|
249
|
+
// A fine gradient: bars soften toward their base, one or two lines glow down into the plot (more would muddy),
|
|
250
|
+
// and donut segments catch a highlight.
|
|
251
|
+
const solid = (color) => (color && /^#[0-9a-f]{6}$/i.test(color) ? color : undefined);
|
|
252
|
+
const barFill = (color) => horizontal
|
|
253
|
+
? { type: 'linear', x: 0, y: 0, x2: 1, y2: 0, colorStops: [{ offset: 0, color: `${color}b3` }, { offset: 1, color }] }
|
|
254
|
+
: { type: 'linear', x: 0, y: 0, x2: 0, y2: 1, colorStops: [{ offset: 0, color }, { offset: 1, color: `${color}b3` }] };
|
|
255
|
+
const areaFill = (color) => ({
|
|
256
|
+
type: 'linear', x: 0, y: 0, x2: 0, y2: 1,
|
|
257
|
+
colorStops: [{ offset: 0, color: `${color}38` }, { offset: 1, color: `${color}00` }],
|
|
258
|
+
});
|
|
259
|
+
const sliceFill = (color) => ({
|
|
260
|
+
type: 'linear', x: 0, y: 0, x2: 1, y2: 1,
|
|
261
|
+
colorStops: [{ offset: 0, color: hex(mixColor(parseColor(color), [255, 255, 255], onLight ? 0.24 : 0.16)) }, { offset: 1, color }],
|
|
262
|
+
});
|
|
263
|
+
const axis = {
|
|
264
|
+
axisLine: { show: false },
|
|
265
|
+
axisTick: { show: false },
|
|
266
|
+
axisLabel: { color: text, fontSize: 13 },
|
|
267
|
+
splitLine: { show: grid, lineStyle: { color: line } },
|
|
268
|
+
};
|
|
269
|
+
// Grid lines run along the value axis only, as `grid` promises; the category axis stays clean.
|
|
270
|
+
const categoryOnly = { splitLine: { show: false } };
|
|
271
|
+
// A caller's formatValue labels the value axis too, so ticks read like the tooltips ($40k, not 40,000).
|
|
272
|
+
const valueLabel = formatValueProp ? { ...axis.axisLabel, formatter: (value) => formatValueProp(value) } : axis.axisLabel;
|
|
273
|
+
const negative = series.some((item) => item.values.some((value) => value !== null && value < 0));
|
|
274
|
+
const domain = valueDomain && valueDomain.every(Number.isFinite) && valueDomain[1] > valueDomain[0]
|
|
275
|
+
? { min: valueDomain[0], max: valueDomain[1] }
|
|
276
|
+
: negative
|
|
277
|
+
? {}
|
|
278
|
+
: { min: 0 };
|
|
279
|
+
const cleanValues = (values) => values.map((value) => (value === null || !Number.isFinite(value) ? null : value));
|
|
280
|
+
function chartNodes(items) {
|
|
281
|
+
return items.map((item, index) => ({
|
|
282
|
+
...item,
|
|
283
|
+
children: item.children ? chartNodes(item.children) : undefined,
|
|
284
|
+
itemStyle: circular && solid(item.color ?? colors[index % colors.length])
|
|
285
|
+
? { color: sliceFill(solid(item.color ?? colors[index % colors.length])) }
|
|
286
|
+
: item.color ? { color: item.color } : undefined,
|
|
287
|
+
label: { color: badgeForeground(item.color ?? colors[index % colors.length]) },
|
|
288
|
+
}));
|
|
289
|
+
}
|
|
290
|
+
const chartData = chartNodes(safeData);
|
|
291
|
+
const option = {
|
|
292
|
+
animation: false,
|
|
293
|
+
color: colors,
|
|
294
|
+
textStyle: { fontFamily },
|
|
295
|
+
aria: { enabled: true },
|
|
296
|
+
tooltip: {
|
|
297
|
+
show: tooltip,
|
|
298
|
+
trigger: cartesian ? 'axis' : 'item',
|
|
299
|
+
renderMode: 'richText',
|
|
300
|
+
backgroundColor: hex(surface),
|
|
301
|
+
textStyle: { color: hex(ink) },
|
|
302
|
+
borderColor: line,
|
|
303
|
+
confine: true,
|
|
304
|
+
valueFormatter: formatValue,
|
|
305
|
+
},
|
|
306
|
+
legend: {
|
|
307
|
+
show: showLegend,
|
|
308
|
+
bottom: 0,
|
|
309
|
+
textStyle: { color: text, fontSize: 13 },
|
|
310
|
+
icon: 'circle',
|
|
311
|
+
// Legend dots stay the solid series color, whatever the fill.
|
|
312
|
+
data: circular
|
|
313
|
+
? safeData.map((item, index) => ({ name: item.name, itemStyle: { color: item.color ?? colors[index % colors.length] } }))
|
|
314
|
+
: cartesian
|
|
315
|
+
? series.map((item, index) => ({ name: item.name, itemStyle: { color: item.color ?? colors[index % colors.length] } }))
|
|
316
|
+
: undefined,
|
|
317
|
+
itemWidth: 10,
|
|
318
|
+
itemHeight: 10,
|
|
319
|
+
itemGap: compactCircular ? compactLegendGap : 18,
|
|
320
|
+
// A width change must retain the user's hidden/shown legend categories.
|
|
321
|
+
selected: chart.current.getOption?.()?.legend?.[0]?.selected,
|
|
322
|
+
},
|
|
323
|
+
grid: { left: 12, right: 16, top: 16, bottom: showLegend ? 64 : 16, containLabel: true },
|
|
324
|
+
...(cartesian
|
|
325
|
+
? {
|
|
326
|
+
xAxis: {
|
|
327
|
+
...axis,
|
|
328
|
+
...(horizontal ? { ...domain, axisLabel: valueLabel } : categoryOnly),
|
|
329
|
+
type: horizontal ? 'value' : 'category',
|
|
330
|
+
data: horizontal ? undefined : categories,
|
|
331
|
+
boundaryGap: type === 'bar' || type === 'combo',
|
|
332
|
+
},
|
|
333
|
+
yAxis: {
|
|
334
|
+
...axis,
|
|
335
|
+
...(!horizontal ? { ...domain, axisLabel: valueLabel } : categoryOnly),
|
|
336
|
+
type: horizontal ? 'category' : 'value',
|
|
337
|
+
data: horizontal ? categories : undefined,
|
|
338
|
+
},
|
|
339
|
+
}
|
|
340
|
+
: {}),
|
|
341
|
+
series: circular
|
|
342
|
+
? [
|
|
343
|
+
{
|
|
344
|
+
type: 'pie',
|
|
345
|
+
radius: circularRadius,
|
|
346
|
+
center: ['50%', circularCenter],
|
|
347
|
+
startAngle: 90,
|
|
348
|
+
percentPrecision: 0,
|
|
349
|
+
padAngle: type === 'pie' ? 0 : 2.5,
|
|
350
|
+
minAngle: 3,
|
|
351
|
+
avoidLabelOverlap: true,
|
|
352
|
+
label: {
|
|
353
|
+
show: type === 'pie',
|
|
354
|
+
position: 'inside',
|
|
355
|
+
formatter: '{d}%',
|
|
356
|
+
fontSize: 13,
|
|
357
|
+
fontWeight: 500,
|
|
358
|
+
},
|
|
359
|
+
labelLine: { show: false },
|
|
360
|
+
emphasis: { scale: false },
|
|
361
|
+
itemStyle: {
|
|
362
|
+
borderRadius: type === 'pie' ? 0 : 5,
|
|
363
|
+
borderWidth: type === 'pie' ? 1.5 : 0,
|
|
364
|
+
borderColor: hex(surface),
|
|
365
|
+
},
|
|
366
|
+
data: chartData,
|
|
367
|
+
},
|
|
368
|
+
]
|
|
369
|
+
: type === 'treemap'
|
|
370
|
+
? [
|
|
371
|
+
{
|
|
372
|
+
type: 'treemap',
|
|
373
|
+
roam: false,
|
|
374
|
+
nodeClick: false,
|
|
375
|
+
breadcrumb: { show: false },
|
|
376
|
+
top: 0,
|
|
377
|
+
bottom: 0,
|
|
378
|
+
left: 0,
|
|
379
|
+
right: 0,
|
|
380
|
+
visibleMin: 0,
|
|
381
|
+
squareRatio: 1.2,
|
|
382
|
+
label: {
|
|
383
|
+
show: true,
|
|
384
|
+
formatter: (item) => item.name + '\n' + formatValue(item.value),
|
|
385
|
+
fontSize: 13,
|
|
386
|
+
lineHeight: 24,
|
|
387
|
+
color: hex(ink),
|
|
388
|
+
},
|
|
389
|
+
upperLabel: { show: false },
|
|
390
|
+
itemStyle: { borderWidth: 0, gapWidth: 4, borderRadius: 8 },
|
|
391
|
+
levels: [
|
|
392
|
+
{ itemStyle: { borderWidth: 0, gapWidth: 4 } },
|
|
393
|
+
{ itemStyle: { borderWidth: 0, gapWidth: 4 } },
|
|
394
|
+
],
|
|
395
|
+
data: chartData,
|
|
396
|
+
},
|
|
397
|
+
]
|
|
398
|
+
: series.map((item, index) => ({
|
|
399
|
+
name: item.name,
|
|
400
|
+
type: item.type ??
|
|
401
|
+
(type === 'bar' || (type === 'combo' && index < series.length - 1)
|
|
402
|
+
? 'bar'
|
|
403
|
+
: 'line'),
|
|
404
|
+
data: cleanValues(item.values),
|
|
405
|
+
smooth: variant === 'smooth',
|
|
406
|
+
stack: variant === 'stacked' ? 'total' : undefined,
|
|
407
|
+
showSymbol: false,
|
|
408
|
+
lineStyle: { width: 2.5 },
|
|
409
|
+
barMaxWidth: series.length > 3 ? 18 : 28,
|
|
410
|
+
...(() => {
|
|
411
|
+
const color = solid(item.color ?? colors[index % colors.length]);
|
|
412
|
+
const bar = type === 'bar' || (type === 'combo' && index < series.length - 1) || item.type === 'bar';
|
|
413
|
+
if (bar)
|
|
414
|
+
return { itemStyle: { borderRadius: horizontal ? [0, 6, 6, 0] : [6, 6, 0, 0], color: color ? barFill(color) : item.color } };
|
|
415
|
+
return {
|
|
416
|
+
itemStyle: { color: color ?? item.color },
|
|
417
|
+
...(color && variant !== 'stacked' && (type === 'area' || series.length <= 2)
|
|
418
|
+
? { areaStyle: { opacity: 1, origin: 'start', color: areaFill(color) } }
|
|
419
|
+
: type === 'area' ? { areaStyle: { opacity: 0.14 } } : {}),
|
|
420
|
+
};
|
|
421
|
+
})(),
|
|
422
|
+
})),
|
|
423
|
+
};
|
|
424
|
+
chart.current.setOption(option, { notMerge: true });
|
|
425
|
+
chart.current.resize();
|
|
426
|
+
}, [
|
|
427
|
+
type,
|
|
428
|
+
categories,
|
|
429
|
+
series,
|
|
430
|
+
data,
|
|
431
|
+
variant,
|
|
432
|
+
legend,
|
|
433
|
+
grid,
|
|
434
|
+
tooltip,
|
|
435
|
+
valueDomain,
|
|
436
|
+
palette,
|
|
437
|
+
formatValue,
|
|
438
|
+
theme,
|
|
439
|
+
glass,
|
|
440
|
+
brandTokens,
|
|
441
|
+
fontFamily,
|
|
442
|
+
fontEpoch,
|
|
443
|
+
height,
|
|
444
|
+
chartWidth,
|
|
445
|
+
empty,
|
|
446
|
+
]);
|
|
447
|
+
if (empty)
|
|
448
|
+
return (_jsx("div", { ...props, ref: ref, className: cx('diya-chart-frame', className), style: { minHeight: height, ...props.style }, children: _jsx("p", { role: "status", children: t.empty }) }));
|
|
449
|
+
return (_jsxs("div", { ...props, ref: ref, className: cx('diya-chart-frame', className), children: [_jsx("div", { ref: host, className: "diya-data-viz", style: { height }, role: "img", "aria-label": label }), type === 'donut' && (_jsxs("div", { className: "diya-donut-total", style: { top: circularCenter }, "aria-hidden": "true", children: [_jsx("strong", { style: { fontSize: totalSize }, children: total }), _jsx("span", { children: centerLabel ?? t.total })] }))] }));
|
|
450
|
+
});
|
|
451
|
+
/** A chart in a titled card, with an optional summary, actions and a show-data table. */
|
|
452
|
+
export const DataViz = forwardRef(function DataViz({ title, headingLevel = 3, showHeader = true, description, actions, summary, footer, shadow = false, showDataToggle = true, dataCaption, type, categories, series, data, variant, legend, grid, tooltip, valueDomain, height, palette, centerLabel, formatValue, className, ...props }, ref) {
|
|
453
|
+
const t = useDiyaMessages('chart', chartMessages);
|
|
454
|
+
const locale = useDiyaLocale();
|
|
455
|
+
const [showData, setShowData] = useState(false);
|
|
456
|
+
const tableId = useId();
|
|
457
|
+
const Heading = `h${headingLevel}`;
|
|
458
|
+
const circular = type === 'pie' || type === 'donut' || type === 'treemap';
|
|
459
|
+
const rows = dataRows(resolveData(data ?? []));
|
|
460
|
+
const format = formatValue ?? ((value) => value.toLocaleString(locale));
|
|
461
|
+
const chart = { type, categories, series, data, variant, legend, grid, tooltip, valueDomain, height, palette, centerLabel, formatValue };
|
|
462
|
+
return (_jsxs("article", { ...props, ref: ref, className: cx('diya-analytics-card', className), "data-shadow": shadow, children: [(showHeader || actions) && _jsxs("header", { className: "diya-analytics-header", children: [showHeader && _jsxs("div", { children: [_jsx(Heading, { className: "diya-analytics-title", children: title }), description && _jsx("p", { children: description })] }), actions && _jsx("div", { className: "diya-analytics-tools", children: actions })] }), summary && _jsx("div", { className: "diya-analytics-metric", children: summary }), _jsx(Chart, { ...chart, label: title }), showData && (_jsx("div", { className: "diya-analytics-data-scroll", role: "region", "aria-label": t.dataTable(title), tabIndex: 0, children: _jsxs("table", { id: tableId, className: "diya-analytics-data", children: [_jsx("caption", { children: dataCaption ?? t.dataCaption(title) }), _jsx("thead", { children: _jsx("tr", { children: (circular
|
|
463
|
+
? [t.category, t.value]
|
|
464
|
+
: [t.category, ...(series ?? []).map((item) => item.name)]).map((name, index) => (_jsx("th", { scope: "col", children: name }, index))) }) }), _jsx("tbody", { children: circular
|
|
465
|
+
? rows.map((item, index) => (_jsxs("tr", { children: [_jsx("th", { scope: "row", children: item.name }), _jsx("td", { children: format(item.value) })] }, index)))
|
|
466
|
+
: (categories ?? []).map((name, index) => (_jsxs("tr", { children: [_jsx("th", { scope: "row", children: name }), (series ?? []).map((item, seriesIndex) => (_jsx("td", { children: item.values[index] == null || !Number.isFinite(item.values[index])
|
|
467
|
+
? '—'
|
|
468
|
+
: format(item.values[index]) }, seriesIndex)))] }, index))) })] }) })), (footer || showDataToggle) && (_jsxs("footer", { className: "diya-analytics-footer", children: [_jsx("span", { children: footer }), showDataToggle && (_jsx(Button, { variant: "quiet", size: "small", "aria-expanded": showData, "aria-controls": showData ? tableId : undefined, onClick: () => setShowData(!showData), children: showData ? t.hideData : t.showData }))] }))] }));
|
|
469
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { Chart, DataViz } from './DataViz.js';
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/** chart: English defaults, overridable through DiyaProvider `messages`. */
|
|
2
|
+
export declare const chartMessages: {
|
|
3
|
+
total: string;
|
|
4
|
+
empty: string;
|
|
5
|
+
showData: string;
|
|
6
|
+
hideData: string;
|
|
7
|
+
dataTable: (title: string) => string;
|
|
8
|
+
dataCaption: (title: string) => string;
|
|
9
|
+
category: string;
|
|
10
|
+
value: string;
|
|
11
|
+
};
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
/** chart: English defaults, overridable through DiyaProvider `messages`. */
|
|
2
|
+
export const chartMessages = { total: 'Total', empty: 'No chart data available.', showData: 'Show data table', hideData: 'Hide data table', dataTable: (title) => `${title} data table`, dataCaption: (title) => `${title} data`, category: 'Category', value: 'Value' };
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { type ButtonHTMLAttributes } from 'react';
|
|
2
|
+
import { type DateRange } from '../calendar/month.js';
|
|
3
|
+
import { datePickerMessages } from './messages.js';
|
|
4
|
+
declare module '../messages.js' {
|
|
5
|
+
interface DiyaMessages {
|
|
6
|
+
datePicker: typeof datePickerMessages;
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
export type { DateRange };
|
|
10
|
+
type DatePickerBase = Omit<ButtonHTMLAttributes<HTMLButtonElement>, 'value' | 'defaultValue' | 'type' | 'name' | 'onChange' | 'children'> & {
|
|
11
|
+
/** Visible label above the field; also names the trigger for assistive tech. Hide it with hideLabel. */
|
|
12
|
+
label: string;
|
|
13
|
+
/** Prevents opening the calendar and dims the field. */
|
|
14
|
+
disabled?: boolean;
|
|
15
|
+
/** Visible error message below the field; marks it invalid while set. */
|
|
16
|
+
error?: string;
|
|
17
|
+
/** Hides the label visually but keeps it as the trigger's accessible name. */
|
|
18
|
+
hideLabel?: boolean;
|
|
19
|
+
/** Visible hint below the field, announced with it; `error` replaces it while set. */
|
|
20
|
+
helperText?: string;
|
|
21
|
+
/** Trigger height and type size; defaults to 'medium'. */
|
|
22
|
+
size?: 'small' | 'medium' | 'large';
|
|
23
|
+
/** Earliest date that can be chosen, as ISO YYYY-MM-DD; earlier days show as unavailable. */
|
|
24
|
+
min?: string;
|
|
25
|
+
/** Latest date that can be chosen, as ISO YYYY-MM-DD; later days show as unavailable. */
|
|
26
|
+
max?: string;
|
|
27
|
+
/** Called with each ISO YYYY-MM-DD day on show; return true to mark the day unavailable (announced, and it can't be chosen), as with `min` and `max`. */
|
|
28
|
+
isDateUnavailable?: (date: string) => boolean;
|
|
29
|
+
/** Form field name: the chosen date is sent with the form under it (in range mode, the start and then the end). */
|
|
30
|
+
name?: string;
|
|
31
|
+
};
|
|
32
|
+
/** One ISO date (YYYY-MM-DD), '' for none. */
|
|
33
|
+
export type DatePickerSingleProps = DatePickerBase & {
|
|
34
|
+
/** Single-date mode (the default): leave unset or false. */
|
|
35
|
+
range?: false;
|
|
36
|
+
/** The chosen date as ISO YYYY-MM-DD, or '' for none, when controlled; pair it with `onValueChange`. */
|
|
37
|
+
value?: string;
|
|
38
|
+
/** The initially chosen ISO date when uncontrolled; a form reset returns to it. */
|
|
39
|
+
defaultValue?: string;
|
|
40
|
+
/** Called with the chosen ISO YYYY-MM-DD date, or '' when cleared. */
|
|
41
|
+
onValueChange?: (value: string) => void;
|
|
42
|
+
};
|
|
43
|
+
/** A start and end date. */
|
|
44
|
+
export type DatePickerRangeProps = DatePickerBase & {
|
|
45
|
+
/** Range mode: the first click sets the start, the second the end (swapped if earlier). */
|
|
46
|
+
range: true;
|
|
47
|
+
/** The chosen range as ISO YYYY-MM-DD dates, a bound '' while unset, when controlled; pair it with `onValueChange`. */
|
|
48
|
+
value?: DateRange;
|
|
49
|
+
/** The initially chosen range when uncontrolled; a form reset returns to it. */
|
|
50
|
+
defaultValue?: DateRange;
|
|
51
|
+
/** Called with the new range after each click; `end` is '' until the second date is chosen. */
|
|
52
|
+
onValueChange?: (value: DateRange) => void;
|
|
53
|
+
};
|
|
54
|
+
/** One ISO date ('' for none) by default; `range` takes and returns a DateRange. Native button attributes and the ref go on the trigger; `className` and `style` go on the outer field wrapper. */
|
|
55
|
+
export type DatePickerProps = DatePickerSingleProps | DatePickerRangeProps;
|
|
56
|
+
/** A labeled date field that opens a calendar popup; picks one ISO date, or a start and end date with `range`. */
|
|
57
|
+
export declare const DatePicker: import("react").ForwardRefExoticComponent<DatePickerProps & import("react").RefAttributes<HTMLButtonElement>>;
|