@ratio-ui/core 0.2.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (143) hide show
  1. package/dist/catalogue.json +2690 -749
  2. package/dist/components/accordion/Accordion.d.ts +19 -0
  3. package/dist/components/accordion/Accordion.js +52 -0
  4. package/dist/components/accordion/index.d.ts +2 -0
  5. package/dist/components/accordion/index.js +1 -0
  6. package/dist/components/alert/Alert.d.ts +18 -0
  7. package/dist/components/alert/Alert.js +67 -0
  8. package/dist/components/alert/index.d.ts +2 -0
  9. package/dist/components/alert/index.js +1 -0
  10. package/dist/components/alert-dialog/AlertDialog.d.ts +48 -0
  11. package/dist/components/alert-dialog/AlertDialog.js +76 -0
  12. package/dist/components/alert-dialog/index.d.ts +2 -0
  13. package/dist/components/alert-dialog/index.js +1 -0
  14. package/dist/components/app-shell/AppShell.js +1 -1
  15. package/dist/components/breadcrumb/Breadcrumb.d.ts +19 -0
  16. package/dist/components/breadcrumb/Breadcrumb.js +50 -0
  17. package/dist/components/breadcrumb/index.d.ts +2 -0
  18. package/dist/components/breadcrumb/index.js +1 -0
  19. package/dist/components/calendar/Calendar.d.ts +20 -0
  20. package/dist/components/calendar/Calendar.js +119 -0
  21. package/dist/components/calendar/index.d.ts +2 -0
  22. package/dist/components/calendar/index.js +1 -0
  23. package/dist/components/chart/Chart.d.ts +137 -0
  24. package/dist/components/chart/Chart.js +238 -0
  25. package/dist/components/chart/index.d.ts +2 -0
  26. package/dist/components/chart/index.js +1 -0
  27. package/dist/components/checkbox-list/CheckboxList.d.ts +36 -0
  28. package/dist/components/checkbox-list/CheckboxList.js +93 -0
  29. package/dist/components/checkbox-list/index.d.ts +2 -0
  30. package/dist/components/checkbox-list/index.js +1 -0
  31. package/dist/components/collapsible/Collapsible.d.ts +24 -0
  32. package/dist/components/collapsible/Collapsible.js +58 -0
  33. package/dist/components/collapsible/index.d.ts +2 -0
  34. package/dist/components/collapsible/index.js +1 -0
  35. package/dist/components/combobox/Combobox.d.ts +52 -0
  36. package/dist/components/combobox/Combobox.js +92 -0
  37. package/dist/components/combobox/index.d.ts +2 -0
  38. package/dist/components/combobox/index.js +1 -0
  39. package/dist/components/data-table/DataTable.js +2 -2
  40. package/dist/components/date-picker/DatePicker.d.ts +34 -0
  41. package/dist/components/date-picker/DatePicker.js +108 -0
  42. package/dist/components/date-picker/index.d.ts +2 -0
  43. package/dist/components/date-picker/index.js +1 -0
  44. package/dist/components/date-range-picker/DateRangePicker.d.ts +40 -0
  45. package/dist/components/date-range-picker/DateRangePicker.js +146 -0
  46. package/dist/components/date-range-picker/index.d.ts +2 -0
  47. package/dist/components/date-range-picker/index.js +1 -0
  48. package/dist/components/drawer/Drawer.d.ts +52 -0
  49. package/dist/components/drawer/Drawer.js +120 -0
  50. package/dist/components/drawer/index.d.ts +2 -0
  51. package/dist/components/drawer/index.js +1 -0
  52. package/dist/components/field/Field.d.ts +38 -0
  53. package/dist/components/field/Field.js +73 -0
  54. package/dist/components/field/Label.d.ts +8 -0
  55. package/dist/components/field/Label.js +13 -0
  56. package/dist/components/field/index.d.ts +3 -0
  57. package/dist/components/field/index.js +3 -0
  58. package/dist/components/filter-tabs/FilterTabs.js +1 -1
  59. package/dist/components/global-search/GlobalSearch.js +1 -1
  60. package/dist/components/icon/registry.d.ts +2 -2
  61. package/dist/components/icon/registry.js +5 -1
  62. package/dist/components/input-group/InputGroup.js +1 -1
  63. package/dist/components/input-otp/InputOtp.d.ts +43 -0
  64. package/dist/components/input-otp/InputOtp.js +45 -0
  65. package/dist/components/input-otp/index.d.ts +2 -0
  66. package/dist/components/input-otp/index.js +1 -0
  67. package/dist/components/key-value-list/KeyValueList.d.ts +25 -0
  68. package/dist/components/key-value-list/KeyValueList.js +40 -0
  69. package/dist/components/key-value-list/index.d.ts +2 -0
  70. package/dist/components/key-value-list/index.js +1 -0
  71. package/dist/components/link/Link.d.ts +22 -0
  72. package/dist/components/link/Link.js +31 -0
  73. package/dist/components/link/index.d.ts +2 -0
  74. package/dist/components/link/index.js +1 -0
  75. package/dist/components/media-grid/MediaGrid.d.ts +49 -0
  76. package/dist/components/media-grid/MediaGrid.js +303 -0
  77. package/dist/components/media-grid/index.d.ts +2 -0
  78. package/dist/components/media-grid/index.js +1 -0
  79. package/dist/components/money-input/MoneyInput.d.ts +30 -0
  80. package/dist/components/money-input/MoneyInput.js +72 -0
  81. package/dist/components/money-input/index.d.ts +2 -0
  82. package/dist/components/money-input/index.js +1 -0
  83. package/dist/components/page-header/PageHeader.js +9 -9
  84. package/dist/components/progress/Progress.d.ts +25 -0
  85. package/dist/components/progress/Progress.js +38 -0
  86. package/dist/components/progress/index.d.ts +2 -0
  87. package/dist/components/progress/index.js +1 -0
  88. package/dist/components/radio-group/RadioGroup.d.ts +18 -0
  89. package/dist/components/radio-group/RadioGroup.js +32 -0
  90. package/dist/components/radio-group/index.d.ts +2 -0
  91. package/dist/components/radio-group/index.js +1 -0
  92. package/dist/components/save-bar/SaveBar.d.ts +27 -0
  93. package/dist/components/save-bar/SaveBar.js +37 -0
  94. package/dist/components/save-bar/index.d.ts +2 -0
  95. package/dist/components/save-bar/index.js +1 -0
  96. package/dist/components/scroll-area/ScrollArea.d.ts +12 -0
  97. package/dist/components/scroll-area/ScrollArea.js +34 -0
  98. package/dist/components/scroll-area/index.d.ts +2 -0
  99. package/dist/components/scroll-area/index.js +1 -0
  100. package/dist/components/search-field/SearchField.js +1 -1
  101. package/dist/components/select/Select.d.ts +40 -0
  102. package/dist/components/select/Select.js +84 -0
  103. package/dist/components/select/index.d.ts +2 -0
  104. package/dist/components/select/index.js +1 -0
  105. package/dist/components/separator/Separator.d.ts +15 -0
  106. package/dist/components/separator/Separator.js +25 -0
  107. package/dist/components/separator/index.d.ts +2 -0
  108. package/dist/components/separator/index.js +1 -0
  109. package/dist/components/sheet/Sheet.d.ts +52 -0
  110. package/dist/components/sheet/Sheet.js +89 -0
  111. package/dist/components/sheet/index.d.ts +2 -0
  112. package/dist/components/sheet/index.js +1 -0
  113. package/dist/components/side-nav/SideNav.js +3 -3
  114. package/dist/components/summary-chips/SummaryChips.d.ts +17 -0
  115. package/dist/components/summary-chips/SummaryChips.js +19 -0
  116. package/dist/components/summary-chips/index.d.ts +2 -0
  117. package/dist/components/summary-chips/index.js +1 -0
  118. package/dist/components/switch/Switch.d.ts +10 -0
  119. package/dist/components/switch/Switch.js +20 -0
  120. package/dist/components/switch/index.d.ts +2 -0
  121. package/dist/components/switch/index.js +1 -0
  122. package/dist/components/table/Table.js +1 -1
  123. package/dist/components/tag-input/TagInput.d.ts +44 -0
  124. package/dist/components/tag-input/TagInput.js +102 -0
  125. package/dist/components/tag-input/index.d.ts +2 -0
  126. package/dist/components/tag-input/index.js +1 -0
  127. package/dist/components/textarea/Textarea.d.ts +15 -0
  128. package/dist/components/textarea/Textarea.js +21 -0
  129. package/dist/components/textarea/index.d.ts +2 -0
  130. package/dist/components/textarea/index.js +1 -0
  131. package/dist/components/top-bar/StoreSwitcher.js +1 -1
  132. package/dist/components/top-bar/TopBar.js +2 -2
  133. package/dist/components/unit-input/UnitInput.d.ts +33 -0
  134. package/dist/components/unit-input/UnitInput.js +47 -0
  135. package/dist/components/unit-input/index.d.ts +2 -0
  136. package/dist/components/unit-input/index.js +1 -0
  137. package/dist/index.d.ts +70 -9
  138. package/dist/index.js +38 -7
  139. package/dist/lib/cn.js +9 -2
  140. package/dist/lib/format.d.ts +65 -4
  141. package/dist/lib/format.js +163 -6
  142. package/dist/styles.css +1 -1
  143. package/package.json +129 -1
@@ -0,0 +1,38 @@
1
+ import { JSX, ReactElement, ReactNode } from "react";
2
+ /** The props of Field. */
3
+ export type FieldProps = {
4
+ /** The label text. */
5
+ label: ReactNode;
6
+ /** Help text under the control. An error replaces it. */
7
+ hint?: ReactNode;
8
+ /** The error message. It shows under the control beside the alert glyph and marks the control invalid. */
9
+ error?: ReactNode;
10
+ /** An explanation of the field, opened from a small info icon beside the label by keyboard, touch or hover. */
11
+ info?: ReactNode;
12
+ /** Shows a star after the label and marks the control aria-required. */
13
+ required?: boolean;
14
+ /** Dims the label and disables the control. */
15
+ disabled?: boolean;
16
+ /**
17
+ * The control: one element, such as Input, which gets an id, aria-labelledby, aria-describedby, aria-invalid,
18
+ * aria-required and disabled. When the control sits inside a wrapper such as InputGroup, pass a function instead and
19
+ * spread the props it receives onto the input itself.
20
+ */
21
+ children: ReactElement<Record<string, unknown>> | ((props: FieldControlProps) => ReactNode);
22
+ };
23
+ /** The props Field hands to a function child, to spread onto the control. */
24
+ export type FieldControlProps = {
25
+ id: string;
26
+ /** The label's id, so a control that a label cannot point at with htmlFor, such as RadioGroup, still has a name. */
27
+ 'aria-labelledby'?: string;
28
+ 'aria-describedby'?: string;
29
+ 'aria-invalid'?: true;
30
+ 'aria-required'?: true;
31
+ disabled?: true;
32
+ };
33
+ /**
34
+ * A form row: label, control, hint and error. It wires the label to the control by id and aria-labelledby (which also
35
+ * names a group such as RadioGroup), links the hint and error with aria-describedby, and sets aria-invalid when there is
36
+ * an error. The control keeps an id it already has.
37
+ */
38
+ export declare function Field({ label, hint, error, info, required, disabled, children }: FieldProps): JSX.Element;
@@ -0,0 +1,73 @@
1
+ import { Icon } from "../icon/Icon.js";
2
+ import { Definition } from "../definition/Definition.js";
3
+ import { Label } from "./Label.js";
4
+ import { jsx, jsxs } from "react/jsx-runtime";
5
+ import { cloneElement, useId } from "react";
6
+ // src/components/field/Field.tsx
7
+ /**
8
+ * A form row: label, control, hint and error. It wires the label to the control by id and aria-labelledby (which also
9
+ * names a group such as RadioGroup), links the hint and error with aria-describedby, and sets aria-invalid when there is
10
+ * an error. The control keeps an id it already has.
11
+ */
12
+ function Field({ label, hint, error, info, required, disabled, children }) {
13
+ const generatedId = useId();
14
+ const own = typeof children === "function" ? void 0 : children.props.id;
15
+ const id = typeof own === "string" && own !== "" ? own : generatedId;
16
+ const hasError = error !== void 0 && error !== null && error !== false && error !== "";
17
+ const hasHint = !hasError && hint !== void 0 && hint !== null && hint !== false && hint !== "";
18
+ const messageId = `${id}-message`;
19
+ const labelId = `${id}-label`;
20
+ const ownProp = (name) => typeof children === "function" ? void 0 : children.props[name];
21
+ const ids = (...parts) => parts.filter((part) => typeof part === "string" && part !== "").join(" ");
22
+ const described = ids(ownProp("aria-describedby"), hasError || hasHint ? messageId : void 0);
23
+ const wiring = {
24
+ id,
25
+ "aria-labelledby": ids(labelId, ownProp("aria-labelledby")),
26
+ ...described === "" ? {} : { "aria-describedby": described },
27
+ ...hasError ? { "aria-invalid": true } : {},
28
+ ...required === true ? { "aria-required": true } : {},
29
+ ...disabled === true ? { disabled: true } : {}
30
+ };
31
+ const control = typeof children === "function" ? children(wiring) : cloneElement(children, { ...wiring });
32
+ return /* @__PURE__ */ jsxs("div", {
33
+ "data-slot": "field",
34
+ className: "rt:flex rt:w-full rt:flex-col rt:gap-1.5",
35
+ children: [
36
+ /* @__PURE__ */ jsxs("div", {
37
+ className: `rt:flex rt:items-center rt:gap-1.5${disabled === true ? " rt:opacity-50" : ""}`,
38
+ children: [/* @__PURE__ */ jsxs(Label, {
39
+ id: labelId,
40
+ htmlFor: id,
41
+ children: [label, required === true ? /* @__PURE__ */ jsx("span", {
42
+ "aria-hidden": "true",
43
+ className: "rt:text-foreground-subdued",
44
+ children: "*"
45
+ }) : null]
46
+ }), info === void 0 || info === null || info === false || info === "" ? null : /* @__PURE__ */ jsx(Definition, {
47
+ definition: info,
48
+ children: /* @__PURE__ */ jsx(Icon, {
49
+ name: "info-circle",
50
+ tone: "subdued",
51
+ label: "More information"
52
+ })
53
+ })]
54
+ }),
55
+ control,
56
+ hasError ? /* @__PURE__ */ jsxs("p", {
57
+ id: messageId,
58
+ role: "alert",
59
+ className: "rt:flex rt:items-start rt:gap-1.5 rt:text-b1 rt:text-error",
60
+ children: [/* @__PURE__ */ jsx("span", {
61
+ className: "rt:mt-1 rt:text-error-icon",
62
+ children: /* @__PURE__ */ jsx(Icon, { name: "alert-triangle" })
63
+ }), /* @__PURE__ */ jsx("span", { children: error })]
64
+ }) : null,
65
+ hasHint ? /* @__PURE__ */ jsx("p", {
66
+ id: messageId,
67
+ className: "rt:text-b1 rt:text-foreground-subdued",
68
+ children: hint
69
+ }) : null
70
+ ]
71
+ });
72
+ }
73
+ export { Field };
@@ -0,0 +1,8 @@
1
+ import { JSX, LabelHTMLAttributes, Ref } from "react";
2
+ /** The props of Label: the HTML attributes of a label, except className and style. */
3
+ export type LabelProps = Omit<LabelHTMLAttributes<HTMLLabelElement>, 'className' | 'style'> & {
4
+ /** Receives the label element. */
5
+ ref?: Ref<HTMLLabelElement>;
6
+ };
7
+ /** The text that names a control, in the 12px medium label type. Point htmlFor at the control's id; Field does this for you. */
8
+ export declare function Label({ htmlFor, ref, ...rest }: LabelProps): JSX.Element;
@@ -0,0 +1,13 @@
1
+ import { jsx } from "react/jsx-runtime";
2
+ // src/components/field/Label.tsx
3
+ /** The text that names a control, in the 12px medium label type. Point htmlFor at the control's id; Field does this for you. */
4
+ function Label({ htmlFor, ref, ...rest }) {
5
+ return /* @__PURE__ */ jsx("label", {
6
+ "data-slot": "label",
7
+ htmlFor,
8
+ ref,
9
+ className: "rt:flex rt:w-fit rt:items-center rt:gap-1 rt:text-b2 rt:text-foreground rt:select-none",
10
+ ...rest
11
+ });
12
+ }
13
+ export { Label };
@@ -0,0 +1,3 @@
1
+ import { Field, FieldControlProps, FieldProps } from "./Field.js";
2
+ import { Label, LabelProps } from "./Label.js";
3
+ export { Field, type FieldControlProps, type FieldProps, Label, type LabelProps };
@@ -0,0 +1,3 @@
1
+ import { Label } from "./Label.js";
2
+ import { Field } from "./Field.js";
3
+ export { Field, Label };
@@ -1,6 +1,6 @@
1
1
  import { cn } from "../../lib/cn.js";
2
- import { useState } from "react";
3
2
  import { jsx } from "react/jsx-runtime";
3
+ import { useState } from "react";
4
4
  import { Toggle } from "@base-ui/react/toggle";
5
5
  import { ToggleGroup } from "@base-ui/react/toggle-group";
6
6
  // src/components/filter-tabs/FilterTabs.tsx
@@ -2,8 +2,8 @@ import { Icon } from "../icon/Icon.js";
2
2
  import { formatCount } from "../../lib/format.js";
3
3
  import { InputGroup, InputGroupAddon, InputGroupShortcut } from "../input-group/InputGroup.js";
4
4
  import { Kbd, KbdGroup } from "../kbd/Kbd.js";
5
- import { useEffect, useRef, useState } from "react";
6
5
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
6
+ import { useEffect, useRef, useState } from "react";
7
7
  import { Autocomplete } from "@base-ui/react/autocomplete";
8
8
  // src/components/global-search/GlobalSearch.tsx
9
9
  /**
@@ -2,6 +2,6 @@ import "@tabler/icons-react";
2
2
  /**
3
3
  * The name of an icon. The design system's own glyphs come first (plus, search, export, the two pagination chevrons and
4
4
  * the five navigation glyphs); the rest are Tabler icons drawn with a 1.5px line: check, x, alert-triangle, info-circle,
5
- * the dashboard's navigation glyphs (nav-orders to nav-settings), import, more and menu (the phone menu button).
5
+ * the dashboard's navigation glyphs (nav-orders to nav-settings), import, more and menu (the phone menu button) and calendar (the date pickers' open button).
6
6
  */
7
- export type IconName = 'plus' | 'search' | 'export' | 'chevron-left' | 'chevron-right' | 'nav-home' | 'nav-shortcut' | 'nav-product' | 'nav-product-2' | 'nav-chevron' | 'nav-caret-down' | 'check' | 'x' | 'alert-triangle' | 'info-circle' | 'nav-orders' | 'nav-customers' | 'nav-growth' | 'nav-discounts' | 'nav-content' | 'nav-markets' | 'nav-analytics' | 'nav-sales-channels' | 'nav-apps' | 'nav-settings' | 'import' | 'more' | 'menu';
7
+ export type IconName = 'plus' | 'search' | 'export' | 'chevron-left' | 'chevron-right' | 'nav-home' | 'nav-shortcut' | 'nav-product' | 'nav-product-2' | 'nav-chevron' | 'nav-caret-down' | 'check' | 'x' | 'alert-triangle' | 'info-circle' | 'nav-orders' | 'nav-customers' | 'nav-growth' | 'nav-discounts' | 'nav-content' | 'nav-markets' | 'nav-analytics' | 'nav-sales-channels' | 'nav-apps' | 'nav-settings' | 'import' | 'more' | 'menu' | 'calendar';
@@ -1,5 +1,5 @@
1
1
  import { CHEVRON_LEFT, CHEVRON_RIGHT, EXPORT, NAV_CHEVRON, NAV_HOME, NAV_PRODUCT, NAV_PRODUCT_2, NAV_SHORTCUT, PLUS, SEARCH } from "./glyphs.js";
2
- import { IconAlertTriangle, IconApps, IconBuildingStore, IconChartBar, IconCheck, IconDiscount2, IconDots, IconFileImport, IconFileText, IconInfoCircle, IconMenu2, IconReceipt, IconSettings, IconTrendingUp, IconUsers, IconWorld, IconX } from "@tabler/icons-react";
2
+ import { IconAlertTriangle, IconApps, IconBuildingStore, IconCalendar, IconChartBar, IconCheck, IconDiscount2, IconDots, IconFileImport, IconFileText, IconInfoCircle, IconMenu2, IconReceipt, IconSettings, IconTrendingUp, IconUsers, IconWorld, IconX } from "@tabler/icons-react";
3
3
  // src/components/icon/registry.ts
4
4
  /**
5
5
  * Every icon by name. nav-chevron and nav-caret-down are one glyph under two names, as in the design system, whose
@@ -117,6 +117,10 @@ const ICONS = {
117
117
  menu: {
118
118
  kind: "tabler",
119
119
  component: IconMenu2
120
+ },
121
+ calendar: {
122
+ kind: "tabler",
123
+ component: IconCalendar
120
124
  }
121
125
  };
122
126
  export { ICONS };
@@ -1,6 +1,6 @@
1
1
  import { cn } from "../../lib/cn.js";
2
- import { useEffect, useRef } from "react";
3
2
  import { jsx } from "react/jsx-runtime";
3
+ import { useEffect, useRef } from "react";
4
4
  import { cva } from "class-variance-authority";
5
5
  import { Input } from "@base-ui/react/input";
6
6
  // src/components/input-group/InputGroup.tsx
@@ -0,0 +1,43 @@
1
+ import { JSX, Ref } from "react";
2
+ /**
3
+ * The props of InputOtp. Mark a wrong code with aria-invalid, which draws the error ring on every slot. Inside a Field,
4
+ * spread the props of its function child: they reach the hidden input.
5
+ */
6
+ export type InputOtpProps = {
7
+ /** The number of digits. Defaults to 6. */
8
+ length?: number;
9
+ /** The code typed so far, for a controlled field. */
10
+ value?: string;
11
+ /** The code typed so far, for an uncontrolled field. */
12
+ defaultValue?: string;
13
+ /** Called with the whole code on every change, including a paste. */
14
+ onValueChange?: (value: string) => void;
15
+ /** Called once with the code when every slot is filled. */
16
+ onComplete?: (value: string) => void;
17
+ /** Stops editing and halves the opacity. */
18
+ disabled?: boolean;
19
+ /** Draws the error ring on every slot. */
20
+ 'aria-invalid'?: boolean | 'true' | 'false';
21
+ /** The accessible name, for example "6-digit code". */
22
+ 'aria-label'?: string;
23
+ /** The id of an element that names the field. */
24
+ 'aria-labelledby'?: string;
25
+ /** The ids of the elements that describe the field, such as its hint or error. Field sets it. */
26
+ 'aria-describedby'?: string;
27
+ /** Tells assistive technology that the code must be filled. Field sets it for a required field. */
28
+ 'aria-required'?: boolean | 'true' | 'false';
29
+ /** The id of the hidden input, for a label's htmlFor. Field sets it. */
30
+ id?: string;
31
+ /** The form field name. */
32
+ name?: string;
33
+ /** Focuses the field on mount. */
34
+ autoFocus?: boolean;
35
+ /** Receives the hidden input element. */
36
+ ref?: Ref<HTMLInputElement>;
37
+ };
38
+ /**
39
+ * A one-time-code field: six 44px digit slots (set `length` for another count) over one hidden input, so the browser
40
+ * offers the SMS code (autocomplete one-time-code), a pasted code fills every slot, and the numeric keypad opens on phones.
41
+ * Digits only.
42
+ */
43
+ export declare function InputOtp({ length, ref, disabled, onValueChange, ...rest }: InputOtpProps): JSX.Element;
@@ -0,0 +1,45 @@
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
+ import { use } from "react";
3
+ import { OTPInput, OTPInputContext, REGEXP_ONLY_DIGITS } from "input-otp";
4
+ // src/components/input-otp/InputOtp.tsx
5
+ const slotClass = "rt:relative rt:flex rt:size-11 rt:items-center rt:justify-center rt:rounded-lg rt:bg-background rt:text-otp rt:text-foreground rt:shadow-ring-subtle rt:transition-colors rt:forced-outline rt:data-active:shadow-ring-focus rt:aria-invalid:shadow-ring-invalid rt:aria-invalid:data-active:shadow-ring-invalid";
6
+ function Slot({ index, invalid }) {
7
+ const { char, hasFakeCaret, isActive } = use(OTPInputContext).slots[index] ?? {};
8
+ return /* @__PURE__ */ jsxs("div", {
9
+ "data-slot": "input-otp-slot",
10
+ "data-active": isActive ? "" : void 0,
11
+ "aria-invalid": invalid || void 0,
12
+ className: slotClass,
13
+ children: [char, hasFakeCaret ? /* @__PURE__ */ jsx("div", {
14
+ className: "rt:pointer-events-none rt:absolute rt:inset-0 rt:flex rt:items-center rt:justify-center",
15
+ children: /* @__PURE__ */ jsx("div", { className: "rt:h-5 rt:w-px rt:bg-foreground" })
16
+ }) : null]
17
+ });
18
+ }
19
+ /**
20
+ * A one-time-code field: six 44px digit slots (set `length` for another count) over one hidden input, so the browser
21
+ * offers the SMS code (autocomplete one-time-code), a pasted code fills every slot, and the numeric keypad opens on phones.
22
+ * Digits only.
23
+ */
24
+ function InputOtp({ length = 6, ref, disabled, onValueChange, ...rest }) {
25
+ return /* @__PURE__ */ jsx(OTPInput, {
26
+ "data-slot": "input-otp",
27
+ ref,
28
+ maxLength: length,
29
+ pattern: REGEXP_ONLY_DIGITS,
30
+ pasteTransformer: (pasted) => pasted.replace(/\D/g, ""),
31
+ inputMode: "numeric",
32
+ autoComplete: "one-time-code",
33
+ spellCheck: false,
34
+ disabled,
35
+ onChange: onValueChange,
36
+ containerClassName: "rt:flex rt:items-center rt:gap-2 rt:has-disabled:opacity-50",
37
+ className: "rt:disabled:cursor-not-allowed",
38
+ ...rest,
39
+ children: Array.from({ length }, (_, index) => /* @__PURE__ */ jsx(Slot, {
40
+ index,
41
+ invalid: rest["aria-invalid"] === true || rest["aria-invalid"] === "true"
42
+ }, index))
43
+ });
44
+ }
45
+ export { InputOtp };
@@ -0,0 +1,2 @@
1
+ import { InputOtp, InputOtpProps } from "./InputOtp.js";
2
+ export { InputOtp, type InputOtpProps };
@@ -0,0 +1 @@
1
+ export { InputOtp } from "./InputOtp.js";
@@ -0,0 +1,25 @@
1
+ import { JSX, ReactNode } from "react";
2
+ /** One row of a KeyValueList. */
3
+ export type KeyValueItem = {
4
+ /** The label, shown on the left. It is the row key, so keep labels unique. */
5
+ label: string;
6
+ /** The value, shown on the right, already formatted (money and dates come from the format module). Empty shows the placeholder. */
7
+ value?: ReactNode;
8
+ /** An optional action, such as Edit, shown after the value. Its button is named with the action label and the row label. */
9
+ action?: {
10
+ label: string;
11
+ onClick: () => void;
12
+ };
13
+ };
14
+ /** The props of KeyValueList. */
15
+ export type KeyValueListProps = {
16
+ /** The rows, in order. */
17
+ items: KeyValueItem[];
18
+ /** What an empty value shows. Defaults to "Not set". */
19
+ emptyValue?: string;
20
+ };
21
+ /**
22
+ * Settings rows in a hairline bordered panel: the label on the left, the value on the right and an optional action. It is
23
+ * a description list, so a screen reader announces each label with its value.
24
+ */
25
+ export declare function KeyValueList({ items, emptyValue }: KeyValueListProps): JSX.Element;
@@ -0,0 +1,40 @@
1
+ import { Text } from "../text/Text.js";
2
+ import { Button } from "../button/Button.js";
3
+ import { jsx, jsxs } from "react/jsx-runtime";
4
+ // src/components/key-value-list/KeyValueList.tsx
5
+ /**
6
+ * Settings rows in a hairline bordered panel: the label on the left, the value on the right and an optional action. It is
7
+ * a description list, so a screen reader announces each label with its value.
8
+ */
9
+ function KeyValueList({ items, emptyValue = "Not set" }) {
10
+ return /* @__PURE__ */ jsx("dl", {
11
+ className: "rt:m-0 rt:overflow-hidden rt:rounded-lg rt:border rt:border-border rt:bg-background",
12
+ children: items.map((item) => {
13
+ const empty = item.value === void 0 || item.value === null || item.value === "";
14
+ return /* @__PURE__ */ jsxs("div", {
15
+ className: "rt:flex rt:min-h-11 rt:items-center rt:justify-between rt:gap-3 rt:border-border rt:px-3 rt:py-2 rt:not-first:border-t",
16
+ children: [/* @__PURE__ */ jsx(Text, {
17
+ as: "dt",
18
+ variant: "b3",
19
+ tone: "subdued",
20
+ children: item.label
21
+ }), /* @__PURE__ */ jsxs("dd", {
22
+ className: "rt:m-0 rt:flex rt:min-w-0 rt:items-center rt:justify-end rt:gap-3 rt:text-right",
23
+ children: [/* @__PURE__ */ jsx(Text, {
24
+ variant: "b3",
25
+ tone: empty ? "muted" : "default",
26
+ children: empty ? emptyValue : item.value
27
+ }), item.action && /* @__PURE__ */ jsxs(Button, {
28
+ variant: "link",
29
+ onClick: item.action.onClick,
30
+ children: [item.action.label, /* @__PURE__ */ jsxs("span", {
31
+ className: "rt:sr-only",
32
+ children: [" ", item.label]
33
+ })]
34
+ })]
35
+ })]
36
+ }, item.label);
37
+ })
38
+ });
39
+ }
40
+ export { KeyValueList };
@@ -0,0 +1,2 @@
1
+ import { KeyValueItem, KeyValueList, KeyValueListProps } from "./KeyValueList.js";
2
+ export { type KeyValueItem, KeyValueList, type KeyValueListProps };
@@ -0,0 +1 @@
1
+ export { KeyValueList } from "./KeyValueList.js";
@@ -0,0 +1,22 @@
1
+ import { AnchorHTMLAttributes, JSX, ReactNode, Ref } from "react";
2
+ /**
3
+ * The props of Link: the attributes of an anchor, except className and style, with the address and the two arrows.
4
+ */
5
+ export type LinkProps = Omit<AnchorHTMLAttributes<HTMLAnchorElement>, 'className' | 'style' | 'href' | 'children'> & {
6
+ /** Where the link goes. */
7
+ href: string;
8
+ /** The link text. */
9
+ children: ReactNode;
10
+ /** Shows a back arrow before the text, as in "← Products". Screen readers skip it. */
11
+ back?: boolean;
12
+ /** Shows a forward arrow after the text, as in "View all variants →". Screen readers skip it. */
13
+ forward?: boolean;
14
+ /** Receives the anchor element. */
15
+ ref?: Ref<HTMLAnchorElement>;
16
+ };
17
+ /**
18
+ * A text link to another page, such as "← Products" or "View all variants →". It is drawn by the app's LinkProvider, so a
19
+ * router navigates without a reload; without one it is a plain <a href>. The arrows are decoration and are hidden from
20
+ * screen readers.
21
+ */
22
+ export declare function Link({ href, children, back, forward, ref, ...rest }: LinkProps): JSX.Element;
@@ -0,0 +1,31 @@
1
+ import { cn } from "../../lib/cn.js";
2
+ import { RatioLink } from "../link-provider/RatioLink.js";
3
+ import { jsx, jsxs } from "react/jsx-runtime";
4
+ // src/components/link/Link.tsx
5
+ const LINK = "rt:inline-flex rt:items-center rt:gap-1 rt:rounded-sm rt:text-primary rt:transition-colors rt:hover:underline rt:focus-visible:focus-ring";
6
+ /**
7
+ * A text link to another page, such as "← Products" or "View all variants →". It is drawn by the app's LinkProvider, so a
8
+ * router navigates without a reload; without one it is a plain <a href>. The arrows are decoration and are hidden from
9
+ * screen readers.
10
+ */
11
+ function Link({ href, children, back = false, forward = false, ref, ...rest }) {
12
+ return /* @__PURE__ */ jsxs(RatioLink, {
13
+ ...rest,
14
+ ref,
15
+ href,
16
+ "data-slot": "link",
17
+ className: cn(LINK),
18
+ children: [
19
+ back && /* @__PURE__ */ jsx("span", {
20
+ "aria-hidden": "true",
21
+ children: "←"
22
+ }),
23
+ children,
24
+ forward && /* @__PURE__ */ jsx("span", {
25
+ "aria-hidden": "true",
26
+ children: "→"
27
+ })
28
+ ]
29
+ });
30
+ }
31
+ export { Link };
@@ -0,0 +1,2 @@
1
+ import { Link, LinkProps } from "./Link.js";
2
+ export { Link, type LinkProps };
@@ -0,0 +1 @@
1
+ export { Link } from "./Link.js";
@@ -0,0 +1,49 @@
1
+ import { JSX } from "react";
2
+ import { Accept } from "react-dropzone";
3
+ /** One image in a MediaGrid. */
4
+ export type MediaGridItem = {
5
+ /** A stable id, reported back by onReorder, onRemove and onMakeCover. */
6
+ id: string;
7
+ /** The image URL, or an object URL while the file uploads. */
8
+ src: string;
9
+ /** The text alternative. It also names the item in the action group and the announcements. */
10
+ alt: string;
11
+ /** Upload progress from 0 to 100. A thin bar shows on the thumb while it is below 100. */
12
+ progress?: number;
13
+ /** Why the upload failed, shown under the thumb beside the warning glyph; the thumb takes the error ring. */
14
+ error?: string;
15
+ };
16
+ /** The props of MediaGrid. The app uploads the files and owns the list; the grid reports what the person did. */
17
+ export type MediaGridProps = {
18
+ /** The images in order. The first one is the cover. */
19
+ items: MediaGridItem[];
20
+ /** Called with the files chosen in the add tile or dropped on the grid. */
21
+ onAdd: (files: File[]) => void;
22
+ /** Called with every id in the new order after a drag, Move left, Move right or Make cover. */
23
+ onReorder: (ids: string[]) => void;
24
+ /** Called with the id of the item to take off the grid. */
25
+ onRemove: (id: string) => void;
26
+ /** Called with the id after Make cover has moved it first through onReorder, for an app that saves the cover on its own. */
27
+ onMakeCover?: (id: string) => void;
28
+ /** The file types the add tile takes, as react-dropzone's accept map. Defaults to every image type. */
29
+ accept?: Accept;
30
+ /** Stops adding, dragging and every button. */
31
+ disabled?: boolean;
32
+ /** The accessible name of the add tile. Defaults to "Add media". */
33
+ addLabel?: string;
34
+ /** The id of the grid. */
35
+ id?: string;
36
+ /** The accessible name of the grid. Defaults to "Media" when there is no aria-labelledby. */
37
+ 'aria-label'?: string;
38
+ /** The id of the visible label that names the grid, such as a Field label. */
39
+ 'aria-labelledby'?: string;
40
+ /** The id of help text for the grid. */
41
+ 'aria-describedby'?: string;
42
+ };
43
+ /**
44
+ * The media of a product: a row of square thumbs with an add tile that opens the file picker and takes dropped files.
45
+ * Thumbs reorder by drag (pointer, touch or keyboard) and by the Move left, Move right and Make cover buttons, so no
46
+ * drag is needed. The first image is the cover and carries a badge. Uploads show a thin progress bar and failures an
47
+ * error under the thumb. Every move, cover change and removal is announced to screen readers.
48
+ */
49
+ export declare function MediaGrid({ items, onAdd, onReorder, onRemove, onMakeCover, accept, disabled, addLabel, 'aria-label': ariaLabel, 'aria-labelledby': labelledBy, ...rest }: MediaGridProps): JSX.Element;