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.
- package/CHANGELOG.md +14 -0
- package/README.md +85 -0
- package/dist/components/calendar/calendar.cjs +377 -0
- package/dist/components/calendar/calendar.cjs.map +1 -0
- package/dist/components/calendar/calendar.d.cts +56 -0
- package/dist/components/calendar/calendar.d.ts +56 -0
- package/dist/components/calendar/calendar.js +368 -0
- package/dist/components/calendar/calendar.js.map +1 -0
- package/dist/components/calendar/index.d.cts +2 -0
- package/dist/components/calendar/index.d.ts +2 -0
- package/dist/components/combobox/combobox.cjs +13 -1
- package/dist/components/combobox/combobox.cjs.map +1 -1
- package/dist/components/combobox/combobox.d.cts +7 -2
- package/dist/components/combobox/combobox.d.ts +7 -2
- package/dist/components/combobox/combobox.js +13 -2
- package/dist/components/combobox/combobox.js.map +1 -1
- package/dist/components/combobox/index.d.cts +2 -2
- package/dist/components/combobox/index.d.ts +2 -2
- package/dist/components/command/command.cjs +55 -34
- package/dist/components/command/command.cjs.map +1 -1
- package/dist/components/command/command.d.cts +1 -0
- package/dist/components/command/command.d.ts +1 -0
- package/dist/components/command/command.js +55 -34
- package/dist/components/command/command.js.map +1 -1
- package/dist/components/controls/input.cjs +26 -5
- package/dist/components/controls/input.cjs.map +1 -1
- package/dist/components/controls/input.d.cts +5 -2
- package/dist/components/controls/input.d.ts +5 -2
- package/dist/components/controls/input.js +28 -7
- package/dist/components/controls/input.js.map +1 -1
- package/dist/components/controls/textarea.cjs +26 -5
- package/dist/components/controls/textarea.cjs.map +1 -1
- package/dist/components/controls/textarea.d.cts +5 -2
- package/dist/components/controls/textarea.d.ts +5 -2
- package/dist/components/controls/textarea.js +28 -7
- package/dist/components/controls/textarea.js.map +1 -1
- package/dist/components/data-toolbar/data-toolbar.cjs +141 -0
- package/dist/components/data-toolbar/data-toolbar.cjs.map +1 -0
- package/dist/components/data-toolbar/data-toolbar.d.cts +50 -0
- package/dist/components/data-toolbar/data-toolbar.d.ts +50 -0
- package/dist/components/data-toolbar/data-toolbar.js +132 -0
- package/dist/components/data-toolbar/data-toolbar.js.map +1 -0
- package/dist/components/data-toolbar/index.d.cts +2 -0
- package/dist/components/data-toolbar/index.d.ts +2 -0
- package/dist/components/data-view/data-view.cjs +33 -1
- package/dist/components/data-view/data-view.cjs.map +1 -1
- package/dist/components/data-view/data-view.d.cts +12 -1
- package/dist/components/data-view/data-view.d.ts +12 -1
- package/dist/components/data-view/data-view.js +31 -2
- package/dist/components/data-view/data-view.js.map +1 -1
- package/dist/components/data-view/index.d.cts +2 -2
- package/dist/components/data-view/index.d.ts +2 -2
- package/dist/components/date-picker/date-picker.cjs +9 -9
- package/dist/components/date-picker/date-picker.cjs.map +1 -1
- package/dist/components/date-picker/date-picker.d.cts +3 -1
- package/dist/components/date-picker/date-picker.d.ts +3 -1
- package/dist/components/date-picker/date-picker.js +9 -9
- package/dist/components/date-picker/date-picker.js.map +1 -1
- package/dist/components/date-range-picker/date-range-picker.cjs +589 -0
- package/dist/components/date-range-picker/date-range-picker.cjs.map +1 -0
- package/dist/components/date-range-picker/date-range-picker.d.cts +134 -0
- package/dist/components/date-range-picker/date-range-picker.d.ts +134 -0
- package/dist/components/date-range-picker/date-range-picker.js +568 -0
- package/dist/components/date-range-picker/date-range-picker.js.map +1 -0
- package/dist/components/date-range-picker/index.d.cts +2 -0
- package/dist/components/date-range-picker/index.d.ts +2 -0
- package/dist/components/field/field.cjs +106 -11
- package/dist/components/field/field.cjs.map +1 -1
- package/dist/components/field/field.d.cts +22 -4
- package/dist/components/field/field.d.ts +22 -4
- package/dist/components/field/field.js +107 -13
- package/dist/components/field/field.js.map +1 -1
- package/dist/components/field/index.d.cts +2 -2
- package/dist/components/field/index.d.ts +2 -2
- package/dist/components/list/list.cjs +3 -1
- package/dist/components/list/list.cjs.map +1 -1
- package/dist/components/list/list.d.cts +3 -1
- package/dist/components/list/list.d.ts +3 -1
- package/dist/components/list/list.js +3 -1
- package/dist/components/list/list.js.map +1 -1
- package/dist/components/pagination/index.d.cts +2 -2
- package/dist/components/pagination/index.d.ts +2 -2
- package/dist/components/pagination/pagination.cjs +122 -21
- package/dist/components/pagination/pagination.cjs.map +1 -1
- package/dist/components/pagination/pagination.d.cts +25 -13
- package/dist/components/pagination/pagination.d.ts +25 -13
- package/dist/components/pagination/pagination.js +122 -23
- package/dist/components/pagination/pagination.js.map +1 -1
- package/dist/components/pin-input/index.d.cts +2 -0
- package/dist/components/pin-input/index.d.ts +2 -0
- package/dist/components/pin-input/pin-input.cjs +266 -0
- package/dist/components/pin-input/pin-input.cjs.map +1 -0
- package/dist/components/pin-input/pin-input.d.cts +46 -0
- package/dist/components/pin-input/pin-input.d.ts +46 -0
- package/dist/components/pin-input/pin-input.js +258 -0
- package/dist/components/pin-input/pin-input.js.map +1 -0
- package/dist/components/select/index.d.cts +2 -2
- package/dist/components/select/index.d.ts +2 -2
- package/dist/components/select/select.cjs +80 -3
- package/dist/components/select/select.cjs.map +1 -1
- package/dist/components/select/select.d.cts +18 -3
- package/dist/components/select/select.d.ts +18 -3
- package/dist/components/select/select.js +78 -5
- package/dist/components/select/select.js.map +1 -1
- package/dist/components/table/index.d.cts +2 -2
- package/dist/components/table/index.d.ts +2 -2
- package/dist/components/table/table.cjs +265 -46
- package/dist/components/table/table.cjs.map +1 -1
- package/dist/components/table/table.d.cts +41 -8
- package/dist/components/table/table.d.ts +41 -8
- package/dist/components/table/table.js +264 -48
- package/dist/components/table/table.js.map +1 -1
- package/dist/components/tags-input/tags-input.cjs +83 -5
- package/dist/components/tags-input/tags-input.cjs.map +1 -1
- package/dist/components/tags-input/tags-input.d.cts +3 -0
- package/dist/components/tags-input/tags-input.d.ts +3 -0
- package/dist/components/tags-input/tags-input.js +83 -5
- package/dist/components/tags-input/tags-input.js.map +1 -1
- package/dist/components/tree-view/index.d.cts +2 -2
- package/dist/components/tree-view/index.d.ts +2 -2
- package/dist/components/tree-view/tree-view.cjs +60 -3
- package/dist/components/tree-view/tree-view.cjs.map +1 -1
- package/dist/components/tree-view/tree-view.d.cts +12 -1
- package/dist/components/tree-view/tree-view.d.ts +12 -1
- package/dist/components/tree-view/tree-view.js +58 -4
- package/dist/components/tree-view/tree-view.js.map +1 -1
- package/dist/components/upload/index.d.cts +2 -2
- package/dist/components/upload/index.d.ts +2 -2
- package/dist/components/upload/upload.cjs +90 -10
- package/dist/components/upload/upload.cjs.map +1 -1
- package/dist/components/upload/upload.d.cts +25 -2
- package/dist/components/upload/upload.d.ts +25 -2
- package/dist/components/upload/upload.js +89 -11
- package/dist/components/upload/upload.js.map +1 -1
- package/dist/index.cjs +74 -1
- package/dist/index.d.cts +17 -9
- package/dist/index.d.ts +17 -9
- package/dist/index.js +14 -10
- package/dist/styled-system/recipes/calendar.cjs +43 -0
- package/dist/styled-system/recipes/calendar.cjs.map +1 -0
- package/dist/styled-system/recipes/calendar.js +43 -0
- package/dist/styled-system/recipes/calendar.js.map +1 -0
- package/dist/styled-system/recipes/combobox.cjs +1 -0
- package/dist/styled-system/recipes/combobox.cjs.map +1 -1
- package/dist/styled-system/recipes/combobox.js +1 -0
- package/dist/styled-system/recipes/combobox.js.map +1 -1
- package/dist/styled-system/recipes/data-toolbar.cjs +40 -0
- package/dist/styled-system/recipes/data-toolbar.cjs.map +1 -0
- package/dist/styled-system/recipes/data-toolbar.js +40 -0
- package/dist/styled-system/recipes/data-toolbar.js.map +1 -0
- package/dist/styled-system/recipes/data-view.cjs +3 -0
- package/dist/styled-system/recipes/data-view.cjs.map +1 -1
- package/dist/styled-system/recipes/data-view.js +3 -0
- package/dist/styled-system/recipes/data-view.js.map +1 -1
- package/dist/styled-system/recipes/date-range-picker.cjs +52 -0
- package/dist/styled-system/recipes/date-range-picker.cjs.map +1 -0
- package/dist/styled-system/recipes/date-range-picker.js +52 -0
- package/dist/styled-system/recipes/date-range-picker.js.map +1 -0
- package/dist/styled-system/recipes/list.cjs +7 -1
- package/dist/styled-system/recipes/list.cjs.map +1 -1
- package/dist/styled-system/recipes/list.js +7 -1
- package/dist/styled-system/recipes/list.js.map +1 -1
- package/dist/styled-system/recipes/pagination.cjs +9 -1
- package/dist/styled-system/recipes/pagination.cjs.map +1 -1
- package/dist/styled-system/recipes/pagination.js +9 -1
- package/dist/styled-system/recipes/pagination.js.map +1 -1
- package/dist/styled-system/recipes/pin-input.cjs +39 -0
- package/dist/styled-system/recipes/pin-input.cjs.map +1 -0
- package/dist/styled-system/recipes/pin-input.js +39 -0
- package/dist/styled-system/recipes/pin-input.js.map +1 -0
- package/dist/styled-system/recipes/select.cjs +5 -1
- package/dist/styled-system/recipes/select.cjs.map +1 -1
- package/dist/styled-system/recipes/select.js +5 -1
- package/dist/styled-system/recipes/select.js.map +1 -1
- package/dist/styled-system/recipes/table.cjs +15 -2
- package/dist/styled-system/recipes/table.cjs.map +1 -1
- package/dist/styled-system/recipes/table.js +15 -2
- package/dist/styled-system/recipes/table.js.map +1 -1
- package/dist/styled-system/recipes/tree-view.cjs +4 -1
- package/dist/styled-system/recipes/tree-view.cjs.map +1 -1
- package/dist/styled-system/recipes/tree-view.js +4 -1
- package/dist/styled-system/recipes/tree-view.js.map +1 -1
- package/dist/styled-system/recipes/upload.cjs +2 -0
- package/dist/styled-system/recipes/upload.cjs.map +1 -1
- package/dist/styled-system/recipes/upload.js +2 -0
- package/dist/styled-system/recipes/upload.js.map +1 -1
- package/dist/styles.css +995 -95
- package/package.json +3 -2
|
@@ -18,12 +18,24 @@ function changedEvent(event, value) {
|
|
|
18
18
|
target: nextTarget
|
|
19
19
|
};
|
|
20
20
|
}
|
|
21
|
-
|
|
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
|
-
|
|
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
|
|
70
|
-
|
|
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":";;;;;;AA6CA,SAAS,aAAa,OAAsC,OAAe;CACzE,MAAM,SAAS,MAAM;CACrB,MAAM,aAAa;EACjB,SAAS,OAAO;EAChB,IAAI,OAAO;EACX,MAAM,OAAO;EACb;CACF;CAEA,OAAO;EACL,GAAG;EACH,eAAe;EACf,QAAQ;CACV;AACF;AAEA,SAAS,OAAO,KAAa,OAAe;CAC1C,OAAO,GAAG,IAAI,GAAG;AACnB;AAEA,SAAS,aAAa,OAAe,WAA4B;CAC/D,IAAI,OAAO,cAAc,UAAU,OAAO,UAAU,SAAS,KAAK,MAAM,SAAS,SAAS;CAC1F,UAAU,YAAY;CACtB,MAAM,SAAS,UAAU,KAAK,KAAK;CACnC,UAAU,YAAY;CACtB,OAAO;AACT;AAEA,MAAa,aAAA,GAAA,MAAA,WAAA,CAAyD,SAAS,UAC7E,EACE,oBAAoB,iBACpB,gBAAgB,aAChB,cAAc,WACd,kBAAkB,OAClB,WACA,OAAO,CAAC,GACR,cAAc,CAAC,GACf,eAAe,IACf,WAAW,OACX,YAAY,UACZ,WAAW,OACX,IAAI,YACJ,OACA,QACA,UACA,SACA,oBACA,cACA,aACA,MAAM,gBACN,OAAO,sBACP,GAAG,SAEL,KACA;CACA,MAAM,eAAA,GAAA,MAAA,MAAA,CAAoB;CAC1B,MAAM,KAAK,cAAc;CACzB,MAAM,CAAC,kBAAkB,wBAAA,GAAA,MAAA,SAAA,OAAgD,CAAC,GAAG,WAAW,CAAC;CACzF,MAAM,CAAC,wBAAwB,8BAAA,GAAA,MAAA,SAAA,CAAsC,YAAY;CACjF,MAAM,CAAC,SAAS,eAAA,GAAA,MAAA,SAAA,CAAuB,KAAK;CAC5C,MAAM,CAAC,WAAW,iBAAA,GAAA,MAAA,SAAA,CAAyB,KAAK;CAChD,MAAM,CAAC,YAAY,kBAAA,GAAA,MAAA,SAAA,CAAmE,IAAI;CAC1F,MAAM,WAAA,GAAA,MAAA,OAAA,CAAiC,IAAI;CAC3C,MAAM,SAASA,mBAAAA,UAAU;CACzB,MAAM,eAAe,mBAAmB,KAAA,IAAY,mBAAmB,CAAC,GAAG,cAAc;CACzF,MAAM,aACJ,yBAAyB,KAAA,IAAY,yBAAyB;CAEhE,MAAM,cAAA,GAAA,MAAA,YAAA,EACH,aAAuB;EACtB,IAAI,mBAAmB,KAAA,GAAW,oBAAoB,QAAQ;EAC9D,eAAe,QAAQ;CACzB,GACA,CAAC,gBAAgB,YAAY,CAC/B;CAEA,MAAM,cAAA,GAAA,MAAA,YAAA,EACH,UAA0C;EACzC,IAAI,yBAAyB,KAAA,GAAW,0BAA0B,EAAE;EACpE,qBAAqB,EAAE;EACvB,IAAI,OAAO,WAAW,aAAa,OAAO,EAAE,CAAC;CAC/C,GACA;EAAC;EAAsB;EAAU;CAAkB,CACrD;CAEA,MAAM,UAAA,GAAA,MAAA,YAAA,EACH,QAAgB,UAA0C;EACzD,MAAM,MAAM,OAAO,KAAK;EACxB,MAAM,YAAY,KAAK,WAAW,KAAK,KAAK,SAAS,GAAG;EACxD,MAAM,YAAY,aAAa,SAAS,GAAG;EAC3C,IAAI,OAAO,cAAc,mBAAmB,CAAC,YAC3C,IAAI,YAAY;GACd,WAAW;IACT,GAAG,aAAa,MAAM,GAAG,WAAW,KAAK;IACzC;IACA,GAAG,aAAa,MAAM,WAAW,KAAK;GACxC,CAAC;GACD,cAAc,IAAI;EACpB,OACE,WAAW,CAAC,GAAG,cAAc,GAAG,CAAC;OAE9B,IAAI,YACT;EAEF,WAAW,KAAK;CAClB,GACA;EAAC;EAAiB;EAAY;EAAM;EAAY;EAAc;CAAU,CAC1E;CAEA,MAAM,gBAAA,GAAA,MAAA,YAAA,EACH,UAAkB,UAA0C;EAC3D,MAAM,YACJ,OAAO,cAAc,YAAY,UAAU,WAAW,IAAI,WAAW;EACvE,MAAM,SAAS,SACZ,MAAM,SAAS,CAAC,CAChB,KAAK,UAAU,MAAM,KAAK,CAAC,CAAC,CAC5B,OAAO,OAAO;EACjB,MAAM,WAAW,CAAC,GAAG,YAAY;EACjC,KAAK,MAAM,SAAS,QAElB,KADkB,KAAK,WAAW,KAAK,KAAK,SAAS,KAAK,OACxC,mBAAmB,CAAC,SAAS,SAAS,KAAK,IAAI,SAAS,KAAK,KAAK;EAEtF,IAAI,SAAS,WAAW,aAAa,QAAQ,WAAW,QAAQ;EAChE,WAAW,KAAK;CAClB,GACA;EAAC;EAAiB;EAAY;EAAM;EAAW;EAAc;CAAU,CACzE;CAEA,MAAM,eAAA,GAAA,MAAA,QAAA,OAA4B;EAChC,MAAM,QAAQ,WAAW,KAAK,CAAC,CAAC,kBAAkB;EAClD,OAAO,KAAK,QACT,SACC,CAAC,aAAa,SAAS,IAAI,MAC1B,MAAM,WAAW,KAAK,KAAK,kBAAkB,CAAC,CAAC,SAAS,KAAK,EAClE;CACF,GAAG;EAAC;EAAM;EAAY;CAAY,CAAC;CAEnC,SAAS,aAAa,OAAsC;EAC1D,MAAM,YAAY,MAAM,cAAc;EACtC,IAAI,UAAU,KAAK,MAAM,IAAI;GAC3B,WAAW,KAAK;GAChB,WAAW,KAAK;GAChB;EACF;EAEA,IAAI,CAAC,aAAa,aAAa,WAAW,SAAS,GAAG;GACpD,aAAa,WAAW,KAAK;GAC7B;EACF;EAEA,IAAI,yBAAyB,KAAA,GAAW,0BAA0B,SAAS;EAC3E,qBAAqB,SAAS;EAC9B,WAAW,KAAK;CAClB;CAEA,SAAS,WAAW,OAAqC;EACvD,WAAW,KAAK;EAChB,SAAS,KAAK;CAChB;CAEA,SAAS,YAAY,OAAyC;EAC5D,MAAM,SAAS,MAAM,cAAc,QAAQ,MAAM;EACjD,IAAI,CAAC,UAAW,CAAC,aAAa,QAAQ,SAAS,KAAK,CAAC,OAAO,SAAS,IAAI,GAAI;EAC7E,MAAM,eAAe;EACrB,aAAa,MAAM;CACrB;CAEA,SAAS,cAAc,OAAwC;EAC7D,IAAI,MAAM,YAAY,eAAe,WAAW;EAChD,IAAI,MAAM,QAAQ,SAAS;GACzB,MAAM,eAAe;GACrB,IAAI,WAAW,KAAK,GAAG,OAAO,UAAU;QACnC,IAAI,YAAY,cAAc,IAAI;EACzC,OAAO,IAAI,MAAM,QAAQ,YAAY,YAAY;GAC/C,MAAM,eAAe;GACrB,WAAW;IACT,GAAG,aAAa,MAAM,GAAG,WAAW,KAAK;IACzC,WAAW;IACX,GAAG,aAAa,MAAM,WAAW,KAAK;GACxC,CAAC;GACD,WAAW;GACX,cAAc,IAAI;EACpB,OAAO,IAAI,MAAM,QAAQ,eAAe,CAAC,cAAc,YAAY,aAAa,SAAS,GAAG;GAC1F,MAAM,QAAQ,aAAa,SAAS;GACpC,MAAM,MAAM,aAAa;GACzB,IAAI,QAAQ,KAAA,GAAW;GACvB,cAAc;IAAE;IAAO,OAAO;GAAI,CAAC;GACnC,WAAW,aAAa,MAAM,GAAG,EAAE,CAAC;GACpC,IAAI,yBAAyB,KAAA,GAAW,0BAA0B,GAAG;GACrE,qBAAqB,GAAG;EAC1B;EACA,MAAM,YAAY,KAAK;CACzB;CAEA,SAAS,UAAU,OAAe;EAChC,WAAW,aAAa,QAAQ,GAAG,iBAAiB,iBAAiB,KAAK,CAAC;CAC7E;CAEA,OACE,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;EACE,WAAW,OAAO;EAClB,uBAAoB;EACpB,aAAU;EACV,KAAK;YAJP;GAMG,QACC,iBAAA,GAAA,kBAAA,IAAA,CAAC,SAAD;IAAO,WAAW,OAAO;IAAO,aAAU;IAAmB,SAAS;cACnE;GACI,CAAA,IACL;GACJ,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;IACE,WAAWC,WAAAA,GAAG,OAAO,SAAS,SAAS;IACvC,iBAAe,YAAY,KAAA;IAC3B,cAAY,WAAW,KAAA;IACvB,aAAU;cAJZ,CAME,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;KAAK,WAAW,OAAO;KAAS,aAAU;eACvC,aAAa,KAAK,KAAK,UACtB,iBAAA,GAAA,kBAAA,KAAA,CAAC,QAAD;MAAM,WAAW,OAAO;MAAK,aAAU;gBAAvC,CACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;OAAM,WAAW,OAAO;OAAU,aAAU;iBACzC;MACG,CAAA,GACN,iBAAA,GAAA,kBAAA,IAAA,CAAC,UAAD;OACE,cAAY,UAAU;OACtB,WAAW,OAAO;OAClB,aAAU;OACA;OACV,eAAe,UAAU,KAAK;OAC9B,MAAK;iBACN;MAEO,CAAA,CACJ;QAduD,OAAO,KAAK,KAAK,CAcxE,CACP;IACE,CAAA,GACL,iBAAA,GAAA,kBAAA,IAAA,CAAC,SAAD;KACE,GAAI;KACJ,GAAK,kBAAkB,EAAE,oBAAoB,gBAAgB,IAAI,CAAC;KAClE,GAAK,gBAAgB,KAAA,IAAY,EAAE,gBAAgB,YAAY,IAAI,CAAC;KACpE,GAAK,YAAY,EAAE,cAAc,UAAU,IAAI,CAAC;KAChD,cAAa;KACb,WAAW,OAAO;KACR;KACN;KACJ,QAAQ;KACR,UAAU;KACV,wBAAwB,aAAa,KAAK;KAC1C,0BAA0B,aAAa,IAAI;KAC3C,UAAU,UAAU;MAClB,WAAW,IAAI;MACf,UAAU,KAAK;KACjB;KACA,WAAW;KACX,SAAS;KACI;KACR;KACL,MAAK;KACL,OAAO;IACR,CAAA,CACE;;GACJ,WAAW,KAAK,SAAS,IACxB,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;IAAK,WAAW,OAAO;IAAY,aAAU;cAC3C,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;KACE,cAAW;KACX,WAAW,OAAO;KAClB,aAAU;KACV,MAAK;eAEJ,YAAY,SAAS,IACpB,YAAY,KAAK,SACf,iBAAA,GAAA,kBAAA,IAAA,CAAC,UAAD;MACE,iBAAe;MACf,WAAW,OAAO;MAClB,aAAU;MAEV,eAAe,OAAO,IAAI;MAC1B,cAAc,UAAyC,MAAM,eAAe;MAC5E,MAAK;MACL,MAAK;gBAEJ;KACK,GAPD,IAOC,CACT,IAED,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;MAAK,WAAW,OAAO;MAAO,aAAU;gBAAmB;KAEtD,CAAA;IAEJ,CAAA;GACF,CAAA,IACH;EACD;;AAET,CAAC"}
|
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
|
70
|
-
|
|
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
|
|