@kennycorrea/ui 0.11.4 → 1.6.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.
Files changed (242) hide show
  1. package/CHANGELOG.md +182 -0
  2. package/README.md +251 -1
  3. package/dist/atoms/ActionMenu/ActionMenu.d.ts +37 -0
  4. package/dist/atoms/ActionMenu/ActionMenu.js +28 -0
  5. package/dist/atoms/ActionMenu/index.d.ts +1 -0
  6. package/dist/atoms/ActionMenu/index.js +1 -0
  7. package/dist/atoms/ActionSheet/ActionSheet.d.ts +10 -4
  8. package/dist/atoms/ActionSheet/ActionSheet.js +6 -15
  9. package/dist/atoms/ActionSheet/confirm.d.ts +1 -1
  10. package/dist/atoms/ActionSheet/confirm.js +1 -1
  11. package/dist/atoms/Avatar/Avatar.d.ts +1 -1
  12. package/dist/atoms/Badge/Badge.js +4 -4
  13. package/dist/atoms/Button/Button.js +2 -3
  14. package/dist/atoms/Button/Button.types.d.ts +5 -8
  15. package/dist/atoms/ContextMenu/ContextMenu.d.ts +1 -1
  16. package/dist/atoms/ContextMenu/ContextMenu.js +1 -1
  17. package/dist/atoms/Icon/Icon.d.ts +7 -9
  18. package/dist/atoms/Icon/Icon.js +23 -8
  19. package/dist/atoms/Pill/Pill.d.ts +17 -0
  20. package/dist/atoms/Pill/Pill.js +12 -0
  21. package/dist/atoms/Pill/index.d.ts +1 -0
  22. package/dist/atoms/Pill/index.js +1 -0
  23. package/dist/atoms/ResponsiveDialog/ResponsiveDialog.d.ts +9 -2
  24. package/dist/atoms/ResponsiveDialog/ResponsiveDialog.js +6 -15
  25. package/dist/atoms/StatusIndicator/StatusIndicator.d.ts +14 -0
  26. package/dist/atoms/StatusIndicator/StatusIndicator.js +8 -0
  27. package/dist/atoms/StatusIndicator/index.d.ts +1 -0
  28. package/dist/atoms/StatusIndicator/index.js +1 -0
  29. package/dist/atoms/TextLink/TextLink.d.ts +36 -0
  30. package/dist/atoms/TextLink/TextLink.js +17 -0
  31. package/dist/atoms/TextLink/index.d.ts +1 -0
  32. package/dist/atoms/TextLink/index.js +1 -0
  33. package/dist/atoms/Toast/Toast.d.ts +1 -1
  34. package/dist/atoms/Toast/Toast.js +1 -1
  35. package/dist/atoms/index.d.ts +4 -0
  36. package/dist/atoms/index.js +4 -0
  37. package/dist/hooks/breakpoints.d.ts +20 -0
  38. package/dist/hooks/breakpoints.js +13 -0
  39. package/dist/hooks/index.d.ts +4 -0
  40. package/dist/hooks/index.js +4 -0
  41. package/dist/hooks/useControllableState.d.ts +17 -0
  42. package/dist/hooks/useControllableState.js +22 -0
  43. package/dist/hooks/useElementWidth.d.ts +7 -0
  44. package/dist/hooks/useElementWidth.js +22 -0
  45. package/dist/hooks/useMediaQuery.d.ts +18 -0
  46. package/dist/hooks/useMediaQuery.js +51 -0
  47. package/dist/index.d.ts +4 -0
  48. package/dist/index.js +4 -0
  49. package/dist/molecules/AccentPicker/AccentPicker.d.ts +30 -0
  50. package/dist/molecules/AccentPicker/AccentPicker.js +27 -0
  51. package/dist/molecules/AccentPicker/index.d.ts +1 -0
  52. package/dist/molecules/AccentPicker/index.js +1 -0
  53. package/dist/molecules/Breadcrumbs/Breadcrumbs.d.ts +27 -0
  54. package/dist/molecules/Breadcrumbs/Breadcrumbs.js +40 -0
  55. package/dist/molecules/Breadcrumbs/index.d.ts +1 -0
  56. package/dist/molecules/Breadcrumbs/index.js +1 -0
  57. package/dist/molecules/Calendar/Calendar.d.ts +42 -0
  58. package/dist/molecules/Calendar/Calendar.js +111 -0
  59. package/dist/molecules/Calendar/index.d.ts +1 -0
  60. package/dist/molecules/Calendar/index.js +1 -0
  61. package/dist/molecules/DateInput/DateInput.d.ts +35 -0
  62. package/dist/molecules/DateInput/DateInput.js +41 -0
  63. package/dist/molecules/DateInput/index.d.ts +1 -0
  64. package/dist/molecules/DateInput/index.js +1 -0
  65. package/dist/molecules/EmptyState/EmptyState.d.ts +19 -0
  66. package/dist/molecules/EmptyState/EmptyState.js +7 -0
  67. package/dist/molecules/EmptyState/index.d.ts +1 -0
  68. package/dist/molecules/EmptyState/index.js +1 -0
  69. package/dist/molecules/ErrorState/ErrorState.d.ts +18 -0
  70. package/dist/molecules/ErrorState/ErrorState.js +12 -0
  71. package/dist/molecules/ErrorState/index.d.ts +1 -0
  72. package/dist/molecules/ErrorState/index.js +1 -0
  73. package/dist/molecules/ErrorSummary/ErrorSummary.d.ts +25 -0
  74. package/dist/molecules/ErrorSummary/ErrorSummary.js +32 -0
  75. package/dist/molecules/ErrorSummary/index.d.ts +1 -0
  76. package/dist/molecules/ErrorSummary/index.js +1 -0
  77. package/dist/molecules/FilterChips/FilterChips.d.ts +10 -0
  78. package/dist/molecules/FilterChips/FilterChips.js +13 -0
  79. package/dist/molecules/FilterChips/index.d.ts +1 -0
  80. package/dist/molecules/FilterChips/index.js +1 -0
  81. package/dist/molecules/FilterPopover/FilterPopover.d.ts +27 -0
  82. package/dist/molecules/FilterPopover/FilterPopover.js +32 -0
  83. package/dist/molecules/FilterPopover/index.d.ts +1 -0
  84. package/dist/molecules/FilterPopover/index.js +1 -0
  85. package/dist/molecules/FormActions/FormActions.d.ts +16 -0
  86. package/dist/molecules/FormActions/FormActions.js +5 -0
  87. package/dist/molecules/FormActions/index.d.ts +1 -0
  88. package/dist/molecules/FormActions/index.js +1 -0
  89. package/dist/molecules/FormField/FormField.d.ts +52 -0
  90. package/dist/molecules/FormField/FormField.js +63 -0
  91. package/dist/molecules/FormField/index.d.ts +1 -0
  92. package/dist/molecules/FormField/index.js +1 -0
  93. package/dist/molecules/FormSection/FormSection.d.ts +14 -0
  94. package/dist/molecules/FormSection/FormSection.js +10 -0
  95. package/dist/molecules/FormSection/index.d.ts +1 -0
  96. package/dist/molecules/FormSection/index.js +1 -0
  97. package/dist/molecules/KeyValueList/KeyValueList.d.ts +22 -0
  98. package/dist/molecules/KeyValueList/KeyValueList.js +9 -0
  99. package/dist/molecules/KeyValueList/index.d.ts +1 -0
  100. package/dist/molecules/KeyValueList/index.js +1 -0
  101. package/dist/molecules/LoadingState/LoadingState.d.ts +20 -0
  102. package/dist/molecules/LoadingState/LoadingState.js +22 -0
  103. package/dist/molecules/LoadingState/index.d.ts +1 -0
  104. package/dist/molecules/LoadingState/index.js +1 -0
  105. package/dist/molecules/NavItem/NavItem.d.ts +31 -0
  106. package/dist/molecules/NavItem/NavItem.js +72 -0
  107. package/dist/molecules/NavItem/context.d.ts +14 -0
  108. package/dist/molecules/NavItem/context.js +5 -0
  109. package/dist/molecules/NavItem/index.d.ts +1 -0
  110. package/dist/molecules/NavItem/index.js +1 -0
  111. package/dist/molecules/Pagination/Pagination.d.ts +32 -0
  112. package/dist/molecules/Pagination/Pagination.js +57 -0
  113. package/dist/molecules/Pagination/index.d.ts +1 -0
  114. package/dist/molecules/Pagination/index.js +1 -0
  115. package/dist/molecules/PasswordInput/PasswordInput.d.ts +14 -0
  116. package/dist/molecules/PasswordInput/PasswordInput.js +16 -0
  117. package/dist/molecules/PasswordInput/index.d.ts +1 -0
  118. package/dist/molecules/PasswordInput/index.js +1 -0
  119. package/dist/molecules/SearchField/SearchField.d.ts +24 -0
  120. package/dist/molecules/SearchField/SearchField.js +58 -0
  121. package/dist/molecules/SearchField/index.d.ts +1 -0
  122. package/dist/molecules/SearchField/index.js +1 -0
  123. package/dist/molecules/StatGroup/StatGroup.d.ts +21 -0
  124. package/dist/molecules/StatGroup/StatGroup.js +10 -0
  125. package/dist/molecules/StatGroup/index.d.ts +1 -0
  126. package/dist/molecules/StatGroup/index.js +1 -0
  127. package/dist/molecules/ThemeToggle/ThemeToggle.d.ts +23 -0
  128. package/dist/molecules/ThemeToggle/ThemeToggle.js +26 -0
  129. package/dist/molecules/ThemeToggle/index.d.ts +1 -0
  130. package/dist/molecules/ThemeToggle/index.js +1 -0
  131. package/dist/molecules/UserMenu/UserMenu.d.ts +20 -0
  132. package/dist/molecules/UserMenu/UserMenu.js +14 -0
  133. package/dist/molecules/UserMenu/index.d.ts +1 -0
  134. package/dist/molecules/UserMenu/index.js +1 -0
  135. package/dist/molecules/index.d.ts +21 -0
  136. package/dist/molecules/index.js +21 -0
  137. package/dist/organisms/AppShell/AppShell.d.ts +29 -0
  138. package/dist/organisms/AppShell/AppShell.js +34 -0
  139. package/dist/organisms/AppShell/context.d.ts +14 -0
  140. package/dist/organisms/AppShell/context.js +6 -0
  141. package/dist/organisms/AppShell/index.d.ts +1 -0
  142. package/dist/organisms/AppShell/index.js +1 -0
  143. package/dist/organisms/AuthForms/AuthForms.d.ts +83 -0
  144. package/dist/organisms/AuthForms/AuthForms.js +57 -0
  145. package/dist/organisms/AuthForms/index.d.ts +2 -0
  146. package/dist/organisms/AuthForms/index.js +1 -0
  147. package/dist/organisms/AuthForms/shared.d.ts +36 -0
  148. package/dist/organisms/AuthForms/shared.js +36 -0
  149. package/dist/organisms/BulkActionBar/BulkActionBar.d.ts +25 -0
  150. package/dist/organisms/BulkActionBar/BulkActionBar.js +18 -0
  151. package/dist/organisms/BulkActionBar/index.d.ts +1 -0
  152. package/dist/organisms/BulkActionBar/index.js +1 -0
  153. package/dist/organisms/Chart/CategoryCharts.d.ts +34 -0
  154. package/dist/organisms/Chart/CategoryCharts.js +93 -0
  155. package/dist/organisms/Chart/ChartFrame.d.ts +26 -0
  156. package/dist/organisms/Chart/ChartFrame.js +18 -0
  157. package/dist/organisms/Chart/DonutChart.d.ts +25 -0
  158. package/dist/organisms/Chart/DonutChart.js +53 -0
  159. package/dist/organisms/Chart/Sparkline.d.ts +15 -0
  160. package/dist/organisms/Chart/Sparkline.js +18 -0
  161. package/dist/organisms/Chart/index.d.ts +5 -0
  162. package/dist/organisms/Chart/index.js +5 -0
  163. package/dist/organisms/Chart/shared.d.ts +51 -0
  164. package/dist/organisms/Chart/shared.js +113 -0
  165. package/dist/organisms/DataTable/DataTable.d.ts +63 -0
  166. package/dist/organisms/DataTable/DataTable.js +119 -0
  167. package/dist/organisms/DataTable/index.d.ts +3 -0
  168. package/dist/organisms/DataTable/index.js +3 -0
  169. package/dist/organisms/DataTable/types.d.ts +39 -0
  170. package/dist/organisms/DataTable/types.js +1 -0
  171. package/dist/organisms/DataTable/useClientTable.d.ts +27 -0
  172. package/dist/organisms/DataTable/useClientTable.js +51 -0
  173. package/dist/organisms/DateRangePicker/DateRangePicker.d.ts +43 -0
  174. package/dist/organisms/DateRangePicker/DateRangePicker.js +54 -0
  175. package/dist/organisms/DateRangePicker/index.d.ts +1 -0
  176. package/dist/organisms/DateRangePicker/index.js +1 -0
  177. package/dist/organisms/FilterBar/FilterBar.d.ts +28 -0
  178. package/dist/organisms/FilterBar/FilterBar.js +16 -0
  179. package/dist/organisms/FilterBar/index.d.ts +1 -0
  180. package/dist/organisms/FilterBar/index.js +1 -0
  181. package/dist/organisms/PageHeader/PageHeader.d.ts +31 -0
  182. package/dist/organisms/PageHeader/PageHeader.js +19 -0
  183. package/dist/organisms/PageHeader/index.d.ts +1 -0
  184. package/dist/organisms/PageHeader/index.js +1 -0
  185. package/dist/organisms/PageTabs/PageTabs.d.ts +27 -0
  186. package/dist/organisms/PageTabs/PageTabs.js +31 -0
  187. package/dist/organisms/PageTabs/index.d.ts +1 -0
  188. package/dist/organisms/PageTabs/index.js +1 -0
  189. package/dist/organisms/Sidebar/Sidebar.d.ts +48 -0
  190. package/dist/organisms/Sidebar/Sidebar.js +61 -0
  191. package/dist/organisms/Sidebar/index.d.ts +1 -0
  192. package/dist/organisms/Sidebar/index.js +1 -0
  193. package/dist/organisms/Topbar/Topbar.d.ts +24 -0
  194. package/dist/organisms/Topbar/Topbar.js +18 -0
  195. package/dist/organisms/Topbar/index.d.ts +1 -0
  196. package/dist/organisms/Topbar/index.js +1 -0
  197. package/dist/organisms/index.d.ts +11 -0
  198. package/dist/organisms/index.js +11 -0
  199. package/dist/strings.d.ts +108 -15
  200. package/dist/strings.js +73 -7
  201. package/dist/templates/AdminLayout/AdminLayout.d.ts +14 -0
  202. package/dist/templates/AdminLayout/AdminLayout.js +9 -0
  203. package/dist/templates/AdminLayout/index.d.ts +1 -0
  204. package/dist/templates/AdminLayout/index.js +1 -0
  205. package/dist/templates/AuthLayout/AuthLayout.d.ts +28 -0
  206. package/dist/templates/AuthLayout/AuthLayout.js +11 -0
  207. package/dist/templates/AuthLayout/index.d.ts +1 -0
  208. package/dist/templates/AuthLayout/index.js +1 -0
  209. package/dist/templates/DashboardPage/DashboardPage.d.ts +25 -0
  210. package/dist/templates/DashboardPage/DashboardPage.js +12 -0
  211. package/dist/templates/DashboardPage/index.d.ts +1 -0
  212. package/dist/templates/DashboardPage/index.js +1 -0
  213. package/dist/templates/DetailPage/DetailPage.d.ts +20 -0
  214. package/dist/templates/DetailPage/DetailPage.js +11 -0
  215. package/dist/templates/DetailPage/index.d.ts +1 -0
  216. package/dist/templates/DetailPage/index.js +1 -0
  217. package/dist/templates/FormPage/FormPage.d.ts +26 -0
  218. package/dist/templates/FormPage/FormPage.js +11 -0
  219. package/dist/templates/FormPage/index.d.ts +1 -0
  220. package/dist/templates/FormPage/index.js +1 -0
  221. package/dist/templates/ListPage/ListPage.d.ts +17 -0
  222. package/dist/templates/ListPage/ListPage.js +8 -0
  223. package/dist/templates/ListPage/index.d.ts +1 -0
  224. package/dist/templates/ListPage/index.js +1 -0
  225. package/dist/templates/SettingsPage/SettingsPage.d.ts +32 -0
  226. package/dist/templates/SettingsPage/SettingsPage.js +13 -0
  227. package/dist/templates/SettingsPage/index.d.ts +1 -0
  228. package/dist/templates/SettingsPage/index.js +1 -0
  229. package/dist/templates/StatusPage/StatusPage.d.ts +18 -0
  230. package/dist/templates/StatusPage/StatusPage.js +8 -0
  231. package/dist/templates/StatusPage/index.d.ts +1 -0
  232. package/dist/templates/StatusPage/index.js +1 -0
  233. package/dist/templates/index.d.ts +8 -0
  234. package/dist/templates/index.js +8 -0
  235. package/dist/utils/dates.d.ts +67 -0
  236. package/dist/utils/dates.js +154 -0
  237. package/dist/utils/index.d.ts +1 -0
  238. package/dist/utils/index.js +1 -0
  239. package/dist/utils/theme.d.ts +4 -0
  240. package/dist/utils/theme.js +7 -3
  241. package/package.json +4 -1
  242. package/tokens.css +494 -21
@@ -0,0 +1,34 @@
1
+ import { type ChartSeries } from './shared.js';
2
+ export interface CategoryChartProps {
3
+ /** The x-axis labels, one per value. */
4
+ categories: string[];
5
+ series: ChartSeries[];
6
+ /**
7
+ * What the chart shows, in a sentence: its accessible name ("Revenue by month, January to
8
+ * June, peaking in April"). The chart itself is one image to a screen reader; ChartFrame's
9
+ * table view carries the numbers.
10
+ */
11
+ label: string;
12
+ /** Plot height in pixels. Default 240. The width follows the container. */
13
+ height?: number;
14
+ /** Formats axis ticks and values. Default: `Intl.NumberFormat` in the provider locale. */
15
+ formatValue?: (value: number) => string;
16
+ /** Shows the legend under the chart. Default: when there is more than one series. */
17
+ legend?: boolean;
18
+ className?: string;
19
+ }
20
+ export interface BarChartProps extends CategoryChartProps {
21
+ /** Series stack on top of each other instead of standing side by side. */
22
+ stacked?: boolean;
23
+ }
24
+ /**
25
+ * Vertical bars per category, grouped or stacked, in plain SVG. Every series after the first
26
+ * also carries a pattern, and the legend repeats it, so series can be told apart without colour.
27
+ */
28
+ export declare function BarChart({ categories, series, label, height, formatValue, legend, stacked, className, }: BarChartProps): import("react").JSX.Element;
29
+ /**
30
+ * Lines per series over the categories, in plain SVG. Each series has its own point marker
31
+ * (circle, square, triangle…), repeated in the legend, so lines can be told apart without
32
+ * colour. `null` values break the line.
33
+ */
34
+ export declare function LineChart({ categories, series, label, height, formatValue, legend, className, }: CategoryChartProps): import("react").JSX.Element;
@@ -0,0 +1,93 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useId, useRef } from 'react';
3
+ import { useElementWidth } from '../../hooks/useElementWidth.js';
4
+ import { useUiLocale } from '../../strings.js';
5
+ import { ChartLegend, chartColor, niceTicks, SeriesMarker, SeriesPattern, } from './shared.js';
6
+ const M = { top: 8, right: 8, bottom: 28, left: 48 };
7
+ const FALLBACK_WIDTH = 600;
8
+ function useFrame(categories, series, height, stackedTotals) {
9
+ const ref = useRef(null);
10
+ const width = useElementWidth(ref) || FALLBACK_WIDTH;
11
+ const values = stackedTotals ?? series.flatMap((s) => s.values.filter((v) => v !== null));
12
+ const ticks = niceTicks(Math.min(0, ...values), Math.max(0, ...values), Math.max(3, Math.round(height / 60)));
13
+ const lo = ticks[0];
14
+ const hi = ticks[ticks.length - 1];
15
+ const plotW = Math.max(1, width - M.left - M.right);
16
+ const plotH = Math.max(1, height - M.top - M.bottom);
17
+ const y = (v) => M.top + plotH - ((v - lo) / (hi - lo || 1)) * plotH;
18
+ const band = plotW / Math.max(1, categories.length);
19
+ // Thin out x labels so they never overlap: roughly 7px per character plus a gap.
20
+ const longest = Math.max(1, ...categories.map((c) => c.length));
21
+ const every = Math.max(1, Math.ceil((longest * 7 + 8) / band));
22
+ return { ref, width, height, ticks, y, band, plotW, plotH, every };
23
+ }
24
+ function Axes({ frame, categories, format, }) {
25
+ return (_jsxs("g", { children: [frame.ticks.map((t) => (_jsxs("g", { children: [_jsx("line", { x1: M.left, x2: M.left + frame.plotW, y1: frame.y(t), y2: frame.y(t), className: t === 0 ? 'ui-chart__axis' : 'ui-chart__grid' }), _jsx("text", { x: M.left - 6, y: frame.y(t), dy: "0.32em", textAnchor: "end", className: "ui-chart__tick", children: format(t) })] }, t))), categories.map((c, i) => i % frame.every === 0 ? (_jsx("text", { x: M.left + frame.band * (i + 0.5), y: frame.height - 8, textAnchor: "middle", className: "ui-chart__tick", children: c }, c)) : null)] }));
26
+ }
27
+ function useFormat(formatValue) {
28
+ const locale = useUiLocale();
29
+ if (formatValue)
30
+ return formatValue;
31
+ const nf = new Intl.NumberFormat(locale, { maximumFractionDigits: 2 });
32
+ return (n) => nf.format(n);
33
+ }
34
+ /**
35
+ * Vertical bars per category, grouped or stacked, in plain SVG. Every series after the first
36
+ * also carries a pattern, and the legend repeats it, so series can be told apart without colour.
37
+ */
38
+ export function BarChart({ categories, series, label, height = 240, formatValue, legend, stacked = false, className, }) {
39
+ const id = useId().replace(/[^a-zA-Z0-9-]/g, '');
40
+ const format = useFormat(formatValue);
41
+ const totals = stacked
42
+ ? categories.map((_, i) => series.reduce((sum, s) => sum + Math.max(0, s.values[i] ?? 0), 0))
43
+ : undefined;
44
+ const frame = useFrame(categories, series, height, totals);
45
+ const groupW = frame.band * 0.72;
46
+ const barW = stacked ? groupW : groupW / Math.max(1, series.length);
47
+ const zero = frame.y(Math.max(0, frame.ticks[0]));
48
+ return (_jsxs("div", { ref: frame.ref, className: ['ui-chart', className].filter(Boolean).join(' '), children: [_jsxs("svg", { role: "img", "aria-label": label, width: "100%", height: height, viewBox: `0 0 ${frame.width} ${height}`, children: [_jsx("defs", { children: series.map((s, si) => (_jsx(SeriesPattern, { id: `${id}-p${si}`, index: si }, s.id))) }), _jsx(Axes, { frame: frame, categories: categories, format: format }), _jsx("g", { children: categories.map((c, ci) => {
49
+ const x0 = M.left + frame.band * ci + (frame.band - groupW) / 2;
50
+ let stackTop = zero;
51
+ return (_jsx("g", { children: series.map((s, si) => {
52
+ const v = s.values[ci];
53
+ if (v === null || v === undefined)
54
+ return null;
55
+ let top;
56
+ let bottom;
57
+ if (stacked) {
58
+ const h = zero - frame.y(Math.max(0, v));
59
+ bottom = stackTop;
60
+ top = stackTop - h;
61
+ stackTop = top;
62
+ }
63
+ else {
64
+ top = Math.min(frame.y(v), zero);
65
+ bottom = Math.max(frame.y(v), zero);
66
+ }
67
+ const x = stacked ? x0 : x0 + barW * si;
68
+ return (_jsx("rect", { x: x, y: top, width: Math.max(1, barW - (stacked ? 0 : 2)), height: Math.max(0, bottom - top), rx: 2, fill: `url(#${id}-p${si})`, children: _jsx("title", { children: `${s.label}, ${c}: ${format(v)}` }) }, s.id));
69
+ }) }, c));
70
+ }) })] }), (legend ?? series.length > 1) ? (_jsx(ChartLegend, { kind: "fill", idPrefix: id, entries: series.map((s) => ({ id: s.id, label: s.label })) })) : null] }));
71
+ }
72
+ /**
73
+ * Lines per series over the categories, in plain SVG. Each series has its own point marker
74
+ * (circle, square, triangle…), repeated in the legend, so lines can be told apart without
75
+ * colour. `null` values break the line.
76
+ */
77
+ export function LineChart({ categories, series, label, height = 240, formatValue, legend, className, }) {
78
+ const format = useFormat(formatValue);
79
+ const frame = useFrame(categories, series, height);
80
+ const x = (i) => M.left + frame.band * (i + 0.5);
81
+ return (_jsxs("div", { ref: frame.ref, className: ['ui-chart', className].filter(Boolean).join(' '), children: [_jsxs("svg", { role: "img", "aria-label": label, width: "100%", height: height, viewBox: `0 0 ${frame.width} ${height}`, children: [_jsx(Axes, { frame: frame, categories: categories, format: format }), _jsx("g", { children: series.map((s, si) => {
82
+ const segments = [[]];
83
+ s.values.forEach((v, i) => {
84
+ if (v === null)
85
+ segments.push([]);
86
+ else
87
+ segments[segments.length - 1].push(`${x(i)},${frame.y(v)}`);
88
+ });
89
+ return (_jsxs("g", { children: [segments
90
+ .filter((pts) => pts.length > 1)
91
+ .map((pts) => (_jsx("polyline", { points: pts.join(' '), fill: "none", stroke: chartColor(si), strokeWidth: 2, strokeLinejoin: "round", strokeLinecap: "round" }, pts[0]))), s.values.map((v, i) => v === null ? null : (_jsxs("g", { children: [_jsx("title", { children: `${s.label}, ${categories[i]}: ${format(v)}` }), _jsx(SeriesMarker, { index: si, x: x(i), y: frame.y(v) })] }, categories[i])))] }, s.id));
92
+ }) })] }), (legend ?? series.length > 1) ? (_jsx(ChartLegend, { kind: "line", idPrefix: "line", entries: series.map((s) => ({ id: s.id, label: s.label })) })) : null] }));
93
+ }
@@ -0,0 +1,26 @@
1
+ import { type ReactNode } from 'react';
2
+ import { type CardVariant, type Responsive } from '../../atoms/Card/Card.js';
3
+ import type { ChartTableData } from './shared.js';
4
+ export interface ChartFrameProps {
5
+ title: ReactNode;
6
+ description?: ReactNode;
7
+ /** The chart: one of this library's, or any other library's SVG or canvas. */
8
+ children: ReactNode;
9
+ /**
10
+ * The same numbers as a table (see `chartTable` and `donutTable`). With it, a "View as table"
11
+ * toggle is shown, so the data is never only in the picture.
12
+ */
13
+ table?: ChartTableData;
14
+ /** Beside the toggle: a period select, an export button. */
15
+ actions?: ReactNode;
16
+ /** The title's level. Default `h2`; `h3` inside a section that already has an `h2`. */
17
+ headingLevel?: 2 | 3;
18
+ variant?: Responsive<CardVariant>;
19
+ className?: string;
20
+ }
21
+ /**
22
+ * The card a chart sits in: a heading, an optional description, the chart, and a switch to
23
+ * the same data as a table. The frame works with any chart, including another library's, so
24
+ * an app can bring its own and keep the heading, the table and the layout.
25
+ */
26
+ export declare function ChartFrame({ title, description, children, table, actions, headingLevel, variant, className, }: ChartFrameProps): import("react").JSX.Element;
@@ -0,0 +1,18 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ // biome-ignore-all lint/suspicious/noArrayIndexKey: table columns are positional and header text can repeat or be empty.
3
+ import { useId, useState } from 'react';
4
+ import { Button } from '../../atoms/Button/Button.js';
5
+ import { Card } from '../../atoms/Card/Card.js';
6
+ import { useUiStrings } from '../../strings.js';
7
+ /**
8
+ * The card a chart sits in: a heading, an optional description, the chart, and a switch to
9
+ * the same data as a table. The frame works with any chart, including another library's, so
10
+ * an app can bring its own and keep the heading, the table and the layout.
11
+ */
12
+ export function ChartFrame({ title, description, children, table, actions, headingLevel = 2, variant, className, }) {
13
+ const strings = useUiStrings();
14
+ const [asTable, setAsTable] = useState(false);
15
+ const titleId = useId();
16
+ const Heading = headingLevel === 3 ? 'h3' : 'h2';
17
+ return (_jsx(Card, { variant: variant, className: ['ui-chart-frame', className].filter(Boolean).join(' '), children: _jsxs("section", { "aria-labelledby": titleId, children: [_jsxs("div", { className: "ui-chart-frame__header", children: [_jsxs("div", { className: "min-w-0", children: [_jsx(Heading, { id: titleId, className: "ui-chart-frame__title", children: title }), description ? _jsx("p", { className: "ui-chart-frame__description", children: description }) : null] }), _jsxs("div", { className: "ui-chart-frame__actions", children: [actions, table ? (_jsx(Button, { variant: "ghost", size: "sm", "aria-pressed": asTable, onClick: () => setAsTable((v) => !v), children: asTable ? strings.viewAsChart : strings.viewAsTable })) : null] })] }), _jsx("div", { className: "ui-chart-frame__body", children: asTable && table ? (_jsx("div", { className: "ui-chart-frame__table", children: _jsxs("table", { className: "ui-table__table", children: [_jsx("caption", { className: "ui-sr-only", children: title }), _jsx("thead", { children: _jsx("tr", { children: table.headers.map((h, i) => (_jsx("th", { scope: "col", "data-align": i > 0 ? 'end' : undefined, children: h }, `${i}-${h}`))) }) }), _jsx("tbody", { children: table.rows.map((row) => (_jsx("tr", { children: row.map((cell, i) => i === 0 ? (_jsx("th", { scope: "row", children: cell }, "h")) : (_jsx("td", { "data-align": "end", children: cell }, `${i}-${table.headers[i]}`))) }, String(row[0])))) })] }) })) : (children) })] }) }));
18
+ }
@@ -0,0 +1,25 @@
1
+ import { type ChartTableData } from './shared.js';
2
+ export interface DonutSlice {
3
+ id: string;
4
+ label: string;
5
+ value: number;
6
+ }
7
+ export interface DonutChartProps {
8
+ data: DonutSlice[];
9
+ /** The accessible name: what the parts are of ("Orders by status, mostly paid"). */
10
+ label: string;
11
+ /** Under the total in the middle, e.g. "orders". */
12
+ centerLabel?: string;
13
+ /** Diameter in pixels. Default 180. */
14
+ size?: number;
15
+ formatValue?: (value: number) => string;
16
+ className?: string;
17
+ }
18
+ /** The slices as a table: label, value and share. */
19
+ export declare function donutTable(data: DonutSlice[], format?: (n: number) => string, headers?: [string, string, string]): ChartTableData;
20
+ /**
21
+ * Parts of a whole, with the total in the middle. The legend lists every slice with its value
22
+ * and share, so the numbers are readable without matching colours to wedges, and each slice
23
+ * carries the same pattern as its legend swatch.
24
+ */
25
+ export declare function DonutChart({ data, label, centerLabel, size, formatValue, className, }: DonutChartProps): import("react").JSX.Element;
@@ -0,0 +1,53 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useId } from 'react';
3
+ import { useUiLocale } from '../../strings.js';
4
+ import { ChartLegend, SeriesPattern } from './shared.js';
5
+ function arc(cx, cy, r, r0, a0, a1) {
6
+ const p = (rad, a) => [cx + rad * Math.sin(a), cy - rad * Math.cos(a)];
7
+ const large = a1 - a0 > Math.PI ? 1 : 0;
8
+ const [x0, y0] = p(r, a0);
9
+ const [x1, y1] = p(r, a1);
10
+ const [x2, y2] = p(r0, a1);
11
+ const [x3, y3] = p(r0, a0);
12
+ return `M${x0},${y0} A${r},${r} 0 ${large} 1 ${x1},${y1} L${x2},${y2} A${r0},${r0} 0 ${large} 0 ${x3},${y3} Z`;
13
+ }
14
+ /** The slices as a table: label, value and share. */
15
+ export function donutTable(data, format = String, headers = ['', '', '%']) {
16
+ const total = data.reduce((s, d) => s + Math.max(0, d.value), 0) || 1;
17
+ return {
18
+ headers,
19
+ rows: data.map((d) => [
20
+ d.label,
21
+ format(d.value),
22
+ `${Math.round((Math.max(0, d.value) / total) * 100)}%`,
23
+ ]),
24
+ };
25
+ }
26
+ /**
27
+ * Parts of a whole, with the total in the middle. The legend lists every slice with its value
28
+ * and share, so the numbers are readable without matching colours to wedges, and each slice
29
+ * carries the same pattern as its legend swatch.
30
+ */
31
+ export function DonutChart({ data, label, centerLabel, size = 180, formatValue, className, }) {
32
+ const id = useId().replace(/[^a-zA-Z0-9-]/g, '');
33
+ const locale = useUiLocale();
34
+ const nf = new Intl.NumberFormat(locale, { maximumFractionDigits: 2 });
35
+ const format = formatValue ?? ((n) => nf.format(n));
36
+ const pct = new Intl.NumberFormat(locale, { style: 'percent', maximumFractionDigits: 0 });
37
+ const total = data.reduce((s, d) => s + Math.max(0, d.value), 0);
38
+ const r = size / 2;
39
+ const r0 = r * 0.62;
40
+ let angle = 0;
41
+ return (_jsxs("div", { className: ['ui-chart ui-chart--donut', className].filter(Boolean).join(' '), children: [_jsxs("svg", { role: "img", "aria-label": label, width: size, height: size, viewBox: `0 0 ${size} ${size}`, children: [_jsx("defs", { children: data.map((d, i) => (_jsx(SeriesPattern, { id: `${id}-p${i}`, index: i }, d.id))) }), _jsxs("g", { children: [total > 0 ? (data.map((d, i) => {
42
+ const share = Math.max(0, d.value) / total;
43
+ const a0 = angle;
44
+ // A full circle cannot be drawn as one arc, so a single slice stops just short.
45
+ const a1 = angle + Math.min(share * Math.PI * 2, Math.PI * 2 - 0.0001);
46
+ angle += share * Math.PI * 2;
47
+ return share > 0 ? (_jsx("path", { d: arc(r, r, r - 1, r0, a0, a1), fill: `url(#${id}-p${i})`, stroke: "var(--color-panel-solid)", strokeWidth: 1.5, children: _jsx("title", { children: `${d.label}: ${format(d.value)} (${pct.format(share)})` }) }, d.id)) : null;
48
+ })) : (_jsx("circle", { cx: r, cy: r, r: (r + r0) / 2, fill: "none", stroke: "var(--gray-a4)", strokeWidth: r - r0 })), _jsx("text", { x: r, y: r, textAnchor: "middle", dy: centerLabel ? '-0.1em' : '0.35em', className: "ui-chart__total", children: format(total) }), centerLabel ? (_jsx("text", { x: r, y: r, textAnchor: "middle", dy: "1.3em", className: "ui-chart__tick", children: centerLabel })) : null] })] }), _jsx(ChartLegend, { kind: "fill", idPrefix: id, entries: data.map((d) => ({
49
+ id: d.id,
50
+ label: d.label,
51
+ detail: `${format(d.value)} · ${pct.format(total ? Math.max(0, d.value) / total : 0)}`,
52
+ })) })] }));
53
+ }
@@ -0,0 +1,15 @@
1
+ export interface SparklineProps {
2
+ values: number[];
3
+ /** The accessible name: the trend in words ("Up 12% over 30 days"). */
4
+ label: string;
5
+ /** Default 32. The width follows the container. */
6
+ height?: number;
7
+ /** Shades the area under the line. */
8
+ area?: boolean;
9
+ className?: string;
10
+ }
11
+ /**
12
+ * A small trend line with no axes, sized for StatTile's `sparkline` slot. It draws in
13
+ * `currentColor`, so a parent's text colour (or the default accent) sets it.
14
+ */
15
+ export declare function Sparkline({ values, label, height, area, className }: SparklineProps): import("react").JSX.Element;
@@ -0,0 +1,18 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useRef } from 'react';
3
+ import { useElementWidth } from '../../hooks/useElementWidth.js';
4
+ /**
5
+ * A small trend line with no axes, sized for StatTile's `sparkline` slot. It draws in
6
+ * `currentColor`, so a parent's text colour (or the default accent) sets it.
7
+ */
8
+ export function Sparkline({ values, label, height = 32, area = false, className }) {
9
+ const ref = useRef(null);
10
+ const width = useElementWidth(ref) || 120;
11
+ const lo = Math.min(...values);
12
+ const hi = Math.max(...values);
13
+ const pad = 2;
14
+ const x = (i) => pad + (i / Math.max(1, values.length - 1)) * (width - pad * 2);
15
+ const y = (v) => pad + (1 - (v - lo) / (hi - lo || 1)) * (height - pad * 2);
16
+ const points = values.map((v, i) => `${x(i)},${y(v)}`).join(' ');
17
+ return (_jsx("div", { ref: ref, className: ['ui-sparkline', className].filter(Boolean).join(' '), children: _jsx("svg", { role: "img", "aria-label": label, width: "100%", height: height, viewBox: `0 0 ${width} ${height}`, preserveAspectRatio: "none", children: values.length > 1 ? (_jsxs("g", { children: [area ? (_jsx("polygon", { points: `${x(0)},${height} ${points} ${x(values.length - 1)},${height}`, fill: "currentColor", opacity: 0.12 })) : null, _jsx("polyline", { points: points, fill: "none", stroke: "currentColor", strokeWidth: 1.75, strokeLinejoin: "round", strokeLinecap: "round", vectorEffect: "non-scaling-stroke" })] })) : null }) }));
18
+ }
@@ -0,0 +1,5 @@
1
+ export * from './CategoryCharts.js';
2
+ export * from './ChartFrame.js';
3
+ export * from './DonutChart.js';
4
+ export * from './Sparkline.js';
5
+ export { CHART_COLORS, ChartLegend, type ChartSeries, type ChartTableData, chartColor, chartTable, type LegendEntry, niceTicks, } from './shared.js';
@@ -0,0 +1,5 @@
1
+ export * from './CategoryCharts.js';
2
+ export * from './ChartFrame.js';
3
+ export * from './DonutChart.js';
4
+ export * from './Sparkline.js';
5
+ export { CHART_COLORS, ChartLegend, chartColor, chartTable, niceTicks, } from './shared.js';
@@ -0,0 +1,51 @@
1
+ export interface ChartSeries {
2
+ id: string;
3
+ label: string;
4
+ /** One value per category, in category order. `null` is a gap. */
5
+ values: (number | null)[];
6
+ }
7
+ export interface ChartTableData {
8
+ headers: string[];
9
+ rows: (string | number)[][];
10
+ }
11
+ /** The palette has eight entries; series past the eighth repeat with a different pattern. */
12
+ export declare const CHART_COLORS = 8;
13
+ export declare function chartColor(index: number): string;
14
+ /**
15
+ * Round tick values covering `[min, max]`: steps of 1, 2, 2.5 or 5 times a power of ten, about
16
+ * `count` of them, always including zero when the range crosses it.
17
+ */
18
+ export declare function niceTicks(min: number, max: number, count?: number): number[];
19
+ /** A table of the same numbers a category chart draws, for ChartFrame's table view. */
20
+ export declare function chartTable(categories: string[], series: ChartSeries[], format?: (n: number) => string, categoryHeader?: string): ChartTableData;
21
+ type PatternKind = 'solid' | 'diagonal' | 'dots' | 'cross' | 'horizontal' | 'vertical' | 'reverse' | 'grid';
22
+ /** The fill for series `index`: a colour, and for every series after the first a pattern too. */
23
+ export declare function patternFor(index: number): PatternKind;
24
+ /** The `<pattern>` for series `index`, to place in an SVG's `<defs>`. */
25
+ export declare function SeriesPattern({ id, index }: {
26
+ id: string;
27
+ index: number;
28
+ }): import("react").JSX.Element;
29
+ /** A point marker for line series `index`, centred on (x, y). Each series has its own shape. */
30
+ export declare function SeriesMarker({ index, x, y, r, }: {
31
+ index: number;
32
+ x: number;
33
+ y: number;
34
+ r?: number;
35
+ }): import("react").JSX.Element;
36
+ export interface LegendEntry {
37
+ id: string;
38
+ label: string;
39
+ /** Extra text after the label, e.g. a value and share for a donut. */
40
+ detail?: string;
41
+ }
42
+ /**
43
+ * The key to a chart's series. Each swatch repeats the pattern (bars, donut) or the marker
44
+ * (lines) the chart uses, so the legend works without telling colours apart.
45
+ */
46
+ export declare function ChartLegend({ entries, kind, idPrefix, }: {
47
+ entries: LegendEntry[];
48
+ kind: 'fill' | 'line';
49
+ idPrefix: string;
50
+ }): import("react").JSX.Element;
51
+ export {};
@@ -0,0 +1,113 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ /** The palette has eight entries; series past the eighth repeat with a different pattern. */
3
+ export const CHART_COLORS = 8;
4
+ export function chartColor(index) {
5
+ return `var(--chart-${(index % CHART_COLORS) + 1})`;
6
+ }
7
+ /**
8
+ * Round tick values covering `[min, max]`: steps of 1, 2, 2.5 or 5 times a power of ten, about
9
+ * `count` of them, always including zero when the range crosses it.
10
+ */
11
+ export function niceTicks(min, max, count = 5) {
12
+ if (!Number.isFinite(min) || !Number.isFinite(max))
13
+ return [0];
14
+ if (min === max) {
15
+ if (min === 0)
16
+ return [0, 1];
17
+ const pad = Math.abs(min) * 0.5;
18
+ return niceTicks(Math.min(0, min - pad), Math.max(0, max + pad), count);
19
+ }
20
+ const lo = Math.min(min, max);
21
+ const hi = Math.max(min, max);
22
+ const raw = (hi - lo) / Math.max(1, count - 1);
23
+ const power = 10 ** Math.floor(Math.log10(raw));
24
+ const step = [1, 2, 2.5, 5, 10].map((m) => m * power).find((s) => s >= raw) ?? 10 * power;
25
+ const start = Math.floor(lo / step) * step;
26
+ const end = Math.ceil(hi / step) * step;
27
+ const ticks = [];
28
+ for (let v = start; v <= end + step / 2; v += step)
29
+ ticks.push(Number(v.toFixed(10)));
30
+ return ticks;
31
+ }
32
+ /** A table of the same numbers a category chart draws, for ChartFrame's table view. */
33
+ export function chartTable(categories, series, format = String, categoryHeader = '') {
34
+ return {
35
+ headers: [categoryHeader, ...series.map((s) => s.label)],
36
+ rows: categories.map((c, i) => [
37
+ c,
38
+ ...series.map((s) => (s.values[i] === null ? '' : format(s.values[i]))),
39
+ ]),
40
+ };
41
+ }
42
+ const PATTERNS = [
43
+ 'solid',
44
+ 'diagonal',
45
+ 'dots',
46
+ 'cross',
47
+ 'horizontal',
48
+ 'vertical',
49
+ 'reverse',
50
+ 'grid',
51
+ ];
52
+ /** The fill for series `index`: a colour, and for every series after the first a pattern too. */
53
+ export function patternFor(index) {
54
+ return PATTERNS[index % PATTERNS.length];
55
+ }
56
+ /** The `<pattern>` for series `index`, to place in an SVG's `<defs>`. */
57
+ export function SeriesPattern({ id, index }) {
58
+ const color = chartColor(index);
59
+ const kind = patternFor(index);
60
+ const ink = 'var(--color-panel-solid)';
61
+ const marks = {
62
+ solid: null,
63
+ diagonal: _jsx("path", { d: "M-1,1 l2,-2 M0,6 l6,-6 M5,7 l2,-2", stroke: ink, strokeWidth: "1.2" }),
64
+ reverse: _jsx("path", { d: "M-1,5 l2,2 M0,0 l6,6 M5,-1 l2,2", stroke: ink, strokeWidth: "1.2" }),
65
+ dots: _jsx("circle", { cx: "3", cy: "3", r: "1.1", fill: ink }),
66
+ cross: _jsx("path", { d: "M0,0 l6,6 M6,0 l-6,6", stroke: ink, strokeWidth: "1" }),
67
+ horizontal: _jsx("path", { d: "M0,3 h6", stroke: ink, strokeWidth: "1.2" }),
68
+ vertical: _jsx("path", { d: "M3,0 v6", stroke: ink, strokeWidth: "1.2" }),
69
+ grid: _jsx("path", { d: "M0,3 h6 M3,0 v6", stroke: ink, strokeWidth: "1" }),
70
+ };
71
+ return (_jsxs("pattern", { id: id, width: "6", height: "6", patternUnits: "userSpaceOnUse", children: [_jsx("rect", { width: "6", height: "6", fill: color }), marks[kind]] }));
72
+ }
73
+ const MARKERS = [
74
+ 'circle',
75
+ 'square',
76
+ 'triangle',
77
+ 'diamond',
78
+ 'down',
79
+ 'cross',
80
+ 'plus',
81
+ 'star',
82
+ ];
83
+ /** A point marker for line series `index`, centred on (x, y). Each series has its own shape. */
84
+ export function SeriesMarker({ index, x, y, r = 3.5, }) {
85
+ const color = chartColor(index);
86
+ const kind = MARKERS[index % MARKERS.length];
87
+ const common = { fill: color, stroke: 'var(--color-panel-solid)', strokeWidth: 1 };
88
+ switch (kind) {
89
+ case 'circle':
90
+ return _jsx("circle", { cx: x, cy: y, r: r, ...common });
91
+ case 'square':
92
+ return _jsx("rect", { x: x - r, y: y - r, width: r * 2, height: r * 2, ...common });
93
+ case 'triangle':
94
+ return (_jsx("path", { d: `M${x},${y - r * 1.2} L${x + r * 1.1},${y + r * 0.8} L${x - r * 1.1},${y + r * 0.8} Z`, ...common }));
95
+ case 'down':
96
+ return (_jsx("path", { d: `M${x},${y + r * 1.2} L${x + r * 1.1},${y - r * 0.8} L${x - r * 1.1},${y - r * 0.8} Z`, ...common }));
97
+ case 'diamond':
98
+ return (_jsx("path", { d: `M${x},${y - r * 1.3} L${x + r * 1.3},${y} L${x},${y + r * 1.3} L${x - r * 1.3},${y} Z`, ...common }));
99
+ case 'cross':
100
+ return (_jsx("path", { d: `M${x - r},${y - r} L${x + r},${y + r} M${x + r},${y - r} L${x - r},${y + r}`, stroke: color, strokeWidth: 2 }));
101
+ case 'plus':
102
+ return (_jsx("path", { d: `M${x - r},${y} H${x + r} M${x},${y - r} V${y + r}`, stroke: color, strokeWidth: 2 }));
103
+ default:
104
+ return (_jsx("path", { d: `M${x},${y - r * 1.3} L${x + r * 0.4},${y - r * 0.4} L${x + r * 1.3},${y} L${x + r * 0.4},${y + r * 0.4} L${x},${y + r * 1.3} L${x - r * 0.4},${y + r * 0.4} L${x - r * 1.3},${y} L${x - r * 0.4},${y - r * 0.4} Z`, ...common }));
105
+ }
106
+ }
107
+ /**
108
+ * The key to a chart's series. Each swatch repeats the pattern (bars, donut) or the marker
109
+ * (lines) the chart uses, so the legend works without telling colours apart.
110
+ */
111
+ export function ChartLegend({ entries, kind, idPrefix, }) {
112
+ return (_jsx("ul", { className: "ui-chart-legend", children: entries.map((entry, i) => (_jsxs("li", { children: [_jsx("svg", { width: "14", height: "14", viewBox: "0 0 14 14", "aria-hidden": "true", children: kind === 'fill' ? (_jsxs(_Fragment, { children: [_jsx("defs", { children: _jsx(SeriesPattern, { id: `${idPrefix}-legend-${i}`, index: i }) }), _jsx("rect", { width: "14", height: "14", rx: "3", fill: `url(#${idPrefix}-legend-${i})` })] })) : (_jsxs(_Fragment, { children: [_jsx("path", { d: "M0,7 H14", stroke: chartColor(i), strokeWidth: "2" }), _jsx(SeriesMarker, { index: i, x: 7, y: 7, r: 3.5 })] })) }), _jsx("span", { children: entry.label }), entry.detail ? _jsx("span", { className: "ui-chart-legend__detail", children: entry.detail }) : null] }, entry.id))) }));
113
+ }
@@ -0,0 +1,63 @@
1
+ import { type ReactNode } from 'react';
2
+ import { type ActionMenuItem } from '../../atoms/ActionMenu/ActionMenu.js';
3
+ import { type PaginationProps } from '../../molecules/Pagination/Pagination.js';
4
+ import type { DataTableColumn, SortState } from './types.js';
5
+ export type DataTableState = 'ready' | 'loading' | 'empty' | 'error';
6
+ export interface DataTableProps<Row> {
7
+ columns: DataTableColumn<Row>[];
8
+ rows: readonly Row[];
9
+ getRowId: (row: Row) => string;
10
+ /** The table's name. Required; visually hidden unless `showCaption`. */
11
+ caption: string;
12
+ showCaption?: boolean;
13
+ /**
14
+ * How a row is named to screen readers ("Select Order 1042"). Default: the row-header
15
+ * column's `sortValue`, else the row id.
16
+ */
17
+ getRowLabel?: (row: Row) => string;
18
+ sort?: SortState;
19
+ onSortChange?: (sort: SortState) => void;
20
+ /** Selected row ids. With `onSelectionChange`, adds a checkbox column. */
21
+ selection?: ReadonlySet<string>;
22
+ onSelectionChange?: (selection: Set<string>) => void;
23
+ /** Per-row actions, in an overflow menu at the end of the row. */
24
+ rowActions?: (row: Row) => ActionMenuItem[];
25
+ onRowAction?: (actionId: string, row: Row) => void;
26
+ /**
27
+ * Makes the row clickable with a mouse, and turns the row-header cell into a button for
28
+ * keyboard and screen-reader users, so the action has a real control.
29
+ */
30
+ onRowClick?: (row: Row) => void;
31
+ /** Default `ready`; `empty` is also inferred from zero rows. */
32
+ state?: DataTableState;
33
+ /** Replaces the default EmptyState ("No results"). */
34
+ empty?: ReactNode;
35
+ /** Replaces the default ErrorState. */
36
+ error?: ReactNode;
37
+ /** Skeleton rows while loading. Default 5. */
38
+ loadingRows?: number;
39
+ /** Renders a Pagination under the table. */
40
+ pagination?: PaginationProps;
41
+ density?: 'comfortable' | 'compact';
42
+ /**
43
+ * Below this table width the rows become stacked cards with labels: `sm` 480px, `md` 640px
44
+ * (the default), `lg` 800px. `false` keeps the table and scrolls it sideways instead.
45
+ */
46
+ stackBelow?: 'sm' | 'md' | 'lg' | false;
47
+ /** Keeps the row-header column in view while the table scrolls sideways. */
48
+ stickyFirstColumn?: boolean;
49
+ /** Caps the height, so the header stays in view while the rows scroll. Any CSS length. */
50
+ maxHeight?: string;
51
+ className?: string;
52
+ }
53
+ /**
54
+ * A data table: sortable headers, row selection, row actions, loading/empty/error states and
55
+ * pagination. Fully controlled: it never sorts, pages or selects on its own, so the data can
56
+ * come from a server as easily as from `useClientTable`.
57
+ *
58
+ * It responds to its own width (a container query), not the viewport's. Narrow, it drops
59
+ * low-priority columns and then stacks each row into a labelled card. Explicit table roles
60
+ * keep it a table to screen readers in the stacked layout, where `display: block` would
61
+ * otherwise erase that in some browsers.
62
+ */
63
+ export declare function DataTable<Row>({ columns, rows, getRowId, caption, showCaption, getRowLabel, sort, onSortChange, selection, onSelectionChange, rowActions, onRowAction, onRowClick, state, empty, error, loadingRows, pagination, density, stackBelow, stickyFirstColumn, maxHeight, className, }: DataTableProps<Row>): import("react").JSX.Element;