@ratio-ui/core 0.3.0 → 0.5.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 (96) hide show
  1. package/dist/catalogue.json +935 -75
  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.js +4 -3
  7. package/dist/components/breadcrumb/Breadcrumb.d.ts +19 -0
  8. package/dist/components/breadcrumb/Breadcrumb.js +50 -0
  9. package/dist/components/breadcrumb/index.d.ts +2 -0
  10. package/dist/components/breadcrumb/index.js +1 -0
  11. package/dist/components/button/Button.js +6 -6
  12. package/dist/components/calendar/Calendar.js +2 -2
  13. package/dist/components/chart/Chart.d.ts +137 -0
  14. package/dist/components/chart/Chart.js +238 -0
  15. package/dist/components/chart/index.d.ts +2 -0
  16. package/dist/components/chart/index.js +1 -0
  17. package/dist/components/checkbox/Checkbox.js +1 -1
  18. package/dist/components/combobox/Combobox.d.ts +52 -0
  19. package/dist/components/combobox/Combobox.js +92 -0
  20. package/dist/components/combobox/index.d.ts +2 -0
  21. package/dist/components/combobox/index.js +1 -0
  22. package/dist/components/data-table/DataTable.js +12 -18
  23. package/dist/components/date-picker/DatePicker.d.ts +3 -2
  24. package/dist/components/date-picker/DatePicker.js +7 -3
  25. package/dist/components/date-range-picker/DateRangePicker.d.ts +3 -2
  26. package/dist/components/date-range-picker/DateRangePicker.js +7 -3
  27. package/dist/components/definition/Definition.js +1 -1
  28. package/dist/components/dialog/Dialog.js +1 -1
  29. package/dist/components/drawer/Drawer.d.ts +52 -0
  30. package/dist/components/drawer/Drawer.js +120 -0
  31. package/dist/components/drawer/index.d.ts +2 -0
  32. package/dist/components/drawer/index.js +1 -0
  33. package/dist/components/field/Field.d.ts +8 -5
  34. package/dist/components/field/Field.js +9 -3
  35. package/dist/components/icon/Icon.d.ts +1 -1
  36. package/dist/components/icon/Icon.js +16 -5
  37. package/dist/components/icon/glyphs.js +19 -8
  38. package/dist/components/icon/registry.d.ts +3 -2
  39. package/dist/components/icon/registry.js +14 -2
  40. package/dist/components/input/Input.js +1 -1
  41. package/dist/components/input-group/InputGroup.d.ts +3 -1
  42. package/dist/components/input-group/InputGroup.js +20 -8
  43. package/dist/components/input-otp/InputOtp.d.ts +43 -0
  44. package/dist/components/input-otp/InputOtp.js +45 -0
  45. package/dist/components/input-otp/index.d.ts +2 -0
  46. package/dist/components/input-otp/index.js +1 -0
  47. package/dist/components/key-value-list/KeyValueList.d.ts +25 -0
  48. package/dist/components/key-value-list/KeyValueList.js +40 -0
  49. package/dist/components/key-value-list/index.d.ts +2 -0
  50. package/dist/components/key-value-list/index.js +1 -0
  51. package/dist/components/link/Link.js +1 -1
  52. package/dist/components/metric-strip/MetricStrip.js +1 -1
  53. package/dist/components/page-header/PageHeader.js +8 -8
  54. package/dist/components/pagination/Pagination.js +1 -1
  55. package/dist/components/progress/Progress.d.ts +25 -0
  56. package/dist/components/progress/Progress.js +38 -0
  57. package/dist/components/progress/index.d.ts +2 -0
  58. package/dist/components/progress/index.js +1 -0
  59. package/dist/components/radio-group/RadioGroup.d.ts +18 -0
  60. package/dist/components/radio-group/RadioGroup.js +32 -0
  61. package/dist/components/radio-group/index.d.ts +2 -0
  62. package/dist/components/radio-group/index.js +1 -0
  63. package/dist/components/scroll-area/ScrollArea.d.ts +12 -0
  64. package/dist/components/scroll-area/ScrollArea.js +34 -0
  65. package/dist/components/scroll-area/index.d.ts +2 -0
  66. package/dist/components/scroll-area/index.js +1 -0
  67. package/dist/components/search-field/SearchField.js +8 -4
  68. package/dist/components/select/Select.js +1 -1
  69. package/dist/components/separator/Separator.d.ts +15 -0
  70. package/dist/components/separator/Separator.js +25 -0
  71. package/dist/components/separator/index.d.ts +2 -0
  72. package/dist/components/separator/index.js +1 -0
  73. package/dist/components/sheet/Sheet.d.ts +52 -0
  74. package/dist/components/sheet/Sheet.js +89 -0
  75. package/dist/components/sheet/index.d.ts +2 -0
  76. package/dist/components/sheet/index.js +1 -0
  77. package/dist/components/side-nav/SideNav.js +15 -10
  78. package/dist/components/status-pill/StatusPill.js +1 -1
  79. package/dist/components/summary-chips/SummaryChips.js +1 -1
  80. package/dist/components/switch/Switch.js +2 -2
  81. package/dist/components/table/Table.d.ts +1 -1
  82. package/dist/components/table/Table.js +24 -3
  83. package/dist/components/tag-input/TagInput.js +1 -1
  84. package/dist/components/text/Text.d.ts +3 -2
  85. package/dist/components/text/Text.js +3 -1
  86. package/dist/components/textarea/Textarea.js +1 -1
  87. package/dist/components/toaster/Toaster.js +1 -1
  88. package/dist/components/tooltip/Tooltip.d.ts +1 -1
  89. package/dist/components/tooltip/Tooltip.js +2 -2
  90. package/dist/components/top-bar/TopBar.d.ts +2 -4
  91. package/dist/components/top-bar/TopBar.js +2 -7
  92. package/dist/index.d.ts +28 -4
  93. package/dist/index.js +13 -1
  94. package/dist/lib/cn.js +3 -1
  95. package/dist/styles.css +1 -1
  96. package/package.json +52 -1
@@ -72,24 +72,35 @@ const NAV_CHEVRON = {
72
72
  }]
73
73
  };
74
74
  /**
75
- * The right chevron. The design system draws its pagination chevrons as a 1.5px CSS caret in an 8px box and has no
76
- * vector for them (design question 9), so this is a stand-in, to be replaced when the designer sends real vectors. It
77
- * is the outline of a line 1.5 units wide with round ends: two legs at 45 degrees, each 2.5 units across and 2.5
78
- * down, from (2.75, 1.5) through (5.25, 4) to (2.75, 6.5) in an 8 unit box, which centres the caret in the box.
75
+ * The right chevron. The design system draws its pagination chevrons as a CSS caret in an 8px box and has no vector for
76
+ * them (design question 9): a 4.48px square with 1.5px top and right borders, rotated 45 degrees, square ends, about 8.5px
77
+ * tall. This is that caret as a path. The borders are an L of 1.5 units round the square's 5.98 unit outside (the corners
78
+ * (0,0), (5.98,0), (5.98,5.98), (4.48,5.98), (4.48,1.5), (0,1.5)), rotated 45 degrees about the square's centre, which
79
+ * sits at (3.5, 4) in the 8 unit box: the design system's margin-left of -1 moves the centred caret half a unit left. The
80
+ * caret reaches past the box (y from -0.23 to 8.23), as the CSS one does, so Icon lets a glyph overflow.
79
81
  */
80
82
  const CHEVRON_RIGHT = {
81
83
  viewBox: "0 0 8 8",
82
84
  paths: [{
83
- d: "M3.2803 0.9697L5.7803 3.4697A0.75 0.75 0 0 1 5.7803 4.5303L3.2803 7.0303A0.75 0.75 0 0 1 2.2197 5.9697L4.1893 4L2.2197 2.0303A0.75 0.75 0 0 1 3.2803 0.9697Z",
85
+ d: "M3.5000 -0.2285L7.7285 4.0000L3.5000 8.2285L2.4393 7.1678L5.6072 4.0000L2.4393 0.8322Z",
84
86
  fillRule: "nonzero"
85
87
  }]
86
88
  };
87
- /** The left chevron: the right chevron mirrored in x = 4. */
89
+ /** The left chevron: the right chevron mirrored in x = 4, as the design system's 225 degree rotation and margin-left of 1 do. */
88
90
  const CHEVRON_LEFT = {
89
91
  viewBox: "0 0 8 8",
90
92
  paths: [{
91
- d: "M4.7197 0.9697L2.2197 3.4697A0.75 0.75 0 0 0 2.2197 4.5303L4.7197 7.0303A0.75 0.75 0 0 0 5.7803 5.9697L3.8107 4L5.7803 2.0303A0.75 0.75 0 0 0 4.7197 0.9697Z",
93
+ d: "M4.5000 -0.2285L0.2715 4.0000L4.5000 8.2285L5.5607 7.1678L2.3928 4.0000L5.5607 0.8322Z",
92
94
  fillRule: "nonzero"
93
95
  }]
94
96
  };
95
- export { CHEVRON_LEFT, CHEVRON_RIGHT, EXPORT, NAV_CHEVRON, NAV_HOME, NAV_PRODUCT, NAV_PRODUCT_2, NAV_SHORTCUT, PLUS, SEARCH };
97
+ /** The check mark of the design system's checkbox (Listing.jsx): the path M1 4.2 L3 6.2 L7 1.8 in an 8 unit box, a 1.4 line with round ends. */
98
+ const CHECK_MARK = {
99
+ viewBox: "0 0 8 8",
100
+ paths: [{
101
+ d: "M1 4.2 L3 6.2 L7 1.8",
102
+ fillRule: "nonzero"
103
+ }],
104
+ stroke: 1.4
105
+ };
106
+ export { CHECK_MARK, CHEVRON_LEFT, CHEVRON_RIGHT, EXPORT, NAV_CHEVRON, NAV_HOME, NAV_PRODUCT, NAV_PRODUCT_2, NAV_SHORTCUT, PLUS, SEARCH };
@@ -2,6 +2,7 @@ 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) and calendar (the date pickers' open button).
5
+ * the dashboard's navigation glyphs (nav-orders to nav-settings), import, more and menu (the phone menu button), calendar (the date pickers' open button) and arrow-up and arrow-down (the table's sort direction; the
6
+ * design system has no sort glyph, so Tabler's, question 7). check-mark is the design system's own checkbox check, drawn as a line.
6
7
  */
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';
8
+ 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' | 'check-mark' | 'arrow-up' | 'arrow-down';
@@ -1,5 +1,5 @@
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, IconCalendar, IconChartBar, IconCheck, IconDiscount2, IconDots, IconFileImport, IconFileText, IconInfoCircle, IconMenu2, IconReceipt, IconSettings, IconTrendingUp, IconUsers, IconWorld, IconX } from "@tabler/icons-react";
1
+ import { CHECK_MARK, 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, IconArrowDown, IconArrowUp, 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
@@ -121,6 +121,18 @@ const ICONS = {
121
121
  calendar: {
122
122
  kind: "tabler",
123
123
  component: IconCalendar
124
+ },
125
+ "check-mark": {
126
+ kind: "glyph",
127
+ glyph: CHECK_MARK
128
+ },
129
+ "arrow-up": {
130
+ kind: "tabler",
131
+ component: IconArrowUp
132
+ },
133
+ "arrow-down": {
134
+ kind: "tabler",
135
+ component: IconArrowDown
124
136
  }
125
137
  };
126
138
  export { ICONS };
@@ -3,7 +3,7 @@ import { jsx } from "react/jsx-runtime";
3
3
  import { cva } from "class-variance-authority";
4
4
  import { Input } from "@base-ui/react/input";
5
5
  // src/components/input/Input.tsx
6
- const inputVariants = cva("rt:w-full rt:min-w-0 rt:rounded-lg rt:bg-background rt:text-b3 rt:text-foreground rt:placeholder:text-muted-foreground rt:shadow-ring-subtle rt:outline-none rt:transition-colors rt:forced-outline rt:focus-visible:shadow-ring-focus rt:aria-invalid:shadow-ring-invalid rt:aria-invalid:focus-visible:shadow-ring-invalid rt:disabled:pointer-events-none rt:disabled:cursor-not-allowed rt:disabled:opacity-50", {
6
+ const inputVariants = cva("rt:w-full rt:min-w-0 rt:rounded-lg rt:bg-background rt:text-b3 rt:text-foreground rt:placeholder:text-muted-foreground rt:shadow-ring-subtle rt:outline-none rt:ring-fade rt:forced-outline rt:focus-visible:shadow-ring-focus rt:aria-invalid:shadow-ring-invalid rt:aria-invalid:focus-visible:shadow-ring-invalid rt:disabled:pointer-events-none rt:disabled:cursor-not-allowed rt:disabled:opacity-50", {
7
7
  variants: { size: {
8
8
  36: "rt:h-9 rt:px-3",
9
9
  32: "rt:h-8 rt:px-2.75"
@@ -5,6 +5,8 @@ export type InputGroupSize = 36 | 32;
5
5
  export type InputGroupProps = Omit<HTMLAttributes<HTMLDivElement>, 'className' | 'style'> & {
6
6
  /** The height in px: 36 for forms and search, 32 for fields inside a table. Defaults to 36. */
7
7
  size?: InputGroupSize;
8
+ /** Draws the design system search bar's 8px side padding instead of 12px. SearchField sets it; forms leave it off. */
9
+ search?: boolean;
8
10
  /** Receives the group element. */
9
11
  ref?: Ref<HTMLDivElement>;
10
12
  };
@@ -12,7 +14,7 @@ export type InputGroupProps = Omit<HTMLAttributes<HTMLDivElement>, 'className' |
12
14
  * A field with a prefix, suffix, icon or shortcut hint beside the input, inside one ring. Put an InputGroupInput in it
13
15
  * and an InputGroupAddon on either side, for example a rupee sign before a price or kg after a weight.
14
16
  */
15
- export declare function InputGroup({ size, ref, ...rest }: InputGroupProps): JSX.Element;
17
+ export declare function InputGroup({ size, search, ref, ...rest }: InputGroupProps): JSX.Element;
16
18
  /** The side of the input an addon sits on: start is before it, end is after it. */
17
19
  export type InputGroupAddonAlign = 'start' | 'end';
18
20
  /** The props of InputGroupAddon: the HTML attributes of a div, except className and style, with its side. */
@@ -4,23 +4,35 @@ 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
7
- const groupVariants = cva("rt:flex rt:w-full rt:min-w-0 rt:items-center rt:gap-2 rt:rounded-lg rt:bg-background rt:shadow-ring-subtle rt:transition-colors rt:forced-outline rt:focus-within:shadow-ring-focus rt:has-aria-invalid:shadow-ring-invalid rt:has-aria-invalid:focus-within:shadow-ring-invalid rt:has-disabled:opacity-50", {
8
- variants: { size: {
9
- 36: "rt:h-9 rt:px-3",
10
- 32: "rt:h-8 rt:px-2.75"
11
- } },
12
- defaultVariants: { size: 36 }
7
+ const groupVariants = cva("rt:flex rt:w-full rt:min-w-0 rt:items-center rt:gap-2 rt:rounded-lg rt:bg-background rt:shadow-ring-subtle rt:ring-fade rt:forced-outline rt:focus-within:shadow-ring-focus rt:has-aria-invalid:shadow-ring-invalid rt:has-aria-invalid:focus-within:shadow-ring-invalid rt:has-disabled:opacity-50", {
8
+ variants: {
9
+ size: {
10
+ 36: "rt:h-9 rt:px-3",
11
+ 32: "rt:h-8 rt:px-2.75"
12
+ },
13
+ search: {
14
+ true: "rt:px-2",
15
+ false: ""
16
+ }
17
+ },
18
+ defaultVariants: {
19
+ size: 36,
20
+ search: false
21
+ }
13
22
  });
14
23
  /**
15
24
  * A field with a prefix, suffix, icon or shortcut hint beside the input, inside one ring. Put an InputGroupInput in it
16
25
  * and an InputGroupAddon on either side, for example a rupee sign before a price or kg after a weight.
17
26
  */
18
- function InputGroup({ size = 36, ref, ...rest }) {
27
+ function InputGroup({ size = 36, search = false, ref, ...rest }) {
19
28
  return /* @__PURE__ */ jsx("div", {
20
29
  "data-slot": "input-group",
21
30
  role: "group",
22
31
  ref,
23
- className: cn(groupVariants({ size })),
32
+ className: cn(groupVariants({
33
+ size,
34
+ search
35
+ })),
24
36
  ...rest
25
37
  });
26
38
  }
@@ -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-b4 rt:text-foreground rt:shadow-ring-subtle rt:ring-fade 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";
@@ -2,7 +2,7 @@ import { cn } from "../../lib/cn.js";
2
2
  import { RatioLink } from "../link-provider/RatioLink.js";
3
3
  import { jsx, jsxs } from "react/jsx-runtime";
4
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 rt:forced-outline";
5
+ const LINK = "rt:inline-flex rt:items-center rt:gap-1 rt:rounded-sm rt:text-primary rt:transition-colors rt:hover:text-primary-hover rt:hover:underline rt:focus-visible:focus-ring";
6
6
  /**
7
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
8
  * router navigates without a reload; without one it is a plain <a href>. The arrows are decoration and are hidden from
@@ -13,7 +13,7 @@ function MetricStrip({ segments, label }) {
13
13
  return /* @__PURE__ */ jsx("div", {
14
14
  role: "group",
15
15
  "aria-label": label,
16
- className: "rt:overflow-hidden rt:rounded-md rt:border rt:border-border rt:bg-background",
16
+ className: "rt:overflow-hidden rt:rounded-lg rt:border rt:border-border rt:bg-background",
17
17
  children: /* @__PURE__ */ jsx("div", {
18
18
  className: "rt:-mr-px rt:-mb-px rt:flex rt:flex-wrap",
19
19
  children: segments.map((segment) => {
@@ -1,7 +1,7 @@
1
1
  import { Icon } from "../icon/Icon.js";
2
2
  import { Button } from "../button/Button.js";
3
- import { RatioLink } from "../link-provider/RatioLink.js";
4
3
  import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from "../dropdown-menu/DropdownMenu.js";
4
+ import { Link } from "../link/Link.js";
5
5
  import { StatusPill } from "../status-pill/StatusPill.js";
6
6
  import { jsx, jsxs } from "react/jsx-runtime";
7
7
  // src/components/page-header/PageHeader.tsx
@@ -13,13 +13,13 @@ function PageHeader({ title, icon, status, backHref, backLabel = "Back", actions
13
13
  const hasMenu = menuItems !== void 0 && menuItems.length > 0;
14
14
  return /* @__PURE__ */ jsxs("header", {
15
15
  className: "rt:flex rt:flex-col rt:gap-2",
16
- children: [backHref !== void 0 && /* @__PURE__ */ jsxs(RatioLink, {
17
- href: backHref,
18
- className: "rt:inline-flex rt:w-fit rt:items-center rt:gap-1 rt:rounded-sm rt:text-b1 rt:text-muted-foreground rt:transition-colors rt:hover:text-foreground rt:focus-visible:focus-ring",
19
- children: [/* @__PURE__ */ jsx(Icon, {
20
- name: "chevron-left",
21
- size: 8
22
- }), backLabel]
16
+ children: [backHref !== void 0 && /* @__PURE__ */ jsx("div", {
17
+ className: "rt:flex rt:text-b1",
18
+ children: /* @__PURE__ */ jsx(Link, {
19
+ href: backHref,
20
+ back: true,
21
+ children: backLabel
22
+ })
23
23
  }), /* @__PURE__ */ jsxs("div", {
24
24
  className: "rt:flex rt:flex-wrap rt:items-center rt:justify-between rt:gap-x-3 rt:gap-y-2",
25
25
  children: [/* @__PURE__ */ jsxs("div", {
@@ -51,7 +51,7 @@ function Pagination({ page, pageSize, total, onPageChange, "aria-label": label =
51
51
  })]
52
52
  }), /* @__PURE__ */ jsx("p", {
53
53
  role: "status",
54
- className: "rt:min-w-26 rt:px-2 rt:text-c1 rt:whitespace-nowrap rt:text-foreground-subdued",
54
+ className: "rt:min-w-26.5 rt:px-2 rt:text-c1 rt:whitespace-nowrap rt:text-foreground-subdued",
55
55
  children: summary
56
56
  })]
57
57
  });
@@ -0,0 +1,25 @@
1
+ import { JSX } from "react";
2
+ /**
3
+ * The props of Progress: a value, or none for indeterminate, with an optional visible label and value.
4
+ * Without a visible label, pass aria-label so the bar has a name.
5
+ */
6
+ export type ProgressProps = {
7
+ /** The current value; leave it out or pass null for an indeterminate bar. */
8
+ value?: number | null;
9
+ /** The value at which the bar is full. Defaults to 100; for steps, the number of steps. */
10
+ max?: number;
11
+ /** A visible label, which also names the bar for screen readers. */
12
+ label?: string;
13
+ /** Shows the value on the right: a percent, or "value/max" with steps. */
14
+ showValue?: boolean;
15
+ /** The step variant: counts completed steps out of max, such as 0/5 for the setup guide. Implies showValue. */
16
+ steps?: boolean;
17
+ /** The accessible name when there is no visible label. */
18
+ 'aria-label'?: string;
19
+ };
20
+ /**
21
+ * The thin progress bar of question 5, 2px high, for page loads and refetches. Determinate with a value, indeterminate
22
+ * without one (a partial bar that moves only when the loading animation token names a motion, question 5). Base UI gives it the
23
+ * progressbar role, its label and its value text.
24
+ */
25
+ export declare function Progress({ value, max, label, showValue, steps, ...rest }: ProgressProps): JSX.Element;
@@ -0,0 +1,38 @@
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
+ import { Progress } from "@base-ui/react/progress";
3
+ // src/components/progress/Progress.tsx
4
+ /**
5
+ * The thin progress bar of question 5, 2px high, for page loads and refetches. Determinate with a value, indeterminate
6
+ * without one (a partial bar that moves only when the loading animation token names a motion, question 5). Base UI gives it the
7
+ * progressbar role, its label and its value text.
8
+ */
9
+ function Progress$1({ value = null, max = 100, label, showValue, steps, ...rest }) {
10
+ const withValue = showValue || steps;
11
+ return /* @__PURE__ */ jsxs(Progress.Root, {
12
+ "data-slot": "progress",
13
+ value,
14
+ max,
15
+ "aria-label": label ? void 0 : rest["aria-label"],
16
+ getAriaValueText: steps ? (_f, v) => v === null ? "In progress" : `${v} of ${max}` : void 0,
17
+ className: "rt:flex rt:flex-wrap rt:gap-2",
18
+ children: [
19
+ label ? /* @__PURE__ */ jsx(Progress.Label, {
20
+ className: "rt:text-b3 rt:text-foreground",
21
+ children: label
22
+ }) : null,
23
+ withValue ? /* @__PURE__ */ jsx(Progress.Value, {
24
+ className: "rt:ml-auto rt:text-b3 rt:text-muted-foreground rt:tabular-nums",
25
+ children: steps ? (_formatted, v) => v === null ? "" : `${v}/${max}` : void 0
26
+ }) : null,
27
+ /* @__PURE__ */ jsx(Progress.Track, {
28
+ "data-slot": "progress-track",
29
+ className: "rt:h-progress rt:w-full rt:overflow-hidden rt:bg-progress-track",
30
+ children: /* @__PURE__ */ jsx(Progress.Indicator, {
31
+ "data-slot": "progress-indicator",
32
+ className: "rt:h-full rt:bg-progress rt:data-indeterminate:w-1/3 rt:data-indeterminate:loading-motion"
33
+ })
34
+ })
35
+ ]
36
+ });
37
+ }
38
+ export { Progress$1 as Progress };
@@ -0,0 +1,2 @@
1
+ import { Progress, ProgressProps } from "./Progress.js";
2
+ export { Progress, type ProgressProps };
@@ -0,0 +1 @@
1
+ export { Progress } from "./Progress.js";
@@ -0,0 +1,18 @@
1
+ import { JSX } from "react";
2
+ import { Radio } from "@base-ui/react/radio";
3
+ import { RadioGroup } from "@base-ui/react/radio-group";
4
+ /** The props of RadioGroup: Base UI's radio group props, except className, style and render. Give it an aria-label or a label that points to it. */
5
+ export type RadioGroupProps = Omit<RadioGroup.Props, 'className' | 'style' | 'render'>;
6
+ /** The props of RadioGroupItem: Base UI's radio props, except className, style and render. Give it an aria-label or a label that points to it. */
7
+ export type RadioGroupItemProps = Omit<Radio.Root.Props, 'className' | 'style' | 'render'>;
8
+ /**
9
+ * A column of radio buttons where exactly one can be on. The arrow keys move the choice. aria-invalid on the group, as
10
+ * Field sets it, draws the error ring on every item.
11
+ */
12
+ declare function RadioGroup$1({ ...props }: RadioGroupProps): JSX.Element;
13
+ /**
14
+ * One choice in a RadioGroup: a 14px round control like the checkbox, drawn with a hairline ring and filled in the
15
+ * primary colour with a white dot when chosen. An invisible 24px area around it takes the tap.
16
+ */
17
+ export declare function RadioGroupItem({ ...props }: RadioGroupItemProps): JSX.Element;
18
+ export { RadioGroup$1 as RadioGroup };
@@ -0,0 +1,32 @@
1
+ import { cn } from "../../lib/cn.js";
2
+ import { jsx } from "react/jsx-runtime";
3
+ import { Radio } from "@base-ui/react/radio";
4
+ import { RadioGroup } from "@base-ui/react/radio-group";
5
+ // src/components/radio-group/RadioGroup.tsx
6
+ /**
7
+ * A column of radio buttons where exactly one can be on. The arrow keys move the choice. aria-invalid on the group, as
8
+ * Field sets it, draws the error ring on every item.
9
+ */
10
+ function RadioGroup$1({ ...props }) {
11
+ return /* @__PURE__ */ jsx(RadioGroup, {
12
+ "data-slot": "radio-group",
13
+ className: "rt:group/radio-group rt:grid rt:w-full rt:gap-2",
14
+ ...props
15
+ });
16
+ }
17
+ /**
18
+ * One choice in a RadioGroup: a 14px round control like the checkbox, drawn with a hairline ring and filled in the
19
+ * primary colour with a white dot when chosen. An invisible 24px area around it takes the tap.
20
+ */
21
+ function RadioGroupItem({ ...props }) {
22
+ return /* @__PURE__ */ jsx(Radio.Root, {
23
+ "data-slot": "radio-group-item",
24
+ className: cn("rt:relative rt:flex rt:size-3.5 rt:shrink-0 rt:cursor-pointer rt:items-center rt:justify-center rt:rounded-3xl rt:bg-background rt:shadow-ring-hairline rt:transition-colors rt:outline-none", "rt:after:absolute rt:after:-inset-1.25", "rt:hover:bg-primary-tint rt:data-checked:bg-primary rt:data-checked:shadow-none rt:data-checked:hover:bg-primary-hover", "rt:focus-visible:focus-ring rt:forced-outline", "rt:aria-invalid:shadow-ring-invalid rt:group-aria-invalid/radio-group:shadow-ring-invalid", "rt:data-disabled:cursor-not-allowed rt:data-disabled:opacity-50"),
25
+ ...props,
26
+ children: /* @__PURE__ */ jsx(Radio.Indicator, {
27
+ "data-slot": "radio-group-indicator",
28
+ className: "rt:block rt:size-1.5 rt:rounded-3xl rt:bg-primary-foreground"
29
+ })
30
+ });
31
+ }
32
+ export { RadioGroup$1 as RadioGroup, RadioGroupItem };
@@ -0,0 +1,2 @@
1
+ import { RadioGroup, RadioGroupItem, RadioGroupItemProps, RadioGroupProps } from "./RadioGroup.js";
2
+ export { RadioGroup, RadioGroupItem, type RadioGroupItemProps, type RadioGroupProps };
@@ -0,0 +1 @@
1
+ export { RadioGroup, RadioGroupItem } from "./RadioGroup.js";
@@ -0,0 +1,12 @@
1
+ import { JSX, ReactNode, Ref } from "react";
2
+ /** The props of ScrollArea. */
3
+ export type ScrollAreaProps = {
4
+ /** The scrolling content. */
5
+ children: ReactNode;
6
+ /** Which scrollbars to offer. Defaults to vertical. */
7
+ orientation?: 'vertical' | 'horizontal' | 'both';
8
+ /** Receives the rendered element. */
9
+ ref?: Ref<HTMLDivElement>;
10
+ };
11
+ /** A scrolling region that fills its parent, which sets the height or width, with a thin scrollbar in the design system greys. */
12
+ export declare function ScrollArea({ children, orientation, ref }: ScrollAreaProps): JSX.Element;
@@ -0,0 +1,34 @@
1
+ import { cn } from "../../lib/cn.js";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ import { ScrollArea } from "@base-ui/react/scroll-area";
4
+ // src/components/scroll-area/ScrollArea.tsx
5
+ function Bar({ orientation }) {
6
+ return /* @__PURE__ */ jsx(ScrollArea.Scrollbar, {
7
+ "data-slot": "scroll-area-scrollbar",
8
+ orientation,
9
+ className: cn("rt:flex rt:touch-none rt:p-px rt:select-none rt:data-horizontal:h-1.5 rt:data-horizontal:flex-col rt:data-vertical:h-full rt:data-vertical:w-1.5"),
10
+ children: /* @__PURE__ */ jsx(ScrollArea.Thumb, {
11
+ "data-slot": "scroll-area-thumb",
12
+ className: "rt:relative rt:flex-1 rt:rounded-sm rt:bg-border-strong rt:transition-colors rt:hover:bg-foreground-subdued"
13
+ })
14
+ });
15
+ }
16
+ /** A scrolling region that fills its parent, which sets the height or width, with a thin scrollbar in the design system greys. */
17
+ function ScrollArea$1({ children, orientation = "vertical", ref }) {
18
+ return /* @__PURE__ */ jsxs(ScrollArea.Root, {
19
+ ref,
20
+ "data-slot": "scroll-area",
21
+ className: "rt:relative rt:size-full",
22
+ children: [
23
+ /* @__PURE__ */ jsx(ScrollArea.Viewport, {
24
+ "data-slot": "scroll-area-viewport",
25
+ className: "rt:size-full rt:outline-none rt:focus-visible:focus-ring",
26
+ children
27
+ }),
28
+ orientation !== "horizontal" && /* @__PURE__ */ jsx(Bar, { orientation: "vertical" }),
29
+ orientation !== "vertical" && /* @__PURE__ */ jsx(Bar, { orientation: "horizontal" }),
30
+ /* @__PURE__ */ jsx(ScrollArea.Corner, {})
31
+ ]
32
+ });
33
+ }
34
+ export { ScrollArea$1 as ScrollArea };
@@ -0,0 +1,2 @@
1
+ import { ScrollArea, ScrollAreaProps } from "./ScrollArea.js";
2
+ export { ScrollArea, type ScrollAreaProps };
@@ -0,0 +1 @@
1
+ export { ScrollArea } from "./ScrollArea.js";
@@ -28,10 +28,14 @@ function SearchField({ label, value, defaultValue = "", onValueChange, size = 36
28
28
  };
29
29
  return /* @__PURE__ */ jsxs(InputGroup, {
30
30
  size,
31
+ search: true,
31
32
  children: [
32
- /* @__PURE__ */ jsx(InputGroupAddon, { children: /* @__PURE__ */ jsx(Icon, {
33
- name: "search",
34
- tone: "icon"
33
+ /* @__PURE__ */ jsx(InputGroupAddon, { children: /* @__PURE__ */ jsx("span", {
34
+ className: "rt:flex rt:w-6 rt:items-center rt:pl-2",
35
+ children: /* @__PURE__ */ jsx(Icon, {
36
+ name: "search",
37
+ tone: "icon"
38
+ })
35
39
  }) }),
36
40
  /* @__PURE__ */ jsx(InputGroupInput, {
37
41
  ...rest,
@@ -56,7 +60,7 @@ function SearchField({ label, value, defaultValue = "", onValueChange, size = 36
56
60
  type: "button",
57
61
  "aria-label": "Clear search",
58
62
  onClick: clear,
59
- className: "rt:inline-flex rt:size-5 rt:items-center rt:justify-center rt:rounded-sm rt:text-icon rt:outline-none rt:transition-colors rt:hover:bg-muted rt:focus-visible:shadow-ring-focus",
63
+ className: "rt:inline-flex rt:size-5 rt:items-center rt:justify-center rt:rounded-sm rt:text-icon rt:outline-none rt:transition-colors rt:hover:bg-muted rt:focus-visible:focus-ring",
60
64
  children: /* @__PURE__ */ jsx(Icon, { name: "x" })
61
65
  })
62
66
  }) : null
@@ -8,7 +8,7 @@ import { Select } from "@base-ui/react/select";
8
8
  function Select$1(props) {
9
9
  return /* @__PURE__ */ jsx(Select.Root, { ...props });
10
10
  }
11
- const triggerVariants = cva("rt:flex rt:w-full rt:min-w-0 rt:items-center rt:justify-between rt:gap-2 rt:rounded-lg rt:bg-background rt:text-b3 rt:text-foreground rt:whitespace-nowrap rt:shadow-ring-subtle rt:outline-none rt:select-none rt:transition-colors rt:forced-outline rt:focus-visible:shadow-ring-focus rt:aria-invalid:shadow-ring-invalid rt:aria-invalid:focus-visible:shadow-ring-invalid rt:data-placeholder:text-muted-foreground rt:disabled:pointer-events-none rt:disabled:cursor-not-allowed rt:disabled:opacity-50", {
11
+ const triggerVariants = cva("rt:flex rt:w-full rt:min-w-0 rt:items-center rt:justify-between rt:gap-2 rt:rounded-lg rt:bg-background rt:text-b3 rt:text-foreground rt:whitespace-nowrap rt:shadow-ring-subtle rt:outline-none rt:select-none rt:ring-fade rt:forced-outline rt:focus-visible:shadow-ring-focus rt:aria-invalid:shadow-ring-invalid rt:aria-invalid:focus-visible:shadow-ring-invalid rt:data-placeholder:text-muted-foreground rt:disabled:pointer-events-none rt:disabled:cursor-not-allowed rt:disabled:opacity-50", {
12
12
  variants: { size: {
13
13
  36: "rt:h-9 rt:pr-8 rt:pl-3",
14
14
  32: "rt:h-8 rt:pr-8 rt:pl-2.75"