@craftzbay/ui 0.10.0 → 0.11.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (187) hide show
  1. package/README.md +18 -12
  2. package/dist-lib/__tests__/helpers/color.d.ts +9 -0
  3. package/dist-lib/__tests__/helpers/theme.d.ts +18 -0
  4. package/dist-lib/components/ui/Accordion.js +18 -7
  5. package/dist-lib/components/ui/Accordion.js.map +1 -1
  6. package/dist-lib/components/ui/Alert.d.ts +16 -0
  7. package/dist-lib/components/ui/Alert.js +47 -34
  8. package/dist-lib/components/ui/Alert.js.map +1 -1
  9. package/dist-lib/components/ui/Avatar.d.ts +1 -1
  10. package/dist-lib/components/ui/Avatar.js +45 -47
  11. package/dist-lib/components/ui/Avatar.js.map +1 -1
  12. package/dist-lib/components/ui/Badge.d.ts +3 -1
  13. package/dist-lib/components/ui/Badge.js +24 -15
  14. package/dist-lib/components/ui/Badge.js.map +1 -1
  15. package/dist-lib/components/ui/Breadcrumbs.d.ts +2 -0
  16. package/dist-lib/components/ui/Breadcrumbs.js +46 -28
  17. package/dist-lib/components/ui/Breadcrumbs.js.map +1 -1
  18. package/dist-lib/components/ui/Button.d.ts +5 -3
  19. package/dist-lib/components/ui/Button.js +59 -45
  20. package/dist-lib/components/ui/Button.js.map +1 -1
  21. package/dist-lib/components/ui/Calendar.d.ts +1 -1
  22. package/dist-lib/components/ui/Calendar.js +87 -59
  23. package/dist-lib/components/ui/Calendar.js.map +1 -1
  24. package/dist-lib/components/ui/Card.d.ts +2 -0
  25. package/dist-lib/components/ui/Card.js +48 -67
  26. package/dist-lib/components/ui/Card.js.map +1 -1
  27. package/dist-lib/components/ui/Carousel.d.ts +6 -6
  28. package/dist-lib/components/ui/Carousel.js +133 -102
  29. package/dist-lib/components/ui/Carousel.js.map +1 -1
  30. package/dist-lib/components/ui/Chart.d.ts +49 -10
  31. package/dist-lib/components/ui/Chart.js +415 -181
  32. package/dist-lib/components/ui/Chart.js.map +1 -1
  33. package/dist-lib/components/ui/Checkbox.js +42 -28
  34. package/dist-lib/components/ui/Checkbox.js.map +1 -1
  35. package/dist-lib/components/ui/Combobox.d.ts +2 -0
  36. package/dist-lib/components/ui/Combobox.js +142 -111
  37. package/dist-lib/components/ui/Combobox.js.map +1 -1
  38. package/dist-lib/components/ui/CommandPalette.d.ts +6 -3
  39. package/dist-lib/components/ui/CommandPalette.js +92 -87
  40. package/dist-lib/components/ui/CommandPalette.js.map +1 -1
  41. package/dist-lib/components/ui/ContextMenu.d.ts +7 -2
  42. package/dist-lib/components/ui/ContextMenu.js +77 -62
  43. package/dist-lib/components/ui/ContextMenu.js.map +1 -1
  44. package/dist-lib/components/ui/DataGrid.d.ts +17 -4
  45. package/dist-lib/components/ui/DataGrid.js +65 -50
  46. package/dist-lib/components/ui/DataGrid.js.map +1 -1
  47. package/dist-lib/components/ui/DatePicker.js +118 -99
  48. package/dist-lib/components/ui/DatePicker.js.map +1 -1
  49. package/dist-lib/components/ui/DesignSystemProvider.d.ts +94 -40
  50. package/dist-lib/components/ui/DesignSystemProvider.js +111 -55
  51. package/dist-lib/components/ui/DesignSystemProvider.js.map +1 -1
  52. package/dist-lib/components/ui/Dialog.d.ts +18 -3
  53. package/dist-lib/components/ui/Dialog.js +105 -80
  54. package/dist-lib/components/ui/Dialog.js.map +1 -1
  55. package/dist-lib/components/ui/Drawer.d.ts +17 -2
  56. package/dist-lib/components/ui/Drawer.js +89 -55
  57. package/dist-lib/components/ui/Drawer.js.map +1 -1
  58. package/dist-lib/components/ui/DropdownMenu.js +14 -9
  59. package/dist-lib/components/ui/DropdownMenu.js.map +1 -1
  60. package/dist-lib/components/ui/EmptyState.d.ts +2 -0
  61. package/dist-lib/components/ui/EmptyState.js +30 -16
  62. package/dist-lib/components/ui/EmptyState.js.map +1 -1
  63. package/dist-lib/components/ui/ErrorState.d.ts +13 -2
  64. package/dist-lib/components/ui/ErrorState.js +44 -23
  65. package/dist-lib/components/ui/ErrorState.js.map +1 -1
  66. package/dist-lib/components/ui/FileUpload.js +59 -56
  67. package/dist-lib/components/ui/FileUpload.js.map +1 -1
  68. package/dist-lib/components/ui/Form.d.ts +15 -2
  69. package/dist-lib/components/ui/Form.js +97 -71
  70. package/dist-lib/components/ui/Form.js.map +1 -1
  71. package/dist-lib/components/ui/Icon.d.ts +1 -1
  72. package/dist-lib/components/ui/Icon.js +20 -14
  73. package/dist-lib/components/ui/Icon.js.map +1 -1
  74. package/dist-lib/components/ui/IconButton.d.ts +2 -2
  75. package/dist-lib/components/ui/IconButton.js +28 -20
  76. package/dist-lib/components/ui/IconButton.js.map +1 -1
  77. package/dist-lib/components/ui/Input.d.ts +3 -1
  78. package/dist-lib/components/ui/Input.js +102 -79
  79. package/dist-lib/components/ui/Input.js.map +1 -1
  80. package/dist-lib/components/ui/Kbd.js +1 -1
  81. package/dist-lib/components/ui/Kbd.js.map +1 -1
  82. package/dist-lib/components/ui/MultiSelect.js +121 -107
  83. package/dist-lib/components/ui/MultiSelect.js.map +1 -1
  84. package/dist-lib/components/ui/Pagination.js +67 -62
  85. package/dist-lib/components/ui/Pagination.js.map +1 -1
  86. package/dist-lib/components/ui/Popover.js +14 -14
  87. package/dist-lib/components/ui/Popover.js.map +1 -1
  88. package/dist-lib/components/ui/Progress.d.ts +3 -3
  89. package/dist-lib/components/ui/Progress.js +77 -72
  90. package/dist-lib/components/ui/Progress.js.map +1 -1
  91. package/dist-lib/components/ui/RadioGroup.d.ts +1 -1
  92. package/dist-lib/components/ui/RadioGroup.js +69 -52
  93. package/dist-lib/components/ui/RadioGroup.js.map +1 -1
  94. package/dist-lib/components/ui/RelativeTime.d.ts +19 -0
  95. package/dist-lib/components/ui/RelativeTime.js +31 -0
  96. package/dist-lib/components/ui/RelativeTime.js.map +1 -0
  97. package/dist-lib/components/ui/ScrollArea.d.ts +9 -1
  98. package/dist-lib/components/ui/ScrollArea.js +43 -27
  99. package/dist-lib/components/ui/ScrollArea.js.map +1 -1
  100. package/dist-lib/components/ui/Select.d.ts +6 -1
  101. package/dist-lib/components/ui/Select.js +79 -71
  102. package/dist-lib/components/ui/Select.js.map +1 -1
  103. package/dist-lib/components/ui/Separator.js +1 -1
  104. package/dist-lib/components/ui/Separator.js.map +1 -1
  105. package/dist-lib/components/ui/Sheet.d.ts +9 -0
  106. package/dist-lib/components/ui/Sheet.js +69 -46
  107. package/dist-lib/components/ui/Sheet.js.map +1 -1
  108. package/dist-lib/components/ui/Sidebar.d.ts +12 -9
  109. package/dist-lib/components/ui/Sidebar.js +152 -109
  110. package/dist-lib/components/ui/Sidebar.js.map +1 -1
  111. package/dist-lib/components/ui/Skeleton.d.ts +13 -2
  112. package/dist-lib/components/ui/Skeleton.js +15 -14
  113. package/dist-lib/components/ui/Skeleton.js.map +1 -1
  114. package/dist-lib/components/ui/Slider.js +41 -29
  115. package/dist-lib/components/ui/Slider.js.map +1 -1
  116. package/dist-lib/components/ui/Snackbar.js +23 -22
  117. package/dist-lib/components/ui/Snackbar.js.map +1 -1
  118. package/dist-lib/components/ui/Spinner.js +18 -6
  119. package/dist-lib/components/ui/Spinner.js.map +1 -1
  120. package/dist-lib/components/ui/Stepper.d.ts +6 -0
  121. package/dist-lib/components/ui/Stepper.js +71 -48
  122. package/dist-lib/components/ui/Stepper.js.map +1 -1
  123. package/dist-lib/components/ui/Switch.js +46 -43
  124. package/dist-lib/components/ui/Switch.js.map +1 -1
  125. package/dist-lib/components/ui/Table.d.ts +33 -5
  126. package/dist-lib/components/ui/Table.js +141 -119
  127. package/dist-lib/components/ui/Table.js.map +1 -1
  128. package/dist-lib/components/ui/Tabs.js +50 -48
  129. package/dist-lib/components/ui/Tabs.js.map +1 -1
  130. package/dist-lib/components/ui/TagInput.js +45 -45
  131. package/dist-lib/components/ui/TagInput.js.map +1 -1
  132. package/dist-lib/components/ui/Textarea.js +65 -54
  133. package/dist-lib/components/ui/Textarea.js.map +1 -1
  134. package/dist-lib/components/ui/Timeline.js +39 -35
  135. package/dist-lib/components/ui/Timeline.js.map +1 -1
  136. package/dist-lib/components/ui/Toast.js +44 -57
  137. package/dist-lib/components/ui/Toast.js.map +1 -1
  138. package/dist-lib/components/ui/Tooltip.d.ts +4 -1
  139. package/dist-lib/components/ui/Tooltip.js +24 -22
  140. package/dist-lib/components/ui/Tooltip.js.map +1 -1
  141. package/dist-lib/components/ui/TopNav.d.ts +7 -3
  142. package/dist-lib/components/ui/TopNav.js +39 -22
  143. package/dist-lib/components/ui/TopNav.js.map +1 -1
  144. package/dist-lib/components/ui/Tree.d.ts +15 -10
  145. package/dist-lib/components/ui/Tree.js +82 -79
  146. package/dist-lib/components/ui/Tree.js.map +1 -1
  147. package/dist-lib/hooks/use-debounce.d.ts +19 -0
  148. package/dist-lib/hooks/use-debounce.js +36 -0
  149. package/dist-lib/hooks/use-debounce.js.map +1 -0
  150. package/dist-lib/hooks/use-delayed-loading.d.ts +21 -0
  151. package/dist-lib/hooks/use-delayed-loading.js +20 -0
  152. package/dist-lib/hooks/use-delayed-loading.js.map +1 -0
  153. package/dist-lib/hooks/use-field-ids.d.ts +2 -1
  154. package/dist-lib/hooks/use-field-ids.js +9 -9
  155. package/dist-lib/hooks/use-field-ids.js.map +1 -1
  156. package/dist-lib/hooks/use-media-query.d.ts +3 -1
  157. package/dist-lib/hooks/use-media-query.js +16 -12
  158. package/dist-lib/hooks/use-media-query.js.map +1 -1
  159. package/dist-lib/hooks/use-modifier-key.d.ts +13 -0
  160. package/dist-lib/hooks/use-modifier-key.js +19 -0
  161. package/dist-lib/hooks/use-modifier-key.js.map +1 -0
  162. package/dist-lib/hooks/use-toast.d.ts +7 -2
  163. package/dist-lib/hooks/use-toast.js +61 -30
  164. package/dist-lib/hooks/use-toast.js.map +1 -1
  165. package/dist-lib/icons/index.d.ts +9 -0
  166. package/dist-lib/icons/index.js +148 -130
  167. package/dist-lib/icons/index.js.map +1 -1
  168. package/dist-lib/illustrations/index.d.ts +1 -0
  169. package/dist-lib/illustrations/index.js +15 -6
  170. package/dist-lib/illustrations/index.js.map +1 -1
  171. package/dist-lib/index.d.ts +6 -0
  172. package/dist-lib/index.js +280 -260
  173. package/dist-lib/index.js.map +1 -1
  174. package/dist-lib/lib/format.d.ts +65 -0
  175. package/dist-lib/lib/format.js +95 -0
  176. package/dist-lib/lib/format.js.map +1 -0
  177. package/dist-lib/lib/return-focus.d.ts +11 -0
  178. package/dist-lib/lib/return-focus.js +12 -0
  179. package/dist-lib/lib/return-focus.js.map +1 -0
  180. package/dist-lib/lib/strings.d.ts +62 -1
  181. package/dist-lib/lib/strings.js +75 -19
  182. package/dist-lib/lib/strings.js.map +1 -1
  183. package/dist-lib/lib/strings.mn.js +50 -5
  184. package/dist-lib/lib/strings.mn.js.map +1 -1
  185. package/dist-lib/styles.css +1 -1
  186. package/dist-lib/theme.css +141 -78
  187. package/package.json +11 -4
@@ -1,66 +1,66 @@
1
1
  "use client";
2
- import { jsxs as u, jsx as o } from "react/jsx-runtime";
3
- import { forwardRef as q, useState as I, useCallback as A } from "react";
2
+ import { jsxs as f, jsx as o } from "react/jsx-runtime";
3
+ import { forwardRef as q, useState as k, useCallback as A } from "react";
4
4
  import { X as G } from "../../icons/index.js";
5
- import { cn as f } from "../../lib/utils.js";
5
+ import { cn as m } from "../../lib/utils.js";
6
6
  import { useStrings as J } from "../../hooks/use-strings.js";
7
7
  import { formatString as L } from "../../lib/strings.js";
8
8
  import { useFieldIds as O } from "../../hooks/use-field-ids.js";
9
9
  const P = q(function({
10
- value: a,
11
- defaultValue: N = [],
10
+ value: s,
11
+ defaultValue: B = [],
12
12
  onChange: i,
13
- label: m,
14
- hideLabel: k,
15
- description: s,
16
- placeholder: B,
13
+ label: a,
14
+ hideLabel: E,
15
+ description: l,
16
+ placeholder: D,
17
17
  max: g,
18
18
  disabled: p,
19
- error: l,
20
- separators: E = ["Enter", ","],
21
- id: D,
22
- "aria-describedby": S,
23
- "aria-label": T,
24
- className: j
25
- }, F) {
26
- const [K, $] = I(N), [n, d] = I(""), r = a ?? K, h = J(), z = B ?? h.tagInput.placeholder, c = !!l, b = c && l !== !0, { fieldId: x, helperId: H, errorId: M, describedBy: R } = O(D, {
27
- hasHelper: !!s,
28
- hasError: b,
29
- extra: S
30
- }), v = A(
19
+ error: c,
20
+ separators: S = ["Enter", ","],
21
+ id: b,
22
+ "aria-describedby": T,
23
+ "aria-label": j,
24
+ className: F
25
+ }, K) {
26
+ const [$, z] = k(B), [n, d] = k(""), r = s ?? $, h = J(), x = D ?? h.tagInput.placeholder, u = !!c, v = u && c !== !0, { fieldId: y, helperId: H, errorId: M, describedBy: R } = O(b, {
27
+ hasHelper: !!l,
28
+ hasError: v,
29
+ extra: T
30
+ }), w = A(
31
31
  (e) => {
32
- a === void 0 && $(e), i == null || i(e);
32
+ s === void 0 && z(e), i == null || i(e);
33
33
  },
34
- [i, a]
35
- ), y = (e) => {
34
+ [i, s]
35
+ ), I = (e) => {
36
36
  const t = e.trim();
37
- !t || r.includes(t) || g !== void 0 && r.length >= g || v([...r, t]);
38
- }, w = (e) => v(r.filter((t, _) => _ !== e)), X = (e) => {
39
- E.includes(e.key) ? (e.preventDefault(), y(n), d("")) : e.key === "Backspace" && !n && r.length && w(r.length - 1);
37
+ !t || r.includes(t) || g !== void 0 && r.length >= g || w([...r, t]);
38
+ }, N = (e) => w(r.filter((t, _) => _ !== e)), X = (e) => {
39
+ S.includes(e.key) ? (e.preventDefault(), I(n), d("")) : e.key === "Backspace" && !n && r.length && N(r.length - 1);
40
40
  };
41
- return /* @__PURE__ */ u("div", { ref: F, className: f("flex flex-col gap-1.5", j), children: [
42
- m && /* @__PURE__ */ o(
41
+ return /* @__PURE__ */ f("div", { ref: K, className: m("flex flex-col gap-1.5", F), children: [
42
+ a && /* @__PURE__ */ o(
43
43
  "label",
44
44
  {
45
- htmlFor: x,
46
- className: f("text-foreground text-sm font-medium", k && "sr-only"),
47
- children: m
45
+ htmlFor: y,
46
+ className: m("text-foreground text-sm font-medium", E && "sr-only"),
47
+ children: a
48
48
  }
49
49
  ),
50
- /* @__PURE__ */ u(
50
+ /* @__PURE__ */ f(
51
51
  "div",
52
52
  {
53
- className: f(
53
+ className: m(
54
54
  // Cap growth at ~3 chip rows and scroll inside — an unbounded field
55
55
  // pushes the surrounding layout around as tags are added.
56
56
  "bg-card flex max-h-24 min-h-9 w-full flex-wrap items-center gap-1.5 overflow-y-auto rounded-md border px-2 py-1.5",
57
57
  "transition-colors duration-[var(--duration-fast)] ease-[var(--ease-out)]",
58
58
  "focus-within:ring-offset-background focus-within:ring-2 focus-within:ring-offset-2",
59
- c ? "border-danger focus-within:border-danger focus-within:ring-danger" : "border-border focus-within:border-accent focus-within:ring-ring",
59
+ u ? "border-danger focus-within:border-danger focus-within:ring-danger" : "border-border-input focus-within:border-accent focus-within:ring-ring",
60
60
  p && "pointer-events-none opacity-50"
61
61
  ),
62
62
  children: [
63
- r.map((e, t) => /* @__PURE__ */ u(
63
+ r.map((e, t) => /* @__PURE__ */ f(
64
64
  "span",
65
65
  {
66
66
  className: "bg-background-muted inline-flex items-center gap-1 rounded-sm px-2 py-0.5 text-xs",
@@ -70,8 +70,8 @@ const P = q(function({
70
70
  "button",
71
71
  {
72
72
  type: "button",
73
- onClick: () => w(t),
74
- className: "text-foreground-muted hover:bg-background-subtle hover:text-foreground focus-visible:ring-ring rounded-sm p-0.5 outline-none focus-visible:ring-2",
73
+ onClick: () => N(t),
74
+ className: "text-foreground-muted hover:bg-background-subtle hover:text-foreground focus-visible:ring-ring focus-visible:ring-offset-background relative rounded-sm p-0.5 outline-none before:absolute before:-inset-1.5 before:content-[''] focus-visible:ring-2 focus-visible:ring-offset-1",
75
75
  "aria-label": L(h.tagInput.remove, { tag: e }),
76
76
  children: /* @__PURE__ */ o(G, { className: "size-3", "aria-hidden": !0 })
77
77
  }
@@ -83,25 +83,25 @@ const P = q(function({
83
83
  /* @__PURE__ */ o(
84
84
  "input",
85
85
  {
86
- id: x,
86
+ id: y,
87
87
  value: n,
88
88
  disabled: p,
89
89
  onChange: (e) => d(e.target.value),
90
90
  onKeyDown: X,
91
91
  onBlur: () => {
92
- n && (y(n), d(""));
92
+ n && (I(n), d(""));
93
93
  },
94
- placeholder: r.length === 0 ? z : void 0,
95
- "aria-label": T,
96
- "aria-invalid": c || void 0,
94
+ placeholder: r.length === 0 ? x : void 0,
95
+ "aria-label": j ?? (a || b ? void 0 : x),
96
+ "aria-invalid": u || void 0,
97
97
  "aria-describedby": R,
98
- className: "placeholder:text-foreground-subtle min-w-[8ch] flex-1 bg-transparent text-sm outline-none"
98
+ className: "placeholder:text-foreground-subtle min-w-[8ch] flex-1 bg-transparent text-lg outline-none md:text-sm"
99
99
  }
100
100
  )
101
101
  ]
102
102
  }
103
103
  ),
104
- b ? /* @__PURE__ */ o("p", { id: M, role: "alert", className: "text-danger-text text-xs", children: l }) : s ? /* @__PURE__ */ o("p", { id: H, className: "text-foreground-subtle text-xs", children: s }) : null
104
+ v ? /* @__PURE__ */ o("p", { id: M, className: "text-danger-text text-xs", children: c }) : l ? /* @__PURE__ */ o("p", { id: H, className: "text-foreground-subtle text-xs", children: l }) : null
105
105
  ] });
106
106
  });
107
107
  P.displayName = "TagInput";
@@ -1 +1 @@
1
- {"version":3,"file":"TagInput.js","sources":["../../../src/components/ui/TagInput.tsx"],"sourcesContent":["'use client';\n\nimport { forwardRef, useCallback, useState, type KeyboardEvent, type ReactNode } from 'react';\nimport { X } from '@/icons';\nimport { cn } from '@/lib/utils';\nimport { useStrings } from '@/hooks/use-strings';\nimport { formatString } from '@/lib/strings';\nimport { useFieldIds } from '@/hooks/use-field-ids';\n\nexport interface TagInputProps {\n /** Controlled list of tags. */\n value?: string[];\n /** Default tags when uncontrolled. */\n defaultValue?: string[];\n /** Called when the tag list changes. */\n onChange?: (tags: string[]) => void;\n /** Visible label rendered above the field. */\n label?: ReactNode;\n /** Visually conceal the label while keeping it for screen readers. */\n hideLabel?: boolean;\n /** Hint below the field. Hidden while `error` is set. */\n description?: ReactNode;\n /** Placeholder shown inside the inline input. */\n placeholder?: string;\n /** Maximum number of tags. */\n max?: number;\n /** Disable the editor. */\n disabled?: boolean;\n /**\n * Validation state. `true` only paints the danger border; a ReactNode also\n * renders the message below the field and wires `aria-describedby`.\n */\n error?: boolean | ReactNode;\n /** Treat these characters as separators (default: Enter + comma). */\n separators?: string[];\n /** Consumer-supplied id for the inline input. */\n id?: string;\n /** Extra ids to append to `aria-describedby`. */\n 'aria-describedby'?: string;\n 'aria-label'?: string;\n className?: string;\n}\n\n/**\n * Chip-based multi-value input. Press Enter or comma to add. Backspace on an\n * empty input removes the last tag.\n */\nexport const TagInput = forwardRef<HTMLDivElement, TagInputProps>(function TagInput(\n {\n value,\n defaultValue = [],\n onChange,\n label,\n hideLabel,\n description,\n placeholder: placeholderProp,\n max,\n disabled,\n error,\n separators = ['Enter', ','],\n id,\n 'aria-describedby': ariaDescribedby,\n 'aria-label': ariaLabel,\n className,\n },\n ref,\n) {\n const [internal, setInternal] = useState<string[]>(defaultValue);\n const [draft, setDraft] = useState('');\n const tags = value ?? internal;\n\n const strings = useStrings();\n const placeholder = placeholderProp ?? strings.tagInput.placeholder;\n const isError = Boolean(error);\n const hasErrorMessage = isError && error !== true;\n const { fieldId, helperId, errorId, describedBy } = useFieldIds(id, {\n hasHelper: Boolean(description),\n hasError: hasErrorMessage,\n extra: ariaDescribedby,\n });\n\n const update = useCallback(\n (next: string[]) => {\n if (value === undefined) setInternal(next);\n onChange?.(next);\n },\n [onChange, value],\n );\n\n const commit = (raw: string) => {\n const t = raw.trim();\n if (!t || tags.includes(t) || (max !== undefined && tags.length >= max)) return;\n update([...tags, t]);\n };\n\n const remove = (idx: number) => update(tags.filter((_, i) => i !== idx));\n\n const onKey = (e: KeyboardEvent<HTMLInputElement>) => {\n if (separators.includes(e.key)) {\n e.preventDefault();\n commit(draft);\n setDraft('');\n } else if (e.key === 'Backspace' && !draft && tags.length) {\n remove(tags.length - 1);\n }\n };\n\n return (\n <div ref={ref} className={cn('flex flex-col gap-1.5', className)}>\n {label && (\n <label\n htmlFor={fieldId}\n className={cn('text-foreground text-sm font-medium', hideLabel && 'sr-only')}\n >\n {label}\n </label>\n )}\n <div\n className={cn(\n // Cap growth at ~3 chip rows and scroll inside — an unbounded field\n // pushes the surrounding layout around as tags are added.\n 'bg-card flex max-h-24 min-h-9 w-full flex-wrap items-center gap-1.5 overflow-y-auto rounded-md border px-2 py-1.5',\n 'transition-colors duration-[var(--duration-fast)] ease-[var(--ease-out)]',\n 'focus-within:ring-offset-background focus-within:ring-2 focus-within:ring-offset-2',\n isError\n ? 'border-danger focus-within:border-danger focus-within:ring-danger'\n : 'border-border focus-within:border-accent focus-within:ring-ring',\n disabled && 'pointer-events-none opacity-50',\n )}\n >\n {tags.map((t, i) => (\n <span\n key={`${t}-${i}`}\n className=\"bg-background-muted inline-flex items-center gap-1 rounded-sm px-2 py-0.5 text-xs\"\n >\n {t}\n <button\n type=\"button\"\n onClick={() => remove(i)}\n className=\"text-foreground-muted hover:bg-background-subtle hover:text-foreground focus-visible:ring-ring rounded-sm p-0.5 outline-none focus-visible:ring-2\"\n aria-label={formatString(strings.tagInput.remove, { tag: t })}\n >\n <X className=\"size-3\" aria-hidden />\n </button>\n </span>\n ))}\n <input\n id={fieldId}\n value={draft}\n disabled={disabled}\n onChange={(e) => setDraft(e.target.value)}\n onKeyDown={onKey}\n onBlur={() => {\n if (draft) {\n commit(draft);\n setDraft('');\n }\n }}\n placeholder={tags.length === 0 ? placeholder : undefined}\n aria-label={ariaLabel}\n aria-invalid={isError || undefined}\n aria-describedby={describedBy}\n className=\"placeholder:text-foreground-subtle min-w-[8ch] flex-1 bg-transparent text-sm outline-none\"\n />\n </div>\n {hasErrorMessage ? (\n <p id={errorId} role=\"alert\" className=\"text-danger-text text-xs\">\n {error}\n </p>\n ) : description ? (\n <p id={helperId} className=\"text-foreground-subtle text-xs\">\n {description}\n </p>\n ) : null}\n </div>\n );\n});\nTagInput.displayName = 'TagInput';\n"],"names":["TagInput","forwardRef","value","defaultValue","onChange","label","hideLabel","description","placeholderProp","max","disabled","error","separators","id","ariaDescribedby","ariaLabel","className","ref","internal","setInternal","useState","draft","setDraft","tags","strings","useStrings","placeholder","isError","hasErrorMessage","fieldId","helperId","errorId","describedBy","useFieldIds","update","useCallback","next","commit","raw","remove","idx","_","i","onKey","cn","jsx","jsxs","t","formatString","X"],"mappings":"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"}
1
+ {"version":3,"file":"TagInput.js","sources":["../../../src/components/ui/TagInput.tsx"],"sourcesContent":["'use client';\n\nimport { forwardRef, useCallback, useState, type KeyboardEvent, type ReactNode } from 'react';\nimport { X } from '@/icons';\nimport { cn } from '@/lib/utils';\nimport { useStrings } from '@/hooks/use-strings';\nimport { formatString } from '@/lib/strings';\nimport { useFieldIds } from '@/hooks/use-field-ids';\n\nexport interface TagInputProps {\n /** Controlled list of tags. */\n value?: string[];\n /** Default tags when uncontrolled. */\n defaultValue?: string[];\n /** Called when the tag list changes. */\n onChange?: (tags: string[]) => void;\n /** Visible label rendered above the field. */\n label?: ReactNode;\n /** Visually conceal the label while keeping it for screen readers. */\n hideLabel?: boolean;\n /** Hint below the field. Hidden while `error` is set. */\n description?: ReactNode;\n /** Placeholder shown inside the inline input. */\n placeholder?: string;\n /** Maximum number of tags. */\n max?: number;\n /** Disable the editor. */\n disabled?: boolean;\n /**\n * Validation state. `true` only paints the danger border; a ReactNode also\n * renders the message below the field and wires `aria-describedby`.\n */\n error?: boolean | ReactNode;\n /** Treat these characters as separators (default: Enter + comma). */\n separators?: string[];\n /** Consumer-supplied id for the inline input. */\n id?: string;\n /** Extra ids to append to `aria-describedby`. */\n 'aria-describedby'?: string;\n 'aria-label'?: string;\n className?: string;\n}\n\n/**\n * Chip-based multi-value input. Press Enter or comma to add. Backspace on an\n * empty input removes the last tag.\n */\nexport const TagInput = forwardRef<HTMLDivElement, TagInputProps>(function TagInput(\n {\n value,\n defaultValue = [],\n onChange,\n label,\n hideLabel,\n description,\n placeholder: placeholderProp,\n max,\n disabled,\n error,\n separators = ['Enter', ','],\n id,\n 'aria-describedby': ariaDescribedby,\n 'aria-label': ariaLabel,\n className,\n },\n ref,\n) {\n const [internal, setInternal] = useState<string[]>(defaultValue);\n const [draft, setDraft] = useState('');\n const tags = value ?? internal;\n\n const strings = useStrings();\n const placeholder = placeholderProp ?? strings.tagInput.placeholder;\n const isError = Boolean(error);\n const hasErrorMessage = isError && error !== true;\n const { fieldId, helperId, errorId, describedBy } = useFieldIds(id, {\n hasHelper: Boolean(description),\n hasError: hasErrorMessage,\n extra: ariaDescribedby,\n });\n\n const update = useCallback(\n (next: string[]) => {\n if (value === undefined) setInternal(next);\n onChange?.(next);\n },\n [onChange, value],\n );\n\n const commit = (raw: string) => {\n const t = raw.trim();\n if (!t || tags.includes(t) || (max !== undefined && tags.length >= max)) return;\n update([...tags, t]);\n };\n\n const remove = (idx: number) => update(tags.filter((_, i) => i !== idx));\n\n const onKey = (e: KeyboardEvent<HTMLInputElement>) => {\n if (separators.includes(e.key)) {\n e.preventDefault();\n commit(draft);\n setDraft('');\n } else if (e.key === 'Backspace' && !draft && tags.length) {\n remove(tags.length - 1);\n }\n };\n\n return (\n <div ref={ref} className={cn('flex flex-col gap-1.5', className)}>\n {label && (\n <label\n htmlFor={fieldId}\n className={cn('text-foreground text-sm font-medium', hideLabel && 'sr-only')}\n >\n {label}\n </label>\n )}\n <div\n className={cn(\n // Cap growth at ~3 chip rows and scroll inside — an unbounded field\n // pushes the surrounding layout around as tags are added.\n 'bg-card flex max-h-24 min-h-9 w-full flex-wrap items-center gap-1.5 overflow-y-auto rounded-md border px-2 py-1.5',\n 'transition-colors duration-[var(--duration-fast)] ease-[var(--ease-out)]',\n 'focus-within:ring-offset-background focus-within:ring-2 focus-within:ring-offset-2',\n isError\n ? 'border-danger focus-within:border-danger focus-within:ring-danger'\n : 'border-border-input focus-within:border-accent focus-within:ring-ring',\n disabled && 'pointer-events-none opacity-50',\n )}\n >\n {tags.map((t, i) => (\n <span\n key={`${t}-${i}`}\n className=\"bg-background-muted inline-flex items-center gap-1 rounded-sm px-2 py-0.5 text-xs\"\n >\n {t}\n <button\n type=\"button\"\n onClick={() => remove(i)}\n // 16px glyph box + halo → ≥24px hit area (WCAG 2.5.8) without growing the chip.\n className=\"text-foreground-muted hover:bg-background-subtle hover:text-foreground focus-visible:ring-ring focus-visible:ring-offset-background relative rounded-sm p-0.5 outline-none before:absolute before:-inset-1.5 before:content-[''] focus-visible:ring-2 focus-visible:ring-offset-1\"\n aria-label={formatString(strings.tagInput.remove, { tag: t })}\n >\n <X className=\"size-3\" aria-hidden />\n </button>\n </span>\n ))}\n <input\n id={fieldId}\n value={draft}\n disabled={disabled}\n onChange={(e) => setDraft(e.target.value)}\n onKeyDown={onKey}\n onBlur={() => {\n if (draft) {\n commit(draft);\n setDraft('');\n }\n }}\n placeholder={tags.length === 0 ? placeholder : undefined}\n // Unlabelled field: fall back to the placeholder. A consumer `id`\n // implies an external <label htmlFor>.\n aria-label={ariaLabel ?? (label || id ? undefined : placeholder)}\n aria-invalid={isError || undefined}\n aria-describedby={describedBy}\n className=\"placeholder:text-foreground-subtle min-w-[8ch] flex-1 bg-transparent text-lg outline-none md:text-sm\"\n />\n </div>\n {hasErrorMessage ? (\n <p id={errorId} className=\"text-danger-text text-xs\">\n {error}\n </p>\n ) : description ? (\n <p id={helperId} className=\"text-foreground-subtle text-xs\">\n {description}\n </p>\n ) : null}\n </div>\n );\n});\nTagInput.displayName = 'TagInput';\n"],"names":["TagInput","forwardRef","value","defaultValue","onChange","label","hideLabel","description","placeholderProp","max","disabled","error","separators","id","ariaDescribedby","ariaLabel","className","ref","internal","setInternal","useState","draft","setDraft","tags","strings","useStrings","placeholder","isError","hasErrorMessage","fieldId","helperId","errorId","describedBy","useFieldIds","update","useCallback","next","commit","raw","remove","idx","_","i","onKey","cn","jsx","jsxs","t","formatString","X"],"mappings":"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"}
@@ -1,77 +1,88 @@
1
1
  "use client";
2
- import { jsxs as $, jsx as s } from "react/jsx-runtime";
3
- import { forwardRef as j, useId as k, useRef as F, useCallback as B, useEffect as E } from "react";
4
- import { cn as c } from "../../lib/utils.js";
5
- const M = j(function({
6
- className: h,
7
- label: a,
8
- helperText: n,
9
- error: r,
10
- autoResize: t,
11
- minRows: v = 3,
12
- maxRows: f = 12,
13
- hideLabel: y,
14
- id: H,
2
+ import { jsxs as Y, jsx as n } from "react/jsx-runtime";
3
+ import { forwardRef as $, useRef as q, useCallback as A, useEffect as H } from "react";
4
+ import { cn as b } from "../../lib/utils.js";
5
+ import { useFieldIds as D } from "../../hooks/use-field-ids.js";
6
+ const G = $(function({
7
+ className: N,
8
+ label: p,
9
+ helperText: a,
10
+ error: i,
11
+ autoResize: r,
12
+ minRows: d = 3,
13
+ maxRows: m = 12,
14
+ hideLabel: E,
15
+ id: T,
15
16
  onChange: l,
16
- value: u,
17
- defaultValue: g,
18
- ...N
19
- }, i) {
20
- const I = k(), o = H ?? I, m = `${o}-helper`, b = `${o}-error`, x = F(null), w = (e) => {
21
- x.current = e, typeof i == "function" ? i(e) : i && (i.current = e);
22
- }, d = B(() => {
23
- const e = x.current;
24
- if (!e || !t) return;
17
+ value: g,
18
+ defaultValue: x,
19
+ "aria-describedby": j,
20
+ "aria-invalid": c,
21
+ ...k
22
+ }, s) {
23
+ const w = !!i || c === !0 || c === "true", { fieldId: h, helperId: I, errorId: M, describedBy: O } = D(T, {
24
+ hasHelper: !!a,
25
+ hasError: !!i,
26
+ extra: j
27
+ }), f = q(null), W = (e) => {
28
+ f.current = e, typeof s == "function" ? s(e) : s && (s.current = e);
29
+ }, o = A(() => {
30
+ const e = f.current;
31
+ if (!e || !r) return;
32
+ const t = getComputedStyle(e), v = parseFloat(t.lineHeight) || 20, y = (parseFloat(t.paddingTop) || 0) + (parseFloat(t.paddingBottom) || 0), u = (parseFloat(t.borderTopWidth) || 0) + (parseFloat(t.borderBottomWidth) || 0), S = v * d + y + u, F = v * m + y + u;
25
33
  e.style.height = "auto";
26
- const p = (parseFloat(getComputedStyle(e).lineHeight) || 20) * f;
27
- e.style.height = `${Math.min(e.scrollHeight, p)}px`, e.style.overflowY = e.scrollHeight > p ? "auto" : "hidden";
28
- }, [t, f]);
29
- return E(() => {
30
- t && d();
31
- }, [t, d, u, g]), /* @__PURE__ */ $("div", { className: c("flex flex-col gap-1.5", h), children: [
32
- a && /* @__PURE__ */ s(
34
+ const B = e.scrollHeight + u;
35
+ e.style.height = `${Math.min(Math.max(B, S), F)}px`, e.style.overflowY = B > F ? "auto" : "hidden";
36
+ }, [r, d, m]);
37
+ return H(() => {
38
+ r && o();
39
+ }, [r, o, g, x]), H(() => {
40
+ const e = f.current;
41
+ if (!r || !e || typeof ResizeObserver > "u") return;
42
+ const t = new ResizeObserver(() => o());
43
+ return t.observe(e), () => t.disconnect();
44
+ }, [r, o]), /* @__PURE__ */ Y("div", { className: b("flex flex-col gap-1.5", N), children: [
45
+ p && /* @__PURE__ */ n(
33
46
  "label",
34
47
  {
35
- htmlFor: o,
36
- className: c(
37
- "text-sm font-medium text-foreground",
38
- y && "sr-only"
39
- ),
40
- children: a
48
+ htmlFor: h,
49
+ className: b("text-foreground text-sm font-medium", E && "sr-only"),
50
+ children: p
41
51
  }
42
52
  ),
43
- /* @__PURE__ */ s(
53
+ /* @__PURE__ */ n(
44
54
  "textarea",
45
55
  {
46
- ref: w,
47
- id: o,
48
- rows: v,
49
- value: u,
50
- defaultValue: g,
56
+ ref: W,
57
+ id: h,
58
+ rows: d,
59
+ value: g,
60
+ defaultValue: x,
51
61
  onChange: (e) => {
52
- l == null || l(e), t && d();
62
+ l == null || l(e), r && o();
53
63
  },
54
- "aria-invalid": !!r || void 0,
55
- "aria-describedby": r ? b : n ? m : void 0,
56
- className: c(
57
- "rounded-md border bg-card px-3 py-2 text-lg md:text-sm text-foreground",
64
+ "aria-invalid": w ? !0 : c,
65
+ "aria-describedby": O,
66
+ className: b(
67
+ "bg-card text-foreground rounded-md border px-3 py-2 text-lg md:text-sm",
58
68
  "placeholder:text-foreground-subtle",
59
69
  "transition-colors duration-[var(--duration-fast)] ease-[var(--ease-out)]",
60
70
  "outline-none",
61
- "focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
71
+ "focus-visible:ring-ring focus-visible:ring-2 focus-visible:ring-offset-2",
62
72
  "focus-visible:ring-offset-background",
63
- "disabled:opacity-50 disabled:cursor-not-allowed",
64
- r ? "border-danger focus-visible:border-danger focus-visible:ring-danger" : "border-border-input focus-visible:border-accent",
65
- t ? "resize-none" : "resize-y min-h-20"
73
+ "disabled:cursor-not-allowed disabled:opacity-50",
74
+ "border-border-input focus-visible:border-accent",
75
+ "aria-invalid:border-danger aria-invalid:focus-visible:border-danger aria-invalid:focus-visible:ring-danger",
76
+ r ? "resize-none" : "min-h-20 resize-y"
66
77
  ),
67
- ...N
78
+ ...k
68
79
  }
69
80
  ),
70
- r ? /* @__PURE__ */ s("p", { id: b, role: "alert", className: "text-xs text-danger-text", children: r }) : n ? /* @__PURE__ */ s("p", { id: m, className: "text-xs text-foreground-subtle", children: n }) : null
81
+ i ? /* @__PURE__ */ n("p", { id: M, className: "text-danger-text text-xs", children: i }) : a ? /* @__PURE__ */ n("p", { id: I, className: "text-foreground-subtle text-xs", children: a }) : null
71
82
  ] });
72
83
  });
73
- M.displayName = "Textarea";
84
+ G.displayName = "Textarea";
74
85
  export {
75
- M as Textarea
86
+ G as Textarea
76
87
  };
77
88
  //# sourceMappingURL=Textarea.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"Textarea.js","sources":["../../../src/components/ui/Textarea.tsx"],"sourcesContent":["'use client';\n\nimport {\n forwardRef,\n useCallback,\n useEffect,\n useId,\n useRef,\n type TextareaHTMLAttributes,\n type ReactNode,\n} from 'react';\nimport { cn } from '@/lib/utils';\n\nexport interface TextareaProps\n extends TextareaHTMLAttributes<HTMLTextAreaElement> {\n /** Visible label above the field. */\n label?: ReactNode;\n /** Hint below the field. Hidden when `error` is set. */\n helperText?: ReactNode;\n /** Validation message. Sets `aria-invalid`. */\n error?: ReactNode;\n /** Grow vertically as content is added. */\n autoResize?: boolean;\n /** Minimum rows when `autoResize`. Defaults to 3. */\n minRows?: number;\n /** Maximum rows before scrolling when `autoResize`. Defaults to 12. */\n maxRows?: number;\n /** Visually hide the label while keeping it accessible. */\n hideLabel?: boolean;\n}\n\n/**\n * Multi-line text input. With `autoResize`, height tracks content from\n * `minRows` to `maxRows`. Without it, behaves like a native `<textarea>`.\n *\n * @example Comment box\n * <Textarea label=\"Note\" autoResize minRows={3} maxRows={10} />\n *\n * @example With error\n * <Textarea label=\"Description\"\n * error={errors.description?.message}\n * {...register('description')} />\n *\n * @do Default to `autoResize` for multi-line free-form input.\n * @dont Add `resize-none` without auto-resize — users will be stuck with a\n * 3-line box for long content.\n */\nexport const Textarea = forwardRef<HTMLTextAreaElement, TextareaProps>(function Textarea(\n {\n className,\n label,\n helperText,\n error,\n autoResize,\n minRows = 3,\n maxRows = 12,\n hideLabel,\n id,\n onChange,\n value,\n defaultValue,\n ...props\n },\n ref,\n) {\n const autoId = useId();\n const fieldId = id ?? autoId;\n const helperId = `${fieldId}-helper`;\n const errorId = `${fieldId}-error`;\n const innerRef = useRef<HTMLTextAreaElement | null>(null);\n\n // Stash a ref locally and forward to the consumer.\n const setRef = (node: HTMLTextAreaElement | null) => {\n innerRef.current = node;\n if (typeof ref === 'function') ref(node);\n else if (ref) (ref as React.MutableRefObject<HTMLTextAreaElement | null>).current = node;\n };\n\n const recompute = useCallback(() => {\n const el = innerRef.current;\n if (!el || !autoResize) return;\n el.style.height = 'auto';\n const lineHeight = parseFloat(getComputedStyle(el).lineHeight) || 20;\n const maxH = lineHeight * maxRows;\n el.style.height = `${Math.min(el.scrollHeight, maxH)}px`;\n el.style.overflowY = el.scrollHeight > maxH ? 'auto' : 'hidden';\n }, [autoResize, maxRows]);\n\n useEffect(() => {\n if (autoResize) recompute();\n }, [autoResize, recompute, value, defaultValue]);\n\n return (\n <div className={cn('flex flex-col gap-1.5', className)}>\n {label && (\n <label\n htmlFor={fieldId}\n className={cn(\n 'text-sm font-medium text-foreground',\n hideLabel && 'sr-only',\n )}\n >\n {label}\n </label>\n )}\n <textarea\n ref={setRef}\n id={fieldId}\n rows={minRows}\n value={value}\n defaultValue={defaultValue}\n onChange={(e) => {\n onChange?.(e);\n if (autoResize) recompute();\n }}\n aria-invalid={Boolean(error) || undefined}\n aria-describedby={error ? errorId : helperText ? helperId : undefined}\n className={cn(\n 'rounded-md border bg-card px-3 py-2 text-lg md:text-sm text-foreground',\n 'placeholder:text-foreground-subtle',\n 'transition-colors duration-[var(--duration-fast)] ease-[var(--ease-out)]',\n 'outline-none',\n 'focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2',\n 'focus-visible:ring-offset-background',\n 'disabled:opacity-50 disabled:cursor-not-allowed',\n error\n ? 'border-danger focus-visible:border-danger focus-visible:ring-danger'\n : 'border-border-input focus-visible:border-accent',\n autoResize ? 'resize-none' : 'resize-y min-h-20',\n )}\n {...props}\n />\n {error ? (\n <p id={errorId} role=\"alert\" className=\"text-xs text-danger-text\">\n {error}\n </p>\n ) : helperText ? (\n <p id={helperId} className=\"text-xs text-foreground-subtle\">\n {helperText}\n </p>\n ) : null}\n </div>\n );\n});\n\nTextarea.displayName = 'Textarea';\n"],"names":["Textarea","forwardRef","className","label","helperText","error","autoResize","minRows","maxRows","hideLabel","id","onChange","value","defaultValue","props","ref","autoId","useId","fieldId","helperId","errorId","innerRef","useRef","setRef","node","recompute","useCallback","el","maxH","useEffect","cn","jsx"],"mappings":";;;;AA+CO,MAAMA,IAAWC,EAA+C,SACrE;AAAA,EACE,WAAAC;AAAA,EACA,OAAAC;AAAA,EACA,YAAAC;AAAA,EACA,OAAAC;AAAA,EACA,YAAAC;AAAA,EACA,SAAAC,IAAU;AAAA,EACV,SAAAC,IAAU;AAAA,EACV,WAAAC;AAAA,EACA,IAAAC;AAAA,EACA,UAAAC;AAAA,EACA,OAAAC;AAAA,EACA,cAAAC;AAAA,EACA,GAAGC;AACL,GACAC,GACA;AACA,QAAMC,IAASC,EAAA,GACTC,IAAUR,KAAMM,GAChBG,IAAW,GAAGD,CAAO,WACrBE,IAAU,GAAGF,CAAO,UACpBG,IAAWC,EAAmC,IAAI,GAGlDC,IAAS,CAACC,MAAqC;AACnD,IAAAH,EAAS,UAAUG,GACf,OAAOT,KAAQ,aAAYA,EAAIS,CAAI,IAC9BT,MAAMA,EAA2D,UAAUS;AAAA,EACtF,GAEMC,IAAYC,EAAY,MAAM;AAClC,UAAMC,IAAKN,EAAS;AACpB,QAAI,CAACM,KAAM,CAACrB,EAAY;AACxB,IAAAqB,EAAG,MAAM,SAAS;AAElB,UAAMC,KADa,WAAW,iBAAiBD,CAAE,EAAE,UAAU,KAAK,MACxCnB;AAC1B,IAAAmB,EAAG,MAAM,SAAS,GAAG,KAAK,IAAIA,EAAG,cAAcC,CAAI,CAAC,MACpDD,EAAG,MAAM,YAAYA,EAAG,eAAeC,IAAO,SAAS;AAAA,EACzD,GAAG,CAACtB,GAAYE,CAAO,CAAC;AAExB,SAAAqB,EAAU,MAAM;AACd,IAAIvB,KAAYmB,EAAA;AAAA,EAClB,GAAG,CAACnB,GAAYmB,GAAWb,GAAOC,CAAY,CAAC,qBAG5C,OAAA,EAAI,WAAWiB,EAAG,yBAAyB5B,CAAS,GAClD,UAAA;AAAA,IAAAC,KACC,gBAAA4B;AAAA,MAAC;AAAA,MAAA;AAAA,QACC,SAASb;AAAA,QACT,WAAWY;AAAA,UACT;AAAA,UACArB,KAAa;AAAA,QAAA;AAAA,QAGd,UAAAN;AAAA,MAAA;AAAA,IAAA;AAAA,IAGL,gBAAA4B;AAAA,MAAC;AAAA,MAAA;AAAA,QACC,KAAKR;AAAA,QACL,IAAIL;AAAA,QACJ,MAAMX;AAAA,QACN,OAAAK;AAAA,QACA,cAAAC;AAAA,QACA,UAAU,CAAC,MAAM;AACf,UAAAF,KAAA,QAAAA,EAAW,IACPL,KAAYmB,EAAA;AAAA,QAClB;AAAA,QACA,gBAAc,EAAQpB,KAAU;AAAA,QAChC,oBAAkBA,IAAQe,IAAUhB,IAAae,IAAW;AAAA,QAC5D,WAAWW;AAAA,UACT;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACAzB,IACI,wEACA;AAAA,UACJC,IAAa,gBAAgB;AAAA,QAAA;AAAA,QAE9B,GAAGQ;AAAA,MAAA;AAAA,IAAA;AAAA,IAELT,IACC,gBAAA0B,EAAC,KAAA,EAAE,IAAIX,GAAS,MAAK,SAAQ,WAAU,4BACpC,aACH,IACEhB,sBACD,KAAA,EAAE,IAAIe,GAAU,WAAU,kCACxB,aACH,IACE;AAAA,EAAA,GACN;AAEJ,CAAC;AAEDnB,EAAS,cAAc;"}
1
+ {"version":3,"file":"Textarea.js","sources":["../../../src/components/ui/Textarea.tsx"],"sourcesContent":["'use client';\n\nimport {\n forwardRef,\n useCallback,\n useEffect,\n useRef,\n type TextareaHTMLAttributes,\n type ReactNode,\n} from 'react';\nimport { cn } from '@/lib/utils';\nimport { useFieldIds } from '@/hooks/use-field-ids';\n\nexport interface TextareaProps extends TextareaHTMLAttributes<HTMLTextAreaElement> {\n /** Visible label above the field. */\n label?: ReactNode;\n /** Hint below the field. Hidden when `error` is set. */\n helperText?: ReactNode;\n /** Validation message. Sets `aria-invalid`. */\n error?: ReactNode;\n /** Grow vertically as content is added. */\n autoResize?: boolean;\n /** Minimum rows when `autoResize`. Defaults to 3. */\n minRows?: number;\n /** Maximum rows before scrolling when `autoResize`. Defaults to 12. */\n maxRows?: number;\n /** Visually hide the label while keeping it accessible. */\n hideLabel?: boolean;\n}\n\n/**\n * Multi-line text input. With `autoResize`, height tracks content from\n * `minRows` to `maxRows`. Without it, behaves like a native `<textarea>`.\n *\n * @example Comment box\n * <Textarea label=\"Note\" autoResize minRows={3} maxRows={10} />\n *\n * @example With error\n * <Textarea label=\"Description\"\n * error={errors.description?.message}\n * {...register('description')} />\n *\n * @do Default to `autoResize` for multi-line free-form input.\n * @dont Add `resize-none` without auto-resize — users will be stuck with a\n * 3-line box for long content.\n */\nexport const Textarea = forwardRef<HTMLTextAreaElement, TextareaProps>(function Textarea(\n {\n className,\n label,\n helperText,\n error,\n autoResize,\n minRows = 3,\n maxRows = 12,\n hideLabel,\n id,\n onChange,\n value,\n defaultValue,\n 'aria-describedby': ariaDescribedby,\n 'aria-invalid': ariaInvalid,\n ...props\n },\n ref,\n) {\n const isError = Boolean(error) || ariaInvalid === true || ariaInvalid === 'true';\n const { fieldId, helperId, errorId, describedBy } = useFieldIds(id, {\n hasHelper: Boolean(helperText),\n hasError: Boolean(error),\n extra: ariaDescribedby,\n });\n const innerRef = useRef<HTMLTextAreaElement | null>(null);\n\n // Stash a ref locally and forward to the consumer.\n const setRef = (node: HTMLTextAreaElement | null) => {\n innerRef.current = node;\n if (typeof ref === 'function') ref(node);\n else if (ref) (ref as React.MutableRefObject<HTMLTextAreaElement | null>).current = node;\n };\n\n const recompute = useCallback(() => {\n const el = innerRef.current;\n if (!el || !autoResize) return;\n const cs = getComputedStyle(el);\n const lineHeight = parseFloat(cs.lineHeight) || 20;\n // scrollHeight includes padding but not borders; the element is border-box.\n const padding = (parseFloat(cs.paddingTop) || 0) + (parseFloat(cs.paddingBottom) || 0);\n const border = (parseFloat(cs.borderTopWidth) || 0) + (parseFloat(cs.borderBottomWidth) || 0);\n const minH = lineHeight * minRows + padding + border;\n const maxH = lineHeight * maxRows + padding + border;\n el.style.height = 'auto';\n const content = el.scrollHeight + border;\n el.style.height = `${Math.min(Math.max(content, minH), maxH)}px`;\n el.style.overflowY = content > maxH ? 'auto' : 'hidden';\n }, [autoResize, minRows, maxRows]);\n\n useEffect(() => {\n if (autoResize) recompute();\n }, [autoResize, recompute, value, defaultValue]);\n\n // Width changes re-wrap the text, so recompute on resize too.\n useEffect(() => {\n const el = innerRef.current;\n if (!autoResize || !el || typeof ResizeObserver === 'undefined') return;\n const ro = new ResizeObserver(() => recompute());\n ro.observe(el);\n return () => ro.disconnect();\n }, [autoResize, recompute]);\n\n return (\n <div className={cn('flex flex-col gap-1.5', className)}>\n {label && (\n <label\n htmlFor={fieldId}\n className={cn('text-foreground text-sm font-medium', hideLabel && 'sr-only')}\n >\n {label}\n </label>\n )}\n <textarea\n ref={setRef}\n id={fieldId}\n rows={minRows}\n value={value}\n defaultValue={defaultValue}\n onChange={(e) => {\n onChange?.(e);\n if (autoResize) recompute();\n }}\n aria-invalid={isError ? true : ariaInvalid}\n aria-describedby={describedBy}\n className={cn(\n 'bg-card text-foreground rounded-md border px-3 py-2 text-lg md:text-sm',\n 'placeholder:text-foreground-subtle',\n 'transition-colors duration-[var(--duration-fast)] ease-[var(--ease-out)]',\n 'outline-none',\n 'focus-visible:ring-ring focus-visible:ring-2 focus-visible:ring-offset-2',\n 'focus-visible:ring-offset-background',\n 'disabled:cursor-not-allowed disabled:opacity-50',\n 'border-border-input focus-visible:border-accent',\n 'aria-invalid:border-danger aria-invalid:focus-visible:border-danger aria-invalid:focus-visible:ring-danger',\n autoResize ? 'resize-none' : 'min-h-20 resize-y',\n )}\n {...props}\n />\n {error ? (\n <p id={errorId} className=\"text-danger-text text-xs\">\n {error}\n </p>\n ) : helperText ? (\n <p id={helperId} className=\"text-foreground-subtle text-xs\">\n {helperText}\n </p>\n ) : null}\n </div>\n );\n});\n\nTextarea.displayName = 'Textarea';\n"],"names":["Textarea","forwardRef","className","label","helperText","error","autoResize","minRows","maxRows","hideLabel","id","onChange","value","defaultValue","ariaDescribedby","ariaInvalid","props","ref","isError","fieldId","helperId","errorId","describedBy","useFieldIds","innerRef","useRef","setRef","node","recompute","useCallback","el","cs","lineHeight","padding","border","minH","maxH","content","useEffect","ro","cn","jsx"],"mappings":"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"}
@@ -1,52 +1,56 @@
1
1
  "use client";
2
- import { jsx as i, jsxs as s } from "react/jsx-runtime";
3
- import { forwardRef as a } from "react";
4
- import { cn as n } from "../../lib/utils.js";
5
- const d = a(function({ className: e, children: l, ...m }, r) {
6
- return /* @__PURE__ */ i("ol", { ref: r, className: n("relative flex flex-col gap-6", e), ...m, children: l });
2
+ import { jsx as t, jsxs as a } from "react/jsx-runtime";
3
+ import { forwardRef as s } from "react";
4
+ import { cn as l } from "../../lib/utils.js";
5
+ const c = s(function({ className: i, children: r, ...n }, m) {
6
+ return /* @__PURE__ */ t("ol", { ref: m, className: l("relative flex flex-col gap-6", i), ...n, children: r });
7
7
  });
8
- d.displayName = "Timeline";
9
- const f = a(function({ bullet: e, isLast: l, className: m, children: r, ...o }, c) {
10
- return /* @__PURE__ */ s("li", { ref: c, className: n("relative flex gap-4 pb-1", m), ...o, children: [
11
- /* @__PURE__ */ s("div", { className: "relative flex shrink-0 flex-col items-center", children: [
12
- /* @__PURE__ */ i("div", { className: "border-border bg-card text-foreground-muted flex size-6 items-center justify-center rounded-full border", children: e ?? /* @__PURE__ */ i("span", { className: "bg-accent size-2 rounded-full", "aria-hidden": !0 }) }),
13
- !l && /* @__PURE__ */ i("span", { className: "bg-border mt-1 w-px flex-1", "aria-hidden": !0 })
8
+ c.displayName = "Timeline";
9
+ const u = s(function({ bullet: i, isLast: r, className: n, children: m, ...o }, d) {
10
+ return /* @__PURE__ */ a("li", { ref: d, className: l("relative flex gap-4 pb-1", n), ...o, children: [
11
+ /* @__PURE__ */ a("div", { className: "relative flex shrink-0 flex-col items-center", children: [
12
+ /* @__PURE__ */ t("div", { className: "border-border bg-card text-foreground-muted flex size-6 items-center justify-center rounded-full border", children: i ?? /* @__PURE__ */ t("span", { className: "bg-accent size-2 rounded-full", "aria-hidden": !0 }) }),
13
+ !r && /* @__PURE__ */ t("span", { className: "bg-border mt-1 w-px flex-1", "aria-hidden": !0 })
14
14
  ] }),
15
- /* @__PURE__ */ i("div", { className: "min-w-0 flex-1 pb-4", children: r })
15
+ /* @__PURE__ */ t("div", { className: "min-w-0 flex-1 pb-4", children: m })
16
16
  ] });
17
17
  });
18
- f.displayName = "TimelineItem";
19
- function u({ className: t, children: e, dateTime: l, ...m }) {
20
- return /* @__PURE__ */ i(
18
+ u.displayName = "TimelineItem";
19
+ function f(e) {
20
+ if (!(!e || Number.isNaN(Date.parse(e))))
21
+ return e;
22
+ }
23
+ function p({ className: e, children: i, dateTime: r, ...n }) {
24
+ return /* @__PURE__ */ t(
21
25
  "time",
22
26
  {
23
- dateTime: l ?? (typeof e == "string" ? e : void 0),
24
- className: n("text-foreground-subtle block text-xs", t),
25
- ...m,
26
- children: e
27
+ dateTime: f(r ?? (typeof i == "string" ? i : void 0)),
28
+ className: l("text-foreground-subtle block text-xs", e),
29
+ ...n,
30
+ children: i
27
31
  }
28
32
  );
29
33
  }
30
- u.displayName = "TimelineTime";
31
- function p({
32
- className: t,
33
- children: e
34
+ p.displayName = "TimelineTime";
35
+ function x({
36
+ className: e,
37
+ children: i
34
38
  }) {
35
- return /* @__PURE__ */ i("p", { className: n("text-sm font-medium", t), children: e });
39
+ return /* @__PURE__ */ t("p", { className: l("text-sm font-medium", e), children: i });
36
40
  }
37
- p.displayName = "TimelineTitle";
38
- function x({
39
- className: t,
40
- children: e
41
+ x.displayName = "TimelineTitle";
42
+ function N({
43
+ className: e,
44
+ children: i
41
45
  }) {
42
- return /* @__PURE__ */ i("p", { className: n("text-foreground-muted mt-0.5 text-sm", t), children: e });
46
+ return /* @__PURE__ */ t("p", { className: l("text-foreground-muted mt-0.5 text-sm", e), children: i });
43
47
  }
44
- x.displayName = "TimelineDescription";
48
+ N.displayName = "TimelineDescription";
45
49
  export {
46
- d as Timeline,
47
- x as TimelineDescription,
48
- f as TimelineItem,
49
- u as TimelineTime,
50
- p as TimelineTitle
50
+ c as Timeline,
51
+ N as TimelineDescription,
52
+ u as TimelineItem,
53
+ p as TimelineTime,
54
+ x as TimelineTitle
51
55
  };
52
56
  //# sourceMappingURL=Timeline.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"Timeline.js","sources":["../../../src/components/ui/Timeline.tsx"],"sourcesContent":["'use client';\n\nimport { forwardRef, type HTMLAttributes, type ReactNode, type TimeHTMLAttributes } from 'react';\nimport { cn } from '@/lib/utils';\n\nexport type TimelineProps = HTMLAttributes<HTMLOListElement>;\n\nexport const Timeline = forwardRef<HTMLOListElement, TimelineProps>(function Timeline(\n { className, children, ...props },\n ref,\n) {\n return (\n <ol ref={ref} className={cn('relative flex flex-col gap-6', className)} {...props}>\n {children}\n </ol>\n );\n});\nTimeline.displayName = 'Timeline';\n\nexport interface TimelineItemProps extends HTMLAttributes<HTMLLIElement> {\n /** Optional bullet override. Default: small accent dot. */\n bullet?: ReactNode;\n /** Hide the connector line below this item — use for the last item. */\n isLast?: boolean;\n}\n\nexport const TimelineItem = forwardRef<HTMLLIElement, TimelineItemProps>(function TimelineItem(\n { bullet, isLast, className, children, ...props },\n ref,\n) {\n return (\n <li ref={ref} className={cn('relative flex gap-4 pb-1', className)} {...props}>\n <div className=\"relative flex shrink-0 flex-col items-center\">\n <div className=\"border-border bg-card text-foreground-muted flex size-6 items-center justify-center rounded-full border\">\n {bullet ?? <span className=\"bg-accent size-2 rounded-full\" aria-hidden />}\n </div>\n {!isLast && <span className=\"bg-border mt-1 w-px flex-1\" aria-hidden />}\n </div>\n <div className=\"min-w-0 flex-1 pb-4\">{children}</div>\n </li>\n );\n});\nTimelineItem.displayName = 'TimelineItem';\n\nexport interface TimelineTimeProps extends TimeHTMLAttributes<HTMLTimeElement> {\n /** Machine-readable value (ISO 8601). Defaults to `children` when it is a string. */\n dateTime?: string;\n children: ReactNode;\n}\n\nexport function TimelineTime({ className, children, dateTime, ...props }: TimelineTimeProps) {\n return (\n <time\n dateTime={dateTime ?? (typeof children === 'string' ? children : undefined)}\n className={cn('text-foreground-subtle block text-xs', className)}\n {...props}\n >\n {children}\n </time>\n );\n}\nTimelineTime.displayName = 'TimelineTime';\n\nexport function TimelineTitle({\n className,\n children,\n}: {\n className?: string;\n children: ReactNode;\n}) {\n return <p className={cn('text-sm font-medium', className)}>{children}</p>;\n}\nTimelineTitle.displayName = 'TimelineTitle';\n\nexport function TimelineDescription({\n className,\n children,\n}: {\n className?: string;\n children: ReactNode;\n}) {\n return <p className={cn('text-foreground-muted mt-0.5 text-sm', className)}>{children}</p>;\n}\nTimelineDescription.displayName = 'TimelineDescription';\n"],"names":["Timeline","forwardRef","className","children","props","ref","jsx","cn","TimelineItem","bullet","isLast","jsxs","TimelineTime","dateTime","TimelineTitle","TimelineDescription"],"mappings":";;;;AAOO,MAAMA,IAAWC,EAA4C,SAClE,EAAE,WAAAC,GAAW,UAAAC,GAAU,GAAGC,EAAA,GAC1BC,GACA;AACA,SACE,gBAAAC,EAAC,MAAA,EAAG,KAAAD,GAAU,WAAWE,EAAG,gCAAgCL,CAAS,GAAI,GAAGE,GACzE,UAAAD,EAAA,CACH;AAEJ,CAAC;AACDH,EAAS,cAAc;AAShB,MAAMQ,IAAeP,EAA6C,SACvE,EAAE,QAAAQ,GAAQ,QAAAC,GAAQ,WAAAR,GAAW,UAAAC,GAAU,GAAGC,EAAA,GAC1CC,GACA;AACA,SACE,gBAAAM,EAAC,QAAG,KAAAN,GAAU,WAAWE,EAAG,4BAA4BL,CAAS,GAAI,GAAGE,GACtE,UAAA;AAAA,IAAA,gBAAAO,EAAC,OAAA,EAAI,WAAU,gDACb,UAAA;AAAA,MAAA,gBAAAL,EAAC,OAAA,EAAI,WAAU,2GACZ,UAAAG,KAAU,gBAAAH,EAAC,UAAK,WAAU,iCAAgC,eAAW,GAAA,CAAC,EAAA,CACzE;AAAA,MACC,CAACI,KAAU,gBAAAJ,EAAC,UAAK,WAAU,8BAA6B,eAAW,GAAA,CAAC;AAAA,IAAA,GACvE;AAAA,IACA,gBAAAA,EAAC,OAAA,EAAI,WAAU,uBAAuB,UAAAH,EAAA,CAAS;AAAA,EAAA,GACjD;AAEJ,CAAC;AACDK,EAAa,cAAc;AAQpB,SAASI,EAAa,EAAE,WAAAV,GAAW,UAAAC,GAAU,UAAAU,GAAU,GAAGT,KAA4B;AAC3F,SACE,gBAAAE;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,UAAUO,MAAa,OAAOV,KAAa,WAAWA,IAAW;AAAA,MACjE,WAAWI,EAAG,wCAAwCL,CAAS;AAAA,MAC9D,GAAGE;AAAA,MAEH,UAAAD;AAAA,IAAA;AAAA,EAAA;AAGP;AACAS,EAAa,cAAc;AAEpB,SAASE,EAAc;AAAA,EAC5B,WAAAZ;AAAA,EACA,UAAAC;AACF,GAGG;AACD,2BAAQ,KAAA,EAAE,WAAWI,EAAG,uBAAuBL,CAAS,GAAI,UAAAC,GAAS;AACvE;AACAW,EAAc,cAAc;AAErB,SAASC,EAAoB;AAAA,EAClC,WAAAb;AAAA,EACA,UAAAC;AACF,GAGG;AACD,2BAAQ,KAAA,EAAE,WAAWI,EAAG,wCAAwCL,CAAS,GAAI,UAAAC,GAAS;AACxF;AACAY,EAAoB,cAAc;"}
1
+ {"version":3,"file":"Timeline.js","sources":["../../../src/components/ui/Timeline.tsx"],"sourcesContent":["'use client';\n\nimport { forwardRef, type HTMLAttributes, type ReactNode, type TimeHTMLAttributes } from 'react';\nimport { cn } from '@/lib/utils';\n\nexport type TimelineProps = HTMLAttributes<HTMLOListElement>;\n\nexport const Timeline = forwardRef<HTMLOListElement, TimelineProps>(function Timeline(\n { className, children, ...props },\n ref,\n) {\n return (\n <ol ref={ref} className={cn('relative flex flex-col gap-6', className)} {...props}>\n {children}\n </ol>\n );\n});\nTimeline.displayName = 'Timeline';\n\nexport interface TimelineItemProps extends HTMLAttributes<HTMLLIElement> {\n /** Optional bullet override. Default: small accent dot. */\n bullet?: ReactNode;\n /** Hide the connector line below this item — use for the last item. */\n isLast?: boolean;\n}\n\nexport const TimelineItem = forwardRef<HTMLLIElement, TimelineItemProps>(function TimelineItem(\n { bullet, isLast, className, children, ...props },\n ref,\n) {\n return (\n <li ref={ref} className={cn('relative flex gap-4 pb-1', className)} {...props}>\n <div className=\"relative flex shrink-0 flex-col items-center\">\n <div className=\"border-border bg-card text-foreground-muted flex size-6 items-center justify-center rounded-full border\">\n {bullet ?? <span className=\"bg-accent size-2 rounded-full\" aria-hidden />}\n </div>\n {!isLast && <span className=\"bg-border mt-1 w-px flex-1\" aria-hidden />}\n </div>\n <div className=\"min-w-0 flex-1 pb-4\">{children}</div>\n </li>\n );\n});\nTimelineItem.displayName = 'TimelineItem';\n\nexport interface TimelineTimeProps extends TimeHTMLAttributes<HTMLTimeElement> {\n /** Machine-readable value (ISO 8601). Defaults to `children` when it is a string. */\n dateTime?: string;\n children: ReactNode;\n}\n\n/** Accept only values `Date` can parse — an invalid `dateTime` is worse than none. */\nfunction validDateTime(v: string | undefined): string | undefined {\n if (!v || Number.isNaN(Date.parse(v))) return undefined;\n return v;\n}\n\nexport function TimelineTime({ className, children, dateTime, ...props }: TimelineTimeProps) {\n return (\n <time\n dateTime={validDateTime(dateTime ?? (typeof children === 'string' ? children : undefined))}\n className={cn('text-foreground-subtle block text-xs', className)}\n {...props}\n >\n {children}\n </time>\n );\n}\nTimelineTime.displayName = 'TimelineTime';\n\nexport function TimelineTitle({\n className,\n children,\n}: {\n className?: string;\n children: ReactNode;\n}) {\n return <p className={cn('text-sm font-medium', className)}>{children}</p>;\n}\nTimelineTitle.displayName = 'TimelineTitle';\n\nexport function TimelineDescription({\n className,\n children,\n}: {\n className?: string;\n children: ReactNode;\n}) {\n return <p className={cn('text-foreground-muted mt-0.5 text-sm', className)}>{children}</p>;\n}\nTimelineDescription.displayName = 'TimelineDescription';\n"],"names":["Timeline","forwardRef","className","children","props","ref","jsx","cn","TimelineItem","bullet","isLast","jsxs","validDateTime","v","TimelineTime","dateTime","TimelineTitle","TimelineDescription"],"mappings":";;;;AAOO,MAAMA,IAAWC,EAA4C,SAClE,EAAE,WAAAC,GAAW,UAAAC,GAAU,GAAGC,EAAA,GAC1BC,GACA;AACA,SACE,gBAAAC,EAAC,MAAA,EAAG,KAAAD,GAAU,WAAWE,EAAG,gCAAgCL,CAAS,GAAI,GAAGE,GACzE,UAAAD,EAAA,CACH;AAEJ,CAAC;AACDH,EAAS,cAAc;AAShB,MAAMQ,IAAeP,EAA6C,SACvE,EAAE,QAAAQ,GAAQ,QAAAC,GAAQ,WAAAR,GAAW,UAAAC,GAAU,GAAGC,EAAA,GAC1CC,GACA;AACA,SACE,gBAAAM,EAAC,QAAG,KAAAN,GAAU,WAAWE,EAAG,4BAA4BL,CAAS,GAAI,GAAGE,GACtE,UAAA;AAAA,IAAA,gBAAAO,EAAC,OAAA,EAAI,WAAU,gDACb,UAAA;AAAA,MAAA,gBAAAL,EAAC,OAAA,EAAI,WAAU,2GACZ,UAAAG,KAAU,gBAAAH,EAAC,UAAK,WAAU,iCAAgC,eAAW,GAAA,CAAC,EAAA,CACzE;AAAA,MACC,CAACI,KAAU,gBAAAJ,EAAC,UAAK,WAAU,8BAA6B,eAAW,GAAA,CAAC;AAAA,IAAA,GACvE;AAAA,IACA,gBAAAA,EAAC,OAAA,EAAI,WAAU,uBAAuB,UAAAH,EAAA,CAAS;AAAA,EAAA,GACjD;AAEJ,CAAC;AACDK,EAAa,cAAc;AAS3B,SAASI,EAAcC,GAA2C;AAChE,MAAI,GAACA,KAAK,OAAO,MAAM,KAAK,MAAMA,CAAC,CAAC;AACpC,WAAOA;AACT;AAEO,SAASC,EAAa,EAAE,WAAAZ,GAAW,UAAAC,GAAU,UAAAY,GAAU,GAAGX,KAA4B;AAC3F,SACE,gBAAAE;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,UAAUM,EAAcG,MAAa,OAAOZ,KAAa,WAAWA,IAAW,OAAU;AAAA,MACzF,WAAWI,EAAG,wCAAwCL,CAAS;AAAA,MAC9D,GAAGE;AAAA,MAEH,UAAAD;AAAA,IAAA;AAAA,EAAA;AAGP;AACAW,EAAa,cAAc;AAEpB,SAASE,EAAc;AAAA,EAC5B,WAAAd;AAAA,EACA,UAAAC;AACF,GAGG;AACD,2BAAQ,KAAA,EAAE,WAAWI,EAAG,uBAAuBL,CAAS,GAAI,UAAAC,GAAS;AACvE;AACAa,EAAc,cAAc;AAErB,SAASC,EAAoB;AAAA,EAClC,WAAAf;AAAA,EACA,UAAAC;AACF,GAGG;AACD,2BAAQ,KAAA,EAAE,WAAWI,EAAG,wCAAwCL,CAAS,GAAI,UAAAC,GAAS;AACxF;AACAc,EAAoB,cAAc;"}