@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
@@ -0,0 +1,52 @@
1
+ import { JSX, Ref } from "react";
2
+ import { Combobox } from "@base-ui/react/combobox";
3
+ /** One option: the value that is stored and the text people see. A plain string stands for both. */
4
+ export type ComboboxItem = string | {
5
+ value: string;
6
+ label: string;
7
+ };
8
+ /** The props of Combobox: one choice from a list, with a search field. */
9
+ export type ComboboxProps = {
10
+ /** The options. Strings or { value, label } objects. */
11
+ items: readonly ComboboxItem[];
12
+ /** The chosen value for a controlled combobox, or null for none. */
13
+ value?: string | null;
14
+ /** The value to start with when the combobox keeps its own state. */
15
+ defaultValue?: string | null;
16
+ /** Called with the new value, or null when the choice is cleared. */
17
+ onValueChange?: (value: string | null, eventDetails: Combobox.Root.ChangeEventDetails) => void;
18
+ /** The text shown while nothing is chosen. */
19
+ placeholder?: string;
20
+ /** The text shown when the search matches no option. Defaults to "No results found". */
21
+ emptyText?: string;
22
+ /** Marks the field invalid and draws the error ring, as on every form control. Field sets it when it shows an error. */
23
+ 'aria-invalid'?: boolean | 'true' | 'false';
24
+ /** The same as aria-invalid, kept as its alias. */
25
+ invalid?: boolean;
26
+ /** Shows a button that clears the choice while one is made. Defaults to true. */
27
+ clearable?: boolean;
28
+ /** Stops people from changing the choice. */
29
+ disabled?: boolean;
30
+ /** The name of the field in a form. */
31
+ name?: string;
32
+ /** The accessible name when no label is wired to the field. */
33
+ 'aria-label'?: string;
34
+ /** The ids of the elements that name the field. Field sets it to its label. */
35
+ 'aria-labelledby'?: string;
36
+ /** The ids of the elements that describe the field, such as its hint or error. Field sets it. */
37
+ 'aria-describedby'?: string;
38
+ /** Tells assistive technology that the field must be filled. Field sets it for a required field. */
39
+ 'aria-required'?: boolean | 'true' | 'false';
40
+ /** The id of the input, for a label's htmlFor. */
41
+ id?: string;
42
+ /** Receives the input element. */
43
+ ref?: Ref<HTMLInputElement>;
44
+ };
45
+ /**
46
+ * A field to choose one option from a list, with a search: type to narrow the list, pick with the mouse or Enter.
47
+ * The field looks like an Input and the list like a Popover panel. For several choices shown as chips, use TagInput.
48
+ * Mark it invalid with aria-invalid; give the list an empty text with emptyText. Inside a Field, the label, hint, error
49
+ * and required mark reach the input.
50
+ */
51
+ declare function Combobox$1({ items, value, defaultValue, onValueChange, placeholder, emptyText, 'aria-invalid': ariaInvalid, invalid, clearable, disabled, name, id, ref, 'aria-label': ariaLabel, 'aria-labelledby': ariaLabelledBy, 'aria-describedby': ariaDescribedBy, 'aria-required': ariaRequired }: ComboboxProps): JSX.Element;
52
+ export { Combobox$1 as Combobox };
@@ -0,0 +1,92 @@
1
+ import { Icon } from "../icon/Icon.js";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ import { useMemo } from "react";
4
+ import { Combobox } from "@base-ui/react/combobox";
5
+ // src/components/combobox/Combobox.tsx
6
+ const toOption = (item) => typeof item === "string" ? {
7
+ value: item,
8
+ label: item
9
+ } : item;
10
+ const ROOT_CLASSES = {
11
+ field: "rt:flex rt:h-9 rt:w-full rt:min-w-0 rt:items-center rt:gap-0.5 rt:rounded-lg rt:bg-background rt:pr-1.5 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",
12
+ input: "rt:h-full rt:min-w-0 rt:flex-1 rt:bg-transparent rt:pl-3 rt:text-b3 rt:text-foreground rt:placeholder:text-muted-foreground rt:outline-none rt:disabled:cursor-not-allowed",
13
+ button: "rt:flex rt:size-6 rt:items-center rt:justify-center rt:rounded-md rt:text-muted-foreground rt:outline-none rt:hover:text-foreground rt:focus-visible:focus-ring rt:disabled:pointer-events-none",
14
+ popup: "rt:group/combobox-content rt:max-h-available rt:w-anchor rt:max-w-available rt:overflow-hidden rt:rounded-lg rt:border rt:border-border rt:bg-popover rt:text-b3 rt:text-popover-foreground rt:outline-hidden rt:transition-colors",
15
+ item: "rt:relative rt:flex rt:h-7 rt:w-full rt:cursor-default rt:items-center rt:gap-2 rt:pr-8 rt:pl-3 rt:text-b3 rt:text-foreground rt:outline-none rt:select-none rt:transition-colors rt:data-highlighted:bg-accent rt:data-highlighted:text-accent-foreground rt:data-disabled:pointer-events-none rt:data-disabled:opacity-50"
16
+ };
17
+ /**
18
+ * A field to choose one option from a list, with a search: type to narrow the list, pick with the mouse or Enter.
19
+ * The field looks like an Input and the list like a Popover panel. For several choices shown as chips, use TagInput.
20
+ * Mark it invalid with aria-invalid; give the list an empty text with emptyText. Inside a Field, the label, hint, error
21
+ * and required mark reach the input.
22
+ */
23
+ function Combobox$1({ items, value, defaultValue, onValueChange, placeholder, emptyText = "No results found", "aria-invalid": ariaInvalid, invalid = false, clearable = true, disabled = false, name, id, ref, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, "aria-describedby": ariaDescribedBy, "aria-required": ariaRequired }) {
24
+ const isInvalid = invalid || ariaInvalid === true || ariaInvalid === "true";
25
+ const options = items.map(toOption);
26
+ const key = JSON.stringify(options);
27
+ const labels = useMemo(() => new Map(JSON.parse(key).map((o) => [o.value, o.label])), [key]);
28
+ const root = {
29
+ items: useMemo(() => [...labels.keys()], [labels]),
30
+ disabled,
31
+ name,
32
+ itemToStringLabel: (v) => labels.get(v) ?? v,
33
+ onValueChange: (v, details) => onValueChange?.(v, details)
34
+ };
35
+ const controlled = value !== void 0 ? { value } : {};
36
+ const initial = defaultValue !== void 0 ? { defaultValue } : {};
37
+ return /* @__PURE__ */ jsxs(Combobox.Root, {
38
+ ...root,
39
+ ...controlled,
40
+ ...initial,
41
+ children: [/* @__PURE__ */ jsxs("div", {
42
+ "data-slot": "combobox",
43
+ className: ROOT_CLASSES.field,
44
+ children: [/* @__PURE__ */ jsx(Combobox.Input, {
45
+ ref,
46
+ id,
47
+ "aria-label": ariaLabel,
48
+ "aria-labelledby": ariaLabelledBy,
49
+ "aria-describedby": ariaDescribedBy,
50
+ "aria-required": ariaRequired,
51
+ "aria-invalid": isInvalid || void 0,
52
+ placeholder,
53
+ className: ROOT_CLASSES.input
54
+ }), /* @__PURE__ */ jsxs("div", {
55
+ className: "rt:flex rt:items-center rt:gap-0.5",
56
+ children: [clearable ? /* @__PURE__ */ jsx(Combobox.Clear, {
57
+ "aria-label": "Clear",
58
+ className: ROOT_CLASSES.button,
59
+ children: /* @__PURE__ */ jsx(Icon, { name: "x" })
60
+ }) : null, /* @__PURE__ */ jsx(Combobox.Trigger, {
61
+ "aria-label": "Show options",
62
+ className: ROOT_CLASSES.button,
63
+ children: /* @__PURE__ */ jsx(Icon, { name: "nav-caret-down" })
64
+ })]
65
+ })]
66
+ }), /* @__PURE__ */ jsx(Combobox.Portal, { children: /* @__PURE__ */ jsx(Combobox.Positioner, {
67
+ side: "bottom",
68
+ align: "start",
69
+ sideOffset: 4,
70
+ className: "rt:isolate rt:z-50",
71
+ children: /* @__PURE__ */ jsxs(Combobox.Popup, {
72
+ "data-slot": "combobox-content",
73
+ className: ROOT_CLASSES.popup,
74
+ children: [/* @__PURE__ */ jsx(Combobox.Empty, {
75
+ className: "rt:hidden rt:justify-center rt:py-3 rt:text-center rt:text-muted-foreground rt:group-data-empty/combobox-content:flex",
76
+ children: emptyText
77
+ }), /* @__PURE__ */ jsx(Combobox.List, {
78
+ className: "rt:max-h-72 rt:overflow-y-auto rt:overscroll-contain rt:py-1.5 rt:data-empty:py-0",
79
+ children: (v) => /* @__PURE__ */ jsxs(Combobox.Item, {
80
+ value: v,
81
+ className: ROOT_CLASSES.item,
82
+ children: [labels.get(v), /* @__PURE__ */ jsx(Combobox.ItemIndicator, {
83
+ className: "rt:pointer-events-none rt:absolute rt:right-3 rt:flex",
84
+ children: /* @__PURE__ */ jsx(Icon, { name: "check" })
85
+ })]
86
+ }, v)
87
+ })]
88
+ })
89
+ }) })]
90
+ });
91
+ }
92
+ export { Combobox$1 as Combobox };
@@ -0,0 +1,2 @@
1
+ import { Combobox, ComboboxItem, ComboboxProps } from "./Combobox.js";
2
+ export { Combobox, type ComboboxItem, type ComboboxProps };
@@ -0,0 +1 @@
1
+ export { Combobox } from "./Combobox.js";
@@ -1,7 +1,8 @@
1
- import { Checkbox } from "../checkbox/Checkbox.js";
1
+ import { Icon } from "../icon/Icon.js";
2
2
  import { RatioLink } from "../link-provider/RatioLink.js";
3
+ import { Checkbox } from "../checkbox/Checkbox.js";
3
4
  import { Pagination } from "../pagination/Pagination.js";
4
- import { Table, TableBody, TableBox, TableCell, TableHead, TableHeader, TableRow } from "../table/Table.js";
5
+ import { SelectionCell, SelectionHead, Table, TableBody, TableBox, TableCell, TableHead, TableHeader, TableRow } from "../table/Table.js";
5
6
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
6
7
  import { useMemo, useSyncExternalStore } from "react";
7
8
  import { FlexRender, rowSelectionFeature, rowSortingFeature, tableFeatures, useTable } from "@tanstack/react-table";
@@ -72,10 +73,10 @@ function DataTable({ columns: allColumns, data, getRowId, selectedIds, onSelecte
72
73
  if (loading) return /* @__PURE__ */ jsx(Fragment, { children: loading });
73
74
  return /* @__PURE__ */ jsxs("div", {
74
75
  "data-slot": "data-table",
75
- className: "rt:flex rt:w-full rt:flex-col rt:gap-2",
76
+ className: "rt:flex rt:w-full rt:flex-col",
76
77
  children: [/* @__PURE__ */ jsx(TableBox, { children: /* @__PURE__ */ jsxs(Table, {
77
78
  ...rest,
78
- children: [/* @__PURE__ */ jsx(TableHeader, { children: /* @__PURE__ */ jsxs(TableRow, { children: [selectable ? /* @__PURE__ */ jsx(TableHead, { children: /* @__PURE__ */ jsx(Checkbox, {
79
+ children: [/* @__PURE__ */ jsx(TableHeader, { children: /* @__PURE__ */ jsxs(TableRow, { children: [selectable ? /* @__PURE__ */ jsx(SelectionHead, { children: /* @__PURE__ */ jsx(Checkbox, {
79
80
  "aria-label": "Select all rows",
80
81
  checked: table.getIsAllRowsSelected() && rows.length > 0,
81
82
  indeterminate: rows.some((r) => r.getIsSelected()) && !table.getIsAllRowsSelected(),
@@ -92,10 +93,7 @@ function DataTable({ columns: allColumns, data, getRowId, selectedIds, onSelecte
92
93
  "aria-label": `Sort by ${c.header}`,
93
94
  className: "rt:inline-flex rt:cursor-pointer rt:items-center rt:gap-1 rt:uppercase rt:focus-visible:focus-ring",
94
95
  onClick: () => h.column.toggleSorting(),
95
- children: [/* @__PURE__ */ jsx(FlexRender, { header: h }), /* @__PURE__ */ jsx("span", {
96
- "aria-hidden": "true",
97
- children: sorted === "asc" ? "↑" : sorted === "desc" ? "↓" : null
98
- })]
96
+ children: [/* @__PURE__ */ jsx(FlexRender, { header: h }), sorted ? /* @__PURE__ */ jsx(Icon, { name: sorted === "asc" ? "arrow-up" : "arrow-down" }) : null]
99
97
  }) : /* @__PURE__ */ jsx(FlexRender, { header: h })
100
98
  }, h.id);
101
99
  })] }) }), /* @__PURE__ */ jsx(TableBody, { children: rows.length === 0 ? /* @__PURE__ */ jsx(TableRow, { children: /* @__PURE__ */ jsx(TableCell, {
@@ -103,7 +101,7 @@ function DataTable({ columns: allColumns, data, getRowId, selectedIds, onSelecte
103
101
  children: empty
104
102
  }) }) : rows.map((row, index) => /* @__PURE__ */ jsxs(TableRow, {
105
103
  selected: row.getIsSelected(),
106
- children: [selectable ? /* @__PURE__ */ jsx(TableCell, { children: /* @__PURE__ */ jsx(Checkbox, {
104
+ children: [selectable ? /* @__PURE__ */ jsx(SelectionCell, { children: /* @__PURE__ */ jsx(Checkbox, {
107
105
  "aria-label": getRowLabel ? `Select ${getRowLabel(row.original)}` : `Select row ${index + 1}`,
108
106
  checked: row.getIsSelected(),
109
107
  onCheckedChange: () => row.toggleSelected()
@@ -126,15 +124,11 @@ function DataTable({ columns: allColumns, data, getRowId, selectedIds, onSelecte
126
124
  }, cell.id);
127
125
  })]
128
126
  }, row.id)) })]
129
- }) }), hasFooter ? /* @__PURE__ */ jsx("div", {
130
- "data-slot": "data-table-pagination",
131
- className: "rt:rounded-lg rt:border rt:border-border rt:bg-background rt:px-2",
132
- children: /* @__PURE__ */ jsx(Pagination, {
133
- page,
134
- pageSize,
135
- total,
136
- onPageChange
137
- })
127
+ }) }), hasFooter ? /* @__PURE__ */ jsx(Pagination, {
128
+ page,
129
+ pageSize,
130
+ total,
131
+ onPageChange
138
132
  }) : null]
139
133
  });
140
134
  }
@@ -12,7 +12,7 @@ export type DatePickerProps = Omit<InputHTMLAttributes<HTMLInputElement>, 'class
12
12
  value?: Date | null;
13
13
  /** The starting day when the field is not controlled. Defaults to empty. */
14
14
  defaultValue?: Date | null;
15
- /** Called with the day when a typed date is complete or a day is picked, and with null when the text is cleared. */
15
+ /** Called with the day when a typed date is complete or a day is picked, and with null when the text is cleared or is not a date. */
16
16
  onValueChange?: (value: Date | null) => void;
17
17
  /** The days that cannot be picked in the calendar, for example { before: new Date() }. */
18
18
  disabledDays?: CalendarProps['disabled'];
@@ -27,7 +27,8 @@ export type DatePickerProps = Omit<InputHTMLAttributes<HTMLInputElement>, 'class
27
27
  };
28
28
  /**
29
29
  * A field that shows a day as DD/MM/YYYY, reads a typed date through the format module and opens a calendar in a popover
30
- * from the button at its end. Text that is not a date is marked invalid when the field loses focus, and reports nothing.
30
+ * from the button at its end. Text that is not a date reports null (as soon as it is typed over a day, and when the field
31
+ * loses focus) and is marked invalid, aria-invalid, when the field loses focus; the app shows Field's error from the null.
31
32
  * Under a name, a form submits the day as YYYY-MM-DD (empty when there is none), never the text on screen.
32
33
  */
33
34
  export declare function DatePicker({ value, defaultValue, onValueChange, disabledDays, startMonth, endMonth, size, placeholder, readOnly, disabled, onBlur, ref, name, 'aria-invalid': ariaInvalid, ...rest }: DatePickerProps): JSX.Element;
@@ -9,7 +9,8 @@ import { useState } from "react";
9
9
  // src/components/date-picker/DatePicker.tsx
10
10
  /**
11
11
  * A field that shows a day as DD/MM/YYYY, reads a typed date through the format module and opens a calendar in a popover
12
- * from the button at its end. Text that is not a date is marked invalid when the field loses focus, and reports nothing.
12
+ * from the button at its end. Text that is not a date reports null (as soon as it is typed over a day, and when the field
13
+ * loses focus) and is marked invalid, aria-invalid, when the field loses focus; the app shows Field's error from the null.
13
14
  * Under a name, a form submits the day as YYYY-MM-DD (empty when there is none), never the text on screen.
14
15
  */
15
16
  function DatePicker({ value, defaultValue = null, onValueChange, disabledDays, startMonth, endMonth, size = 36, placeholder = FORMAT.datePattern, readOnly, disabled, onBlur, ref, name, "aria-invalid": ariaInvalid, ...rest }) {
@@ -49,13 +50,16 @@ function DatePicker({ value, defaultValue = null, onValueChange, disabledDays, s
49
50
  if (next.trim() === "") return commit(null);
50
51
  const parsed = parseDate(next);
51
52
  if (parsed !== null) commit(parsed);
53
+ else if (day !== null) commit(null);
52
54
  },
53
55
  onBlur: (event) => {
54
56
  onBlur?.(event);
55
57
  const parsed = parseDate(text);
56
58
  if (text.trim() === "") setMalformed(false);
57
- else if (parsed === null) setMalformed(true);
58
- else setText(formatDate(parsed));
59
+ else if (parsed === null) {
60
+ setMalformed(true);
61
+ commit(null);
62
+ } else setText(formatDate(parsed));
59
63
  }
60
64
  }), readOnly ? null : /* @__PURE__ */ jsx(InputGroupAddon, {
61
65
  align: "end",
@@ -17,7 +17,7 @@ export type DateRangePickerProps = Omit<InputHTMLAttributes<HTMLInputElement>, '
17
17
  value?: DateRangeValue | null;
18
18
  /** The starting range when the field is not controlled. Defaults to empty. */
19
19
  defaultValue?: DateRangeValue | null;
20
- /** Called with the range when a typed range is complete or two days are picked, and with null when the text is cleared. */
20
+ /** Called with the range when a typed range is complete or two days are picked, and with null when the text is cleared or is not a range. */
21
21
  onValueChange?: (value: DateRangeValue | null) => void;
22
22
  /** The days that cannot be picked in the calendar, for example { after: new Date() } for an analytics period. */
23
23
  disabledDays?: CalendarProps['disabled'];
@@ -33,7 +33,8 @@ export type DateRangePickerProps = Omit<InputHTMLAttributes<HTMLInputElement>, '
33
33
  /**
34
34
  * A field that shows a range as two DD/MM/YYYY days, reads a typed range through the format module and opens a calendar in
35
35
  * a popover from the button at its end, for analytics periods. Pick the first day, then the last. Text that is not a range
36
- * in order is marked invalid when the field loses focus, and reports nothing. Under a name, a form submits two fields,
36
+ * in order reports null (as soon as it is typed over a range, and when the field loses focus) and is marked invalid, aria-invalid,
37
+ * when the field loses focus. Under a name, a form submits two fields,
37
38
  * `name.from` and `name.to`, each as YYYY-MM-DD (empty when there is no range), never the text on screen.
38
39
  */
39
40
  export declare function DateRangePicker({ value, defaultValue, onValueChange, disabledDays, startMonth, endMonth, size, placeholder, readOnly, disabled, onBlur, ref, name, 'aria-invalid': ariaInvalid, ...rest }: DateRangePickerProps): JSX.Element;
@@ -25,7 +25,8 @@ function parseRange(text) {
25
25
  /**
26
26
  * A field that shows a range as two DD/MM/YYYY days, reads a typed range through the format module and opens a calendar in
27
27
  * a popover from the button at its end, for analytics periods. Pick the first day, then the last. Text that is not a range
28
- * in order is marked invalid when the field loses focus, and reports nothing. Under a name, a form submits two fields,
28
+ * in order reports null (as soon as it is typed over a range, and when the field loses focus) and is marked invalid, aria-invalid,
29
+ * when the field loses focus. Under a name, a form submits two fields,
29
30
  * `name.from` and `name.to`, each as YYYY-MM-DD (empty when there is no range), never the text on screen.
30
31
  */
31
32
  function DateRangePicker({ value, defaultValue = null, onValueChange, disabledDays, startMonth, endMonth, size = 36, placeholder = `${FORMAT.datePattern}${SEPARATOR}${FORMAT.datePattern}`, readOnly, disabled, onBlur, ref, name, "aria-invalid": ariaInvalid, ...rest }) {
@@ -66,13 +67,16 @@ function DateRangePicker({ value, defaultValue = null, onValueChange, disabledDa
66
67
  if (next.trim() === "") return commit(null);
67
68
  const parsed = parseRange(next);
68
69
  if (parsed !== null) commit(parsed);
70
+ else if (range !== null) commit(null);
69
71
  },
70
72
  onBlur: (event) => {
71
73
  onBlur?.(event);
72
74
  const parsed = parseRange(text);
73
75
  if (text.trim() === "") setMalformed(false);
74
- else if (parsed === null) setMalformed(true);
75
- else setText(write(parsed));
76
+ else if (parsed === null) {
77
+ setMalformed(true);
78
+ commit(null);
79
+ } else setText(write(parsed));
76
80
  }
77
81
  }), readOnly ? null : /* @__PURE__ */ jsx(InputGroupAddon, {
78
82
  align: "end",
@@ -21,7 +21,7 @@ function Definition({ children, definition }) {
21
21
  className: "rt:isolate rt:z-50",
22
22
  children: /* @__PURE__ */ jsx(Popover.Popup, {
23
23
  "aria-label": "Definition",
24
- className: "rt:max-w-72 rt:rounded-md rt:border rt:border-border rt:bg-popover rt:px-3 rt:py-1.5 rt:text-b1 rt:text-popover-foreground rt:outline-hidden",
24
+ className: "rt:max-w-72 rt:rounded-lg rt:border rt:border-border rt:bg-popover rt:px-3 rt:py-1.5 rt:text-b1 rt:text-popover-foreground rt:outline-hidden",
25
25
  children: definition
26
26
  })
27
27
  }) })] });
@@ -36,7 +36,7 @@ function DialogContent({ children, showCloseButton = true, closeLabel = "Close",
36
36
  children: [children, showCloseButton && /* @__PURE__ */ jsx(Dialog.Close, {
37
37
  "data-slot": "dialog-close",
38
38
  "aria-label": closeLabel,
39
- className: "rt:absolute rt:top-4 rt:right-4 rt:flex rt:size-7 rt:items-center rt:justify-center rt:rounded-md rt:text-icon rt:transition-colors rt:hover:bg-primary-tint rt:focus-visible:focus-ring",
39
+ className: "rt:absolute rt:top-6 rt:right-6 rt:flex rt:size-7 rt:items-center rt:justify-center rt:rounded-md rt:text-icon rt:transition-colors rt:hover:bg-primary-tint rt:focus-visible:focus-ring",
40
40
  children: /* @__PURE__ */ jsx(Icon, {
41
41
  name: "x",
42
42
  size: 12
@@ -0,0 +1,52 @@
1
+ import { ComponentProps, JSX } from "react";
2
+ import { Drawer } from "@base-ui/react/drawer";
3
+ /** Props of Drawer. */
4
+ export interface DrawerProps extends Drawer.Root.Props {
5
+ /** Whether a short bar shows on the edge the drawer is swiped from, as a hint on phones. Defaults to false. */
6
+ showSwipeHandle?: boolean;
7
+ }
8
+ /**
9
+ * The root of a drawer: a panel people swipe away on a phone. It holds the open state and renders nothing itself.
10
+ * `swipeDirection` picks the edge it leaves by (down, the default, makes a bottom drawer). Pass `open` and
11
+ * `onOpenChange` to control it, or `defaultOpen`. The page behind is inert while it is open, with no scrim over it.
12
+ */
13
+ declare function Drawer$1({ modal, showSwipeHandle, swipeDirection, ...props }: DrawerProps): JSX.Element;
14
+ /** Props of DrawerTrigger. */
15
+ export type DrawerTriggerProps = Omit<Drawer.Trigger.Props, 'className' | 'style'>;
16
+ /** The button that opens the drawer. */
17
+ export declare function DrawerTrigger(props: DrawerTriggerProps): JSX.Element;
18
+ /** Props of DrawerClose. */
19
+ export type DrawerCloseProps = Omit<Drawer.Close.Props, 'className' | 'style'>;
20
+ /** A button that closes the drawer, such as Done in the footer. */
21
+ export declare function DrawerClose(props: DrawerCloseProps): JSX.Element;
22
+ /** Props of DrawerContent. */
23
+ export interface DrawerContentProps extends Omit<Drawer.Popup.Props, 'className' | 'style'> {
24
+ /** Whether the corner close button shows. It is the way to close without swiping. Defaults to true. */
25
+ showCloseButton?: boolean;
26
+ /** The accessible name of the corner close button. Defaults to Close. */
27
+ closeLabel?: string;
28
+ }
29
+ /**
30
+ * The panel, white with a hairline and 24px corners on its inner edge. A bottom drawer spans the screen and sizes to its
31
+ * content; a side drawer is full height and three quarters of a phone screen, 384px from the small breakpoint up. It
32
+ * follows the finger while it is swiped and otherwise does not move: no slide in or out, no scrim, blur or shadow.
33
+ * Put DrawerHeader, the content and DrawerFooter inside.
34
+ */
35
+ export declare function DrawerContent({ children, showCloseButton, closeLabel, ...props }: DrawerContentProps): JSX.Element;
36
+ /** Props of DrawerHeader. */
37
+ export type DrawerHeaderProps = Omit<ComponentProps<'div'>, 'className' | 'style'>;
38
+ /** The title and description, stacked, with room on the right for the close button. */
39
+ export declare function DrawerHeader(props: DrawerHeaderProps): JSX.Element;
40
+ /** Props of DrawerFooter. */
41
+ export type DrawerFooterProps = Omit<ComponentProps<'div'>, 'className' | 'style'>;
42
+ /** The actions at the bottom of the drawer, stacked full width for a thumb. */
43
+ export declare function DrawerFooter(props: DrawerFooterProps): JSX.Element;
44
+ /** Props of DrawerTitle. */
45
+ export type DrawerTitleProps = Omit<Drawer.Title.Props, 'className' | 'style'>;
46
+ /** The drawer's name, in the b4 style. It names the drawer for assistive technology. */
47
+ export declare function DrawerTitle(props: DrawerTitleProps): JSX.Element;
48
+ /** Props of DrawerDescription. */
49
+ export type DrawerDescriptionProps = Omit<Drawer.Description.Props, 'className' | 'style'>;
50
+ /** One line that says what the drawer is for, in subdued body text. */
51
+ export declare function DrawerDescription(props: DrawerDescriptionProps): JSX.Element;
52
+ export { Drawer$1 as Drawer };
@@ -0,0 +1,120 @@
1
+ import { cn } from "../../lib/cn.js";
2
+ import { Icon } from "../icon/Icon.js";
3
+ import { jsx, jsxs } from "react/jsx-runtime";
4
+ import { createContext, use, useMemo } from "react";
5
+ import { Drawer } from "@base-ui/react/drawer";
6
+ // src/components/drawer/Drawer.tsx
7
+ const DrawerContext = createContext({
8
+ modal: true,
9
+ showSwipeHandle: false
10
+ });
11
+ /**
12
+ * The root of a drawer: a panel people swipe away on a phone. It holds the open state and renders nothing itself.
13
+ * `swipeDirection` picks the edge it leaves by (down, the default, makes a bottom drawer). Pass `open` and
14
+ * `onOpenChange` to control it, or `defaultOpen`. The page behind is inert while it is open, with no scrim over it.
15
+ */
16
+ function Drawer$1({ modal = true, showSwipeHandle = false, swipeDirection = "down", ...props }) {
17
+ const value = useMemo(() => ({
18
+ modal,
19
+ showSwipeHandle
20
+ }), [modal, showSwipeHandle]);
21
+ return /* @__PURE__ */ jsx(DrawerContext, {
22
+ value,
23
+ children: /* @__PURE__ */ jsx(Drawer.Root, {
24
+ modal,
25
+ swipeDirection,
26
+ ...props
27
+ })
28
+ });
29
+ }
30
+ /** The button that opens the drawer. */
31
+ function DrawerTrigger(props) {
32
+ return /* @__PURE__ */ jsx(Drawer.Trigger, {
33
+ "data-slot": "drawer-trigger",
34
+ ...props
35
+ });
36
+ }
37
+ /** A button that closes the drawer, such as Done in the footer. */
38
+ function DrawerClose(props) {
39
+ return /* @__PURE__ */ jsx(Drawer.Close, {
40
+ "data-slot": "drawer-close",
41
+ ...props
42
+ });
43
+ }
44
+ /**
45
+ * The panel, white with a hairline and 24px corners on its inner edge. A bottom drawer spans the screen and sizes to its
46
+ * content; a side drawer is full height and three quarters of a phone screen, 384px from the small breakpoint up. It
47
+ * follows the finger while it is swiped and otherwise does not move: no slide in or out, no scrim, blur or shadow.
48
+ * Put DrawerHeader, the content and DrawerFooter inside.
49
+ */
50
+ function DrawerContent({ children, showCloseButton = true, closeLabel = "Close", ...props }) {
51
+ const { modal, showSwipeHandle } = use(DrawerContext);
52
+ return /* @__PURE__ */ jsx(Drawer.Portal, { children: /* @__PURE__ */ jsx(Drawer.Viewport, {
53
+ "data-slot": "drawer-viewport",
54
+ className: cn("rt:pointer-events-none rt:fixed rt:inset-0 rt:z-50", modal === true && "rt:pointer-events-auto"),
55
+ children: /* @__PURE__ */ jsxs(Drawer.Popup, {
56
+ "data-slot": "drawer-popup",
57
+ className: cn("rt:group/drawer-popup rt:pointer-events-auto rt:fixed rt:z-50 rt:flex rt:max-h-full rt:min-h-0 rt:flex-col rt:border-border rt:bg-popover rt:text-b3 rt:text-popover-foreground rt:select-none rt:drawer-swipe rt:focus-visible:focus-ring", "rt:swipe-y:inset-x-0 rt:swipe-x:inset-y-0 rt:swipe-x:w-3/4 rt:swipe-x:flex-row rt:swipe-x:sm:w-96", "rt:swipe-down:bottom-0 rt:swipe-down:rounded-t-3xl rt:swipe-down:border-x rt:swipe-down:border-t", "rt:swipe-up:top-0 rt:swipe-up:rounded-b-3xl rt:swipe-up:border-x rt:swipe-up:border-b", "rt:swipe-left:left-0 rt:swipe-left:rounded-r-3xl rt:swipe-left:border-y rt:swipe-left:border-r", "rt:swipe-right:right-0 rt:swipe-right:rounded-l-3xl rt:swipe-right:border-y rt:swipe-right:border-l"),
58
+ ...props,
59
+ children: [
60
+ showSwipeHandle && /* @__PURE__ */ jsx(DrawerSwipeHandle, {}),
61
+ /* @__PURE__ */ jsx(Drawer.Content, {
62
+ "data-slot": "drawer-content",
63
+ className: "rt:flex rt:min-h-0 rt:flex-1 rt:flex-col rt:overflow-y-auto rt:overscroll-contain rt:select-text rt:group-data-swiping/drawer-popup:select-none",
64
+ children
65
+ }),
66
+ showCloseButton && /* @__PURE__ */ jsx(Drawer.Close, {
67
+ "data-slot": "drawer-close",
68
+ "aria-label": closeLabel,
69
+ className: "rt:absolute rt:top-6 rt:right-6 rt:flex rt:size-7 rt:items-center rt:justify-center rt:rounded-md rt:text-icon rt:transition-colors rt:hover:bg-primary-tint rt:focus-visible:focus-ring",
70
+ children: /* @__PURE__ */ jsx(Icon, {
71
+ name: "x",
72
+ size: 12
73
+ })
74
+ })
75
+ ]
76
+ })
77
+ }) });
78
+ }
79
+ /** A short bar on the edge the drawer is swiped from. It is decorative: the whole panel takes the swipe. */
80
+ function DrawerSwipeHandle() {
81
+ return /* @__PURE__ */ jsx("div", {
82
+ "data-slot": "drawer-swipe-handle",
83
+ "aria-hidden": "true",
84
+ className: "rt:flex rt:shrink-0 rt:items-center rt:justify-center rt:cursor-grab rt:group-swipe-y/drawer-popup:h-5 rt:group-swipe-x/drawer-popup:w-5 rt:group-swipe-up/drawer-popup:order-last rt:group-swipe-left/drawer-popup:order-last rt:active:cursor-grabbing",
85
+ children: /* @__PURE__ */ jsx("span", { className: "rt:block rt:rounded-sm rt:bg-muted rt:group-swipe-y/drawer-popup:h-1 rt:group-swipe-y/drawer-popup:w-12 rt:group-swipe-x/drawer-popup:h-12 rt:group-swipe-x/drawer-popup:w-1" })
86
+ });
87
+ }
88
+ /** The title and description, stacked, with room on the right for the close button. */
89
+ function DrawerHeader(props) {
90
+ return /* @__PURE__ */ jsx("div", {
91
+ "data-slot": "drawer-header",
92
+ className: "rt:flex rt:shrink-0 rt:flex-col rt:gap-1 rt:p-6 rt:pr-20",
93
+ ...props
94
+ });
95
+ }
96
+ /** The actions at the bottom of the drawer, stacked full width for a thumb. */
97
+ function DrawerFooter(props) {
98
+ return /* @__PURE__ */ jsx("div", {
99
+ "data-slot": "drawer-footer",
100
+ className: "rt:mt-auto rt:flex rt:shrink-0 rt:flex-col rt:gap-2 rt:px-6 rt:pb-6",
101
+ ...props
102
+ });
103
+ }
104
+ /** The drawer's name, in the b4 style. It names the drawer for assistive technology. */
105
+ function DrawerTitle(props) {
106
+ return /* @__PURE__ */ jsx(Drawer.Title, {
107
+ "data-slot": "drawer-title",
108
+ className: "rt:text-b4 rt:text-foreground-strong",
109
+ ...props
110
+ });
111
+ }
112
+ /** One line that says what the drawer is for, in subdued body text. */
113
+ function DrawerDescription(props) {
114
+ return /* @__PURE__ */ jsx(Drawer.Description, {
115
+ "data-slot": "drawer-description",
116
+ className: "rt:text-b3 rt:text-foreground-subdued",
117
+ ...props
118
+ });
119
+ }
120
+ export { Drawer$1 as Drawer, DrawerClose, DrawerContent, DrawerDescription, DrawerFooter, DrawerHeader, DrawerTitle, DrawerTrigger };
@@ -0,0 +1,2 @@
1
+ import { Drawer, DrawerClose, DrawerCloseProps, DrawerContent, DrawerContentProps, DrawerDescription, DrawerDescriptionProps, DrawerFooter, DrawerFooterProps, DrawerHeader, DrawerHeaderProps, DrawerProps, DrawerTitle, DrawerTitleProps, DrawerTrigger, DrawerTriggerProps } from "./Drawer.js";
2
+ export { Drawer, DrawerClose, type DrawerCloseProps, DrawerContent, type DrawerContentProps, DrawerDescription, type DrawerDescriptionProps, DrawerFooter, type DrawerFooterProps, DrawerHeader, type DrawerHeaderProps, type DrawerProps, DrawerTitle, type DrawerTitleProps, DrawerTrigger, type DrawerTriggerProps };
@@ -0,0 +1 @@
1
+ export { Drawer, DrawerClose, DrawerContent, DrawerDescription, DrawerFooter, DrawerHeader, DrawerTitle, DrawerTrigger } from "./Drawer.js";
@@ -14,22 +14,25 @@ export type FieldProps = {
14
14
  /** Dims the label and disables the control. */
15
15
  disabled?: boolean;
16
16
  /**
17
- * The control: one element, such as Input, which gets an id, aria-describedby, aria-invalid, aria-required and disabled.
18
- * When the control sits inside a wrapper such as InputGroup, pass a function instead and spread the props it receives
19
- * onto the input itself.
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
20
  */
21
21
  children: ReactElement<Record<string, unknown>> | ((props: FieldControlProps) => ReactNode);
22
22
  };
23
23
  /** The props Field hands to a function child, to spread onto the control. */
24
24
  export type FieldControlProps = {
25
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;
26
28
  'aria-describedby'?: string;
27
29
  'aria-invalid'?: true;
28
30
  'aria-required'?: true;
29
31
  disabled?: true;
30
32
  };
31
33
  /**
32
- * A form row: label, control, hint and error. It wires the label to the control by id, links the hint and error with
33
- * aria-describedby, and sets aria-invalid when there is an error. The control keeps an id it already has.
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.
34
37
  */
35
38
  export declare function Field({ label, hint, error, info, required, disabled, children }: FieldProps): JSX.Element;
@@ -5,8 +5,9 @@ import { jsx, jsxs } from "react/jsx-runtime";
5
5
  import { cloneElement, useId } from "react";
6
6
  // src/components/field/Field.tsx
7
7
  /**
8
- * A form row: label, control, hint and error. It wires the label to the control by id, links the hint and error with
9
- * aria-describedby, and sets aria-invalid when there is an error. The control keeps an id it already has.
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.
10
11
  */
11
12
  function Field({ label, hint, error, info, required, disabled, children }) {
12
13
  const generatedId = useId();
@@ -15,9 +16,13 @@ function Field({ label, hint, error, info, required, disabled, children }) {
15
16
  const hasError = error !== void 0 && error !== null && error !== false && error !== "";
16
17
  const hasHint = !hasError && hint !== void 0 && hint !== null && hint !== false && hint !== "";
17
18
  const messageId = `${id}-message`;
18
- const described = [typeof children === "function" ? void 0 : children.props["aria-describedby"], hasError || hasHint ? messageId : void 0].filter((part) => typeof part === "string" && part !== "").join(" ");
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);
19
23
  const wiring = {
20
24
  id,
25
+ "aria-labelledby": ids(labelId, ownProp("aria-labelledby")),
21
26
  ...described === "" ? {} : { "aria-describedby": described },
22
27
  ...hasError ? { "aria-invalid": true } : {},
23
28
  ...required === true ? { "aria-required": true } : {},
@@ -31,6 +36,7 @@ function Field({ label, hint, error, info, required, disabled, children }) {
31
36
  /* @__PURE__ */ jsxs("div", {
32
37
  className: `rt:flex rt:items-center rt:gap-1.5${disabled === true ? " rt:opacity-50" : ""}`,
33
38
  children: [/* @__PURE__ */ jsxs(Label, {
39
+ id: labelId,
34
40
  htmlFor: id,
35
41
  children: [label, required === true ? /* @__PURE__ */ jsx("span", {
36
42
  "aria-hidden": "true",
@@ -27,7 +27,7 @@ export interface IconProps extends Omit<SVGAttributes<SVGSVGElement>, 'className
27
27
  label?: string;
28
28
  }
29
29
  /**
30
- * Draws one icon of the registry: the design system's own glyphs, filled in a single colour, and Tabler's icons for the
30
+ * Draws one icon of the registry: the design system's own glyphs, filled (or, for check-mark, stroked) in a single colour, and Tabler's icons for the
31
31
  * rest, drawn with a 1.5px line at every size. The icon is a square box of 8, 12 or 16px and takes the colour of its
32
32
  * tone.
33
33
  */
@@ -9,7 +9,7 @@ const TONE_CLASSES = {
9
9
  primary: "rt:text-primary"
10
10
  };
11
11
  /**
12
- * Draws one icon of the registry: the design system's own glyphs, filled in a single colour, and Tabler's icons for the
12
+ * Draws one icon of the registry: the design system's own glyphs, filled (or, for check-mark, stroked) in a single colour, and Tabler's icons for the
13
13
  * rest, drawn with a 1.5px line at every size. The icon is a square box of 8, 12 or 16px and takes the colour of its
14
14
  * tone.
15
15
  */
@@ -40,13 +40,24 @@ function Icon({ name, size = 12, tone = "current", label, ...rest }) {
40
40
  strokeLinejoin: "round"
41
41
  });
42
42
  }
43
+ const { glyph } = entry;
44
+ const paint = glyph.stroke === void 0 ? {
45
+ fill: "currentColor",
46
+ stroke: "none"
47
+ } : {
48
+ fill: "none",
49
+ stroke: "currentColor",
50
+ strokeWidth: glyph.stroke,
51
+ strokeLinecap: "round",
52
+ strokeLinejoin: "round"
53
+ };
43
54
  return /* @__PURE__ */ jsx("svg", {
44
55
  ...rest,
45
56
  ...own,
46
- viewBox: entry.glyph.viewBox,
47
- fill: "currentColor",
48
- stroke: "none",
49
- children: entry.glyph.paths.map(({ d, fillRule, transform }) => /* @__PURE__ */ jsx("path", {
57
+ className: cn(own.className, "rt:overflow-visible"),
58
+ viewBox: glyph.viewBox,
59
+ ...paint,
60
+ children: glyph.paths.map(({ d, fillRule, transform }) => /* @__PURE__ */ jsx("path", {
50
61
  d,
51
62
  fillRule,
52
63
  transform