@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,32 @@
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { MixerHorizontalIcon } from '@radix-ui/react-icons';
3
+ import { Popover as ThemedPopover } from '@radix-ui/themes';
4
+ import { Button } from '../../atoms/Button/Button.js';
5
+ import { Sheet } from '../../atoms/Sheet/Sheet.js';
6
+ import { minWidth } from '../../hooks/breakpoints.js';
7
+ import { useControllableState } from '../../hooks/useControllableState.js';
8
+ import { useMediaQuery } from '../../hooks/useMediaQuery.js';
9
+ import { useUiStrings } from '../../strings.js';
10
+ /**
11
+ * A "Filters" button that opens the filter controls: a popover anchored to the button on wide
12
+ * screens, a bottom sheet on narrow ones, where a popover would be cramped and hard to reach.
13
+ */
14
+ export function FilterPopover({ label, activeCount = 0, children, onApply, onReset, open, defaultOpen = false, onOpenChange, sheetBelow = 'sm', }) {
15
+ const strings = useUiStrings();
16
+ const wide = useMediaQuery(minWidth(sheetBelow || 'xs'));
17
+ const [isOpen, setOpen] = useControllableState({
18
+ value: open,
19
+ defaultValue: defaultOpen,
20
+ onChange: onOpenChange,
21
+ });
22
+ const title = label ?? strings.filters;
23
+ const footer = onApply || onReset ? (_jsxs(_Fragment, { children: [onReset ? (_jsx(Button, { variant: "ghost", onClick: onReset, children: strings.reset })) : null, onApply ? (_jsx(Button, { onClick: () => {
24
+ onApply();
25
+ setOpen(false);
26
+ }, children: strings.apply })) : null] })) : null;
27
+ const trigger = (_jsxs(Button, { variant: "outline", onClick: sheetBelow !== false && !wide ? () => setOpen(true) : undefined, children: [_jsx(MixerHorizontalIcon, { "aria-hidden": "true" }), title, activeCount > 0 ? (_jsxs(_Fragment, { children: [_jsx("span", { className: "ui-count", "aria-hidden": "true", children: activeCount }), _jsxs("span", { className: "ui-sr-only", children: [", ", strings.activeCount(activeCount)] })] })) : null] }));
28
+ if (sheetBelow !== false && !wide) {
29
+ return (_jsxs(_Fragment, { children: [trigger, _jsx(Sheet, { open: isOpen, onOpenChange: setOpen, title: title, footer: footer, showCloseButton: true, children: _jsx("div", { className: "flex flex-col gap-3", children: children }) })] }));
30
+ }
31
+ return (_jsxs(ThemedPopover.Root, { open: isOpen, onOpenChange: setOpen, children: [_jsx(ThemedPopover.Trigger, { children: trigger }), _jsxs(ThemedPopover.Content, { align: "start", className: "w-80 max-w-[calc(100vw-1.5rem)] p-0", "aria-label": title, children: [_jsx("div", { className: "ui-filter-popover__body", children: children }), footer ? _jsx("div", { className: "ui-filter-popover__footer", children: footer }) : null] })] }));
32
+ }
@@ -0,0 +1 @@
1
+ export * from './FilterPopover.js';
@@ -0,0 +1 @@
1
+ export * from './FilterPopover.js';
@@ -0,0 +1,16 @@
1
+ import type { ReactNode } from 'react';
2
+ export interface FormActionsProps {
3
+ /** The buttons, in reading order: cancel before save. */
4
+ children: ReactNode;
5
+ /** Default `end`. `between` puts the first child at the start and the rest at the end. */
6
+ align?: 'start' | 'end' | 'between';
7
+ /**
8
+ * Below 768px, pins the row to the bottom of the screen on the bar material, with buttons
9
+ * sharing the width, so saving a long form never needs a scroll back down. It sits above an
10
+ * AppShell's TabBar. Default `true`.
11
+ */
12
+ sticky?: boolean;
13
+ className?: string;
14
+ }
15
+ /** The row of buttons that ends a form. */
16
+ export declare function FormActions({ children, align, sticky, className, }: FormActionsProps): import("react").JSX.Element;
@@ -0,0 +1,5 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ /** The row of buttons that ends a form. */
3
+ export function FormActions({ children, align = 'end', sticky = true, className, }) {
4
+ return (_jsx("div", { className: ['ui-form-actions', className].filter(Boolean).join(' '), "data-align": align, "data-sticky": sticky ? '' : undefined, children: children }));
5
+ }
@@ -0,0 +1 @@
1
+ export * from './FormActions.js';
@@ -0,0 +1 @@
1
+ export * from './FormActions.js';
@@ -0,0 +1,52 @@
1
+ import { type ReactElement, type ReactNode } from 'react';
2
+ /** The ids and states a control needs, for wiring one up by hand with the render form. */
3
+ export interface FormFieldControlProps {
4
+ id: string;
5
+ 'aria-describedby'?: string;
6
+ 'aria-invalid'?: true;
7
+ required?: boolean;
8
+ }
9
+ export interface FormFieldProps {
10
+ label: ReactNode;
11
+ /** Help that stays visible. Linked with `aria-describedby`. */
12
+ hint?: ReactNode;
13
+ /** The validation message. Marks the control invalid and is linked with `aria-describedby`. */
14
+ error?: ReactNode;
15
+ required?: boolean;
16
+ /** Defaults to a generated id. */
17
+ id?: string;
18
+ /**
19
+ * `stacked` (label above, the default) or `inline` (control first, label beside it), which is
20
+ * the right shape for a Checkbox or Switch.
21
+ */
22
+ layout?: 'stacked' | 'inline';
23
+ /**
24
+ * One control. Input, Textarea and Select get `invalid` (their red state); anything else gets
25
+ * `aria-invalid`. Or a function, given {@link FormFieldControlProps}, for a control that needs
26
+ * them placed somewhere else.
27
+ */
28
+ children: ReactElement | ((control: FormFieldControlProps) => ReactNode);
29
+ className?: string;
30
+ }
31
+ /**
32
+ * A label, a control, a hint and an error, wired together: the label names the control, the
33
+ * hint and error describe it, and the error marks it invalid.
34
+ *
35
+ * The error is text with an icon, never colour alone. It is not a live region: announcing each
36
+ * field's error as it appears is noisy, and `ErrorSummary` says it once, on submit.
37
+ */
38
+ export declare function FormField({ label, hint, error, required, id: idProp, layout, children, className, }: FormFieldProps): import("react").JSX.Element;
39
+ export interface FormFieldsetProps {
40
+ /** Names the group: "Notifications", "Plan". */
41
+ legend: ReactNode;
42
+ hint?: ReactNode;
43
+ error?: ReactNode;
44
+ /** Checkboxes, a RadioGroup, several related fields. */
45
+ children: ReactNode;
46
+ className?: string;
47
+ }
48
+ /**
49
+ * A `fieldset` for controls that answer one question together. The legend names the group;
50
+ * hint and error describe it.
51
+ */
52
+ export declare function FormFieldset({ legend, hint, error, children, className }: FormFieldsetProps): import("react").JSX.Element;
@@ -0,0 +1,63 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { ExclamationTriangleIcon } from '@radix-ui/react-icons';
3
+ import { cloneElement, isValidElement, useId } from 'react';
4
+ import { Input } from '../../atoms/Input/Input.js';
5
+ import { Label } from '../../atoms/Label/Label.js';
6
+ import { Select } from '../../atoms/Select/Select.js';
7
+ import { Textarea } from '../../atoms/Textarea/Textarea.js';
8
+ import { PasswordInput } from '../PasswordInput/PasswordInput.js';
9
+ const INVALID_PROP = new Set([Input, Textarea, Select, PasswordInput]);
10
+ /**
11
+ * A label, a control, a hint and an error, wired together: the label names the control, the
12
+ * hint and error describe it, and the error marks it invalid.
13
+ *
14
+ * The error is text with an icon, never colour alone. It is not a live region: announcing each
15
+ * field's error as it appears is noisy, and `ErrorSummary` says it once, on submit.
16
+ */
17
+ export function FormField({ label, hint, error, required, id: idProp, layout = 'stacked', children, className, }) {
18
+ const auto = useId();
19
+ const id = idProp ?? auto;
20
+ const hintId = `${id}-hint`;
21
+ const errorId = `${id}-error`;
22
+ const describedBy = [hint ? hintId : null, error ? errorId : null].filter(Boolean).join(' ');
23
+ const control = {
24
+ id,
25
+ 'aria-describedby': describedBy || undefined,
26
+ 'aria-invalid': error ? true : undefined,
27
+ required: required || undefined,
28
+ };
29
+ let rendered;
30
+ if (typeof children === 'function') {
31
+ rendered = children(control);
32
+ }
33
+ else if (isValidElement(children)) {
34
+ // The house fields set `aria-invalid` themselves from `invalid`, along with their red state.
35
+ // Passing the key at all, even as undefined, would override theirs, so it is left out.
36
+ const { 'aria-invalid': ariaInvalid, ...rest } = control;
37
+ const props = INVALID_PROP.has(children.type)
38
+ ? { ...rest, invalid: !!error }
39
+ : { ...rest, ...(ariaInvalid ? { 'aria-invalid': ariaInvalid } : {}) };
40
+ rendered = cloneElement(children, props);
41
+ }
42
+ else {
43
+ rendered = children;
44
+ }
45
+ const labelEl = (_jsx(Label, { htmlFor: id, required: required, children: label }));
46
+ const hintEl = hint ? (_jsx("p", { id: hintId, className: "ui-field__hint", children: hint })) : null;
47
+ const errorEl = error ? _jsx(FieldError, { id: errorId, children: error }) : null;
48
+ return (_jsx("div", { className: ['ui-field', className].filter(Boolean).join(' '), "data-layout": layout, children: layout === 'inline' ? (_jsxs(_Fragment, { children: [_jsx("span", { className: "ui-field__control", children: rendered }), _jsxs("span", { className: "ui-field__text", children: [labelEl, hintEl, errorEl] })] })) : (_jsxs(_Fragment, { children: [labelEl, hintEl, rendered, errorEl] })) }));
49
+ }
50
+ function FieldError({ id, children }) {
51
+ return (_jsxs("p", { id: id, className: "ui-field__error", children: [_jsx(ExclamationTriangleIcon, { "aria-hidden": "true" }), _jsx("span", { children: children })] }));
52
+ }
53
+ /**
54
+ * A `fieldset` for controls that answer one question together. The legend names the group;
55
+ * hint and error describe it.
56
+ */
57
+ export function FormFieldset({ legend, hint, error, children, className }) {
58
+ const id = useId();
59
+ const describedBy = [hint ? `${id}-hint` : null, error ? `${id}-error` : null]
60
+ .filter(Boolean)
61
+ .join(' ');
62
+ return (_jsxs("fieldset", { className: ['ui-fieldset', className].filter(Boolean).join(' '), "aria-describedby": describedBy || undefined, children: [_jsx("legend", { className: "ui-fieldset__legend", children: legend }), hint ? (_jsx("p", { id: `${id}-hint`, className: "ui-field__hint", children: hint })) : null, children, error ? _jsx(FieldError, { id: `${id}-error`, children: error }) : null] }));
63
+ }
@@ -0,0 +1 @@
1
+ export * from './FormField.js';
@@ -0,0 +1 @@
1
+ export * from './FormField.js';
@@ -0,0 +1,14 @@
1
+ import { type ReactNode } from 'react';
2
+ export interface FormSectionProps {
3
+ title: ReactNode;
4
+ /** Why these fields matter, or what they change. */
5
+ description?: ReactNode;
6
+ /** FormFields. */
7
+ children: ReactNode;
8
+ className?: string;
9
+ }
10
+ /**
11
+ * A titled group of fields: a `section` named by its `h2`. From 720px of its own width the
12
+ * title and description sit in a column beside the fields; narrower, above them.
13
+ */
14
+ export declare function FormSection({ title, description, children, className }: FormSectionProps): import("react").JSX.Element;
@@ -0,0 +1,10 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useId } from 'react';
3
+ /**
4
+ * A titled group of fields: a `section` named by its `h2`. From 720px of its own width the
5
+ * title and description sit in a column beside the fields; narrower, above them.
6
+ */
7
+ export function FormSection({ title, description, children, className }) {
8
+ const titleId = useId();
9
+ return (_jsx("section", { "aria-labelledby": titleId, className: ['ui-form-section', className].filter(Boolean).join(' '), children: _jsxs("div", { className: "ui-form-section__grid", children: [_jsxs("div", { children: [_jsx("h2", { id: titleId, className: "ui-form-section__title", children: title }), description ? _jsx("p", { className: "ui-form-section__description", children: description }) : null] }), _jsx("div", { className: "ui-form-section__fields", children: children })] }) }));
10
+ }
@@ -0,0 +1 @@
1
+ export * from './FormSection.js';
@@ -0,0 +1 @@
1
+ export * from './FormSection.js';
@@ -0,0 +1,22 @@
1
+ import type { ReactNode } from 'react';
2
+ import type { Responsive } from '../../atoms/Card/Card.js';
3
+ export interface KeyValueItem {
4
+ /** React key. Defaults to the label when it is a string. */
5
+ id?: string;
6
+ label: ReactNode;
7
+ value: ReactNode;
8
+ }
9
+ export interface KeyValueListProps {
10
+ items: KeyValueItem[];
11
+ /**
12
+ * Label beside value, or above it. Default: above on phones, beside from `sm` (768px), in
13
+ * Radix's responsive-prop shape.
14
+ */
15
+ orientation?: Responsive<'horizontal' | 'vertical'>;
16
+ className?: string;
17
+ }
18
+ /**
19
+ * Labelled facts about one thing: the side panel of a detail page. Radix Themes' `DataList`
20
+ * (a description list underneath), with items as data and a phone-first default orientation.
21
+ */
22
+ export declare function KeyValueList({ items, orientation, className, }: KeyValueListProps): import("react").JSX.Element;
@@ -0,0 +1,9 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { DataList } from '@radix-ui/themes';
3
+ /**
4
+ * Labelled facts about one thing: the side panel of a detail page. Radix Themes' `DataList`
5
+ * (a description list underneath), with items as data and a phone-first default orientation.
6
+ */
7
+ export function KeyValueList({ items, orientation = { initial: 'vertical', sm: 'horizontal' }, className, }) {
8
+ return (_jsx(DataList.Root, { orientation: orientation, className: className, children: items.map((item, i) => (_jsxs(DataList.Item, { children: [_jsx(DataList.Label, { children: item.label }), _jsx(DataList.Value, { children: item.value })] }, item.id ?? (typeof item.label === 'string' ? item.label : `kv-${i}`)))) }));
9
+ }
@@ -0,0 +1 @@
1
+ export * from './KeyValueList.js';
@@ -0,0 +1 @@
1
+ export * from './KeyValueList.js';
@@ -0,0 +1,20 @@
1
+ export type LoadingStateVariant = 'spinner' | 'rows' | 'cards';
2
+ export interface LoadingStateProps {
3
+ /**
4
+ * `spinner` when the shape of what is coming is unknown; `rows` and `cards` draw its shape,
5
+ * so nothing jumps when it arrives. Default `spinner`.
6
+ */
7
+ variant?: LoadingStateVariant;
8
+ /** How many rows or cards to draw. Default 5 rows, 3 cards. */
9
+ count?: number;
10
+ /** Overrides the announced text (otherwise the `loading` UI string). */
11
+ label?: string;
12
+ className?: string;
13
+ }
14
+ /**
15
+ * A placeholder while something loads. Announced once (`role="status"`); the skeleton shapes
16
+ * themselves are hidden from screen readers.
17
+ *
18
+ * For a table, `DataTable state="loading"` draws skeleton rows inside the table itself.
19
+ */
20
+ export declare function LoadingState({ variant, count, label, className }: LoadingStateProps): import("react").JSX.Element;
@@ -0,0 +1,22 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Skeleton as ThemedSkeleton } from '@radix-ui/themes';
3
+ import { Card } from '../../atoms/Card/Card.js';
4
+ import { SkeletonText } from '../../atoms/Skeleton/Skeleton.js';
5
+ import { Spinner } from '../../atoms/Spinner/Spinner.js';
6
+ import { useUiStrings } from '../../strings.js';
7
+ /**
8
+ * A placeholder while something loads. Announced once (`role="status"`); the skeleton shapes
9
+ * themselves are hidden from screen readers.
10
+ *
11
+ * For a table, `DataTable state="loading"` draws skeleton rows inside the table itself.
12
+ */
13
+ export function LoadingState({ variant = 'spinner', count, label, className }) {
14
+ const strings = useUiStrings();
15
+ const cls = ['ui-loading', className].filter(Boolean).join(' ');
16
+ if (variant === 'spinner') {
17
+ return (_jsx("div", { className: cls, "data-variant": "spinner", children: _jsx(Spinner, { label: label }) }));
18
+ }
19
+ const n = count ?? (variant === 'rows' ? 5 : 3);
20
+ const keys = Array.from({ length: n }, (_, i) => `k${i}`);
21
+ return (_jsx("div", { className: cls, "data-variant": variant, role: "status", "aria-label": label ?? strings.loading, children: keys.map((key) => variant === 'rows' ? (_jsxs("div", { "aria-hidden": "true", className: "flex items-center gap-3", children: [_jsx(ThemedSkeleton, { width: "32px", height: "32px", style: { borderRadius: 8 } }), _jsxs("div", { className: "flex flex-1 flex-col gap-2", children: [_jsx(ThemedSkeleton, { height: "0.875rem", width: "55%" }), _jsx(ThemedSkeleton, { height: "0.75rem", width: "35%" })] })] }, key)) : (_jsx(Card, { "aria-hidden": "true", className: "p-4", children: _jsx(SkeletonText, { lines: 3 }) }, key))) }));
22
+ }
@@ -0,0 +1 @@
1
+ export * from './LoadingState.js';
@@ -0,0 +1 @@
1
+ export * from './LoadingState.js';
@@ -0,0 +1,31 @@
1
+ import { type ReactNode } from 'react';
2
+ import type { RenderLink } from '../../atoms/TextLink/TextLink.js';
3
+ export interface NavItemProps {
4
+ /** React key when the item is rendered from data. Defaults to `label`. */
5
+ id?: string;
6
+ label: string;
7
+ icon?: ReactNode;
8
+ /** A link. Without it (and without `items`) the item is a button. */
9
+ href?: string;
10
+ /** Wraps a router link. Receives the props to spread, like every `render` in this library. */
11
+ render?: RenderLink;
12
+ /** The current page: highlighted, and `aria-current="page"`. */
13
+ active?: boolean;
14
+ /** A number shows a count (99+ caps); `true` shows a dot. */
15
+ badge?: number | boolean;
16
+ /** What the badge means, for screen readers ("3 unread"). A dot is silent without it. */
17
+ badgeLabel?: string;
18
+ onClick?: () => void;
19
+ /** Child items: the item becomes a disclosure that shows them. One level deep. */
20
+ items?: NavItemProps[];
21
+ /** Whether a group starts open. Default: open when one of its children is active. */
22
+ defaultOpen?: boolean;
23
+ }
24
+ /**
25
+ * One entry in a navigation list: a link, a button, or a group of child links.
26
+ *
27
+ * Renders an `<li>`, so put it inside a `<ul>` (Sidebar does). Inside a collapsed Sidebar the
28
+ * label stays in the accessible name and is also shown as a tooltip, and a group opens its
29
+ * children as a menu beside the rail, since there is no room to expand it in place.
30
+ */
31
+ export declare function NavItem(item: NavItemProps): import("react").JSX.Element;
@@ -0,0 +1,72 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { ChevronDownIcon } from '@radix-ui/react-icons';
3
+ import { DropdownMenu as ThemedDropdownMenu } from '@radix-ui/themes';
4
+ import { useId, useState } from 'react';
5
+ import { Tooltip } from '../../atoms/Tooltip/Tooltip.js';
6
+ import { useNavContext } from './context.js';
7
+ function badgeText(badge) {
8
+ if (badge === undefined || badge === false || badge === 0)
9
+ return null;
10
+ if (badge === true)
11
+ return '';
12
+ return badge > 99 ? '99+' : String(badge);
13
+ }
14
+ /** The inside of an item: icon, label, badge. Shared by links, buttons and group toggles. */
15
+ function ItemContent({ item, trailing }) {
16
+ const text = badgeText(item.badge);
17
+ return (_jsxs(_Fragment, { children: [item.icon ? (_jsx("span", { className: "ui-nav-item__icon", "aria-hidden": "true", children: item.icon })) : null, _jsx("span", { className: "ui-nav-item__label", children: item.label }), text !== null ? (_jsx("span", { className: "ui-nav-item__badge", "data-dot": text === '' ? '' : undefined, "aria-hidden": item.badgeLabel ? 'true' : text === '' ? 'true' : undefined, children: text })) : null, item.badgeLabel && text !== null ? (_jsxs("span", { className: "ui-sr-only", children: [", ", item.badgeLabel] })) : null, trailing] }));
18
+ }
19
+ /** A link or a button, depending on what the item has. */
20
+ function ItemControl({ item, onNavigate }) {
21
+ const onClick = () => {
22
+ item.onClick?.();
23
+ onNavigate?.();
24
+ };
25
+ const children = _jsx(ItemContent, { item: item });
26
+ if (item.href !== undefined) {
27
+ const props = {
28
+ href: item.href,
29
+ className: 'ui-nav-item',
30
+ 'aria-current': item.active ? 'page' : undefined,
31
+ onClick,
32
+ children,
33
+ };
34
+ return item.render ? item.render(props) : _jsx("a", { ...props });
35
+ }
36
+ return (_jsx("button", { type: "button", className: "ui-nav-item", "aria-current": item.active ? 'page' : undefined, onClick: onClick, children: children }));
37
+ }
38
+ /**
39
+ * One entry in a navigation list: a link, a button, or a group of child links.
40
+ *
41
+ * Renders an `<li>`, so put it inside a `<ul>` (Sidebar does). Inside a collapsed Sidebar the
42
+ * label stays in the accessible name and is also shown as a tooltip, and a group opens its
43
+ * children as a menu beside the rail, since there is no room to expand it in place.
44
+ */
45
+ export function NavItem(item) {
46
+ const { collapsed, onNavigate } = useNavContext();
47
+ const childActive = item.items?.some((c) => c.active) ?? false;
48
+ const [open, setOpen] = useState(item.defaultOpen ?? childActive);
49
+ const listId = useId();
50
+ if (!item.items) {
51
+ return (_jsx("li", { children: _jsx(Tooltip, { label: collapsed ? item.label : undefined, children: _jsx(ItemControl, { item: item, onNavigate: onNavigate }) }) }));
52
+ }
53
+ if (collapsed) {
54
+ return (_jsx("li", { children: _jsxs(ThemedDropdownMenu.Root, { children: [_jsx(ThemedDropdownMenu.Trigger, { children: _jsx("button", { type: "button", className: "ui-nav-item", "aria-current": childActive ? 'page' : undefined, children: _jsx(ItemContent, { item: item }) }) }), _jsxs(ThemedDropdownMenu.Content, { side: "right", align: "start", children: [_jsx(ThemedDropdownMenu.Label, { children: item.label }), item.items.map((child) => (_jsx(ThemedDropdownMenu.Item, { asChild: child.href !== undefined, onSelect: () => {
55
+ if (child.href === undefined) {
56
+ child.onClick?.();
57
+ onNavigate?.();
58
+ }
59
+ }, children: child.href !== undefined
60
+ ? (child.render ?? ((p) => _jsx("a", { ...p })))({
61
+ href: child.href,
62
+ 'aria-current': child.active ? 'page' : undefined,
63
+ onClick: () => {
64
+ child.onClick?.();
65
+ onNavigate?.();
66
+ },
67
+ children: child.label,
68
+ })
69
+ : child.label }, child.id ?? child.label)))] })] }) }));
70
+ }
71
+ return (_jsxs("li", { children: [_jsx("button", { type: "button", className: "ui-nav-item", "aria-expanded": open, "aria-controls": listId, onClick: () => setOpen((o) => !o), children: _jsx(ItemContent, { item: item, trailing: _jsx("span", { className: "ui-nav-item__chevron", "aria-hidden": "true", children: _jsx(ChevronDownIcon, {}) }) }) }), _jsx("ul", { id: listId, className: "ui-nav-group__items", hidden: !open, children: item.items.map((child) => (_jsx("li", { children: _jsx(ItemControl, { item: child, onNavigate: onNavigate }) }, child.id ?? child.label))) })] }));
72
+ }
@@ -0,0 +1,14 @@
1
+ /**
2
+ * What a NavItem needs from the navigation around it. Provided by `Sidebar`; a NavItem outside
3
+ * one gets the defaults (expanded, nothing to close).
4
+ *
5
+ * Not exported from the package: it is the contract between the two components, not API.
6
+ */
7
+ export interface NavContextValue {
8
+ /** The sidebar is an icon rail: labels become tooltips. */
9
+ collapsed: boolean;
10
+ /** Called after any item is activated, so an off-canvas panel can close. */
11
+ onNavigate?: () => void;
12
+ }
13
+ export declare const NavContext: import("react").Context<NavContextValue>;
14
+ export declare function useNavContext(): NavContextValue;
@@ -0,0 +1,5 @@
1
+ import { createContext, useContext } from 'react';
2
+ export const NavContext = createContext({ collapsed: false });
3
+ export function useNavContext() {
4
+ return useContext(NavContext);
5
+ }
@@ -0,0 +1 @@
1
+ export * from './NavItem.js';
@@ -0,0 +1 @@
1
+ export * from './NavItem.js';
@@ -0,0 +1,32 @@
1
+ export type PaginationEntry = number | 'gap-start' | 'gap-end';
2
+ /**
3
+ * The page numbers to show: always the first and last, `siblings` either side of the current
4
+ * one, and a gap marker wherever pages are skipped. A gap of exactly one page shows that page
5
+ * instead, since "…" would take the same room and say less; for the same reason a short run
6
+ * (up to `2 * siblings + 5` pages) shows every page.
7
+ */
8
+ export declare function paginationRange(page: number, pageCount: number, siblings?: number): PaginationEntry[];
9
+ export interface PaginationProps {
10
+ /** 1-based. */
11
+ page: number;
12
+ onPageChange: (page: number) => void;
13
+ /** Either this, or `total` with `pageSize`. */
14
+ pageCount?: number;
15
+ /** The number of items across all pages. With `pageSize`, also shows "1–25 of 500". */
16
+ total?: number;
17
+ pageSize?: number;
18
+ /** Shows a rows-per-page select. Needs `onPageSizeChange`. */
19
+ pageSizeOptions?: number[];
20
+ onPageSizeChange?: (size: number) => void;
21
+ /** Pages shown either side of the current one. Default 1. */
22
+ siblings?: number;
23
+ className?: string;
24
+ }
25
+ /**
26
+ * Page navigation for a list or table, controlled: it never changes page by itself.
27
+ *
28
+ * A `nav` landmark. At 520px of its own width or more it shows numbered pages; narrower, just
29
+ * previous / "Page 2 of 10" / next, and the rows-per-page select is dropped. Both are in the
30
+ * markup and CSS shows one, so nothing re-renders on resize.
31
+ */
32
+ export declare function Pagination({ page, onPageChange, pageCount: pageCountProp, total, pageSize, pageSizeOptions, onPageSizeChange, siblings, className, }: PaginationProps): import("react").JSX.Element;
@@ -0,0 +1,57 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { ChevronLeftIcon, ChevronRightIcon } from '@radix-ui/react-icons';
3
+ import { useId } from 'react';
4
+ import { Label } from '../../atoms/Label/Label.js';
5
+ import { Select } from '../../atoms/Select/Select.js';
6
+ import { useUiStrings } from '../../strings.js';
7
+ /**
8
+ * The page numbers to show: always the first and last, `siblings` either side of the current
9
+ * one, and a gap marker wherever pages are skipped. A gap of exactly one page shows that page
10
+ * instead, since "…" would take the same room and say less; for the same reason a short run
11
+ * (up to `2 * siblings + 5` pages) shows every page.
12
+ */
13
+ export function paginationRange(page, pageCount, siblings = 1) {
14
+ if (pageCount <= 0)
15
+ return [];
16
+ // When every page fits in the slots a gapped range would take anyway, show them all.
17
+ if (pageCount <= 2 * siblings + 5)
18
+ return Array.from({ length: pageCount }, (_, i) => i + 1);
19
+ const current = Math.min(Math.max(page, 1), pageCount);
20
+ const start = Math.max(2, current - siblings);
21
+ const end = Math.min(pageCount - 1, current + siblings);
22
+ const out = [1];
23
+ if (start === 3)
24
+ out.push(2);
25
+ else if (start > 3)
26
+ out.push('gap-start');
27
+ for (let p = start; p <= end; p++)
28
+ out.push(p);
29
+ if (end === pageCount - 2)
30
+ out.push(pageCount - 1);
31
+ else if (end < pageCount - 2)
32
+ out.push('gap-end');
33
+ if (pageCount > 1)
34
+ out.push(pageCount);
35
+ return out;
36
+ }
37
+ /**
38
+ * Page navigation for a list or table, controlled: it never changes page by itself.
39
+ *
40
+ * A `nav` landmark. At 520px of its own width or more it shows numbered pages; narrower, just
41
+ * previous / "Page 2 of 10" / next, and the rows-per-page select is dropped. Both are in the
42
+ * markup and CSS shows one, so nothing re-renders on resize.
43
+ */
44
+ export function Pagination({ page, onPageChange, pageCount: pageCountProp, total, pageSize, pageSizeOptions, onPageSizeChange, siblings = 1, className, }) {
45
+ const strings = useUiStrings();
46
+ const sizeId = useId();
47
+ const pageCount = Math.max(1, pageCountProp ?? (total !== undefined && pageSize ? Math.ceil(total / pageSize) : 1));
48
+ const current = Math.min(Math.max(page, 1), pageCount);
49
+ const go = (p) => {
50
+ if (p !== current && p >= 1 && p <= pageCount)
51
+ onPageChange(p);
52
+ };
53
+ const range = total !== undefined && pageSize && total > 0
54
+ ? strings.rangeOf((current - 1) * pageSize + 1, Math.min(current * pageSize, total), total)
55
+ : null;
56
+ return (_jsx("nav", { "aria-label": strings.pagination, className: ['ui-pagination', className].filter(Boolean).join(' '), children: _jsxs("div", { className: "ui-pagination__bar", children: [range ? _jsx("span", { className: "tabular-nums", children: range }) : _jsx("span", {}), pageSizeOptions && onPageSizeChange && pageSize ? (_jsxs("span", { className: "ui-pagination__size", children: [_jsx(Label, { htmlFor: sizeId, children: strings.rowsPerPage }), _jsx(Select, { id: sizeId, value: String(pageSize), onValueChange: (v) => onPageSizeChange(Number(v)), options: pageSizeOptions.map((n) => ({ value: String(n), label: String(n) })) })] })) : null, _jsxs("span", { className: "ui-pagination__nav", children: [_jsx("button", { type: "button", className: "ui-pagination__page", "aria-label": strings.previousPage, disabled: current <= 1, onClick: () => go(current - 1), children: _jsx(ChevronLeftIcon, { "aria-hidden": "true" }) }), _jsx("ul", { className: "ui-pagination__pages", children: paginationRange(current, pageCount, siblings).map((entry) => typeof entry === 'number' ? (_jsx("li", { children: _jsx("button", { type: "button", className: "ui-pagination__page", "aria-label": strings.goToPage(entry), "aria-current": entry === current ? 'page' : undefined, onClick: () => go(entry), children: entry }) }, entry)) : (_jsx("li", { className: "ui-pagination__ellipsis", "aria-hidden": "true", children: "\u2026" }, entry))) }), _jsx("span", { className: "ui-pagination__compact", children: strings.pageOf(current, pageCount) }), _jsx("button", { type: "button", className: "ui-pagination__page", "aria-label": strings.nextPage, disabled: current >= pageCount, onClick: () => go(current + 1), children: _jsx(ChevronRightIcon, { "aria-hidden": "true" }) })] })] }) }));
57
+ }
@@ -0,0 +1 @@
1
+ export * from './Pagination.js';
@@ -0,0 +1 @@
1
+ export * from './Pagination.js';
@@ -0,0 +1,14 @@
1
+ import { type InputProps } from '../../atoms/Input/Input.js';
2
+ export interface PasswordInputProps extends Omit<InputProps, 'type' | 'autoComplete'> {
3
+ /**
4
+ * `current-password` to sign in, `new-password` to create one. Required, because password
5
+ * managers rely on it to fill the right one and to offer a strong new one.
6
+ */
7
+ autoComplete: 'current-password' | 'new-password';
8
+ }
9
+ /**
10
+ * A password field with a show/hide toggle. The toggle is a pressed/unpressed button with one
11
+ * name (the `showPassword` UI string). Showing turns off spellcheck and autocapitalisation
12
+ * as well, so the text never reaches a dictionary.
13
+ */
14
+ export declare const PasswordInput: import("react").ForwardRefExoticComponent<PasswordInputProps & import("react").RefAttributes<HTMLInputElement>>;
@@ -0,0 +1,16 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { EyeClosedIcon, EyeOpenIcon } from '@radix-ui/react-icons';
3
+ import { IconButton, TextField } from '@radix-ui/themes';
4
+ import { forwardRef, useState } from 'react';
5
+ import { Input } from '../../atoms/Input/Input.js';
6
+ import { useUiStrings } from '../../strings.js';
7
+ /**
8
+ * A password field with a show/hide toggle. The toggle is a pressed/unpressed button with one
9
+ * name (the `showPassword` UI string). Showing turns off spellcheck and autocapitalisation
10
+ * as well, so the text never reaches a dictionary.
11
+ */
12
+ export const PasswordInput = forwardRef(function PasswordInput({ autoComplete, children, ...props }, ref) {
13
+ const strings = useUiStrings();
14
+ const [visible, setVisible] = useState(false);
15
+ return (_jsxs(Input, { ref: ref, type: visible ? 'text' : 'password', autoComplete: autoComplete, autoCapitalize: "none", autoCorrect: "off", spellCheck: false, ...props, children: [children, _jsx(TextField.Slot, { side: "right", children: _jsx(IconButton, { type: "button", size: "1", variant: "ghost", color: "gray", "aria-label": strings.showPassword, "aria-pressed": visible, onClick: () => setVisible((v) => !v), children: visible ? _jsx(EyeClosedIcon, {}) : _jsx(EyeOpenIcon, {}) }) })] }));
16
+ });
@@ -0,0 +1 @@
1
+ export * from './PasswordInput.js';
@@ -0,0 +1 @@
1
+ export * from './PasswordInput.js';
@@ -0,0 +1,24 @@
1
+ import { type InputSize } from '../../atoms/Input/Input.js';
2
+ export interface SearchFieldProps {
3
+ /** Controlled text. */
4
+ value?: string;
5
+ defaultValue?: string;
6
+ /** Called with the text, after `debounceMs` of no typing. Clearing reports at once. */
7
+ onValueChange: (value: string) => void;
8
+ /** Default 0 (every keystroke). 200–300 suits a search that hits a server. */
9
+ debounceMs?: number;
10
+ placeholder?: string;
11
+ /** The accessible name. Default: the `search` UI string. */
12
+ 'aria-label'?: string;
13
+ /** A single key that focuses the field from anywhere on the page, e.g. `/`. */
14
+ shortcut?: string;
15
+ size?: InputSize;
16
+ id?: string;
17
+ className?: string;
18
+ }
19
+ /**
20
+ * A search input with a magnifier, a clear button once there is text, Escape to clear, and an
21
+ * optional page-wide shortcut key. The shortcut hint is hidden on touch screens, which have no
22
+ * keyboard to press it on, and once the field has text.
23
+ */
24
+ export declare function SearchField({ value, defaultValue, onValueChange, debounceMs, placeholder, 'aria-label': ariaLabel, shortcut, size, id, className, }: SearchFieldProps): import("react").JSX.Element;