jaci-ui 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 (188) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/README.md +85 -0
  3. package/dist/components/calendar/calendar.cjs +377 -0
  4. package/dist/components/calendar/calendar.cjs.map +1 -0
  5. package/dist/components/calendar/calendar.d.cts +56 -0
  6. package/dist/components/calendar/calendar.d.ts +56 -0
  7. package/dist/components/calendar/calendar.js +368 -0
  8. package/dist/components/calendar/calendar.js.map +1 -0
  9. package/dist/components/calendar/index.d.cts +2 -0
  10. package/dist/components/calendar/index.d.ts +2 -0
  11. package/dist/components/combobox/combobox.cjs +13 -1
  12. package/dist/components/combobox/combobox.cjs.map +1 -1
  13. package/dist/components/combobox/combobox.d.cts +7 -2
  14. package/dist/components/combobox/combobox.d.ts +7 -2
  15. package/dist/components/combobox/combobox.js +13 -2
  16. package/dist/components/combobox/combobox.js.map +1 -1
  17. package/dist/components/combobox/index.d.cts +2 -2
  18. package/dist/components/combobox/index.d.ts +2 -2
  19. package/dist/components/command/command.cjs +55 -34
  20. package/dist/components/command/command.cjs.map +1 -1
  21. package/dist/components/command/command.d.cts +1 -0
  22. package/dist/components/command/command.d.ts +1 -0
  23. package/dist/components/command/command.js +55 -34
  24. package/dist/components/command/command.js.map +1 -1
  25. package/dist/components/controls/input.cjs +26 -5
  26. package/dist/components/controls/input.cjs.map +1 -1
  27. package/dist/components/controls/input.d.cts +5 -2
  28. package/dist/components/controls/input.d.ts +5 -2
  29. package/dist/components/controls/input.js +28 -7
  30. package/dist/components/controls/input.js.map +1 -1
  31. package/dist/components/controls/textarea.cjs +26 -5
  32. package/dist/components/controls/textarea.cjs.map +1 -1
  33. package/dist/components/controls/textarea.d.cts +5 -2
  34. package/dist/components/controls/textarea.d.ts +5 -2
  35. package/dist/components/controls/textarea.js +28 -7
  36. package/dist/components/controls/textarea.js.map +1 -1
  37. package/dist/components/data-toolbar/data-toolbar.cjs +141 -0
  38. package/dist/components/data-toolbar/data-toolbar.cjs.map +1 -0
  39. package/dist/components/data-toolbar/data-toolbar.d.cts +50 -0
  40. package/dist/components/data-toolbar/data-toolbar.d.ts +50 -0
  41. package/dist/components/data-toolbar/data-toolbar.js +132 -0
  42. package/dist/components/data-toolbar/data-toolbar.js.map +1 -0
  43. package/dist/components/data-toolbar/index.d.cts +2 -0
  44. package/dist/components/data-toolbar/index.d.ts +2 -0
  45. package/dist/components/data-view/data-view.cjs +33 -1
  46. package/dist/components/data-view/data-view.cjs.map +1 -1
  47. package/dist/components/data-view/data-view.d.cts +12 -1
  48. package/dist/components/data-view/data-view.d.ts +12 -1
  49. package/dist/components/data-view/data-view.js +31 -2
  50. package/dist/components/data-view/data-view.js.map +1 -1
  51. package/dist/components/data-view/index.d.cts +2 -2
  52. package/dist/components/data-view/index.d.ts +2 -2
  53. package/dist/components/date-picker/date-picker.cjs +9 -9
  54. package/dist/components/date-picker/date-picker.cjs.map +1 -1
  55. package/dist/components/date-picker/date-picker.d.cts +3 -1
  56. package/dist/components/date-picker/date-picker.d.ts +3 -1
  57. package/dist/components/date-picker/date-picker.js +9 -9
  58. package/dist/components/date-picker/date-picker.js.map +1 -1
  59. package/dist/components/date-range-picker/date-range-picker.cjs +589 -0
  60. package/dist/components/date-range-picker/date-range-picker.cjs.map +1 -0
  61. package/dist/components/date-range-picker/date-range-picker.d.cts +134 -0
  62. package/dist/components/date-range-picker/date-range-picker.d.ts +134 -0
  63. package/dist/components/date-range-picker/date-range-picker.js +568 -0
  64. package/dist/components/date-range-picker/date-range-picker.js.map +1 -0
  65. package/dist/components/date-range-picker/index.d.cts +2 -0
  66. package/dist/components/date-range-picker/index.d.ts +2 -0
  67. package/dist/components/field/field.cjs +106 -11
  68. package/dist/components/field/field.cjs.map +1 -1
  69. package/dist/components/field/field.d.cts +22 -4
  70. package/dist/components/field/field.d.ts +22 -4
  71. package/dist/components/field/field.js +107 -13
  72. package/dist/components/field/field.js.map +1 -1
  73. package/dist/components/field/index.d.cts +2 -2
  74. package/dist/components/field/index.d.ts +2 -2
  75. package/dist/components/list/list.cjs +3 -1
  76. package/dist/components/list/list.cjs.map +1 -1
  77. package/dist/components/list/list.d.cts +3 -1
  78. package/dist/components/list/list.d.ts +3 -1
  79. package/dist/components/list/list.js +3 -1
  80. package/dist/components/list/list.js.map +1 -1
  81. package/dist/components/pagination/index.d.cts +2 -2
  82. package/dist/components/pagination/index.d.ts +2 -2
  83. package/dist/components/pagination/pagination.cjs +122 -21
  84. package/dist/components/pagination/pagination.cjs.map +1 -1
  85. package/dist/components/pagination/pagination.d.cts +25 -13
  86. package/dist/components/pagination/pagination.d.ts +25 -13
  87. package/dist/components/pagination/pagination.js +122 -23
  88. package/dist/components/pagination/pagination.js.map +1 -1
  89. package/dist/components/pin-input/index.d.cts +2 -0
  90. package/dist/components/pin-input/index.d.ts +2 -0
  91. package/dist/components/pin-input/pin-input.cjs +266 -0
  92. package/dist/components/pin-input/pin-input.cjs.map +1 -0
  93. package/dist/components/pin-input/pin-input.d.cts +46 -0
  94. package/dist/components/pin-input/pin-input.d.ts +46 -0
  95. package/dist/components/pin-input/pin-input.js +258 -0
  96. package/dist/components/pin-input/pin-input.js.map +1 -0
  97. package/dist/components/select/index.d.cts +2 -2
  98. package/dist/components/select/index.d.ts +2 -2
  99. package/dist/components/select/select.cjs +80 -3
  100. package/dist/components/select/select.cjs.map +1 -1
  101. package/dist/components/select/select.d.cts +18 -3
  102. package/dist/components/select/select.d.ts +18 -3
  103. package/dist/components/select/select.js +78 -5
  104. package/dist/components/select/select.js.map +1 -1
  105. package/dist/components/table/index.d.cts +2 -2
  106. package/dist/components/table/index.d.ts +2 -2
  107. package/dist/components/table/table.cjs +265 -46
  108. package/dist/components/table/table.cjs.map +1 -1
  109. package/dist/components/table/table.d.cts +41 -8
  110. package/dist/components/table/table.d.ts +41 -8
  111. package/dist/components/table/table.js +264 -48
  112. package/dist/components/table/table.js.map +1 -1
  113. package/dist/components/tags-input/tags-input.cjs +83 -5
  114. package/dist/components/tags-input/tags-input.cjs.map +1 -1
  115. package/dist/components/tags-input/tags-input.d.cts +3 -0
  116. package/dist/components/tags-input/tags-input.d.ts +3 -0
  117. package/dist/components/tags-input/tags-input.js +83 -5
  118. package/dist/components/tags-input/tags-input.js.map +1 -1
  119. package/dist/components/tree-view/index.d.cts +2 -2
  120. package/dist/components/tree-view/index.d.ts +2 -2
  121. package/dist/components/tree-view/tree-view.cjs +60 -3
  122. package/dist/components/tree-view/tree-view.cjs.map +1 -1
  123. package/dist/components/tree-view/tree-view.d.cts +12 -1
  124. package/dist/components/tree-view/tree-view.d.ts +12 -1
  125. package/dist/components/tree-view/tree-view.js +58 -4
  126. package/dist/components/tree-view/tree-view.js.map +1 -1
  127. package/dist/components/upload/index.d.cts +2 -2
  128. package/dist/components/upload/index.d.ts +2 -2
  129. package/dist/components/upload/upload.cjs +90 -10
  130. package/dist/components/upload/upload.cjs.map +1 -1
  131. package/dist/components/upload/upload.d.cts +25 -2
  132. package/dist/components/upload/upload.d.ts +25 -2
  133. package/dist/components/upload/upload.js +89 -11
  134. package/dist/components/upload/upload.js.map +1 -1
  135. package/dist/index.cjs +74 -1
  136. package/dist/index.d.cts +17 -9
  137. package/dist/index.d.ts +17 -9
  138. package/dist/index.js +14 -10
  139. package/dist/styled-system/recipes/calendar.cjs +43 -0
  140. package/dist/styled-system/recipes/calendar.cjs.map +1 -0
  141. package/dist/styled-system/recipes/calendar.js +43 -0
  142. package/dist/styled-system/recipes/calendar.js.map +1 -0
  143. package/dist/styled-system/recipes/combobox.cjs +1 -0
  144. package/dist/styled-system/recipes/combobox.cjs.map +1 -1
  145. package/dist/styled-system/recipes/combobox.js +1 -0
  146. package/dist/styled-system/recipes/combobox.js.map +1 -1
  147. package/dist/styled-system/recipes/data-toolbar.cjs +40 -0
  148. package/dist/styled-system/recipes/data-toolbar.cjs.map +1 -0
  149. package/dist/styled-system/recipes/data-toolbar.js +40 -0
  150. package/dist/styled-system/recipes/data-toolbar.js.map +1 -0
  151. package/dist/styled-system/recipes/data-view.cjs +3 -0
  152. package/dist/styled-system/recipes/data-view.cjs.map +1 -1
  153. package/dist/styled-system/recipes/data-view.js +3 -0
  154. package/dist/styled-system/recipes/data-view.js.map +1 -1
  155. package/dist/styled-system/recipes/date-range-picker.cjs +52 -0
  156. package/dist/styled-system/recipes/date-range-picker.cjs.map +1 -0
  157. package/dist/styled-system/recipes/date-range-picker.js +52 -0
  158. package/dist/styled-system/recipes/date-range-picker.js.map +1 -0
  159. package/dist/styled-system/recipes/list.cjs +7 -1
  160. package/dist/styled-system/recipes/list.cjs.map +1 -1
  161. package/dist/styled-system/recipes/list.js +7 -1
  162. package/dist/styled-system/recipes/list.js.map +1 -1
  163. package/dist/styled-system/recipes/pagination.cjs +9 -1
  164. package/dist/styled-system/recipes/pagination.cjs.map +1 -1
  165. package/dist/styled-system/recipes/pagination.js +9 -1
  166. package/dist/styled-system/recipes/pagination.js.map +1 -1
  167. package/dist/styled-system/recipes/pin-input.cjs +39 -0
  168. package/dist/styled-system/recipes/pin-input.cjs.map +1 -0
  169. package/dist/styled-system/recipes/pin-input.js +39 -0
  170. package/dist/styled-system/recipes/pin-input.js.map +1 -0
  171. package/dist/styled-system/recipes/select.cjs +5 -1
  172. package/dist/styled-system/recipes/select.cjs.map +1 -1
  173. package/dist/styled-system/recipes/select.js +5 -1
  174. package/dist/styled-system/recipes/select.js.map +1 -1
  175. package/dist/styled-system/recipes/table.cjs +15 -2
  176. package/dist/styled-system/recipes/table.cjs.map +1 -1
  177. package/dist/styled-system/recipes/table.js +15 -2
  178. package/dist/styled-system/recipes/table.js.map +1 -1
  179. package/dist/styled-system/recipes/tree-view.cjs +4 -1
  180. package/dist/styled-system/recipes/tree-view.cjs.map +1 -1
  181. package/dist/styled-system/recipes/tree-view.js +4 -1
  182. package/dist/styled-system/recipes/tree-view.js.map +1 -1
  183. package/dist/styled-system/recipes/upload.cjs +2 -0
  184. package/dist/styled-system/recipes/upload.cjs.map +1 -1
  185. package/dist/styled-system/recipes/upload.js +2 -0
  186. package/dist/styled-system/recipes/upload.js.map +1 -1
  187. package/dist/styles.css +995 -95
  188. package/package.json +3 -2
@@ -18,12 +18,24 @@ function changedEvent(event, value) {
18
18
  target: nextTarget
19
19
  };
20
20
  }
21
- const TagsInput = (0, react.forwardRef)(function TagsInput({ "aria-describedby": ariaDescribedBy, "aria-invalid": ariaInvalid, "aria-label": ariaLabel, className, data = [], defaultTags = [], defaultValue = "", disabled = false, id: providedId, label, onBlur, onChange, onFocus, onInputValueChange, onTagsChange, placeholder, tags: controlledTags, value: controlledInputValue, ...props }, ref) {
21
+ function tagKey(tag, index) {
22
+ return `${tag}-${index}`;
23
+ }
24
+ function hasDelimiter(value, delimiter) {
25
+ if (typeof delimiter === "string") return delimiter.length > 0 && value.includes(delimiter);
26
+ delimiter.lastIndex = 0;
27
+ const result = delimiter.test(value);
28
+ delimiter.lastIndex = 0;
29
+ return result;
30
+ }
31
+ const TagsInput = (0, react.forwardRef)(function TagsInput({ "aria-describedby": ariaDescribedBy, "aria-invalid": ariaInvalid, "aria-label": ariaLabel, allowDuplicates = false, className, data = [], defaultTags = [], defaultValue = "", disabled = false, delimiter = /[,\n]/u, editable = false, id: providedId, label, onBlur, onChange, onFocus, onInputValueChange, onTagsChange, placeholder, tags: controlledTags, value: controlledInputValue, ...props }, ref) {
22
32
  const generatedId = (0, react.useId)();
23
33
  const id = providedId ?? generatedId;
24
34
  const [uncontrolledTags, setUncontrolledTags] = (0, react.useState)(() => [...defaultTags]);
25
35
  const [uncontrolledInputValue, setUncontrolledInputValue] = (0, react.useState)(defaultValue);
26
36
  const [focused, setFocused] = (0, react.useState)(false);
37
+ const [composing, setComposing] = (0, react.useState)(false);
38
+ const [editingTag, setEditingTag] = (0, react.useState)(null);
27
39
  const rootRef = (0, react.useRef)(null);
28
40
  const styles = require_tags_input.tagsInput();
29
41
  const selectedTags = controlledTags === void 0 ? uncontrolledTags : [...controlledTags];
@@ -43,11 +55,38 @@ const TagsInput = (0, react.forwardRef)(function TagsInput({ "aria-describedby":
43
55
  ]);
44
56
  const addTag = (0, react.useCallback)((rawTag, event) => {
45
57
  const tag = rawTag.trim();
46
- if (tag && !selectedTags.includes(tag) && data.includes(tag)) updateTags([...selectedTags, tag]);
58
+ const isAllowed = data.length === 0 || data.includes(tag);
59
+ const duplicate = selectedTags.includes(tag);
60
+ if (tag && isAllowed && (allowDuplicates || !duplicate)) if (editingTag) {
61
+ updateTags([
62
+ ...selectedTags.slice(0, editingTag.index),
63
+ tag,
64
+ ...selectedTags.slice(editingTag.index)
65
+ ]);
66
+ setEditingTag(null);
67
+ } else updateTags([...selectedTags, tag]);
68
+ else if (editingTag) return;
69
+ clearInput(event);
70
+ }, [
71
+ allowDuplicates,
72
+ clearInput,
73
+ data,
74
+ editingTag,
75
+ selectedTags,
76
+ updateTags
77
+ ]);
78
+ const addDelimited = (0, react.useCallback)((rawValue, event) => {
79
+ const separator = typeof delimiter === "string" && delimiter.length === 0 ? /\r?\n/u : delimiter;
80
+ const values = rawValue.split(separator).map((value) => value.trim()).filter(Boolean);
81
+ const nextTags = [...selectedTags];
82
+ for (const value of values) if ((data.length === 0 || data.includes(value)) && (allowDuplicates || !nextTags.includes(value))) nextTags.push(value);
83
+ if (nextTags.length !== selectedTags.length) updateTags(nextTags);
47
84
  clearInput(event);
48
85
  }, [
86
+ allowDuplicates,
49
87
  clearInput,
50
88
  data,
89
+ delimiter,
51
90
  selectedTags,
52
91
  updateTags
53
92
  ]);
@@ -66,8 +105,8 @@ const TagsInput = (0, react.forwardRef)(function TagsInput({ "aria-describedby":
66
105
  onChange?.(event);
67
106
  return;
68
107
  }
69
- if (nextValue.endsWith(",")) {
70
- addTag(nextValue.slice(0, -1), event);
108
+ if (!composing && hasDelimiter(nextValue, delimiter)) {
109
+ addDelimited(nextValue, event);
71
110
  return;
72
111
  }
73
112
  if (controlledInputValue === void 0) setUncontrolledInputValue(nextValue);
@@ -78,6 +117,41 @@ const TagsInput = (0, react.forwardRef)(function TagsInput({ "aria-describedby":
78
117
  setFocused(false);
79
118
  onBlur?.(event);
80
119
  }
120
+ function handlePaste(event) {
121
+ const pasted = event.clipboardData.getData("text");
122
+ if (!pasted || !hasDelimiter(pasted, delimiter) && !pasted.includes("\n")) return;
123
+ event.preventDefault();
124
+ addDelimited(pasted);
125
+ }
126
+ function handleKeyDown(event) {
127
+ if (event.nativeEvent.isComposing || composing) return;
128
+ if (event.key === "Enter") {
129
+ event.preventDefault();
130
+ if (inputValue.trim()) addTag(inputValue);
131
+ else if (editingTag) setEditingTag(null);
132
+ } else if (event.key === "Escape" && editingTag) {
133
+ event.preventDefault();
134
+ updateTags([
135
+ ...selectedTags.slice(0, editingTag.index),
136
+ editingTag.value,
137
+ ...selectedTags.slice(editingTag.index)
138
+ ]);
139
+ clearInput();
140
+ setEditingTag(null);
141
+ } else if (event.key === "Backspace" && !inputValue && editable && selectedTags.length > 0) {
142
+ const index = selectedTags.length - 1;
143
+ const tag = selectedTags[index];
144
+ if (tag === void 0) return;
145
+ setEditingTag({
146
+ index,
147
+ value: tag
148
+ });
149
+ updateTags(selectedTags.slice(0, -1));
150
+ if (controlledInputValue === void 0) setUncontrolledInputValue(tag);
151
+ onInputValueChange?.(tag);
152
+ }
153
+ props.onKeyDown?.(event);
154
+ }
81
155
  function removeTag(index) {
82
156
  updateTags(selectedTags.filter((_, currentIndex) => currentIndex !== index));
83
157
  }
@@ -117,7 +191,7 @@ const TagsInput = (0, react.forwardRef)(function TagsInput({ "aria-describedby":
117
191
  type: "button",
118
192
  children: "×"
119
193
  })]
120
- }, tag))
194
+ }, tagKey(tag, index)))
121
195
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
122
196
  ...props,
123
197
  ...ariaDescribedBy ? { "aria-describedby": ariaDescribedBy } : {},
@@ -129,10 +203,14 @@ const TagsInput = (0, react.forwardRef)(function TagsInput({ "aria-describedby":
129
203
  id,
130
204
  onBlur: handleBlur,
131
205
  onChange: handleChange,
206
+ onCompositionEnd: () => setComposing(false),
207
+ onCompositionStart: () => setComposing(true),
132
208
  onFocus: (event) => {
133
209
  setFocused(true);
134
210
  onFocus?.(event);
135
211
  },
212
+ onKeyDown: handleKeyDown,
213
+ onPaste: handlePaste,
136
214
  placeholder,
137
215
  ref,
138
216
  type: "text",
@@ -1 +1 @@
1
- {"version":3,"file":"tags-input.cjs","names":["tagsInput","cx"],"sources":["../../../src/components/tags-input/tags-input.tsx"],"sourcesContent":["\"use client\";\n\nimport { forwardRef, useCallback, useId, useMemo, useRef, useState } from \"react\";\nimport type {\n ChangeEvent,\n ChangeEventHandler,\n ComponentPropsWithoutRef,\n FocusEvent,\n MouseEvent,\n ReactNode,\n} from \"react\";\n\nimport { cx } from \"../../styled-system/css\";\nimport { tagsInput } from \"../../styled-system/recipes\";\n\nexport interface TagsInputProps\n extends Omit<\n ComponentPropsWithoutRef<\"input\">,\n \"children\" | \"defaultValue\" | \"onChange\" | \"type\" | \"value\"\n > {\n /** Current tags. Omit this prop to use the uncontrolled API. */\n tags?: readonly string[];\n /** Initial tags for the uncontrolled API. */\n defaultTags?: readonly string[];\n /** Called whenever a tag is added or removed. */\n onTagsChange?: (tags: string[]) => void;\n /** Optional suggestions. A tag is accepted after a comma only when it is in this list. */\n data?: readonly string[];\n /** Visible label for the input. */\n label?: ReactNode;\n /** Current text query. This is the native input value, not the selected tags. */\n value?: string;\n /** Initial text query for an uncontrolled input. */\n defaultValue?: string;\n /** Native input change callback. */\n onChange?: ChangeEventHandler<HTMLInputElement>;\n /** Optional callback useful when a controlled query is cleared by selecting a tag. */\n onInputValueChange?: (value: string) => void;\n}\n\nfunction changedEvent(event: ChangeEvent<HTMLInputElement>, value: string) {\n const target = event.target;\n const nextTarget = {\n checked: target.checked,\n id: target.id,\n name: target.name,\n value,\n } as HTMLInputElement;\n\n return {\n ...event,\n currentTarget: nextTarget,\n target: nextTarget,\n } as ChangeEvent<HTMLInputElement>;\n}\n\nexport const TagsInput = forwardRef<HTMLInputElement, TagsInputProps>(function TagsInput(\n {\n \"aria-describedby\": ariaDescribedBy,\n \"aria-invalid\": ariaInvalid,\n \"aria-label\": ariaLabel,\n className,\n data = [],\n defaultTags = [],\n defaultValue = \"\",\n disabled = false,\n id: providedId,\n label,\n onBlur,\n onChange,\n onFocus,\n onInputValueChange,\n onTagsChange,\n placeholder,\n tags: controlledTags,\n value: controlledInputValue,\n ...props\n },\n ref,\n) {\n const generatedId = useId();\n const id = providedId ?? generatedId;\n const [uncontrolledTags, setUncontrolledTags] = useState<string[]>(() => [...defaultTags]);\n const [uncontrolledInputValue, setUncontrolledInputValue] = useState(defaultValue);\n const [focused, setFocused] = useState(false);\n const rootRef = useRef<HTMLDivElement>(null);\n const styles = tagsInput();\n const selectedTags = controlledTags === undefined ? uncontrolledTags : [...controlledTags];\n const inputValue =\n controlledInputValue === undefined ? uncontrolledInputValue : controlledInputValue;\n\n const updateTags = useCallback(\n (nextTags: string[]) => {\n if (controlledTags === undefined) setUncontrolledTags(nextTags);\n onTagsChange?.(nextTags);\n },\n [controlledTags, onTagsChange],\n );\n\n const clearInput = useCallback(\n (event?: ChangeEvent<HTMLInputElement>) => {\n if (controlledInputValue === undefined) setUncontrolledInputValue(\"\");\n onInputValueChange?.(\"\");\n if (event) onChange?.(changedEvent(event, \"\"));\n },\n [controlledInputValue, onChange, onInputValueChange],\n );\n\n const addTag = useCallback(\n (rawTag: string, event?: ChangeEvent<HTMLInputElement>) => {\n const tag = rawTag.trim();\n if (tag && !selectedTags.includes(tag) && data.includes(tag)) {\n updateTags([...selectedTags, tag]);\n }\n clearInput(event);\n },\n [clearInput, data, selectedTags, updateTags],\n );\n\n const suggestions = useMemo(() => {\n const query = inputValue.trim().toLocaleLowerCase();\n return data.filter(\n (item) =>\n !selectedTags.includes(item) &&\n (query.length === 0 || item.toLocaleLowerCase().includes(query)),\n );\n }, [data, inputValue, selectedTags]);\n\n function handleChange(event: ChangeEvent<HTMLInputElement>) {\n const nextValue = event.currentTarget.value;\n if (nextValue.trim() === \"\") {\n clearInput(event);\n onChange?.(event);\n return;\n }\n\n if (nextValue.endsWith(\",\")) {\n addTag(nextValue.slice(0, -1), event);\n return;\n }\n\n if (controlledInputValue === undefined) setUncontrolledInputValue(nextValue);\n onInputValueChange?.(nextValue);\n onChange?.(event);\n }\n\n function handleBlur(event: FocusEvent<HTMLInputElement>) {\n setFocused(false);\n onBlur?.(event);\n }\n\n function removeTag(index: number) {\n updateTags(selectedTags.filter((_, currentIndex) => currentIndex !== index));\n }\n\n return (\n <div\n className={styles.root}\n data-jaci-component=\"tags-input\"\n data-slot=\"tags-input\"\n ref={rootRef}\n >\n {label ? (\n <label className={styles.label} data-slot=\"tags-input-label\" htmlFor={id}>\n {label}\n </label>\n ) : null}\n <div\n className={cx(styles.control, className)}\n data-disabled={disabled || undefined}\n data-focus={focused || undefined}\n data-slot=\"tags-input-control\"\n >\n <div className={styles.tagList} data-slot=\"tags-input-tag-list\">\n {selectedTags.map((tag, index) => (\n <span className={styles.tag} data-slot=\"tags-input-tag\" key={tag}>\n <span className={styles.tagLabel} data-slot=\"tags-input-tag-label\">\n {tag}\n </span>\n <button\n aria-label={`Remove ${tag}`}\n className={styles.tagRemove}\n data-slot=\"tags-input-tag-remove\"\n disabled={disabled}\n onClick={() => removeTag(index)}\n type=\"button\"\n >\n ×\n </button>\n </span>\n ))}\n </div>\n <input\n {...props}\n {...(ariaDescribedBy ? { \"aria-describedby\": ariaDescribedBy } : {})}\n {...(ariaInvalid !== undefined ? { \"aria-invalid\": ariaInvalid } : {})}\n {...(ariaLabel ? { \"aria-label\": ariaLabel } : {})}\n autoComplete=\"off\"\n className={styles.input}\n disabled={disabled}\n id={id}\n onBlur={handleBlur}\n onChange={handleChange}\n onFocus={(event) => {\n setFocused(true);\n onFocus?.(event);\n }}\n placeholder={placeholder}\n ref={ref}\n type=\"text\"\n value={inputValue}\n />\n </div>\n {focused && data.length > 0 ? (\n <div className={styles.positioner} data-slot=\"tags-input-positioner\">\n <div\n aria-label=\"Suggestions\"\n className={styles.list}\n data-slot=\"tags-input-list\"\n role=\"listbox\"\n >\n {suggestions.length > 0 ? (\n suggestions.map((item) => (\n <button\n aria-selected={false}\n className={styles.item}\n data-slot=\"tags-input-item\"\n key={item}\n onClick={() => addTag(item)}\n onMouseDown={(event: MouseEvent<HTMLButtonElement>) => event.preventDefault()}\n role=\"option\"\n type=\"button\"\n >\n {item}\n </button>\n ))\n ) : (\n <div className={styles.empty} data-slot=\"tags-input-empty\">\n No options\n </div>\n )}\n </div>\n </div>\n ) : null}\n </div>\n );\n});\n"],"mappings":"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"}
1
+ {"version":3,"file":"tags-input.cjs","names":["tagsInput","cx"],"sources":["../../../src/components/tags-input/tags-input.tsx"],"sourcesContent":["\"use client\";\n\nimport { forwardRef, useCallback, useId, useMemo, useRef, useState } from \"react\";\nimport type {\n ChangeEvent,\n ChangeEventHandler,\n ComponentPropsWithoutRef,\n FocusEvent,\n KeyboardEvent,\n MouseEvent,\n ClipboardEvent,\n ReactNode,\n} from \"react\";\n\nimport { cx } from \"../../styled-system/css\";\nimport { tagsInput } from \"../../styled-system/recipes\";\n\nexport interface TagsInputProps\n extends Omit<\n ComponentPropsWithoutRef<\"input\">,\n \"children\" | \"defaultValue\" | \"onChange\" | \"type\" | \"value\"\n > {\n /** Current tags. Omit this prop to use the uncontrolled API. */\n tags?: readonly string[];\n /** Initial tags for the uncontrolled API. */\n defaultTags?: readonly string[];\n /** Called whenever a tag is added or removed. */\n onTagsChange?: (tags: string[]) => void;\n /** Optional suggestions. A tag is accepted after a comma only when it is in this list. */\n data?: readonly string[];\n /** Visible label for the input. */\n label?: ReactNode;\n /** Current text query. This is the native input value, not the selected tags. */\n value?: string;\n /** Initial text query for an uncontrolled input. */\n defaultValue?: string;\n /** Native input change callback. */\n onChange?: ChangeEventHandler<HTMLInputElement>;\n /** Optional callback useful when a controlled query is cleared by selecting a tag. */\n onInputValueChange?: (value: string) => void;\n allowDuplicates?: boolean;\n delimiter?: string | RegExp;\n editable?: boolean;\n}\n\nfunction changedEvent(event: ChangeEvent<HTMLInputElement>, value: string) {\n const target = event.target;\n const nextTarget = {\n checked: target.checked,\n id: target.id,\n name: target.name,\n value,\n } as HTMLInputElement;\n\n return {\n ...event,\n currentTarget: nextTarget,\n target: nextTarget,\n } as ChangeEvent<HTMLInputElement>;\n}\n\nfunction tagKey(tag: string, index: number) {\n return `${tag}-${index}`;\n}\n\nfunction hasDelimiter(value: string, delimiter: string | RegExp) {\n if (typeof delimiter === \"string\") return delimiter.length > 0 && value.includes(delimiter);\n delimiter.lastIndex = 0;\n const result = delimiter.test(value);\n delimiter.lastIndex = 0;\n return result;\n}\n\nexport const TagsInput = forwardRef<HTMLInputElement, TagsInputProps>(function TagsInput(\n {\n \"aria-describedby\": ariaDescribedBy,\n \"aria-invalid\": ariaInvalid,\n \"aria-label\": ariaLabel,\n allowDuplicates = false,\n className,\n data = [],\n defaultTags = [],\n defaultValue = \"\",\n disabled = false,\n delimiter = /[,\\n]/u,\n editable = false,\n id: providedId,\n label,\n onBlur,\n onChange,\n onFocus,\n onInputValueChange,\n onTagsChange,\n placeholder,\n tags: controlledTags,\n value: controlledInputValue,\n ...props\n },\n ref,\n) {\n const generatedId = useId();\n const id = providedId ?? generatedId;\n const [uncontrolledTags, setUncontrolledTags] = useState<string[]>(() => [...defaultTags]);\n const [uncontrolledInputValue, setUncontrolledInputValue] = useState(defaultValue);\n const [focused, setFocused] = useState(false);\n const [composing, setComposing] = useState(false);\n const [editingTag, setEditingTag] = useState<{ index: number; value: string } | null>(null);\n const rootRef = useRef<HTMLDivElement>(null);\n const styles = tagsInput();\n const selectedTags = controlledTags === undefined ? uncontrolledTags : [...controlledTags];\n const inputValue =\n controlledInputValue === undefined ? uncontrolledInputValue : controlledInputValue;\n\n const updateTags = useCallback(\n (nextTags: string[]) => {\n if (controlledTags === undefined) setUncontrolledTags(nextTags);\n onTagsChange?.(nextTags);\n },\n [controlledTags, onTagsChange],\n );\n\n const clearInput = useCallback(\n (event?: ChangeEvent<HTMLInputElement>) => {\n if (controlledInputValue === undefined) setUncontrolledInputValue(\"\");\n onInputValueChange?.(\"\");\n if (event) onChange?.(changedEvent(event, \"\"));\n },\n [controlledInputValue, onChange, onInputValueChange],\n );\n\n const addTag = useCallback(\n (rawTag: string, event?: ChangeEvent<HTMLInputElement>) => {\n const tag = rawTag.trim();\n const isAllowed = data.length === 0 || data.includes(tag);\n const duplicate = selectedTags.includes(tag);\n if (tag && isAllowed && (allowDuplicates || !duplicate)) {\n if (editingTag) {\n updateTags([\n ...selectedTags.slice(0, editingTag.index),\n tag,\n ...selectedTags.slice(editingTag.index),\n ]);\n setEditingTag(null);\n } else {\n updateTags([...selectedTags, tag]);\n }\n } else if (editingTag) {\n return;\n }\n clearInput(event);\n },\n [allowDuplicates, clearInput, data, editingTag, selectedTags, updateTags],\n );\n\n const addDelimited = useCallback(\n (rawValue: string, event?: ChangeEvent<HTMLInputElement>) => {\n const separator =\n typeof delimiter === \"string\" && delimiter.length === 0 ? /\\r?\\n/u : delimiter;\n const values = rawValue\n .split(separator)\n .map((value) => value.trim())\n .filter(Boolean);\n const nextTags = [...selectedTags];\n for (const value of values) {\n const isAllowed = data.length === 0 || data.includes(value);\n if (isAllowed && (allowDuplicates || !nextTags.includes(value))) nextTags.push(value);\n }\n if (nextTags.length !== selectedTags.length) updateTags(nextTags);\n clearInput(event);\n },\n [allowDuplicates, clearInput, data, delimiter, selectedTags, updateTags],\n );\n\n const suggestions = useMemo(() => {\n const query = inputValue.trim().toLocaleLowerCase();\n return data.filter(\n (item) =>\n !selectedTags.includes(item) &&\n (query.length === 0 || item.toLocaleLowerCase().includes(query)),\n );\n }, [data, inputValue, selectedTags]);\n\n function handleChange(event: ChangeEvent<HTMLInputElement>) {\n const nextValue = event.currentTarget.value;\n if (nextValue.trim() === \"\") {\n clearInput(event);\n onChange?.(event);\n return;\n }\n\n if (!composing && hasDelimiter(nextValue, delimiter)) {\n addDelimited(nextValue, event);\n return;\n }\n\n if (controlledInputValue === undefined) setUncontrolledInputValue(nextValue);\n onInputValueChange?.(nextValue);\n onChange?.(event);\n }\n\n function handleBlur(event: FocusEvent<HTMLInputElement>) {\n setFocused(false);\n onBlur?.(event);\n }\n\n function handlePaste(event: ClipboardEvent<HTMLInputElement>) {\n const pasted = event.clipboardData.getData(\"text\");\n if (!pasted || (!hasDelimiter(pasted, delimiter) && !pasted.includes(\"\\n\"))) return;\n event.preventDefault();\n addDelimited(pasted);\n }\n\n function handleKeyDown(event: KeyboardEvent<HTMLInputElement>) {\n if (event.nativeEvent.isComposing || composing) return;\n if (event.key === \"Enter\") {\n event.preventDefault();\n if (inputValue.trim()) addTag(inputValue);\n else if (editingTag) setEditingTag(null);\n } else if (event.key === \"Escape\" && editingTag) {\n event.preventDefault();\n updateTags([\n ...selectedTags.slice(0, editingTag.index),\n editingTag.value,\n ...selectedTags.slice(editingTag.index),\n ]);\n clearInput();\n setEditingTag(null);\n } else if (event.key === \"Backspace\" && !inputValue && editable && selectedTags.length > 0) {\n const index = selectedTags.length - 1;\n const tag = selectedTags[index];\n if (tag === undefined) return;\n setEditingTag({ index, value: tag });\n updateTags(selectedTags.slice(0, -1));\n if (controlledInputValue === undefined) setUncontrolledInputValue(tag);\n onInputValueChange?.(tag);\n }\n props.onKeyDown?.(event);\n }\n\n function removeTag(index: number) {\n updateTags(selectedTags.filter((_, currentIndex) => currentIndex !== index));\n }\n\n return (\n <div\n className={styles.root}\n data-jaci-component=\"tags-input\"\n data-slot=\"tags-input\"\n ref={rootRef}\n >\n {label ? (\n <label className={styles.label} data-slot=\"tags-input-label\" htmlFor={id}>\n {label}\n </label>\n ) : null}\n <div\n className={cx(styles.control, className)}\n data-disabled={disabled || undefined}\n data-focus={focused || undefined}\n data-slot=\"tags-input-control\"\n >\n <div className={styles.tagList} data-slot=\"tags-input-tag-list\">\n {selectedTags.map((tag, index) => (\n <span className={styles.tag} data-slot=\"tags-input-tag\" key={tagKey(tag, index)}>\n <span className={styles.tagLabel} data-slot=\"tags-input-tag-label\">\n {tag}\n </span>\n <button\n aria-label={`Remove ${tag}`}\n className={styles.tagRemove}\n data-slot=\"tags-input-tag-remove\"\n disabled={disabled}\n onClick={() => removeTag(index)}\n type=\"button\"\n >\n ×\n </button>\n </span>\n ))}\n </div>\n <input\n {...props}\n {...(ariaDescribedBy ? { \"aria-describedby\": ariaDescribedBy } : {})}\n {...(ariaInvalid !== undefined ? { \"aria-invalid\": ariaInvalid } : {})}\n {...(ariaLabel ? { \"aria-label\": ariaLabel } : {})}\n autoComplete=\"off\"\n className={styles.input}\n disabled={disabled}\n id={id}\n onBlur={handleBlur}\n onChange={handleChange}\n onCompositionEnd={() => setComposing(false)}\n onCompositionStart={() => setComposing(true)}\n onFocus={(event) => {\n setFocused(true);\n onFocus?.(event);\n }}\n onKeyDown={handleKeyDown}\n onPaste={handlePaste}\n placeholder={placeholder}\n ref={ref}\n type=\"text\"\n value={inputValue}\n />\n </div>\n {focused && data.length > 0 ? (\n <div className={styles.positioner} data-slot=\"tags-input-positioner\">\n <div\n aria-label=\"Suggestions\"\n className={styles.list}\n data-slot=\"tags-input-list\"\n role=\"listbox\"\n >\n {suggestions.length > 0 ? (\n suggestions.map((item) => (\n <button\n aria-selected={false}\n className={styles.item}\n data-slot=\"tags-input-item\"\n key={item}\n onClick={() => addTag(item)}\n onMouseDown={(event: MouseEvent<HTMLButtonElement>) => event.preventDefault()}\n role=\"option\"\n type=\"button\"\n >\n {item}\n </button>\n ))\n ) : (\n <div className={styles.empty} data-slot=\"tags-input-empty\">\n No options\n </div>\n )}\n </div>\n </div>\n ) : null}\n </div>\n );\n});\n"],"mappings":"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"}
@@ -19,6 +19,9 @@ interface TagsInputProps extends Omit<ComponentPropsWithoutRef<"input">, "childr
19
19
  onChange?: ChangeEventHandler<HTMLInputElement>;
20
20
  /** Optional callback useful when a controlled query is cleared by selecting a tag. */
21
21
  onInputValueChange?: (value: string) => void;
22
+ allowDuplicates?: boolean;
23
+ delimiter?: string | RegExp;
24
+ editable?: boolean;
22
25
  }
23
26
  declare const TagsInput: import("react").ForwardRefExoticComponent<TagsInputProps & import("react").RefAttributes<HTMLInputElement>>;
24
27
  //#endregion
@@ -19,6 +19,9 @@ interface TagsInputProps extends Omit<ComponentPropsWithoutRef<"input">, "childr
19
19
  onChange?: ChangeEventHandler<HTMLInputElement>;
20
20
  /** Optional callback useful when a controlled query is cleared by selecting a tag. */
21
21
  onInputValueChange?: (value: string) => void;
22
+ allowDuplicates?: boolean;
23
+ delimiter?: string | RegExp;
24
+ editable?: boolean;
22
25
  }
23
26
  declare const TagsInput: import("react").ForwardRefExoticComponent<TagsInputProps & import("react").RefAttributes<HTMLInputElement>>;
24
27
  //#endregion
@@ -18,12 +18,24 @@ function changedEvent(event, value) {
18
18
  target: nextTarget
19
19
  };
20
20
  }
21
- const TagsInput = forwardRef(function TagsInput({ "aria-describedby": ariaDescribedBy, "aria-invalid": ariaInvalid, "aria-label": ariaLabel, className, data = [], defaultTags = [], defaultValue = "", disabled = false, id: providedId, label, onBlur, onChange, onFocus, onInputValueChange, onTagsChange, placeholder, tags: controlledTags, value: controlledInputValue, ...props }, ref) {
21
+ function tagKey(tag, index) {
22
+ return `${tag}-${index}`;
23
+ }
24
+ function hasDelimiter(value, delimiter) {
25
+ if (typeof delimiter === "string") return delimiter.length > 0 && value.includes(delimiter);
26
+ delimiter.lastIndex = 0;
27
+ const result = delimiter.test(value);
28
+ delimiter.lastIndex = 0;
29
+ return result;
30
+ }
31
+ const TagsInput = forwardRef(function TagsInput({ "aria-describedby": ariaDescribedBy, "aria-invalid": ariaInvalid, "aria-label": ariaLabel, allowDuplicates = false, className, data = [], defaultTags = [], defaultValue = "", disabled = false, delimiter = /[,\n]/u, editable = false, id: providedId, label, onBlur, onChange, onFocus, onInputValueChange, onTagsChange, placeholder, tags: controlledTags, value: controlledInputValue, ...props }, ref) {
22
32
  const generatedId = useId();
23
33
  const id = providedId ?? generatedId;
24
34
  const [uncontrolledTags, setUncontrolledTags] = useState(() => [...defaultTags]);
25
35
  const [uncontrolledInputValue, setUncontrolledInputValue] = useState(defaultValue);
26
36
  const [focused, setFocused] = useState(false);
37
+ const [composing, setComposing] = useState(false);
38
+ const [editingTag, setEditingTag] = useState(null);
27
39
  const rootRef = useRef(null);
28
40
  const styles = tagsInput();
29
41
  const selectedTags = controlledTags === void 0 ? uncontrolledTags : [...controlledTags];
@@ -43,11 +55,38 @@ const TagsInput = forwardRef(function TagsInput({ "aria-describedby": ariaDescri
43
55
  ]);
44
56
  const addTag = useCallback((rawTag, event) => {
45
57
  const tag = rawTag.trim();
46
- if (tag && !selectedTags.includes(tag) && data.includes(tag)) updateTags([...selectedTags, tag]);
58
+ const isAllowed = data.length === 0 || data.includes(tag);
59
+ const duplicate = selectedTags.includes(tag);
60
+ if (tag && isAllowed && (allowDuplicates || !duplicate)) if (editingTag) {
61
+ updateTags([
62
+ ...selectedTags.slice(0, editingTag.index),
63
+ tag,
64
+ ...selectedTags.slice(editingTag.index)
65
+ ]);
66
+ setEditingTag(null);
67
+ } else updateTags([...selectedTags, tag]);
68
+ else if (editingTag) return;
69
+ clearInput(event);
70
+ }, [
71
+ allowDuplicates,
72
+ clearInput,
73
+ data,
74
+ editingTag,
75
+ selectedTags,
76
+ updateTags
77
+ ]);
78
+ const addDelimited = useCallback((rawValue, event) => {
79
+ const separator = typeof delimiter === "string" && delimiter.length === 0 ? /\r?\n/u : delimiter;
80
+ const values = rawValue.split(separator).map((value) => value.trim()).filter(Boolean);
81
+ const nextTags = [...selectedTags];
82
+ for (const value of values) if ((data.length === 0 || data.includes(value)) && (allowDuplicates || !nextTags.includes(value))) nextTags.push(value);
83
+ if (nextTags.length !== selectedTags.length) updateTags(nextTags);
47
84
  clearInput(event);
48
85
  }, [
86
+ allowDuplicates,
49
87
  clearInput,
50
88
  data,
89
+ delimiter,
51
90
  selectedTags,
52
91
  updateTags
53
92
  ]);
@@ -66,8 +105,8 @@ const TagsInput = forwardRef(function TagsInput({ "aria-describedby": ariaDescri
66
105
  onChange?.(event);
67
106
  return;
68
107
  }
69
- if (nextValue.endsWith(",")) {
70
- addTag(nextValue.slice(0, -1), event);
108
+ if (!composing && hasDelimiter(nextValue, delimiter)) {
109
+ addDelimited(nextValue, event);
71
110
  return;
72
111
  }
73
112
  if (controlledInputValue === void 0) setUncontrolledInputValue(nextValue);
@@ -78,6 +117,41 @@ const TagsInput = forwardRef(function TagsInput({ "aria-describedby": ariaDescri
78
117
  setFocused(false);
79
118
  onBlur?.(event);
80
119
  }
120
+ function handlePaste(event) {
121
+ const pasted = event.clipboardData.getData("text");
122
+ if (!pasted || !hasDelimiter(pasted, delimiter) && !pasted.includes("\n")) return;
123
+ event.preventDefault();
124
+ addDelimited(pasted);
125
+ }
126
+ function handleKeyDown(event) {
127
+ if (event.nativeEvent.isComposing || composing) return;
128
+ if (event.key === "Enter") {
129
+ event.preventDefault();
130
+ if (inputValue.trim()) addTag(inputValue);
131
+ else if (editingTag) setEditingTag(null);
132
+ } else if (event.key === "Escape" && editingTag) {
133
+ event.preventDefault();
134
+ updateTags([
135
+ ...selectedTags.slice(0, editingTag.index),
136
+ editingTag.value,
137
+ ...selectedTags.slice(editingTag.index)
138
+ ]);
139
+ clearInput();
140
+ setEditingTag(null);
141
+ } else if (event.key === "Backspace" && !inputValue && editable && selectedTags.length > 0) {
142
+ const index = selectedTags.length - 1;
143
+ const tag = selectedTags[index];
144
+ if (tag === void 0) return;
145
+ setEditingTag({
146
+ index,
147
+ value: tag
148
+ });
149
+ updateTags(selectedTags.slice(0, -1));
150
+ if (controlledInputValue === void 0) setUncontrolledInputValue(tag);
151
+ onInputValueChange?.(tag);
152
+ }
153
+ props.onKeyDown?.(event);
154
+ }
81
155
  function removeTag(index) {
82
156
  updateTags(selectedTags.filter((_, currentIndex) => currentIndex !== index));
83
157
  }
@@ -117,7 +191,7 @@ const TagsInput = forwardRef(function TagsInput({ "aria-describedby": ariaDescri
117
191
  type: "button",
118
192
  children: "×"
119
193
  })]
120
- }, tag))
194
+ }, tagKey(tag, index)))
121
195
  }), /* @__PURE__ */ jsx("input", {
122
196
  ...props,
123
197
  ...ariaDescribedBy ? { "aria-describedby": ariaDescribedBy } : {},
@@ -129,10 +203,14 @@ const TagsInput = forwardRef(function TagsInput({ "aria-describedby": ariaDescri
129
203
  id,
130
204
  onBlur: handleBlur,
131
205
  onChange: handleChange,
206
+ onCompositionEnd: () => setComposing(false),
207
+ onCompositionStart: () => setComposing(true),
132
208
  onFocus: (event) => {
133
209
  setFocused(true);
134
210
  onFocus?.(event);
135
211
  },
212
+ onKeyDown: handleKeyDown,
213
+ onPaste: handlePaste,
136
214
  placeholder,
137
215
  ref,
138
216
  type: "text",
@@ -1 +1 @@
1
- {"version":3,"file":"tags-input.js","names":[],"sources":["../../../src/components/tags-input/tags-input.tsx"],"sourcesContent":["\"use client\";\n\nimport { forwardRef, useCallback, useId, useMemo, useRef, useState } from \"react\";\nimport type {\n ChangeEvent,\n ChangeEventHandler,\n ComponentPropsWithoutRef,\n FocusEvent,\n MouseEvent,\n ReactNode,\n} from \"react\";\n\nimport { cx } from \"../../styled-system/css\";\nimport { tagsInput } from \"../../styled-system/recipes\";\n\nexport interface TagsInputProps\n extends Omit<\n ComponentPropsWithoutRef<\"input\">,\n \"children\" | \"defaultValue\" | \"onChange\" | \"type\" | \"value\"\n > {\n /** Current tags. Omit this prop to use the uncontrolled API. */\n tags?: readonly string[];\n /** Initial tags for the uncontrolled API. */\n defaultTags?: readonly string[];\n /** Called whenever a tag is added or removed. */\n onTagsChange?: (tags: string[]) => void;\n /** Optional suggestions. A tag is accepted after a comma only when it is in this list. */\n data?: readonly string[];\n /** Visible label for the input. */\n label?: ReactNode;\n /** Current text query. This is the native input value, not the selected tags. */\n value?: string;\n /** Initial text query for an uncontrolled input. */\n defaultValue?: string;\n /** Native input change callback. */\n onChange?: ChangeEventHandler<HTMLInputElement>;\n /** Optional callback useful when a controlled query is cleared by selecting a tag. */\n onInputValueChange?: (value: string) => void;\n}\n\nfunction changedEvent(event: ChangeEvent<HTMLInputElement>, value: string) {\n const target = event.target;\n const nextTarget = {\n checked: target.checked,\n id: target.id,\n name: target.name,\n value,\n } as HTMLInputElement;\n\n return {\n ...event,\n currentTarget: nextTarget,\n target: nextTarget,\n } as ChangeEvent<HTMLInputElement>;\n}\n\nexport const TagsInput = forwardRef<HTMLInputElement, TagsInputProps>(function TagsInput(\n {\n \"aria-describedby\": ariaDescribedBy,\n \"aria-invalid\": ariaInvalid,\n \"aria-label\": ariaLabel,\n className,\n data = [],\n defaultTags = [],\n defaultValue = \"\",\n disabled = false,\n id: providedId,\n label,\n onBlur,\n onChange,\n onFocus,\n onInputValueChange,\n onTagsChange,\n placeholder,\n tags: controlledTags,\n value: controlledInputValue,\n ...props\n },\n ref,\n) {\n const generatedId = useId();\n const id = providedId ?? generatedId;\n const [uncontrolledTags, setUncontrolledTags] = useState<string[]>(() => [...defaultTags]);\n const [uncontrolledInputValue, setUncontrolledInputValue] = useState(defaultValue);\n const [focused, setFocused] = useState(false);\n const rootRef = useRef<HTMLDivElement>(null);\n const styles = tagsInput();\n const selectedTags = controlledTags === undefined ? uncontrolledTags : [...controlledTags];\n const inputValue =\n controlledInputValue === undefined ? uncontrolledInputValue : controlledInputValue;\n\n const updateTags = useCallback(\n (nextTags: string[]) => {\n if (controlledTags === undefined) setUncontrolledTags(nextTags);\n onTagsChange?.(nextTags);\n },\n [controlledTags, onTagsChange],\n );\n\n const clearInput = useCallback(\n (event?: ChangeEvent<HTMLInputElement>) => {\n if (controlledInputValue === undefined) setUncontrolledInputValue(\"\");\n onInputValueChange?.(\"\");\n if (event) onChange?.(changedEvent(event, \"\"));\n },\n [controlledInputValue, onChange, onInputValueChange],\n );\n\n const addTag = useCallback(\n (rawTag: string, event?: ChangeEvent<HTMLInputElement>) => {\n const tag = rawTag.trim();\n if (tag && !selectedTags.includes(tag) && data.includes(tag)) {\n updateTags([...selectedTags, tag]);\n }\n clearInput(event);\n },\n [clearInput, data, selectedTags, updateTags],\n );\n\n const suggestions = useMemo(() => {\n const query = inputValue.trim().toLocaleLowerCase();\n return data.filter(\n (item) =>\n !selectedTags.includes(item) &&\n (query.length === 0 || item.toLocaleLowerCase().includes(query)),\n );\n }, [data, inputValue, selectedTags]);\n\n function handleChange(event: ChangeEvent<HTMLInputElement>) {\n const nextValue = event.currentTarget.value;\n if (nextValue.trim() === \"\") {\n clearInput(event);\n onChange?.(event);\n return;\n }\n\n if (nextValue.endsWith(\",\")) {\n addTag(nextValue.slice(0, -1), event);\n return;\n }\n\n if (controlledInputValue === undefined) setUncontrolledInputValue(nextValue);\n onInputValueChange?.(nextValue);\n onChange?.(event);\n }\n\n function handleBlur(event: FocusEvent<HTMLInputElement>) {\n setFocused(false);\n onBlur?.(event);\n }\n\n function removeTag(index: number) {\n updateTags(selectedTags.filter((_, currentIndex) => currentIndex !== index));\n }\n\n return (\n <div\n className={styles.root}\n data-jaci-component=\"tags-input\"\n data-slot=\"tags-input\"\n ref={rootRef}\n >\n {label ? (\n <label className={styles.label} data-slot=\"tags-input-label\" htmlFor={id}>\n {label}\n </label>\n ) : null}\n <div\n className={cx(styles.control, className)}\n data-disabled={disabled || undefined}\n data-focus={focused || undefined}\n data-slot=\"tags-input-control\"\n >\n <div className={styles.tagList} data-slot=\"tags-input-tag-list\">\n {selectedTags.map((tag, index) => (\n <span className={styles.tag} data-slot=\"tags-input-tag\" key={tag}>\n <span className={styles.tagLabel} data-slot=\"tags-input-tag-label\">\n {tag}\n </span>\n <button\n aria-label={`Remove ${tag}`}\n className={styles.tagRemove}\n data-slot=\"tags-input-tag-remove\"\n disabled={disabled}\n onClick={() => removeTag(index)}\n type=\"button\"\n >\n ×\n </button>\n </span>\n ))}\n </div>\n <input\n {...props}\n {...(ariaDescribedBy ? { \"aria-describedby\": ariaDescribedBy } : {})}\n {...(ariaInvalid !== undefined ? { \"aria-invalid\": ariaInvalid } : {})}\n {...(ariaLabel ? { \"aria-label\": ariaLabel } : {})}\n autoComplete=\"off\"\n className={styles.input}\n disabled={disabled}\n id={id}\n onBlur={handleBlur}\n onChange={handleChange}\n onFocus={(event) => {\n setFocused(true);\n onFocus?.(event);\n }}\n placeholder={placeholder}\n ref={ref}\n type=\"text\"\n value={inputValue}\n />\n </div>\n {focused && data.length > 0 ? (\n <div className={styles.positioner} data-slot=\"tags-input-positioner\">\n <div\n aria-label=\"Suggestions\"\n className={styles.list}\n data-slot=\"tags-input-list\"\n role=\"listbox\"\n >\n {suggestions.length > 0 ? (\n suggestions.map((item) => (\n <button\n aria-selected={false}\n className={styles.item}\n data-slot=\"tags-input-item\"\n key={item}\n onClick={() => addTag(item)}\n onMouseDown={(event: MouseEvent<HTMLButtonElement>) => event.preventDefault()}\n role=\"option\"\n type=\"button\"\n >\n {item}\n </button>\n ))\n ) : (\n <div className={styles.empty} data-slot=\"tags-input-empty\">\n No options\n </div>\n )}\n </div>\n </div>\n ) : null}\n </div>\n );\n});\n"],"mappings":"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"}
1
+ {"version":3,"file":"tags-input.js","names":[],"sources":["../../../src/components/tags-input/tags-input.tsx"],"sourcesContent":["\"use client\";\n\nimport { forwardRef, useCallback, useId, useMemo, useRef, useState } from \"react\";\nimport type {\n ChangeEvent,\n ChangeEventHandler,\n ComponentPropsWithoutRef,\n FocusEvent,\n KeyboardEvent,\n MouseEvent,\n ClipboardEvent,\n ReactNode,\n} from \"react\";\n\nimport { cx } from \"../../styled-system/css\";\nimport { tagsInput } from \"../../styled-system/recipes\";\n\nexport interface TagsInputProps\n extends Omit<\n ComponentPropsWithoutRef<\"input\">,\n \"children\" | \"defaultValue\" | \"onChange\" | \"type\" | \"value\"\n > {\n /** Current tags. Omit this prop to use the uncontrolled API. */\n tags?: readonly string[];\n /** Initial tags for the uncontrolled API. */\n defaultTags?: readonly string[];\n /** Called whenever a tag is added or removed. */\n onTagsChange?: (tags: string[]) => void;\n /** Optional suggestions. A tag is accepted after a comma only when it is in this list. */\n data?: readonly string[];\n /** Visible label for the input. */\n label?: ReactNode;\n /** Current text query. This is the native input value, not the selected tags. */\n value?: string;\n /** Initial text query for an uncontrolled input. */\n defaultValue?: string;\n /** Native input change callback. */\n onChange?: ChangeEventHandler<HTMLInputElement>;\n /** Optional callback useful when a controlled query is cleared by selecting a tag. */\n onInputValueChange?: (value: string) => void;\n allowDuplicates?: boolean;\n delimiter?: string | RegExp;\n editable?: boolean;\n}\n\nfunction changedEvent(event: ChangeEvent<HTMLInputElement>, value: string) {\n const target = event.target;\n const nextTarget = {\n checked: target.checked,\n id: target.id,\n name: target.name,\n value,\n } as HTMLInputElement;\n\n return {\n ...event,\n currentTarget: nextTarget,\n target: nextTarget,\n } as ChangeEvent<HTMLInputElement>;\n}\n\nfunction tagKey(tag: string, index: number) {\n return `${tag}-${index}`;\n}\n\nfunction hasDelimiter(value: string, delimiter: string | RegExp) {\n if (typeof delimiter === \"string\") return delimiter.length > 0 && value.includes(delimiter);\n delimiter.lastIndex = 0;\n const result = delimiter.test(value);\n delimiter.lastIndex = 0;\n return result;\n}\n\nexport const TagsInput = forwardRef<HTMLInputElement, TagsInputProps>(function TagsInput(\n {\n \"aria-describedby\": ariaDescribedBy,\n \"aria-invalid\": ariaInvalid,\n \"aria-label\": ariaLabel,\n allowDuplicates = false,\n className,\n data = [],\n defaultTags = [],\n defaultValue = \"\",\n disabled = false,\n delimiter = /[,\\n]/u,\n editable = false,\n id: providedId,\n label,\n onBlur,\n onChange,\n onFocus,\n onInputValueChange,\n onTagsChange,\n placeholder,\n tags: controlledTags,\n value: controlledInputValue,\n ...props\n },\n ref,\n) {\n const generatedId = useId();\n const id = providedId ?? generatedId;\n const [uncontrolledTags, setUncontrolledTags] = useState<string[]>(() => [...defaultTags]);\n const [uncontrolledInputValue, setUncontrolledInputValue] = useState(defaultValue);\n const [focused, setFocused] = useState(false);\n const [composing, setComposing] = useState(false);\n const [editingTag, setEditingTag] = useState<{ index: number; value: string } | null>(null);\n const rootRef = useRef<HTMLDivElement>(null);\n const styles = tagsInput();\n const selectedTags = controlledTags === undefined ? uncontrolledTags : [...controlledTags];\n const inputValue =\n controlledInputValue === undefined ? uncontrolledInputValue : controlledInputValue;\n\n const updateTags = useCallback(\n (nextTags: string[]) => {\n if (controlledTags === undefined) setUncontrolledTags(nextTags);\n onTagsChange?.(nextTags);\n },\n [controlledTags, onTagsChange],\n );\n\n const clearInput = useCallback(\n (event?: ChangeEvent<HTMLInputElement>) => {\n if (controlledInputValue === undefined) setUncontrolledInputValue(\"\");\n onInputValueChange?.(\"\");\n if (event) onChange?.(changedEvent(event, \"\"));\n },\n [controlledInputValue, onChange, onInputValueChange],\n );\n\n const addTag = useCallback(\n (rawTag: string, event?: ChangeEvent<HTMLInputElement>) => {\n const tag = rawTag.trim();\n const isAllowed = data.length === 0 || data.includes(tag);\n const duplicate = selectedTags.includes(tag);\n if (tag && isAllowed && (allowDuplicates || !duplicate)) {\n if (editingTag) {\n updateTags([\n ...selectedTags.slice(0, editingTag.index),\n tag,\n ...selectedTags.slice(editingTag.index),\n ]);\n setEditingTag(null);\n } else {\n updateTags([...selectedTags, tag]);\n }\n } else if (editingTag) {\n return;\n }\n clearInput(event);\n },\n [allowDuplicates, clearInput, data, editingTag, selectedTags, updateTags],\n );\n\n const addDelimited = useCallback(\n (rawValue: string, event?: ChangeEvent<HTMLInputElement>) => {\n const separator =\n typeof delimiter === \"string\" && delimiter.length === 0 ? /\\r?\\n/u : delimiter;\n const values = rawValue\n .split(separator)\n .map((value) => value.trim())\n .filter(Boolean);\n const nextTags = [...selectedTags];\n for (const value of values) {\n const isAllowed = data.length === 0 || data.includes(value);\n if (isAllowed && (allowDuplicates || !nextTags.includes(value))) nextTags.push(value);\n }\n if (nextTags.length !== selectedTags.length) updateTags(nextTags);\n clearInput(event);\n },\n [allowDuplicates, clearInput, data, delimiter, selectedTags, updateTags],\n );\n\n const suggestions = useMemo(() => {\n const query = inputValue.trim().toLocaleLowerCase();\n return data.filter(\n (item) =>\n !selectedTags.includes(item) &&\n (query.length === 0 || item.toLocaleLowerCase().includes(query)),\n );\n }, [data, inputValue, selectedTags]);\n\n function handleChange(event: ChangeEvent<HTMLInputElement>) {\n const nextValue = event.currentTarget.value;\n if (nextValue.trim() === \"\") {\n clearInput(event);\n onChange?.(event);\n return;\n }\n\n if (!composing && hasDelimiter(nextValue, delimiter)) {\n addDelimited(nextValue, event);\n return;\n }\n\n if (controlledInputValue === undefined) setUncontrolledInputValue(nextValue);\n onInputValueChange?.(nextValue);\n onChange?.(event);\n }\n\n function handleBlur(event: FocusEvent<HTMLInputElement>) {\n setFocused(false);\n onBlur?.(event);\n }\n\n function handlePaste(event: ClipboardEvent<HTMLInputElement>) {\n const pasted = event.clipboardData.getData(\"text\");\n if (!pasted || (!hasDelimiter(pasted, delimiter) && !pasted.includes(\"\\n\"))) return;\n event.preventDefault();\n addDelimited(pasted);\n }\n\n function handleKeyDown(event: KeyboardEvent<HTMLInputElement>) {\n if (event.nativeEvent.isComposing || composing) return;\n if (event.key === \"Enter\") {\n event.preventDefault();\n if (inputValue.trim()) addTag(inputValue);\n else if (editingTag) setEditingTag(null);\n } else if (event.key === \"Escape\" && editingTag) {\n event.preventDefault();\n updateTags([\n ...selectedTags.slice(0, editingTag.index),\n editingTag.value,\n ...selectedTags.slice(editingTag.index),\n ]);\n clearInput();\n setEditingTag(null);\n } else if (event.key === \"Backspace\" && !inputValue && editable && selectedTags.length > 0) {\n const index = selectedTags.length - 1;\n const tag = selectedTags[index];\n if (tag === undefined) return;\n setEditingTag({ index, value: tag });\n updateTags(selectedTags.slice(0, -1));\n if (controlledInputValue === undefined) setUncontrolledInputValue(tag);\n onInputValueChange?.(tag);\n }\n props.onKeyDown?.(event);\n }\n\n function removeTag(index: number) {\n updateTags(selectedTags.filter((_, currentIndex) => currentIndex !== index));\n }\n\n return (\n <div\n className={styles.root}\n data-jaci-component=\"tags-input\"\n data-slot=\"tags-input\"\n ref={rootRef}\n >\n {label ? (\n <label className={styles.label} data-slot=\"tags-input-label\" htmlFor={id}>\n {label}\n </label>\n ) : null}\n <div\n className={cx(styles.control, className)}\n data-disabled={disabled || undefined}\n data-focus={focused || undefined}\n data-slot=\"tags-input-control\"\n >\n <div className={styles.tagList} data-slot=\"tags-input-tag-list\">\n {selectedTags.map((tag, index) => (\n <span className={styles.tag} data-slot=\"tags-input-tag\" key={tagKey(tag, index)}>\n <span className={styles.tagLabel} data-slot=\"tags-input-tag-label\">\n {tag}\n </span>\n <button\n aria-label={`Remove ${tag}`}\n className={styles.tagRemove}\n data-slot=\"tags-input-tag-remove\"\n disabled={disabled}\n onClick={() => removeTag(index)}\n type=\"button\"\n >\n ×\n </button>\n </span>\n ))}\n </div>\n <input\n {...props}\n {...(ariaDescribedBy ? { \"aria-describedby\": ariaDescribedBy } : {})}\n {...(ariaInvalid !== undefined ? { \"aria-invalid\": ariaInvalid } : {})}\n {...(ariaLabel ? { \"aria-label\": ariaLabel } : {})}\n autoComplete=\"off\"\n className={styles.input}\n disabled={disabled}\n id={id}\n onBlur={handleBlur}\n onChange={handleChange}\n onCompositionEnd={() => setComposing(false)}\n onCompositionStart={() => setComposing(true)}\n onFocus={(event) => {\n setFocused(true);\n onFocus?.(event);\n }}\n onKeyDown={handleKeyDown}\n onPaste={handlePaste}\n placeholder={placeholder}\n ref={ref}\n type=\"text\"\n value={inputValue}\n />\n </div>\n {focused && data.length > 0 ? (\n <div className={styles.positioner} data-slot=\"tags-input-positioner\">\n <div\n aria-label=\"Suggestions\"\n className={styles.list}\n data-slot=\"tags-input-list\"\n role=\"listbox\"\n >\n {suggestions.length > 0 ? (\n suggestions.map((item) => (\n <button\n aria-selected={false}\n className={styles.item}\n data-slot=\"tags-input-item\"\n key={item}\n onClick={() => addTag(item)}\n onMouseDown={(event: MouseEvent<HTMLButtonElement>) => event.preventDefault()}\n role=\"option\"\n type=\"button\"\n >\n {item}\n </button>\n ))\n ) : (\n <div className={styles.empty} data-slot=\"tags-input-empty\">\n No options\n </div>\n )}\n </div>\n </div>\n ) : null}\n </div>\n );\n});\n"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- import { TreeView, TreeViewGroup, TreeViewGroupProps, TreeViewItem, TreeViewItemProps, TreeViewLabel, TreeViewLabelProps, TreeViewRoot, TreeViewRootProps, TreeViewSelection, TreeViewSelectionMode, TreeViewToggle, TreeViewToggleProps } from "./tree-view.cjs";
2
- export { TreeView, TreeViewGroup, type TreeViewGroupProps, TreeViewItem, type TreeViewItemProps, TreeViewLabel, type TreeViewLabelProps, TreeViewRoot, type TreeViewRootProps, type TreeViewSelection, type TreeViewSelectionMode, TreeViewToggle, type TreeViewToggleProps };
1
+ import { TreeView, TreeViewEmpty, TreeViewError, TreeViewGroup, TreeViewGroupProps, TreeViewItem, TreeViewItemProps, TreeViewLabel, TreeViewLabelProps, TreeViewLoading, TreeViewRoot, TreeViewRootProps, TreeViewSelection, TreeViewSelectionMode, TreeViewStateProps, TreeViewToggle, TreeViewToggleProps } from "./tree-view.cjs";
2
+ export { TreeView, TreeViewEmpty, TreeViewError, TreeViewGroup, type TreeViewGroupProps, TreeViewItem, type TreeViewItemProps, TreeViewLabel, type TreeViewLabelProps, TreeViewLoading, TreeViewRoot, type TreeViewRootProps, type TreeViewSelection, type TreeViewSelectionMode, type TreeViewStateProps, TreeViewToggle, type TreeViewToggleProps };
@@ -1,2 +1,2 @@
1
- import { TreeView, TreeViewGroup, TreeViewGroupProps, TreeViewItem, TreeViewItemProps, TreeViewLabel, TreeViewLabelProps, TreeViewRoot, TreeViewRootProps, TreeViewSelection, TreeViewSelectionMode, TreeViewToggle, TreeViewToggleProps } from "./tree-view.js";
2
- export { TreeView, TreeViewGroup, type TreeViewGroupProps, TreeViewItem, type TreeViewItemProps, TreeViewLabel, type TreeViewLabelProps, TreeViewRoot, type TreeViewRootProps, type TreeViewSelection, type TreeViewSelectionMode, TreeViewToggle, type TreeViewToggleProps };
1
+ import { TreeView, TreeViewEmpty, TreeViewError, TreeViewGroup, TreeViewGroupProps, TreeViewItem, TreeViewItemProps, TreeViewLabel, TreeViewLabelProps, TreeViewLoading, TreeViewRoot, TreeViewRootProps, TreeViewSelection, TreeViewSelectionMode, TreeViewStateProps, TreeViewToggle, TreeViewToggleProps } from "./tree-view.js";
2
+ export { TreeView, TreeViewEmpty, TreeViewError, TreeViewGroup, type TreeViewGroupProps, TreeViewItem, type TreeViewItemProps, TreeViewLabel, type TreeViewLabelProps, TreeViewLoading, TreeViewRoot, type TreeViewRootProps, type TreeViewSelection, type TreeViewSelectionMode, type TreeViewStateProps, TreeViewToggle, type TreeViewToggleProps };
@@ -47,7 +47,7 @@ const TreeViewRoot = (0, react.forwardRef)(function TreeViewRoot({ children, cla
47
47
  const node = nodesRef.current.get(id);
48
48
  if (node) node.element = element;
49
49
  }, []);
50
- const hasChildren = (0, react.useCallback)((id) => nodes.some((node) => node.parentId === id), [nodes]);
50
+ const hasChildren = (0, react.useCallback)((id) => nodes.some((node) => node.parentId === id) || Boolean(nodes.find((node) => node.id === id)?.hasChildren), [nodes]);
51
51
  const isVisible = (0, react.useCallback)((id) => {
52
52
  let current = nodes.find((node) => node.id === id);
53
53
  while (current?.parentId) {
@@ -199,7 +199,7 @@ const TreeViewRoot = (0, react.forwardRef)(function TreeViewRoot({ children, cla
199
199
  })
200
200
  });
201
201
  });
202
- const TreeViewItem = (0, react.forwardRef)(function TreeViewItem({ children, className, disabled = false, onClick, onFocus, onKeyDown, style, selectable = true, id, ...props }, ref) {
202
+ const TreeViewItem = (0, react.forwardRef)(function TreeViewItem({ children, className, disabled = false, hasChildren: hasChildrenProp = false, loading = false, onClick, onFocus, onKeyDown, style, selectable = true, id, ...props }, ref) {
203
203
  const context = useTreeViewContext();
204
204
  const parentId = (0, react.useContext)(TreeParentContext);
205
205
  const styles = require_tree_view.treeView();
@@ -207,13 +207,17 @@ const TreeViewItem = (0, react.forwardRef)(function TreeViewItem({ children, cla
207
207
  id,
208
208
  parentId,
209
209
  disabled,
210
+ hasChildren: hasChildrenProp,
211
+ loading,
210
212
  selectable,
211
213
  order: 0,
212
214
  element: null
213
215
  }), [
214
216
  context.registerNode,
215
217
  disabled,
218
+ hasChildrenProp,
216
219
  id,
220
+ loading,
217
221
  parentId,
218
222
  selectable
219
223
  ]);
@@ -239,6 +243,7 @@ const TreeViewItem = (0, react.forwardRef)(function TreeViewItem({ children, cla
239
243
  },
240
244
  "aria-disabled": disabled || void 0,
241
245
  "aria-expanded": context.hasChildren(id) ? context.expanded.has(id) : void 0,
246
+ "aria-busy": loading || void 0,
242
247
  "aria-level": registeredNode ? context.getDepth(id) : parentId ? 2 : 1,
243
248
  "aria-posinset": siblingIndex >= 0 ? siblingIndex + 1 : 1,
244
249
  "aria-setsize": siblings.length || 1,
@@ -248,6 +253,7 @@ const TreeViewItem = (0, react.forwardRef)(function TreeViewItem({ children, cla
248
253
  "data-expanded": context.expanded.has(id) || void 0,
249
254
  "data-jaci-component": "tree-view",
250
255
  "data-selected": selected || void 0,
256
+ "data-loading": loading || void 0,
251
257
  "data-slot": "tree-view-item",
252
258
  onClick: (event) => {
253
259
  event.stopPropagation();
@@ -303,12 +309,16 @@ const TreeViewGroup = (0, react.forwardRef)(function TreeViewGroup({ children, c
303
309
  const TreeViewToggle = (0, react.forwardRef)(function TreeViewToggle({ children, className, onClick, ...props }, ref) {
304
310
  const context = useTreeViewContext();
305
311
  const id = (0, react.useContext)(TreeItemContext);
312
+ const item = id ? context.nodes.find((candidate) => candidate.id === id) : void 0;
313
+ const disabled = context.disabled || Boolean(item?.disabled);
306
314
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
307
315
  ...props,
308
316
  ref,
309
317
  "aria-expanded": id ? context.expanded.has(id) : void 0,
318
+ "aria-disabled": disabled || void 0,
310
319
  className: require_cx.cx(require_tree_view.treeView().toggle, className),
311
320
  "data-slot": "tree-view-toggle",
321
+ disabled: disabled || props.disabled,
312
322
  onClick: (event) => {
313
323
  event.stopPropagation();
314
324
  onClick?.(event);
@@ -327,18 +337,65 @@ const TreeViewLabel = (0, react.forwardRef)(function TreeViewLabel({ className,
327
337
  "data-slot": "tree-view-label"
328
338
  });
329
339
  });
340
+ const TreeViewLoading = (0, react.forwardRef)(function TreeViewLoading({ children = "Loading…", className, ...props }, ref) {
341
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
342
+ ...props,
343
+ ref,
344
+ "aria-disabled": "true",
345
+ "aria-live": "polite",
346
+ "aria-level": 1,
347
+ className: require_cx.cx(require_tree_view.treeView().loading, className),
348
+ "data-slot": "tree-view-loading",
349
+ role: "treeitem",
350
+ tabIndex: -1,
351
+ children
352
+ });
353
+ });
354
+ const TreeViewEmpty = (0, react.forwardRef)(function TreeViewEmpty({ children = "No items found.", className, ...props }, ref) {
355
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
356
+ ...props,
357
+ ref,
358
+ "aria-disabled": "true",
359
+ "aria-level": 1,
360
+ className: require_cx.cx(require_tree_view.treeView().empty, className),
361
+ "data-slot": "tree-view-empty",
362
+ role: "treeitem",
363
+ tabIndex: -1,
364
+ children
365
+ });
366
+ });
367
+ const TreeViewError = (0, react.forwardRef)(function TreeViewError({ children = "Something went wrong.", className, ...props }, ref) {
368
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
369
+ ...props,
370
+ ref,
371
+ "aria-disabled": "true",
372
+ "aria-live": "assertive",
373
+ "aria-level": 1,
374
+ className: require_cx.cx(require_tree_view.treeView().error, className),
375
+ "data-slot": "tree-view-error",
376
+ role: "treeitem",
377
+ tabIndex: -1,
378
+ children
379
+ });
380
+ });
330
381
  const TreeView = {
331
382
  Root: TreeViewRoot,
332
383
  Item: TreeViewItem,
333
384
  Group: TreeViewGroup,
334
385
  Toggle: TreeViewToggle,
335
- Label: TreeViewLabel
386
+ Label: TreeViewLabel,
387
+ Loading: TreeViewLoading,
388
+ Empty: TreeViewEmpty,
389
+ Error: TreeViewError
336
390
  };
337
391
  //#endregion
338
392
  exports.TreeView = TreeView;
393
+ exports.TreeViewEmpty = TreeViewEmpty;
394
+ exports.TreeViewError = TreeViewError;
339
395
  exports.TreeViewGroup = TreeViewGroup;
340
396
  exports.TreeViewItem = TreeViewItem;
341
397
  exports.TreeViewLabel = TreeViewLabel;
398
+ exports.TreeViewLoading = TreeViewLoading;
342
399
  exports.TreeViewRoot = TreeViewRoot;
343
400
  exports.TreeViewToggle = TreeViewToggle;
344
401