@santi020k/lumen-react 1.6.0 → 1.7.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.
@@ -1,9 +1,14 @@
1
1
  'use client';
2
2
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
3
  import { Children, cloneElement, createContext, createElement, Fragment, isValidElement, useContext, useEffect, useId, useMemo, useRef, useState } from 'react';
4
- import { alignLumenChartSeries, composeClassName, createLumenBarGeometry, createLumenLineGeometry, createLumenPieGeometry, formatLumenLanguageLabel, getLumenChartCategories, getLumenChartDomain, getLumenChartTicks, getLumenChartToneClassName, getLumenIcon, getLumenPieChartVariantClassName, hasLumenChartData, hasLumenPieData, lumenCodeTokenClassNames, normalizeLumenFormErrors, resolveLumenChartTone, scaleLumenChartValue, tokenizeLumenCode } from '@santi020k/lumen-core';
4
+ import { alignLumenChartSeries, composeClassName, createLumenBarGeometry, createLumenHeatmapGeometry, createLumenLineGeometry, createLumenPieGeometry, createLumenRangeGeometry, createLumenScatterGeometry, formatLumenChartSummary, formatLumenLanguageLabel, getLumenChartCategories, getLumenChartDomain, getLumenChartTicks, getLumenChartToneClassName, getLumenIcon, getLumenPhoneCountries, getLumenPieChartVariantClassName, hasLumenChartData, hasLumenPieData, lumenCodeTokenClassNames, lumenIllustrations, normalizeLumenFormErrors, resolveLumenChartTone, resolveLumenPhoneNumber, scaleLumenChartValue, tokenizeLumenCode } from '@santi020k/lumen-core';
5
5
  import { renderSVG } from 'uqr';
6
+ import { formatReactChartTableValue } from './chart-recipes.js';
6
7
  import { useCalendar, useDialog, useDropdownMenu, useInputOTP, useLanguageToggle, usePopover, useResizable, useSelect, useTabs, useTooltip } from './hooks.js';
8
+ import { resolveReactPhoneInputCountry, resolveReactPhoneInputValue } from './phone-recipes.js';
9
+ import { Input, Label } from './server-components.js';
10
+ export { Badge, Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, Container, Direction, Grid, Input, Label, Progress, Separator, Skeleton, Spinner, Stack, Textarea, Typography, VisuallyHidden } from './server-components.js';
11
+ const getChartCategoryKey = (value) => `${typeof value}:${String(value)}`;
7
12
  const setRefValue = (ref, value) => {
8
13
  if (typeof ref === 'function') {
9
14
  ref(value);
@@ -17,6 +22,9 @@ const emptyStringOptions = [];
17
22
  const emptyDataTableColumns = [];
18
23
  const emptyDataTableRows = [];
19
24
  const emptyChartSeries = [];
25
+ const emptyHeatmapData = [];
26
+ const emptyRangeData = [];
27
+ const emptyComboSeries = [];
20
28
  const emptyPieSeries = {
21
29
  data: [],
22
30
  id: 'pie',
@@ -91,6 +99,18 @@ const getDataTableSortValue = (cell) => {
91
99
  return undefined;
92
100
  return String(cell.sortValue);
93
101
  };
102
+ const compareDataTableCells = (left, right, sortType) => {
103
+ const leftValue = getDataTableSortValue(left) ?? formatDataTableCell(left);
104
+ const rightValue = getDataTableSortValue(right) ?? formatDataTableCell(right);
105
+ if (sortType === 'number') {
106
+ return Number(leftValue.replaceAll(',', '')) -
107
+ Number(rightValue.replaceAll(',', ''));
108
+ }
109
+ return leftValue.localeCompare(rightValue, undefined, {
110
+ numeric: true,
111
+ sensitivity: 'base'
112
+ });
113
+ };
94
114
  const getDataTableRowValue = (row, index) => String(row.rowValue ?? row.value ?? row.id ?? index);
95
115
  const toInputValue = (value) => {
96
116
  if (value === undefined)
@@ -132,7 +152,6 @@ export const Attachment = ({ className, glass = false, href, ...props }) => {
132
152
  (_jsx("article", { className: nextClassName, ...props }));
133
153
  };
134
154
  export const Avatar = ({ alt = '', children, className, fallback, src, ...props }) => (_jsxs("span", { className: composeClassName('ui-avatar', className), ...props, children: [src ? _jsx("img", { alt: alt, src: src }) : _jsx("span", { children: fallback }), children] }));
135
- export const Badge = ({ className, variant = 'default', ...props }) => (_jsx("span", { className: composeClassName('ui-badge', `ui-badge--${variant}`, className), "data-variant": variant, ...props }));
136
155
  export const Breadcrumb = ({ 'aria-label': ariaLabel = 'Breadcrumb', className, ...props }) => (_jsx("nav", { "aria-label": ariaLabel, className: composeClassName('ui-breadcrumb', className), ...props }));
137
156
  export const Bubble = ({ className, from = 'assistant', glass = false, ...props }) => (_jsx("article", { className: composeClassName('ui-bubble', from === 'user' && 'ui-bubble--user', glassClass('ui-bubble', glass), className), "data-from": from, ...props }));
138
157
  /* eslint-disable complexity -- The compatibility path resolves native, slotted, loading, size, and disabled states in one public component. */
@@ -163,31 +182,21 @@ export const Calendar = ({ className, defaultValue, disabled = false, glass = fa
163
182
  });
164
183
  return (_jsxs("div", { ...props, ...calendar.rootProps, className: composeClassName(calendar.rootProps.className, glassClass('ui-calendar', glass), className), "data-ui-glass-track": glass ? true : undefined, children: [_jsx("input", { ...calendar.inputProps }), _jsxs("div", { className: "ui-calendar__header", children: [_jsx("button", { ...calendar.previousProps, type: "button", children: _jsx("span", { "aria-hidden": "true", children: "\u2039" }) }), _jsx("strong", { ...calendar.labelProps, children: calendar.label }), _jsx("button", { ...calendar.nextProps, type: "button", children: _jsx("span", { "aria-hidden": "true", children: "\u203A" }) })] }), _jsxs("table", { ...calendar.gridProps, children: [_jsx("thead", { children: _jsx("tr", { role: "row", children: calendar.weekdays.map(weekday => (_jsx("th", { role: "columnheader", scope: "col", children: weekday }, weekday))) }) }), _jsx("tbody", { children: calendar.weeks.map(week => (_jsx("tr", { role: "row", children: week.map(day => (_jsx("td", { ...calendar.getDayProps(day), children: day.day }, day.date))) }, week[0]?.date ?? 'week'))) })] })] }));
165
184
  };
166
- export const Card = ({ as, className, glass = false, variant = 'default', ...props }) => (_jsx(Primitive, { ...(as ? { as } : {}), className: composeClassName(variant === 'muted' && 'ui-card--muted', variant === 'interactive' && 'ui-card--interactive', variant === 'unstyled' && 'ui-card--unstyled', (glass || variant === 'glass') &&
167
- composeClassName('ui-card--glass', glassIntensityClass(glass)), className), "data-slot": "card", "data-variant": variant, ...props, uiClassName: "ui-card" }));
168
- export const CardHeader = ({ as, ...props }) => (_jsx(Primitive, { ...(as ? { as } : {}), "data-slot": "card-header", ...props, uiClassName: "ui-card__header" }));
169
- export const CardTitle = ({ as, ...props }) => (_jsx(Primitive, { ...(as ? { as } : {}), "data-slot": "card-title", ...props, uiClassName: "ui-card__title" }));
170
- export const CardDescription = ({ as, ...props }) => (_jsx(Primitive, { ...(as ? { as } : {}), "data-slot": "card-description", ...props, uiClassName: "ui-card__description" }));
171
- export const CardContent = ({ as, ...props }) => (_jsx(Primitive, { ...(as ? { as } : {}), "data-slot": "card-content", ...props, uiClassName: "ui-card__content" }));
172
- export const CardFooter = ({ as, ...props }) => (_jsx(Primitive, { ...(as ? { as } : {}), "data-slot": "card-footer", ...props, uiClassName: "ui-card__footer" }));
173
185
  export const Carousel = ({ className, glass = false, ...props }) => (_jsx("section", { className: composeClassName('ui-carousel', glassClass('ui-carousel', glass), className), "data-ui-carousel": true, ...props }));
174
186
  const ChartHeader = ({ description, heading, value }) => {
175
187
  if (!heading && !description && !value)
176
188
  return null;
177
189
  return (_jsxs("header", { children: [_jsxs("div", { className: "ui-chart__heading", children: [heading && _jsx("h3", { children: heading }), description && _jsx("p", { children: description })] }), value && _jsx("strong", { "data-ui-chart-value": true, children: value })] }));
178
190
  };
179
- export const Chart = ({ caption, children, className, description, glass = false, heading, presentation = 'default', value, ...props }) => (_jsxs("figure", { className: composeClassName('ui-chart', presentation === 'bare' && 'ui-chart--bare', glassClass('ui-chart', glass), className), ...props, children: [_jsx(ChartHeader, { description: description, heading: heading, value: value }), children, caption && _jsx("figcaption", { children: caption })] }));
191
+ export const Chart = ({ caption, children, className, description, glass = false, heading, presentation = 'default', summary, value, ...props }) => (_jsxs("figure", { className: composeClassName('ui-chart', presentation === 'bare' && 'ui-chart--bare', glassClass('ui-chart', glass), className), ...props, children: [_jsx(ChartHeader, { description: description, heading: heading, value: value }), summary && _jsx("p", { className: "ui-sr-only", "data-ui-chart-summary": true, children: summary }), children, caption && _jsx("figcaption", { children: caption })] }));
180
192
  const ChartLegend = ({ series }) => (_jsx("ul", { "aria-label": "Chart legend", className: "ui-chart__legend", children: series.map((item, index) => (_jsxs("li", { className: getLumenChartToneClassName(item.tone, index), children: [_jsx("span", { "aria-hidden": "true" }), item.label] }, item.id))) }));
181
193
  const ChartDataTable = ({ categories, formatCategory = String, formatValue = String, series }) => (_jsxs("details", { className: "ui-chart__data", children: [_jsx("summary", { children: "View chart data" }), _jsx("div", { children: _jsxs("table", { children: [_jsx("thead", { children: _jsxs("tr", { children: [_jsx("th", { scope: "col", children: "Category" }), series.map(item => (_jsx("th", { scope: "col", children: item.label }, item.id)))] }) }), _jsx("tbody", { children: categories.map(category => (_jsxs("tr", { children: [_jsx("th", { scope: "row", children: series
182
194
  .flatMap(item => item.data)
183
195
  .find(datum => datum.x === category)?.xLabel ??
184
196
  formatCategory(category) }), series.map(item => {
185
197
  const datum = item.data.find(candidate => candidate.x === category);
186
- return (_jsx("td", { children: datum?.label ??
187
- (datum?.y === null || datum === undefined ?
188
- 'Not available' :
189
- formatValue(datum.y)) }, item.id));
190
- })] }, `${typeof category}:${String(category)}`))) })] }) })] }));
198
+ return (_jsx("td", { children: datum?.label ?? formatReactChartTableValue(datum?.y, formatValue) }, item.id));
199
+ })] }, getChartCategoryKey(category)))) })] }) })] }));
191
200
  const formatChartPercentage = (percentage) => new Intl.NumberFormat(undefined, {
192
201
  maximumFractionDigits: percentage < 0.01 ? 1 : 0,
193
202
  style: 'percent'
@@ -200,7 +209,7 @@ export const Sparkline = ({ area = false, className, label = 'Trend', showEndpoi
200
209
  geometry.areaPaths.map(path => (_jsx("path", { className: "ui-sparkline__area", d: path }, path))), _jsx("path", { className: "ui-sparkline__line", d: geometry.path }), showEndpoint && endpoint && (_jsx("circle", { className: "ui-sparkline__endpoint", cx: endpoint.xCoordinate, cy: endpoint.yCoordinate, r: "2.5" }))] }), _jsx("span", { className: "ui-sr-only", children: label })] }));
201
210
  };
202
211
  /* eslint-disable complexity -- Chart renderers keep optional semantic and SVG layers colocated. */
203
- export const BarChart = ({ categoryWidth, className, emptyLabel = 'No chart data available.', formatCategory = String, formatValue = String, layout = 'grouped', orientation = 'vertical', series = emptyChartSeries, showLegend = series.length > 1, showTable = true, ...props }) => {
212
+ export const BarChart = ({ categoryWidth, className, emptyLabel = 'No chart data available.', formatCategory = String, formatValue = String, layout = 'grouped', orientation = 'vertical', series = emptyChartSeries, showLegend = series.length > 1, showTable = true, summary, ...props }) => {
204
213
  const geometry = createLumenBarGeometry(series, {
205
214
  ...(categoryWidth === undefined ? {} : { categoryWidth }),
206
215
  layout,
@@ -217,17 +226,17 @@ export const BarChart = ({ categoryWidth, className, emptyLabel = 'No chart data
217
226
  top: 16
218
227
  } :
219
228
  { bottom: 52, left: 52, right: 16, top: 16 };
220
- return (_jsxs(Chart, { className: composeClassName('ui-bar-chart', className), ...props, children: [showLegend && hasData && _jsx(ChartLegend, { series: series }), !hasData && (_jsx("p", { className: "ui-chart__empty", role: "status", children: emptyLabel })), _jsx("div", { className: "ui-chart__plot", hidden: !hasData, children: _jsxs("svg", { "aria-hidden": "true", preserveAspectRatio: "xMidYMid meet", viewBox: `0 0 ${geometry.width} ${geometry.height}`, children: [_jsx("g", { className: "ui-chart__grid", children: ticks.map(tick => {
229
+ return (_jsxs(Chart, { className: composeClassName('ui-bar-chart', className), summary: summary ?? formatLumenChartSummary(series, formatValue), ...props, children: [showLegend && hasData && _jsx(ChartLegend, { series: series }), !hasData && (_jsx("p", { className: "ui-chart__empty", role: "status", children: emptyLabel })), _jsx("div", { className: "ui-chart__plot", hidden: !hasData, children: _jsxs("svg", { "aria-hidden": "true", preserveAspectRatio: "xMidYMid meet", viewBox: `0 0 ${geometry.width} ${geometry.height}`, children: [_jsx("g", { className: "ui-chart__grid", children: ticks.map(tick => {
221
230
  const coordinate = orientation === 'horizontal' ?
222
231
  scaleLumenChartValue(tick, geometry.domain, margin.left, geometry.width - margin.right) :
223
232
  scaleLumenChartValue(tick, geometry.domain, geometry.height - margin.bottom, margin.top);
224
233
  return orientation === 'horizontal' ?
225
234
  (_jsx("line", { x1: coordinate, x2: coordinate, y1: margin.top, y2: geometry.height - margin.bottom }, tick)) :
226
235
  (_jsx("line", { x1: margin.left, x2: geometry.width - margin.right, y1: coordinate, y2: coordinate }, tick));
227
- }) }), _jsx("g", { className: "ui-chart__axis-labels", children: geometry.categories.map(category => (_jsx("text", { dominantBaseline: orientation === 'horizontal' ? 'middle' : undefined, textAnchor: orientation === 'horizontal' ? 'end' : 'middle', x: category.x, y: category.y, children: String(category.label) }, `${typeof category.label}:${String(category.label)}`))) }), _jsx("g", { className: "ui-bar-chart__marks", children: geometry.marks.map(mark => (_jsx("rect", { className: getLumenChartToneClassName(mark.tone), height: mark.height, rx: "4", width: mark.width, x: mark.x, y: mark.y, children: _jsx("title", { children: `${series
236
+ }) }), _jsx("g", { className: "ui-chart__axis-labels", children: geometry.categories.map(category => (_jsx("text", { dominantBaseline: orientation === 'horizontal' ? 'middle' : undefined, textAnchor: orientation === 'horizontal' ? 'end' : 'middle', x: category.x, y: category.y, children: String(category.label) }, getChartCategoryKey(category.label)))) }), _jsx("g", { className: "ui-bar-chart__marks", children: geometry.marks.map(mark => (_jsx("rect", { className: getLumenChartToneClassName(mark.tone), height: mark.height, rx: "4", width: mark.width, x: mark.x, y: mark.y, children: _jsx("title", { children: `${series
228
237
  .flatMap(item => item.data)
229
238
  .find(datum => datum.x === mark.category)?.xLabel ??
230
- formatCategory(mark.category)} · ${mark.seriesLabel}: ${formatValue(mark.value)}` }) }, `${mark.seriesId}:${String(mark.category)}`))) })] }) }), showTable && hasData && (_jsx(ChartDataTable, { categories: categories, formatCategory: formatCategory, formatValue: formatValue, series: series }))] }));
239
+ formatCategory(mark.category)} · ${mark.seriesLabel}: ${formatValue(mark.value)}` }) }, `${mark.seriesId}:${getChartCategoryKey(mark.category)}`))) })] }) }), showTable && hasData && (_jsx(ChartDataTable, { categories: categories, formatCategory: formatCategory, formatValue: formatValue, series: series }))] }));
231
240
  };
232
241
  const getLineChartMarkerStep = (markers, categoryCount) => {
233
242
  if (markers === false || markers === 'none')
@@ -238,13 +247,16 @@ const getLineChartMarkerStep = (markers, categoryCount) => {
238
247
  return Math.max(1, Math.ceil(categoryCount / 24));
239
248
  return 1;
240
249
  };
241
- export const LineChart = ({ area = false, className, emptyLabel = 'No chart data available.', formatCategory = String, formatValue = String, markers = 'auto', referenceValue, series = emptyChartSeries, showLegend = series.length > 1, showTable = true, ...props }) => {
250
+ export const LineChart = ({ area = false, className, emptyLabel = 'No chart data available.', formatCategory = String, formatValue = String, markers = 'auto', referenceValue, series = emptyChartSeries, showLegend = series.length > 1, showTable = true, summary, ...props }) => {
242
251
  const width = 640;
243
252
  const height = 320;
244
253
  const padding = 44;
245
254
  const categories = getLumenChartCategories(series);
246
255
  const hasData = hasLumenChartData(series);
247
- const alignedSeries = series.map(item => alignLumenChartSeries(item, categories));
256
+ const alignedSeries = series.map(item => ({
257
+ ...item,
258
+ data: alignLumenChartSeries(item, categories).data
259
+ }));
248
260
  const domain = getLumenChartDomain([
249
261
  ...alignedSeries.flatMap(item => item.data.map(datum => datum.y)),
250
262
  referenceValue ?? null
@@ -262,7 +274,7 @@ export const LineChart = ({ area = false, className, emptyLabel = 'No chart data
262
274
  const referenceY = referenceValue === undefined ?
263
275
  undefined :
264
276
  scaleLumenChartValue(referenceValue, domain, height - padding, padding);
265
- return (_jsxs(Chart, { className: composeClassName('ui-line-chart', className), ...props, children: [showLegend && hasData && _jsx(ChartLegend, { series: series }), !hasData && (_jsx("p", { className: "ui-chart__empty", role: "status", children: emptyLabel })), _jsx("div", { className: "ui-chart__plot", hidden: !hasData, children: _jsxs("svg", { "aria-hidden": "true", preserveAspectRatio: "xMidYMid meet", viewBox: `0 0 ${width} ${height}`, children: [_jsx("g", { className: "ui-chart__grid", children: ticks.map(tick => {
277
+ return (_jsxs(Chart, { className: composeClassName('ui-line-chart', className), summary: summary ?? formatLumenChartSummary(series, formatValue), ...props, children: [showLegend && hasData && _jsx(ChartLegend, { series: series }), !hasData && (_jsx("p", { className: "ui-chart__empty", role: "status", children: emptyLabel })), _jsx("div", { className: "ui-chart__plot", hidden: !hasData, children: _jsxs("svg", { "aria-hidden": "true", preserveAspectRatio: "xMidYMid meet", viewBox: `0 0 ${width} ${height}`, children: [_jsx("g", { className: "ui-chart__grid", children: ticks.map(tick => {
266
278
  const y = scaleLumenChartValue(tick, domain, height - padding, padding);
267
279
  return (_jsxs(Fragment, { children: [_jsx("line", { x1: padding, x2: width - padding, y1: y, y2: y }), _jsx("text", { x: padding - 8, y: y, children: formatValue(tick) })] }, tick));
268
280
  }) }), _jsx("g", { className: "ui-chart__axis-labels", children: categories.map((category, index) => {
@@ -273,7 +285,7 @@ export const LineChart = ({ area = false, className, emptyLabel = 'No chart data
273
285
  return (_jsx("text", { textAnchor: "middle", x: x, y: height - 14, children: series
274
286
  .flatMap(item => item.data)
275
287
  .find(datum => datum.x === category)?.xLabel ??
276
- formatCategory(category) }, `${typeof category}:${String(category)}`));
288
+ formatCategory(category) }, getChartCategoryKey(category)));
277
289
  }) }), referenceY !== undefined && (_jsx("line", { className: "ui-chart__reference", x1: padding, x2: width - padding, y1: referenceY, y2: referenceY })), geometries.map((geometry, index) => {
278
290
  const item = series[index];
279
291
  if (!item)
@@ -281,15 +293,86 @@ export const LineChart = ({ area = false, className, emptyLabel = 'No chart data
281
293
  const tone = resolveLumenChartTone(item.tone, index);
282
294
  return (_jsxs("g", { className: composeClassName('ui-line-chart__series', getLumenChartToneClassName(tone)), children: [area &&
283
295
  geometry.areaPaths.map(path => (_jsx("path", { className: "ui-line-chart__area", d: path }, path))), _jsx("path", { className: "ui-line-chart__line", d: geometry.path }), Number.isFinite(markerStep) &&
284
- geometry.points.map((point, pointIndex) => pointIndex % markerStep === 0 && (_jsx("circle", { className: "ui-line-chart__point", cx: point.xCoordinate, cy: point.yCoordinate, r: "3", children: _jsx("title", { children: `${point.xLabel ?? formatCategory(point.x)} · ${item.label}: ${formatValue(point.y ?? 0)}` }) }, `${typeof point.x}:${String(point.x)}`)))] }, item.id));
296
+ geometry.points.map((point, pointIndex) => pointIndex % markerStep === 0 && (_jsx("circle", { className: "ui-line-chart__point", cx: point.xCoordinate, cy: point.yCoordinate, r: "3", children: _jsx("title", { children: `${point.xLabel ?? formatCategory(point.x)} · ${item.label}: ${formatValue(point.y ?? 0)}` }) }, getChartCategoryKey(point.x))))] }, item.id));
285
297
  })] }) }), showTable && hasData && (_jsx(ChartDataTable, { categories: categories, formatCategory: formatCategory, formatValue: formatValue, series: series }))] }));
286
298
  };
287
- export const PieChart = ({ centerLabel, centerValue, className, series = emptyPieSeries, showLegend = true, showTable = true, valueFormatter = String, variant = 'donut', ...props }) => {
299
+ export const PieChart = ({ centerLabel, centerValue, className, series = emptyPieSeries, showLegend = true, showTable = true, summary, valueFormatter = String, variant = 'donut', ...props }) => {
288
300
  const geometry = createLumenPieGeometry(series.data, { variant });
289
301
  const hasData = hasLumenPieData(series.data);
302
+ const renderedSeries = {
303
+ ...series,
304
+ data: series.data.filter(datum => datum.y !== null && Number.isFinite(datum.y) && datum.y > 0)
305
+ };
290
306
  const hasCenterLabel = centerLabel !== null && centerLabel !== undefined;
291
307
  const hasCenterValue = centerValue !== null && centerValue !== undefined;
292
- return (_jsxs(Chart, { className: composeClassName('ui-pie-chart', getLumenPieChartVariantClassName(variant), className), ...props, children: [showLegend && hasData && (_jsx("ul", { "aria-label": "Chart legend", className: "ui-chart__legend", children: geometry.slices.map(slice => (_jsxs("li", { className: getLumenChartToneClassName(slice.tone), children: [_jsx("span", { "aria-hidden": "true" }), slice.label] }, `${typeof slice.x}:${String(slice.x)}`))) })), !hasData && (_jsx("p", { className: "ui-chart__empty", role: "status", children: "No chart data available." })), _jsxs("div", { className: "ui-chart__plot ui-pie-chart__plot", hidden: !hasData, children: [_jsx("svg", { "aria-hidden": "true", preserveAspectRatio: "xMidYMid meet", viewBox: `0 0 ${geometry.size} ${geometry.size}`, children: _jsx("g", { className: "ui-pie-chart__slices", children: geometry.slices.map(slice => (_jsx("path", { className: getLumenChartToneClassName(slice.tone), d: slice.path, fillRule: "evenodd", children: _jsx("title", { children: `${slice.label}: ${valueFormatter(slice.value)} (${formatChartPercentage(slice.percentage)})` }) }, `${typeof slice.x}:${String(slice.x)}`))) }) }), variant === 'donut' && (hasCenterLabel || hasCenterValue) && (_jsxs(_Fragment, { children: [_jsxs("div", { "aria-hidden": "true", className: "ui-pie-chart__center", children: [hasCenterValue && _jsx("strong", { children: centerValue }), hasCenterLabel && _jsx("span", { children: centerLabel })] }), _jsxs("div", { className: "ui-sr-only", children: [centerValue, ' ', centerLabel] })] }))] }), showTable && hasData && (_jsxs("details", { className: "ui-chart__data", children: [_jsx("summary", { children: "View chart data" }), _jsx("div", { children: _jsxs("table", { children: [_jsx("thead", { children: _jsxs("tr", { children: [_jsx("th", { scope: "col", children: "Category" }), _jsx("th", { scope: "col", children: "Value" }), _jsx("th", { scope: "col", children: "Share" })] }) }), _jsx("tbody", { children: geometry.slices.map(slice => (_jsxs("tr", { children: [_jsx("th", { scope: "row", children: slice.label }), _jsx("td", { children: valueFormatter(slice.value) }), _jsx("td", { children: formatChartPercentage(slice.percentage) })] }, `${typeof slice.x}:${String(slice.x)}`))) })] }) })] }))] }));
308
+ return (_jsxs(Chart, { className: composeClassName('ui-pie-chart', getLumenPieChartVariantClassName(variant), className), summary: summary ?? formatLumenChartSummary([renderedSeries], valueFormatter), ...props, children: [showLegend && hasData && (_jsx("ul", { "aria-label": "Chart legend", className: "ui-chart__legend", children: geometry.slices.map(slice => (_jsxs("li", { className: getLumenChartToneClassName(slice.tone), children: [_jsx("span", { "aria-hidden": "true" }), slice.label] }, `${typeof slice.x}:${String(slice.x)}`))) })), !hasData && (_jsx("p", { className: "ui-chart__empty", role: "status", children: "No chart data available." })), _jsxs("div", { className: "ui-chart__plot ui-pie-chart__plot", hidden: !hasData, children: [_jsx("svg", { "aria-hidden": "true", preserveAspectRatio: "xMidYMid meet", viewBox: `0 0 ${geometry.size} ${geometry.size}`, children: _jsx("g", { className: "ui-pie-chart__slices", children: geometry.slices.map(slice => (_jsx("path", { className: getLumenChartToneClassName(slice.tone), d: slice.path, fillRule: "evenodd", children: _jsx("title", { children: `${slice.label}: ${valueFormatter(slice.value)} (${formatChartPercentage(slice.percentage)})` }) }, `${typeof slice.x}:${String(slice.x)}`))) }) }), variant === 'donut' && (hasCenterLabel || hasCenterValue) && (_jsxs(_Fragment, { children: [_jsxs("div", { "aria-hidden": "true", className: "ui-pie-chart__center", children: [hasCenterValue && _jsx("strong", { children: centerValue }), hasCenterLabel && _jsx("span", { children: centerLabel })] }), _jsxs("div", { className: "ui-sr-only", children: [centerValue, ' ', centerLabel] })] }))] }), showTable && hasData && (_jsxs("details", { className: "ui-chart__data", children: [_jsx("summary", { children: "View chart data" }), _jsx("div", { children: _jsxs("table", { children: [_jsx("thead", { children: _jsxs("tr", { children: [_jsx("th", { scope: "col", children: "Category" }), _jsx("th", { scope: "col", children: "Value" }), _jsx("th", { scope: "col", children: "Share" })] }) }), _jsx("tbody", { children: geometry.slices.map(slice => (_jsxs("tr", { children: [_jsx("th", { scope: "row", children: slice.label }), _jsx("td", { children: valueFormatter(slice.value) }), _jsx("td", { children: formatChartPercentage(slice.percentage) })] }, `${typeof slice.x}:${String(slice.x)}`))) })] }) })] }))] }));
309
+ };
310
+ export const ScatterChart = ({ className, formatValue = String, series = emptyChartSeries, showLegend = series.length > 1, showTable = true, summary, xScale = 'linear', ...props }) => {
311
+ const geometry = createLumenScatterGeometry(series, { xScale });
312
+ const renderedSeries = series.map(item => ({
313
+ ...item,
314
+ data: geometry.points.filter(point => point.seriesId === item.id)
315
+ })).filter(item => item.data.length > 0);
316
+ const hasData = geometry.points.length > 0;
317
+ return (_jsxs(Chart, { className: composeClassName('ui-scatter-chart', className), summary: summary ?? formatLumenChartSummary(renderedSeries, formatValue), ...props, children: [showLegend && hasData && _jsx(ChartLegend, { series: series }), !hasData && _jsx("p", { className: "ui-chart__empty", role: "status", children: "No chart data available." }), _jsx("div", { className: "ui-chart__plot", hidden: !hasData, children: _jsx("svg", { "aria-hidden": "true", viewBox: `0 0 ${geometry.width} ${geometry.height}`, children: _jsx("g", { className: "ui-scatter-chart__marks", children: geometry.points.map((point, pointIndex) => (_jsx("circle", { className: getLumenChartToneClassName(point.tone), cx: point.xCoordinate, cy: point.yCoordinate, r: point.radius, children: _jsx("title", { children: `${point.xLabel ?? point.x} · ${point.seriesLabel}: ${point.label ?? formatValue(point.y ?? 0)}` }) }, `${point.seriesId}:${point.id ?? `${getChartCategoryKey(point.x)}:${pointIndex}`}`))) }) }) }), showTable && hasData && (_jsxs("details", { className: "ui-chart__data", children: [_jsx("summary", { children: "View chart data" }), _jsx("div", { children: _jsxs("table", { children: [_jsx("thead", { children: _jsxs("tr", { children: [_jsx("th", { scope: "col", children: "X" }), _jsx("th", { scope: "col", children: "Series" }), _jsx("th", { scope: "col", children: "Value" }), _jsx("th", { scope: "col", children: "Size" })] }) }), _jsx("tbody", { children: geometry.points.map((point, pointIndex) => (_jsxs("tr", { children: [_jsx("th", { scope: "row", children: point.xLabel ?? point.x }), _jsx("td", { children: point.seriesLabel }), _jsx("td", { children: point.label ?? formatValue(point.y ?? 0) }), _jsx("td", { children: formatReactChartTableValue(point.size, formatValue) })] }, `${point.seriesId}:${point.id ?? `${getChartCategoryKey(point.x)}:${pointIndex}`}`))) })] }) })] }))] }));
318
+ };
319
+ export const Heatmap = ({ className, data = emptyHeatmapData, formatValue = String, showTable = true, summary, ...props }) => {
320
+ const geometry = createLumenHeatmapGeometry(data);
321
+ const availableCells = geometry.cells.filter(cell => cell.value !== null && Number.isFinite(cell.value));
322
+ const hasData = availableCells.length > 0;
323
+ return (_jsxs(Chart, { className: composeClassName('ui-heatmap', className), summary: summary ?? `${availableCells.length} available heatmap cells.`, ...props, children: [!hasData && _jsx("p", { className: "ui-chart__empty", role: "status", children: "No chart data available." }), _jsx("div", { className: "ui-chart__plot", hidden: !hasData, children: _jsx("svg", { "aria-hidden": "true", viewBox: `0 0 ${geometry.width} ${geometry.height}`, children: _jsx("g", { className: "ui-heatmap__cells", children: availableCells.map(cell => _jsx("rect", { height: Math.max(0, cell.height - 2), opacity: Math.max(0.12, cell.ratio), width: Math.max(0, cell.width - 2), x: cell.xCoordinate + 1, y: cell.yCoordinate + 1, children: _jsx("title", { children: `${cell.xLabel ?? cell.x} · ${cell.yLabel ?? cell.y}: ${cell.label ?? formatValue(cell.value ?? 0)}` }) }, cell.id ?? `${getChartCategoryKey(cell.x)}:${getChartCategoryKey(cell.y)}`)) }) }) }), showTable && (_jsxs("details", { className: "ui-chart__data", children: [_jsx("summary", { children: "View chart data" }), _jsx("div", { children: _jsxs("table", { children: [_jsx("thead", { children: _jsxs("tr", { children: [_jsx("th", { scope: "col", children: "Column" }), _jsx("th", { scope: "col", children: "Row" }), _jsx("th", { scope: "col", children: "Value" })] }) }), _jsx("tbody", { children: data.map(cell => (_jsxs("tr", { children: [_jsx("th", { scope: "row", children: cell.xLabel ?? cell.x }), _jsx("td", { children: cell.yLabel ?? cell.y }), _jsx("td", { children: cell.label ?? (cell.value === null || !Number.isFinite(cell.value) ? 'Not available' : formatValue(cell.value)) })] }, cell.id ?? `${getChartCategoryKey(cell.x)}:${getChartCategoryKey(cell.y)}`))) })] }) })] }))] }));
324
+ };
325
+ export const RangeChart = ({ className, data = emptyRangeData, formatValue = String, showTable = true, summary, tone = 'series-1', ...props }) => {
326
+ const width = 640;
327
+ const height = 320;
328
+ const padding = 44;
329
+ const geometry = createLumenRangeGeometry(data, { height, padding, width });
330
+ return (_jsxs(Chart, { className: composeClassName('ui-range-chart', getLumenChartToneClassName(tone), className), summary: summary ?? (geometry.points.length === 0 ? 'No chart data available.' : `${geometry.points.length} available ranges.`), ...props, children: [geometry.points.length === 0 && _jsx("p", { className: "ui-chart__empty", role: "status", children: "No chart data available." }), _jsx("div", { className: "ui-chart__plot", hidden: geometry.points.length === 0, children: _jsxs("svg", { "aria-hidden": "true", viewBox: `0 0 ${width} ${height}`, children: [_jsx("path", { className: "ui-range-chart__area", d: geometry.areaPath }), geometry.points.map(point => (_jsx("line", { className: "ui-range-chart__interval", x1: point.xCoordinate, x2: point.xCoordinate, y1: point.highCoordinate, y2: point.lowCoordinate, children: _jsx("title", { children: `${point.xLabel ?? point.x}: ${point.label ?? `${formatValue(point.low ?? 0)}–${formatValue(point.high ?? 0)}`}` }) }, point.id ?? getChartCategoryKey(point.x))))] }) }), showTable && (_jsxs("details", { className: "ui-chart__data", children: [_jsx("summary", { children: "View chart data" }), _jsx("div", { children: _jsxs("table", { children: [_jsx("thead", { children: _jsxs("tr", { children: [_jsx("th", { scope: "col", children: "Category" }), _jsx("th", { scope: "col", children: "Low" }), _jsx("th", { scope: "col", children: "High" })] }) }), _jsx("tbody", { children: data.map(item => (_jsxs("tr", { children: [_jsx("th", { scope: "row", children: item.xLabel ?? item.x }), _jsx("td", { children: item.low === null || !Number.isFinite(item.low) ? 'Not available' : formatValue(item.low) }), _jsx("td", { children: item.high === null || !Number.isFinite(item.high) ? 'Not available' : formatValue(item.high) })] }, item.id ?? getChartCategoryKey(item.x)))) })] }) })] }))] }));
331
+ };
332
+ export const ComboChart = ({ className, formatValue = String, series = emptyComboSeries, showLegend = true, showTable = true, summary, ...props }) => {
333
+ const width = 640;
334
+ const height = 320;
335
+ const padding = 44;
336
+ const categories = getLumenChartCategories(series);
337
+ const alignedSeries = series.map(item => ({
338
+ ...item,
339
+ data: alignLumenChartSeries(item, categories).data
340
+ }));
341
+ const domain = getLumenChartDomain(alignedSeries.flatMap(item => item.data.map(datum => datum.y)));
342
+ const barSeries = alignedSeries.filter(item => item.mark === 'bar');
343
+ const lineSeries = alignedSeries.filter(item => item.mark !== 'bar');
344
+ const bars = createLumenBarGeometry(barSeries, { domain, height, width });
345
+ const categoryPositions = new Map(bars.categories.map(category => [getChartCategoryKey(category.category), category.x]));
346
+ const drawableWidth = width - padding * 2;
347
+ const alignComboLineDatum = (datum) => {
348
+ if (barSeries.length === 0)
349
+ return datum;
350
+ return {
351
+ ...datum,
352
+ x: ((categoryPositions.get(`${typeof datum.x}:${String(datum.x)}`) ?? padding) - padding) / drawableWidth
353
+ };
354
+ };
355
+ const lineGeometryOptions = {
356
+ domain,
357
+ height,
358
+ padding,
359
+ width,
360
+ ...(barSeries.length === 0 ?
361
+ {} :
362
+ {
363
+ xDomain: { max: 1, min: 0 },
364
+ xScale: 'linear'
365
+ })
366
+ };
367
+ const lines = lineSeries.map(item => createLumenLineGeometry(item.data.map(alignComboLineDatum), lineGeometryOptions));
368
+ const hasData = hasLumenChartData(series);
369
+ return (_jsxs(Chart, { className: composeClassName('ui-combo-chart', className), summary: summary ?? formatLumenChartSummary(series, formatValue), ...props, children: [showLegend && hasData && _jsx(ChartLegend, { series: series }), !hasData && _jsx("p", { className: "ui-chart__empty", role: "status", children: "No chart data available." }), _jsx("div", { className: "ui-chart__plot", hidden: !hasData, children: _jsxs("svg", { "aria-hidden": "true", viewBox: `0 0 ${width} ${height}`, children: [_jsx("g", { className: "ui-bar-chart__marks", children: bars.marks.map(mark => _jsx("rect", { className: getLumenChartToneClassName(mark.tone), height: mark.height, rx: "4", width: mark.width, x: mark.x, y: mark.y, children: _jsx("title", { children: `${mark.seriesLabel}: ${formatValue(mark.value)}` }) }, `${mark.seriesId}:${getChartCategoryKey(mark.category)}`)) }), lines.map((geometry, index) => {
370
+ const item = lineSeries[index];
371
+ if (!item)
372
+ return null;
373
+ const tone = resolveLumenChartTone(item.tone, index + barSeries.length);
374
+ return (_jsxs("g", { className: composeClassName('ui-line-chart__series', getLumenChartToneClassName(tone)), children: [item.mark === 'area' && geometry.areaPaths.map(path => (_jsx("path", { className: "ui-line-chart__area", d: path }, path))), _jsx("path", { className: "ui-line-chart__line", d: geometry.path })] }, item.id));
375
+ })] }) }), showTable && hasData && _jsx(ChartDataTable, { categories: categories, formatValue: formatValue, series: series })] }));
293
376
  };
294
377
  export const Checkbox = ({ className, ref, ...props }) => (_jsx("input", { className: composeClassName('ui-checkbox', className), ref: ref, type: "checkbox", ...props }));
295
378
  export const CheckboxGroup = ({ children, className, invalid = false, legend, orientation = 'vertical', ...props }) => (_jsxs("fieldset", { "aria-invalid": invalid || undefined, className: composeClassName('ui-checkbox-group', `ui-checkbox-group--${orientation}`, className), "data-invalid": invalid ? 'true' : undefined, "data-orientation": orientation, "data-ui-checkbox-group": true, ...props, children: [legend && _jsx("legend", { children: legend }), children] }));
@@ -367,6 +450,7 @@ export const CopyButton = ({ children = 'Copy', className, copiedLabel = 'Copied
367
450
  };
368
451
  export const Combobox = ({ className, defaultValue, id, label, list, onBlur, onChange, onFocus, onKeyDown, options = emptyStringOptions, type = 'text', value: valueProp, wrapperClassName, ...props }) => {
369
452
  const inputId = id ?? `${list}-input`;
453
+ const rootRef = useRef(null);
370
454
  const [open, setOpen] = useState(false);
371
455
  const [value, setValue] = useState(() => String(defaultValue ?? ''));
372
456
  const renderedValue = valueProp ?? value;
@@ -378,12 +462,33 @@ export const Combobox = ({ className, defaultValue, id, label, list, onBlur, onC
378
462
  }
379
463
  setOpen(false);
380
464
  };
465
+ const getVisibleOptions = () => [
466
+ ...(rootRef.current?.querySelectorAll('[data-ui-combobox-option]:not([hidden]):not([disabled])') ?? [])
467
+ ];
468
+ const focusInputEdgeOption = (key) => {
469
+ const options = getVisibleOptions();
470
+ options[key === 'ArrowUp' ? options.length - 1 : 0]?.focus();
471
+ };
472
+ const focusOption = (option, key) => {
473
+ const options = getVisibleOptions();
474
+ if (!options.length)
475
+ return;
476
+ const currentIndex = Math.max(0, options.indexOf(option));
477
+ let nextIndex = (currentIndex - 1 + options.length) % options.length;
478
+ if (key === 'Home')
479
+ nextIndex = 0;
480
+ else if (key === 'End')
481
+ nextIndex = options.length - 1;
482
+ else if (key === 'ArrowDown')
483
+ nextIndex = (currentIndex + 1) % options.length;
484
+ options[nextIndex]?.focus();
485
+ };
381
486
  return (_jsxs("div", { className: composeClassName('ui-combobox', wrapperClassName), "data-ui-combobox": true, onBlur: event => {
382
487
  const nextTarget = event.relatedTarget;
383
488
  if (!nextTarget || !event.currentTarget.contains(nextTarget)) {
384
489
  setOpen(false);
385
490
  }
386
- }, children: [label && (_jsx("label", { className: "ui-label", htmlFor: inputId, children: label })), _jsx("input", { "aria-autocomplete": "list", "aria-controls": list, "aria-expanded": open, className: composeClassName('ui-input', className), id: inputId, role: "combobox", type: type, value: renderedValue, onBlur: onBlur, onChange: event => {
491
+ }, ref: rootRef, children: [label && (_jsx("label", { className: "ui-label", htmlFor: inputId, children: label })), _jsx("input", { "aria-autocomplete": "list", "aria-controls": list, "aria-expanded": open, className: composeClassName('ui-input', className), id: inputId, role: "combobox", type: type, value: renderedValue, onBlur: onBlur, onChange: event => {
387
492
  if (valueProp === undefined) {
388
493
  setValue(event.currentTarget.value);
389
494
  }
@@ -396,13 +501,37 @@ export const Combobox = ({ className, defaultValue, id, label, list, onBlur, onC
396
501
  if (event.key === 'Escape') {
397
502
  setOpen(false);
398
503
  }
504
+ if (event.key === 'ArrowDown' || event.key === 'ArrowUp') {
505
+ const key = event.key;
506
+ event.preventDefault();
507
+ setOpen(true);
508
+ globalThis.queueMicrotask(() => {
509
+ focusInputEdgeOption(key);
510
+ });
511
+ }
399
512
  if (event.key === 'Enter' && visibleOptions[0]) {
400
513
  event.preventDefault();
401
514
  selectOption(visibleOptions[0]);
402
515
  }
403
516
  onKeyDown?.(event);
404
- }, ...props }), _jsx("div", { className: "ui-combobox__list", hidden: !open, id: list, role: "listbox", children: visibleOptions.map(option => (_jsx("button", { "data-ui-combobox-option": true, "data-value": option, role: "option", type: "button", onClick: () => {
517
+ }, ...props }), _jsx("div", { className: "ui-combobox__list", hidden: !open, id: list, role: "listbox", children: visibleOptions.map(option => (_jsx("button", { "data-ui-combobox-option": true, "data-value": option, role: "option", tabIndex: -1, type: "button", onClick: () => {
405
518
  selectOption(option);
519
+ }, onKeyDown: event => {
520
+ if (event.key === 'Escape') {
521
+ setOpen(false);
522
+ rootRef.current?.querySelector('input[role="combobox"]')?.focus();
523
+ return;
524
+ }
525
+ if (event.key === 'Enter' || event.key === ' ') {
526
+ event.preventDefault();
527
+ rootRef.current?.querySelector('input[role="combobox"]')?.focus();
528
+ selectOption(option);
529
+ return;
530
+ }
531
+ if (!['ArrowDown', 'ArrowUp', 'Home', 'End'].includes(event.key))
532
+ return;
533
+ event.preventDefault();
534
+ focusOption(event.currentTarget, event.key);
406
535
  }, onMouseDown: event => {
407
536
  event.preventDefault();
408
537
  }, children: option }, option))) })] }));
@@ -410,9 +539,39 @@ export const Combobox = ({ className, defaultValue, id, label, list, onBlur, onC
410
539
  export const Command = ({ className, glass = false, ...props }) => (_jsx("div", { className: composeClassName('ui-command', glassClass('ui-command', glass), className), "data-ui-command": true, ...props }));
411
540
  export const ContextMenu = ({ className, glass = false, ...props }) => (_jsx("menu", { className: composeClassName('ui-menu', glassSurfaceClass('ui-menu', glass), className), "data-surface": resolveSurface(glass), "data-ui-context-menu": true, role: props.role ?? 'menu', ...props }));
412
541
  export const ColorPicker = ({ className, type = 'color', ...props }) => (_jsx("input", { className: composeClassName('ui-color-picker', className), type: type, ...props }));
413
- export const DataTable = ({ children, className, columns = emptyDataTableColumns, glass = false, name, rows = emptyDataTableRows, selectable = false, ...props }) => (_jsx("div", { className: composeClassName('ui-data-table', glassClass('ui-data-table', glass), className), "data-ui-datatable": true, "data-ui-datatable-name": name, "data-ui-datatable-selectable": selectable ? 'true' : undefined, "data-ui-glass-track": glass ? true : undefined, ...props, children: columns.length > 0 ?
414
- (_jsxs("table", { children: [_jsx("thead", { children: _jsx("tr", { children: columns.map(column => (_jsx("th", { "data-ui-datatable-sort-type": column.sort, "data-ui-datatable-sortable": column.sortable ? 'true' : undefined, scope: "col", children: column.header ?? column.label ?? column.key }, column.key))) }) }), _jsx("tbody", { children: rows.map((row, rowIndex) => (_jsx("tr", { "data-ui-datatable-row": true, "data-value": getDataTableRowValue(row, rowIndex), children: columns.map(column => (_jsx("td", { "data-sort-value": getDataTableSortValue(row[column.key]), children: formatDataTableCell(row[column.key]) }, column.key))) }, getDataTableRowValue(row, rowIndex)))) })] })) :
415
- (children) }));
542
+ export const DataTable = ({ children, className, columns = emptyDataTableColumns, glass = false, name, rows = emptyDataTableRows, selectable = false, ...props }) => {
543
+ const [sort, setSort] = useState(null);
544
+ const sortedRows = useMemo(() => {
545
+ if (!sort)
546
+ return rows;
547
+ const column = columns.find(candidate => candidate.key === sort.key);
548
+ if (!column)
549
+ return rows;
550
+ const direction = sort.direction === 'ascending' ? 1 : -1;
551
+ return [...rows].sort((left, right) => compareDataTableCells(left[column.key], right[column.key], column.sort) * direction);
552
+ }, [columns, rows, sort]);
553
+ const toggleSort = (column) => {
554
+ setSort(current => ({
555
+ direction: current?.key === column.key && current.direction === 'ascending' ?
556
+ 'descending' :
557
+ 'ascending',
558
+ key: column.key
559
+ }));
560
+ };
561
+ return (_jsx("div", { className: composeClassName('ui-data-table', glassClass('ui-data-table', glass), className), "data-ui-datatable": true, "data-ui-datatable-name": name, "data-ui-datatable-selectable": selectable ? 'true' : undefined, "data-ui-glass-track": glass ? true : undefined, ...props, children: columns.length > 0 ?
562
+ (_jsxs("table", { children: [_jsx("thead", { children: _jsx("tr", { children: columns.map(column => {
563
+ const direction = sort?.key === column.key ?
564
+ sort.direction :
565
+ undefined;
566
+ const label = column.header ?? column.label ?? column.key;
567
+ return (_jsx("th", { "aria-sort": direction ?? (column.sortable ? 'none' : undefined), "data-ui-datatable-sort-type": column.sort, "data-ui-datatable-sortable": column.sortable ? 'true' : undefined, scope: "col", children: column.sortable ?
568
+ (_jsx("button", { className: "ui-data-table__sort", "data-ui-datatable-sort": true, onClick: () => {
569
+ toggleSort(column);
570
+ }, type: "button", children: label })) :
571
+ label }, column.key));
572
+ }) }) }), _jsx("tbody", { children: sortedRows.map((row, rowIndex) => (_jsx("tr", { "data-ui-datatable-row": true, "data-value": getDataTableRowValue(row, rowIndex), children: columns.map(column => (_jsx("td", { "data-sort-value": getDataTableSortValue(row[column.key]), children: formatDataTableCell(row[column.key]) }, column.key))) }, getDataTableRowValue(row, rowIndex)))) })] })) :
573
+ children }));
574
+ };
416
575
  const formatDatePickerDisplayValue = (value, placeholder) => {
417
576
  if (!value || !/^\d{4}-\d{2}-\d{2}$/.test(value))
418
577
  return placeholder;
@@ -545,33 +704,6 @@ export const Dialog = ({ className, defaultOpen, glass = false, layout = 'center
545
704
  const dialog = useDialog({ defaultOpen, onOpenChange, open });
546
705
  return (_jsx("dialog", { ...dialog.dialogProps, ...props, className: composeClassName('ui-dialog', layout === 'fullscreen' && 'ui-dialog--fullscreen', glassSurfaceClass('ui-dialog', glass), className), "data-layout": layout, "data-surface": resolveSurface(glass), onClick: composeHandlers(onClick, dialog.dialogProps.onClick), onClose: composeHandlers(onClose, dialog.dialogProps.onClose) }));
547
706
  };
548
- export const Direction = ({ className, dir = 'ltr', ...props }) => (_jsx("div", { className: composeClassName('ui-direction', className), dir: dir, ...props }));
549
- export const Container = ({ as = 'div', className, size = 'lg', ...props }) => createElement(as, {
550
- className: composeClassName('ui-container', `ui-container--${size}`, className),
551
- 'data-size': size,
552
- 'data-ui-container': true,
553
- ...props
554
- });
555
- export const Grid = ({ as = 'div', className, columns = 'auto', gap = 'md', minItemWidth, style, ...props }) => {
556
- const gridStyle = {
557
- ...style,
558
- '--ui-grid-min': minItemWidth
559
- };
560
- return createElement(as, {
561
- className: composeClassName('ui-grid', `ui-grid--columns-${columns}`, `ui-grid--gap-${gap}`, className),
562
- 'data-columns': columns,
563
- 'data-ui-grid': true,
564
- style: gridStyle,
565
- ...props
566
- });
567
- };
568
- export const Stack = ({ align = 'stretch', as = 'div', className, direction = 'vertical', gap = 'md', justify = 'start', wrap = false, ...props }) => createElement(as, {
569
- className: composeClassName('ui-stack', `ui-stack--${direction}`, `ui-stack--gap-${gap}`, `ui-stack--align-${align}`, `ui-stack--justify-${justify}`, wrap && 'ui-stack--wrap', className),
570
- 'data-direction': direction,
571
- 'data-ui-stack': true,
572
- ...props
573
- });
574
- export const VisuallyHidden = ({ className, focusable = false, ...props }) => (_jsx("span", { className: composeClassName('ui-visually-hidden', focusable && 'ui-visually-hidden--focusable', className), "data-ui-visually-hidden": true, ...props }));
575
707
  export const Drawer = ({ className, glass = false, ...props }) => (_jsx("dialog", { className: composeClassName('ui-drawer', glassSurfaceClass('ui-drawer', glass), className), "data-surface": resolveSurface(glass), "data-ui-drawer": true, ...props }));
576
708
  export const DropdownMenu = ({ children, className, defaultOpen, glass = false, onOpenChange, open, ...props }) => {
577
709
  const menu = useDropdownMenu({ defaultOpen, onOpenChange, open });
@@ -595,6 +727,59 @@ export const DropdownMenuItem = ({ children, className, disabled = false, onClic
595
727
  };
596
728
  export const DropdownMenuSeparator = ({ className, ...props }) => (_jsx("div", { className: composeClassName('ui-menu__separator', className), role: props.role ?? 'separator', ...props }));
597
729
  export const Empty = ({ className, glass = false, variant = 'default', ...props }) => (_jsx("section", { className: composeClassName('ui-empty', variant === 'compact' && 'ui-empty--compact', glassClass('ui-empty', glass), className), "data-variant": variant, ...props }));
730
+ const renderIllustrationElement = (element, index) => {
731
+ if (element.kind === 'circle') {
732
+ return _jsx("circle", { cx: element.cx, cy: element.cy, r: element.r }, index);
733
+ }
734
+ if (element.kind === 'rounded-rect') {
735
+ return (_jsx("rect", { height: element.height, rx: element.radius, width: element.width, x: element.x, y: element.y }, index));
736
+ }
737
+ const pointPairs = [];
738
+ for (let pointIndex = 0; pointIndex < element.points.length; pointIndex += 2) {
739
+ const x = element.points[pointIndex];
740
+ const y = element.points[pointIndex + 1];
741
+ if (x === undefined || y === undefined)
742
+ break;
743
+ pointPairs.push(`${x},${y}`);
744
+ }
745
+ return createElement(element.kind, { key: index, points: pointPairs.join(' ') });
746
+ };
747
+ export const Illustration = ({ className, label, size = 'md', tone = 'auto', variant = 'empty', ...props }) => (_jsx("span", { "aria-hidden": label ? undefined : true, "aria-label": label, className: composeClassName('ui-illustration', `ui-illustration--${variant}`, `ui-illustration--${tone}`, `ui-illustration--${size}`, className), role: label ? 'img' : undefined, ...props, children: _jsxs("svg", { "aria-hidden": "true", fill: "none", viewBox: "0 0 120 120", children: [_jsx("circle", { className: "ui-illustration__wash", cx: "60", cy: "60", r: "48" }), lumenIllustrations[variant].elements.map(renderIllustrationElement)] }) }));
748
+ const resolveErrorStateAnnouncement = (announce, ariaLive, role) => {
749
+ if (announce === 'assertive') {
750
+ return { 'aria-live': ariaLive ?? 'assertive', role: role ?? 'alert' };
751
+ }
752
+ if (announce === 'polite') {
753
+ return { 'aria-live': ariaLive ?? 'polite', role: role ?? 'status' };
754
+ }
755
+ return {
756
+ ...(ariaLive ? { 'aria-live': ariaLive } : {}),
757
+ ...(role ? { role } : {})
758
+ };
759
+ };
760
+ const ErrorStateGraphic = ({ graphic, kind, layout }) => {
761
+ if (graphic === false)
762
+ return null;
763
+ return (_jsx("div", { "aria-hidden": "true", className: "ui-error-state__graphic", "data-slot": "error-state-graphic", children: graphic ?? (_jsx(Illustration, { size: layout === 'compact' ? 'sm' : 'md', variant: kind })) }));
764
+ };
765
+ const ErrorStateContent = ({ children, description, headingLevel, reference, referenceLabel, title, titleId }) => (_jsxs("div", { className: "ui-error-state__content", "data-slot": "error-state-content", children: [createElement(`h${headingLevel}`, {
766
+ className: 'ui-error-state__title',
767
+ 'data-slot': 'error-state-title',
768
+ id: titleId
769
+ }, title), description && (_jsx("p", { className: "ui-error-state__description", "data-slot": "error-state-description", children: description })), children && (_jsx("div", { className: "ui-error-state__details", "data-slot": "error-state-details", children: children })), reference && (_jsxs("p", { className: "ui-error-state__reference", "data-slot": "error-state-reference", children: [_jsxs("span", { children: [referenceLabel, ":"] }), ' ', _jsx("code", { children: reference })] }))] }));
770
+ const ErrorStateActions = ({ actions }) => actions ?
771
+ (_jsx("div", { className: "ui-error-state__actions", "data-slot": "error-state-actions", children: actions })) :
772
+ null;
773
+ export const ErrorState = ({ 'aria-live': ariaLive, actions, announce, children, className, description, graphic, headingLevel, id, kind, layout, reference, referenceLabel, role, title, ...props }) => {
774
+ const resolvedAnnounce = announce ?? 'off';
775
+ const resolvedHeadingLevel = headingLevel ?? 2;
776
+ const resolvedKind = kind ?? 'error';
777
+ const resolvedLayout = layout ?? 'default';
778
+ const resolvedReferenceLabel = referenceLabel ?? 'Reference';
779
+ const titleId = id ? `${id}-title` : undefined;
780
+ const announcementProps = resolveErrorStateAnnouncement(resolvedAnnounce, ariaLive, role);
781
+ return (_jsxs("section", { "aria-label": titleId ? undefined : title, "aria-labelledby": titleId, className: composeClassName('ui-error-state', `ui-error-state--${resolvedKind}`, `ui-error-state--${resolvedLayout}`, className), "data-kind": resolvedKind, "data-layout": resolvedLayout, "data-ui-error-state": true, id: id, ...announcementProps, ...props, children: [_jsx(ErrorStateGraphic, { graphic: graphic, kind: resolvedKind, layout: resolvedLayout }), _jsx(ErrorStateContent, { description: description, headingLevel: resolvedHeadingLevel, reference: reference, referenceLabel: resolvedReferenceLabel, title: title, titleId: titleId, children: children }), _jsx(ErrorStateActions, { actions: actions })] }));
782
+ };
598
783
  export const KanbanBoard = ({ className, tabIndex = 0, ...props }) => (_jsx("section", { "aria-orientation": "horizontal", className: composeClassName('ui-kanban-board ui-kanban', className), "data-ui-kanban": true, tabIndex: tabIndex, ...props }));
599
784
  export const KanbanColumn = ({ className, value, ...props }) => (_jsx("section", { className: composeClassName('ui-kanban__column', className), "data-ui-kanban-column": value, ...props }));
600
785
  export const ErrorSummary = ({ className, errors, heading = 'There is a problem', id = 'ui-error-summary', ...props }) => {
@@ -629,8 +814,6 @@ export const Icon = ({ 'aria-hidden': ariaHidden, 'aria-label': ariaLabel, child
629
814
  });
630
815
  return (_jsx("span", { "aria-hidden": accessibility.ariaHidden, "aria-label": accessibility.ariaLabel, className: composeClassName('ui-icon', iconSizeClass(size), className), role: accessibility.role, ...props, children: icon ? renderNamedIcon(icon) : children }));
631
816
  };
632
- export const Input = ({ className, ref, type = 'text', visualSize = 'default', ...props }) => (_jsx("input", { className: composeClassName('ui-input', visualSize === 'sm' && 'ui-input--sm', visualSize === 'lg' && 'ui-input--lg', className), ref: ref, type: type, ...props }));
633
- export const Label = ({ className, ...props }) => (_jsx("label", { className: composeClassName('ui-label', className), ...props }));
634
817
  const getPasswordFieldDescribedBy = (ariaDescribedBy, hintId, errorId) => [ariaDescribedBy, hintId, errorId].filter(Boolean).join(' ') || undefined;
635
818
  const usePasswordVisibility = (inputRef) => {
636
819
  const [visible, setVisible] = useState(false);
@@ -725,10 +908,56 @@ const phoneInputSizeModifiers = (size) => {
725
908
  }
726
909
  return { inputClass: undefined, selectClass: undefined };
727
910
  };
728
- export const PhoneInput = ({ className, countries = emptyOptions, countryLabel = 'Country code', countryName = 'country', defaultCountryValue, defaultValue, name = 'phone', placeholder = 'Phone number', size = 'default', ...props }) => {
911
+ const getPhoneInputOptions = (locale) => locale === undefined ? {} : { locale };
912
+ const getBooleanAttribute = (value) => value ? true : undefined;
913
+ const getPhoneErrorId = (invalid, name) => (invalid ? `${name}-error` : undefined);
914
+ const PhoneInputError = ({ invalid, message, name }) => invalid ?
915
+ (_jsx("span", { className: "ui-visually-hidden", id: `${name}-error`, role: "alert", children: message })) :
916
+ null;
917
+ const MetadataPhoneInput = ({ className, countryOptions, countryLabel, countryName, defaultCountryValue, defaultValue, invalidNumberMessage, locale, name, onValueChange, placeholder, showValidationError, size, value, ...props }) => {
729
918
  const { selectClass, inputClass } = phoneInputSizeModifiers(size);
919
+ const phoneOptions = useMemo(() => getPhoneInputOptions(locale), [locale]);
920
+ const metadataCountries = useMemo(() => countryOptions ?? getLumenPhoneCountries(phoneOptions), [countryOptions, phoneOptions]);
921
+ const initialCountry = useMemo(() => resolveReactPhoneInputCountry(metadataCountries, defaultCountryValue, locale, value), [defaultCountryValue, locale, metadataCountries, value]);
922
+ const [internalValue, setInternalValue] = useState(() => (defaultValue ?
923
+ resolveReactPhoneInputValue(metadataCountries, initialCountry, defaultValue, phoneOptions) :
924
+ { country: initialCountry, e164: null, isValid: false, nationalNumber: '' }));
925
+ const phoneValue = value === undefined || value.country.regionCode === initialCountry.regionCode ?
926
+ value ?? internalValue :
927
+ resolveReactPhoneInputValue(metadataCountries, initialCountry, value.nationalNumber, phoneOptions);
928
+ const resolvedOptions = metadataCountries.map(country => ({
929
+ disabled: false,
930
+ label: country.pickerLabel,
931
+ value: country.regionCode
932
+ }));
933
+ const commitValue = (nextValue) => {
934
+ if (value === undefined)
935
+ setInternalValue(nextValue);
936
+ onValueChange?.(nextValue);
937
+ };
938
+ const handleCountryChange = (event) => {
939
+ const selectedCountry = metadataCountries.find(country => (country.regionCode === event.currentTarget.value ||
940
+ country.callingCode === event.currentTarget.value));
941
+ if (selectedCountry) {
942
+ commitValue(resolveLumenPhoneNumber(selectedCountry, phoneValue.nationalNumber, phoneOptions));
943
+ }
944
+ };
945
+ const handleNumberChange = (event) => {
946
+ commitValue(resolveReactPhoneInputValue(metadataCountries, phoneValue.country, event.currentTarget.value, phoneOptions));
947
+ };
948
+ const invalid = showValidationError && phoneValue.nationalNumber.length > 0 && !phoneValue.isValid;
949
+ return (_jsxs("div", { className: composeClassName('ui-phone-input ui-input-group', className), "data-invalid": getBooleanAttribute(invalid), ...props, children: [_jsx("select", { "aria-label": countryLabel, className: composeClassName('ui-select ui-phone-input__country', selectClass), name: countryName, onChange: handleCountryChange, value: phoneValue.country.regionCode, children: resolvedOptions.map(option => (_jsx("option", { disabled: option.disabled, value: option.value, children: option.label }, option.value))) }), _jsx("input", { autoComplete: "tel", "aria-errormessage": getPhoneErrorId(invalid, name), "aria-invalid": getBooleanAttribute(invalid), className: composeClassName('ui-input ui-phone-input__number', inputClass), inputMode: "tel", name: name, onChange: handleNumberChange, placeholder: placeholder, type: "tel", value: phoneValue.nationalNumber }), _jsx(PhoneInputError, { invalid: invalid, message: invalidNumberMessage, name: name })] }));
950
+ };
951
+ const LegacyPhoneInput = ({ className, countries, countryOptions: _countryOptions, countryLabel = 'Country code', countryName = 'country', defaultCountryValue, defaultValue, invalidNumberMessage: _invalidNumberMessage, locale: _locale, name = 'phone', onValueChange: _onValueChange, placeholder = 'Phone number', showValidationError: _showValidationError, size = 'default', value: _value, ...props }) => {
952
+ const { inputClass, selectClass } = phoneInputSizeModifiers(size);
730
953
  return (_jsxs("div", { className: composeClassName('ui-phone-input ui-input-group', className), ...props, children: [_jsx("select", { "aria-label": countryLabel, className: composeClassName('ui-select ui-phone-input__country', selectClass), defaultValue: defaultCountryValue, name: countryName, children: countries.map(normalizeOption).map(option => (_jsx("option", { disabled: option.disabled, value: option.value, children: option.label }, option.value))) }), _jsx("input", { autoComplete: "tel", className: composeClassName('ui-input ui-phone-input__number', inputClass), defaultValue: defaultValue, inputMode: "tel", name: name, placeholder: placeholder, type: "tel" })] }));
731
954
  };
955
+ export const PhoneInput = (props) => {
956
+ if (props.countries) {
957
+ return _jsx(LegacyPhoneInput, { ...props, countries: props.countries });
958
+ }
959
+ return (_jsx(MetadataPhoneInput, { ...props, countryLabel: props.countryLabel ?? 'Country code', countryName: props.countryName ?? 'country', defaultCountryValue: props.defaultCountryValue ?? 'US', invalidNumberMessage: props.invalidNumberMessage ?? 'Enter a complete phone number.', name: props.name ?? 'phone', placeholder: props.placeholder ?? 'Phone number', showValidationError: props.showValidationError ?? true, size: props.size ?? 'default' }));
960
+ };
732
961
  export const NavigationMenu = ({ className, glass = false, variant = 'default', ...props }) => (_jsx("nav", { className: composeClassName('ui-navigation-menu', variant === 'unstyled' && 'ui-navigation-menu--unstyled', glassSurfaceClass('ui-navigation-menu', glass), className), "data-slot": "navigation-menu", "data-surface": resolveSurface(glass), "data-ui-navigation-menu": true, "data-variant": variant, ...props }));
733
962
  export const ContextNavigation = ({ children, className, context, variant = 'default', ...props }) => (_jsxs("div", { className: composeClassName('ui-context-navigation', variant === 'unstyled' && 'ui-context-navigation--unstyled', className), "data-slot": "context-navigation", "data-variant": variant, ...props, children: [context && (_jsx("div", { className: "ui-context-navigation__context", "data-slot": "context-navigation-context", children: context })), children] }));
734
963
  export const Pagination = ({ 'aria-label': ariaLabel = 'Pagination', className, ...props }) => (_jsx("nav", { "aria-label": ariaLabel, className: composeClassName('ui-pagination', className), ...props }));
@@ -744,12 +973,6 @@ export const PopoverPanel = ({ onKeyDown, ...props }) => {
744
973
  const popover = requireContext(useContext(PopoverContext), 'PopoverPanel');
745
974
  return (_jsx("div", { ...popover.panelProps, ...props, onKeyDown: composeHandlers(onKeyDown, popover.panelProps.onKeyDown) }));
746
975
  };
747
- export const Progress = ({ className, max = 100, value = 0, ...props }) => {
748
- const safeMax = max > 0 ? max : 100;
749
- const safeValue = Math.min(safeMax, Math.max(0, value));
750
- const percentage = (safeValue / safeMax) * 100;
751
- return (_jsx("div", { "aria-valuemax": safeMax, "aria-valuemin": 0, "aria-valuenow": safeValue, className: composeClassName('ui-progress', className), role: "progressbar", ...props, children: _jsx("span", { className: "ui-progress__bar", "data-slot": "progress-indicator", style: { width: `${percentage}%` } }) }));
752
- };
753
976
  export const RadioGroup = ({ className, ...props }) => (_jsx("fieldset", { className: composeClassName('ui-radio-group', className), "data-ui-radio-group": true, ...props }));
754
977
  const renderResizablePane = (child, index, panelProps) => {
755
978
  if (isValidElement(child)) {
@@ -1055,16 +1278,12 @@ export const ListBox = (inputProps) => {
1055
1278
  (_jsx("p", { className: "ui-list-box__status", role: "status", children: loadingMessage })) :
1056
1279
  null] }));
1057
1280
  };
1058
- export const Separator = ({ className, orientation = 'horizontal', ...props }) => (_jsx("hr", { "aria-orientation": orientation, className: composeClassName('ui-separator', `ui-separator--${orientation}`, className), "data-orientation": orientation, ...props }));
1059
1281
  export const Sheet = ({ className, glass = false, ...props }) => (_jsx("dialog", { className: composeClassName('ui-sheet', glassSurfaceClass('ui-sheet', glass), className), "data-surface": resolveSurface(glass), "data-ui-sheet": true, ...props }));
1060
1282
  export const Sidebar = ({ className, glass = false, variant = 'default', ...props }) => (_jsx("aside", { className: composeClassName('ui-sidebar', variant === 'unstyled' && 'ui-sidebar--unstyled', glassSurfaceClass('ui-sidebar', glass), className), "data-slot": "sidebar", "data-surface": resolveSurface(glass), "data-variant": variant, ...props }));
1061
- export const Skeleton = ({ className, ...props }) => (_jsx("div", { className: composeClassName('ui-skeleton', className), ...props }));
1062
1283
  export const Slider = ({ className, ref, type = 'range', ...props }) => (_jsx("input", { className: composeClassName('ui-slider', className), ref: ref, type: type, ...props }));
1063
- export const Sonner = ({ className, ...props }) => (_jsx("div", { className: composeClassName('ui-sonner', className), "data-ui-sonner": true, ...props }));
1064
- export const Spinner = ({ 'aria-hidden': ariaHidden, 'aria-label': ariaLabel, 'aria-labelledby': ariaLabelledby, className, role, ...props }) => {
1065
- const isAccessible = Boolean(ariaLabel || ariaLabelledby);
1066
- return (_jsx("span", { "aria-hidden": ariaHidden ?? (isAccessible ? undefined : true), "aria-label": ariaLabel, "aria-labelledby": ariaLabelledby, className: composeClassName('ui-spinner', className), role: role ?? (isAccessible ? 'status' : undefined), ...props }));
1067
- };
1284
+ export const ToastViewport = ({ 'aria-atomic': ariaAtomic = 'false', 'aria-label': ariaLabel = 'Notifications', 'aria-live': ariaLive = 'polite', className, maxCount = 3, placement = 'bottom-right', ...props }) => (_jsx("div", { "aria-atomic": ariaAtomic, "aria-label": ariaLabel, "aria-live": ariaLive, className: composeClassName('ui-tvp', className), "data-placement": placement, "data-ui-toast-viewport": true, "data-ui-toast-max": maxCount, ...props }));
1285
+ /** @deprecated Use ToastViewport instead. */
1286
+ export const Sonner = ({ className, ...props }) => (_jsx(ToastViewport, { className: composeClassName('ui-sonner', className), "data-ui-sonner": true, ...props }));
1068
1287
  export const Switch = ({ className, ref, role = 'switch', ...props }) => (_jsx("input", { className: composeClassName('ui-switch', className), ref: ref, role: role, type: "checkbox", ...props }));
1069
1288
  export const Table = ({ className, glass = false, ...props }) => (_jsx("div", { className: composeClassName('ui-table-wrap', glassClass('ui-table-wrap', glass), className), "data-slot": "table", ...props }));
1070
1289
  export const Tabs = ({ children, className, defaultValue, glass = false, onValueChange, orientation, value, ...props }) => {
@@ -1134,7 +1353,6 @@ export const CodeTabs = ({ ariaLabel = 'Code examples', className, copy = true,
1134
1353
  { language: item.language }), theme: theme, variant: "block", wrap: wrap }) }, item.value)))] }));
1135
1354
  };
1136
1355
  export const TagGroup = ({ className, role = 'list', ...props }) => (_jsx("div", { className: composeClassName('ui-tag-group', className), role: role, ...props }));
1137
- export const Textarea = ({ className, ref, rows = 4, ...props }) => (_jsx("textarea", { className: composeClassName('ui-textarea', className), ref: ref, rows: rows, ...props }));
1138
1356
  export const ThemeBuilder = ({ className, glass = false, ...props }) => (_jsx("section", { className: composeClassName('ui-theme-builder', glassClass('ui-theme-builder', glass), className), "data-ui-theme-builder": true, ...props }));
1139
1357
  export const TimeField = ({ className, ref, type = 'time', ...props }) => (_jsx("input", { className: composeClassName('ui-input ui-time-field', className), ref: ref, type: type, ...props }));
1140
1358
  export const Toast = ({ 'aria-live': ariaLive, className, glass = false, role, variant = 'default', ...props }) => (_jsx("aside", { "aria-live": ariaLive ?? (variant === 'destructive' ? 'assertive' : 'polite'), className: composeClassName('ui-toast', variantClass('ui-toast', variant), glassSurfaceClass('ui-toast', glass), className), "data-surface": resolveSurface(glass), "data-ui-toast": true, "data-variant": variant, role: role ?? (variant === 'destructive' ? 'alert' : 'status'), ...props }));
@@ -1150,19 +1368,18 @@ export const TooltipContent = ({ ...props }) => {
1150
1368
  };
1151
1369
  export const Tree = ({ className, glass = false, role = 'tree', ...props }) => (_jsx("div", { className: composeClassName('ui-tree', glassClass('ui-tree', glass), className), role: role, ...props }));
1152
1370
  export const TreeGrid = ({ className, glass = false, role = 'treegrid', ...props }) => (_jsx("div", { className: composeClassName('ui-tree-grid', glassClass('ui-tree-grid', glass), className), role: role, ...props }));
1153
- export const Typography = ({ className, ...props }) => (_jsx("div", { className: composeClassName('ui-typography', className), ...props }));
1154
1371
  export const VirtualList = ({ className, glass = false, itemSize, overscan, ...props }) => (_jsx("div", { className: composeClassName('ui-virtual-list', glassClass('ui-virtual-list', glass), className), "data-ui-item-size": itemSize, "data-ui-overscan": overscan, "data-ui-virtual-list": true, ...props }));
1155
1372
  export const BackToTop = ({ className, type = 'button', ...props }) => (_jsx("button", { className: composeClassName('ui-back-to-top', className), type: type, ...props }));
1156
1373
  export const Callout = ({ className, ...props }) => (_jsx("aside", { className: composeClassName('ui-callout', className), ...props }));
1157
1374
  export const Eyebrow = ({ className, ...props }) => (_jsx("p", { className: composeClassName('ui-eyebrow', className), ...props }));
1158
1375
  export const FloatingBadge = ({ className, ...props }) => (_jsx("span", { className: composeClassName('ui-floating-badge', className), ...props }));
1159
1376
  export const FormattedDate = ({ className, ...props }) => (_jsx("time", { className: composeClassName('ui-formatted-date', className), ...props }));
1160
- export const Image = ({ as, className, invertOnDark = false, loading, ...props }) => {
1377
+ export const Image = ({ as, className, fit = 'cover', invertOnDark = false, loading, radius = 'lg', ...props }) => {
1161
1378
  const Component = as ?? 'img';
1162
1379
  const resolvedLoading = loading ?? (as ? undefined : 'lazy');
1163
1380
  return createElement(Component, {
1164
1381
  ...props,
1165
- className: composeClassName('ui-image', invertOnDark && 'ui-image--invert-dark', className),
1382
+ className: composeClassName('ui-image', invertOnDark && 'ui-image--invert-dark', `ui-image--fit-${fit}`, `ui-image--radius-${radius}`, className),
1166
1383
  ...(resolvedLoading ? { loading: resolvedLoading } : {})
1167
1384
  });
1168
1385
  };
@@ -1374,7 +1591,6 @@ export const TimelineItem = ({ className, children, dot, ...props }) => (_jsxs("
1374
1591
  export const AnimatedLogo = ({ animation = 'reveal', className, ...props }) => (_jsx("span", { className: composeClassName('ui-animated-logo', className), "data-animation": animation, "data-ui-animated-logo": true, ...props }));
1375
1592
  export const Graphic = ({ children, className, label, size = 'md', tone = 'brand', variant = 'orbit', ...props }) => (_jsx("div", { "aria-hidden": label ? undefined : true, "aria-label": label, className: composeClassName('ui-graphic', `ui-graphic--${variant}`, `ui-graphic--${tone}`, `ui-graphic--${size}`, className), role: label ? 'img' : undefined, ...props, children: _jsx("div", { className: "ui-graphic__content", children: children }) }));
1376
1593
  export const Backdrop = ({ children, className, intensity = 'medium', tone = 'brand', variant = 'aurora', ...props }) => (_jsx("div", { className: composeClassName('ui-backdrop', `ui-backdrop--${variant}`, `ui-backdrop--${tone}`, `ui-backdrop--${intensity}`, className), ...props, children: _jsx("div", { className: "ui-backdrop__content", children: children }) }));
1377
- export const Illustration = ({ className, label, size = 'md', tone = 'auto', variant = 'empty', ...props }) => (_jsx("span", { "aria-hidden": label ? undefined : true, "aria-label": label, className: composeClassName('ui-illustration', `ui-illustration--${variant}`, `ui-illustration--${tone}`, `ui-illustration--${size}`, className), role: label ? 'img' : undefined, ...props, children: _jsxs("svg", { "aria-hidden": "true", fill: "none", viewBox: "0 0 120 120", children: [_jsx("circle", { className: "ui-illustration__wash", cx: "60", cy: "60", r: "48" }), variant === 'empty' && (_jsxs("g", { children: [_jsx("path", { d: "M32 48h56l-7 35H39l-7-35Z" }), _jsx("path", { d: "M43 48 49 36h22l6 12M47 66h26" })] })), variant === 'success' && (_jsxs("g", { children: [_jsx("circle", { cx: "60", cy: "60", r: "28" }), _jsx("path", { d: "m45 60 10 10 21-23" })] })), variant === 'error' && (_jsxs("g", { children: [_jsx("circle", { cx: "60", cy: "60", r: "28" }), _jsx("path", { d: "m49 49 22 22m0-22L49 71" })] })), variant === 'offline' && (_jsxs("g", { children: [_jsx("path", { d: "M37 74h44a14 14 0 0 0 1-28 23 23 0 0 0-42-4 16 16 0 0 0-3 32Z" }), _jsx("path", { d: "m38 34 44 52" })] }))] }) }));
1378
1594
  export const AnimatedPortrait = ({ className, ...props }) => (_jsx("div", { className: composeClassName('ui-animated-portrait relative isolate w-full animate-reveal-lcp motion-reduce:animate-none', className), ...props }));
1379
1595
  const getButtonLinkVariantClass = (variant) => {
1380
1596
  if (variant === 'unstyled')