@djangocfg/ui-core 2.1.412 → 2.1.415
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/package.json +4 -4
- package/src/components/data/avatar-group/index.tsx +224 -0
- package/src/components/data/badge-overflow/index.tsx +259 -0
- package/src/components/data/circular-progress/index.tsx +358 -0
- package/src/components/data/relative-time-card/index.tsx +191 -0
- package/src/components/data/stat/index.tsx +140 -0
- package/src/components/data/status/index.tsx +80 -0
- package/src/components/effects/GlowBackground.tsx +9 -1
- package/src/components/effects/swap/index.tsx +289 -0
- package/src/components/feedback/banner/index.tsx +693 -0
- package/src/components/forms/checkbox-group/index.tsx +243 -0
- package/src/components/forms/editable/index.tsx +420 -0
- package/src/components/forms/input-otp/index.tsx +12 -3
- package/src/components/forms/mask-input/index.tsx +466 -0
- package/src/components/forms/otp/index.tsx +12 -8
- package/src/components/forms/segmented-input/index.tsx +319 -0
- package/src/components/forms/tags-input/index.tsx +896 -0
- package/src/components/forms/time-picker/index.tsx +285 -0
- package/src/components/index.ts +51 -0
- package/src/components/layout/key-value/index.tsx +884 -0
- package/src/components/layout/stack/index.tsx +349 -0
- package/src/components/navigation/context-menu/index.tsx +9 -6
- package/src/components/navigation/stepper/index.tsx +1307 -0
- package/src/components/select/multi-select-pro-async.tsx +11 -2
- package/src/components/select/multi-select-pro.tsx +11 -2
- package/src/components/specialized/presence/index.tsx +181 -0
- package/src/components/specialized/primitive/index.tsx +83 -0
- package/src/components/specialized/visually-hidden/index.tsx +19 -0
- package/src/components/specialized/visually-hidden-input/index.tsx +99 -0
- package/src/hooks/dom/index.ts +4 -0
- package/src/hooks/dom/useFormReset.ts +49 -0
- package/src/hooks/dom/useLayoutEffect.ts +16 -0
- package/src/hooks/dom/useSize.ts +57 -0
- package/src/hooks/state/index.ts +4 -0
- package/src/hooks/state/useCallbackRef.ts +25 -0
- package/src/hooks/state/usePrevious.ts +20 -0
- package/src/hooks/state/useStateMachine.ts +29 -0
- package/src/lib/compose-event-handlers.ts +22 -0
- package/src/lib/compose-refs.ts +65 -0
- package/src/lib/create-context.tsx +62 -0
- package/src/lib/get-element-ref.ts +33 -0
- package/src/lib/index.ts +5 -0
- package/src/lib/styles.ts +103 -0
- package/src/styles/README.md +43 -0
- package/src/styles/palette/utils.ts +15 -5
- package/src/styles/utilities/animations.css +135 -0
- package/src/styles/utilities/display.css +62 -0
- package/src/styles/utilities/glass.css +57 -0
- package/src/styles/utilities/marquee.css +69 -0
- package/src/styles/utilities/step.css +25 -0
- package/src/styles/utilities.css +6 -259
|
@@ -0,0 +1,896 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import { X } from "lucide-react";
|
|
4
|
+
import * as React from "react";
|
|
5
|
+
|
|
6
|
+
import { cn } from "../../../lib/utils";
|
|
7
|
+
|
|
8
|
+
// =============================================================================
|
|
9
|
+
// Types
|
|
10
|
+
// =============================================================================
|
|
11
|
+
|
|
12
|
+
export type TagValue = string;
|
|
13
|
+
|
|
14
|
+
export interface TagsInputRootProps
|
|
15
|
+
extends Omit<
|
|
16
|
+
React.ComponentPropsWithoutRef<"div">,
|
|
17
|
+
"value" | "defaultValue" | "onChange" | "children"
|
|
18
|
+
> {
|
|
19
|
+
/** Controlled array of tag values. */
|
|
20
|
+
value?: TagValue[];
|
|
21
|
+
/** Initial array of tag values when uncontrolled. */
|
|
22
|
+
defaultValue?: TagValue[];
|
|
23
|
+
/** Callback when tag values change. */
|
|
24
|
+
onChange?: (value: TagValue[]) => void;
|
|
25
|
+
/** Validate a tag before adding. Return false to reject. */
|
|
26
|
+
onValidate?: (value: TagValue) => boolean;
|
|
27
|
+
/** Callback when a tag is rejected (duplicate or invalid). */
|
|
28
|
+
onTagInvalid?: (value: TagValue) => void;
|
|
29
|
+
/** Function to convert a tag value to its display string. */
|
|
30
|
+
displayValue?: (value: TagValue) => string;
|
|
31
|
+
/** Add tags on paste (split by delimiter). @default false */
|
|
32
|
+
addOnPaste?: boolean;
|
|
33
|
+
/** Add tags on Tab key. @default false */
|
|
34
|
+
addOnTab?: boolean;
|
|
35
|
+
/** Disable the entire input. @default false */
|
|
36
|
+
disabled?: boolean;
|
|
37
|
+
/** Allow editing existing tags. @default false */
|
|
38
|
+
editable?: boolean;
|
|
39
|
+
/** Wrap focus from last to first. @default false */
|
|
40
|
+
loop?: boolean;
|
|
41
|
+
/** Behavior on blur: "add" | "clear" | undefined. @default undefined */
|
|
42
|
+
blurBehavior?: "add" | "clear";
|
|
43
|
+
/** Delimiter for paste splitting. @default "," */
|
|
44
|
+
delimiter?: string;
|
|
45
|
+
/** Max number of tags. @default Infinity */
|
|
46
|
+
max?: number;
|
|
47
|
+
/** Read-only. @default false */
|
|
48
|
+
readOnly?: boolean;
|
|
49
|
+
/** Required in form. @default false */
|
|
50
|
+
required?: boolean;
|
|
51
|
+
/** Form field name. */
|
|
52
|
+
name?: string;
|
|
53
|
+
/** Unique id. */
|
|
54
|
+
id?: string;
|
|
55
|
+
/** Children or render prop. */
|
|
56
|
+
children?: React.ReactNode | ((context: { value: TagValue[] }) => React.ReactNode);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export interface TagsInputInputProps
|
|
60
|
+
extends Omit<React.ComponentPropsWithoutRef<"input">, "value" | "defaultValue"> {}
|
|
61
|
+
|
|
62
|
+
export interface TagsInputItemProps extends React.ComponentPropsWithoutRef<"div"> {
|
|
63
|
+
/** The value of the item. */
|
|
64
|
+
value: TagValue;
|
|
65
|
+
/** Whether the item is disabled. */
|
|
66
|
+
disabled?: boolean;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export interface TagsInputItemTextProps
|
|
70
|
+
extends React.ComponentPropsWithoutRef<"span"> {}
|
|
71
|
+
|
|
72
|
+
export interface TagsInputItemDeleteProps
|
|
73
|
+
extends React.ComponentPropsWithoutRef<"button"> {}
|
|
74
|
+
|
|
75
|
+
// =============================================================================
|
|
76
|
+
// Context
|
|
77
|
+
// =============================================================================
|
|
78
|
+
|
|
79
|
+
interface TagsInputContextValue {
|
|
80
|
+
value: TagValue[];
|
|
81
|
+
onValueChange: (value: TagValue[]) => void;
|
|
82
|
+
onItemAdd: (textValue: string, options?: { viaPaste?: boolean }) => boolean;
|
|
83
|
+
onItemRemove: (index: number) => void;
|
|
84
|
+
onItemUpdate: (index: number, newTextValue: string) => void;
|
|
85
|
+
onInputKeydown: (event: React.KeyboardEvent) => void;
|
|
86
|
+
highlightedIndex: number | null;
|
|
87
|
+
setHighlightedIndex: (index: number | null) => void;
|
|
88
|
+
editingIndex: number | null;
|
|
89
|
+
setEditingIndex: (index: number | null) => void;
|
|
90
|
+
displayValue: (value: TagValue) => string;
|
|
91
|
+
onItemLeave: () => void;
|
|
92
|
+
inputRef: React.RefObject<HTMLInputElement | null>;
|
|
93
|
+
addOnPaste: boolean;
|
|
94
|
+
addOnTab: boolean;
|
|
95
|
+
delimiter: string;
|
|
96
|
+
disabled: boolean;
|
|
97
|
+
editable: boolean;
|
|
98
|
+
isInvalidInput: boolean;
|
|
99
|
+
loop: boolean;
|
|
100
|
+
readOnly: boolean;
|
|
101
|
+
blurBehavior: "add" | "clear" | undefined;
|
|
102
|
+
max: number;
|
|
103
|
+
id: string;
|
|
104
|
+
inputId: string;
|
|
105
|
+
labelId: string;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const TagsInputContext = React.createContext<TagsInputContextValue | null>(null);
|
|
109
|
+
|
|
110
|
+
function useTagsInput(componentName: string) {
|
|
111
|
+
const context = React.useContext(TagsInputContext);
|
|
112
|
+
if (!context) {
|
|
113
|
+
throw new Error(`${componentName} must be used within <TagsInput>`);
|
|
114
|
+
}
|
|
115
|
+
return context;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
interface TagsInputItemContextValue {
|
|
119
|
+
id: string;
|
|
120
|
+
value: TagValue;
|
|
121
|
+
index: number;
|
|
122
|
+
isHighlighted: boolean;
|
|
123
|
+
isEditing: boolean;
|
|
124
|
+
disabled?: boolean;
|
|
125
|
+
textId: string;
|
|
126
|
+
displayValue: string;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const TagsInputItemContext = React.createContext<TagsInputItemContextValue | null>(null);
|
|
130
|
+
|
|
131
|
+
function useTagsInputItem(componentName: string) {
|
|
132
|
+
const context = React.useContext(TagsInputItemContext);
|
|
133
|
+
if (!context) {
|
|
134
|
+
throw new Error(`${componentName} must be used within <TagsInputItem>`);
|
|
135
|
+
}
|
|
136
|
+
return context;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// =============================================================================
|
|
140
|
+
// Root
|
|
141
|
+
// =============================================================================
|
|
142
|
+
|
|
143
|
+
const DATA_ITEM_ATTR = "data-tags-input-item";
|
|
144
|
+
|
|
145
|
+
const TagsInput = React.forwardRef<HTMLDivElement, TagsInputRootProps>(
|
|
146
|
+
(props, ref) => {
|
|
147
|
+
const {
|
|
148
|
+
value: valueProp,
|
|
149
|
+
defaultValue,
|
|
150
|
+
onChange,
|
|
151
|
+
onValidate,
|
|
152
|
+
onTagInvalid,
|
|
153
|
+
displayValue = (value: TagValue) => value.toString(),
|
|
154
|
+
addOnPaste = false,
|
|
155
|
+
addOnTab = false,
|
|
156
|
+
disabled = false,
|
|
157
|
+
editable = false,
|
|
158
|
+
loop = false,
|
|
159
|
+
blurBehavior,
|
|
160
|
+
delimiter = ",",
|
|
161
|
+
max = Number.POSITIVE_INFINITY,
|
|
162
|
+
readOnly = false,
|
|
163
|
+
required = false,
|
|
164
|
+
name,
|
|
165
|
+
children,
|
|
166
|
+
id: idProp,
|
|
167
|
+
className,
|
|
168
|
+
...rootProps
|
|
169
|
+
} = props;
|
|
170
|
+
|
|
171
|
+
const [value = [], setValue] = React.useState<TagValue[] | undefined>(defaultValue);
|
|
172
|
+
const resolvedValue = valueProp !== undefined ? valueProp : (value ?? []);
|
|
173
|
+
|
|
174
|
+
const [highlightedIndex, setHighlightedIndex] = React.useState<number | null>(null);
|
|
175
|
+
const [editingIndex, setEditingIndex] = React.useState<number | null>(null);
|
|
176
|
+
const [isInvalidInput, setIsInvalidInput] = React.useState(false);
|
|
177
|
+
const collectionRef = React.useRef<HTMLDivElement>(null);
|
|
178
|
+
const inputRef = React.useRef<HTMLInputElement>(null);
|
|
179
|
+
|
|
180
|
+
const id = idProp ?? React.useId();
|
|
181
|
+
const inputId = React.useId();
|
|
182
|
+
const labelId = React.useId();
|
|
183
|
+
|
|
184
|
+
const isFormControl = React.useRef(false);
|
|
185
|
+
React.useEffect(() => {
|
|
186
|
+
if (collectionRef.current) {
|
|
187
|
+
isFormControl.current = !!collectionRef.current.closest("form");
|
|
188
|
+
}
|
|
189
|
+
}, []);
|
|
190
|
+
|
|
191
|
+
const getEnabledItems = React.useCallback(() => {
|
|
192
|
+
if (!collectionRef.current) return [];
|
|
193
|
+
return Array.from(collectionRef.current.querySelectorAll(`[${DATA_ITEM_ATTR}]`));
|
|
194
|
+
}, []);
|
|
195
|
+
|
|
196
|
+
const onItemAdd = React.useCallback(
|
|
197
|
+
(textValue: string, options?: { viaPaste?: boolean }) => {
|
|
198
|
+
if (disabled || readOnly) return false;
|
|
199
|
+
|
|
200
|
+
if (addOnPaste && options?.viaPaste) {
|
|
201
|
+
const splitValues = textValue
|
|
202
|
+
.split(delimiter)
|
|
203
|
+
.map((v) => v.trim())
|
|
204
|
+
.filter(Boolean);
|
|
205
|
+
|
|
206
|
+
if (resolvedValue.length + splitValues.length > max && max > 0) {
|
|
207
|
+
onTagInvalid?.(textValue);
|
|
208
|
+
return false;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
const newValues = [...new Set(splitValues.filter((v) => !resolvedValue.includes(v)))];
|
|
212
|
+
const validValues = newValues.filter((v) => !onValidate || onValidate(v));
|
|
213
|
+
|
|
214
|
+
if (validValues.length === 0) return false;
|
|
215
|
+
|
|
216
|
+
const nextValue = [...resolvedValue, ...validValues];
|
|
217
|
+
if (valueProp === undefined) setValue(nextValue);
|
|
218
|
+
onChange?.(nextValue);
|
|
219
|
+
return true;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
if (resolvedValue.length >= max && max > 0) {
|
|
223
|
+
onTagInvalid?.(textValue);
|
|
224
|
+
return false;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
const trimmedValue = textValue.trim();
|
|
228
|
+
if (onValidate && !onValidate(trimmedValue)) {
|
|
229
|
+
setIsInvalidInput(true);
|
|
230
|
+
onTagInvalid?.(trimmedValue);
|
|
231
|
+
return false;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
const exists = resolvedValue.some((v) => v === trimmedValue);
|
|
235
|
+
if (exists) {
|
|
236
|
+
setIsInvalidInput(true);
|
|
237
|
+
onTagInvalid?.(trimmedValue);
|
|
238
|
+
return true;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
const nextValue = [...resolvedValue, trimmedValue];
|
|
242
|
+
if (valueProp === undefined) setValue(nextValue);
|
|
243
|
+
onChange?.(nextValue);
|
|
244
|
+
setHighlightedIndex(null);
|
|
245
|
+
setEditingIndex(null);
|
|
246
|
+
setIsInvalidInput(false);
|
|
247
|
+
return true;
|
|
248
|
+
},
|
|
249
|
+
[resolvedValue, max, addOnPaste, delimiter, onChange, onTagInvalid, onValidate, disabled, readOnly, valueProp]
|
|
250
|
+
);
|
|
251
|
+
|
|
252
|
+
const onItemUpdate = React.useCallback(
|
|
253
|
+
(index: number, newTextValue: string) => {
|
|
254
|
+
if (disabled || readOnly) return;
|
|
255
|
+
if (index === -1) return;
|
|
256
|
+
|
|
257
|
+
const trimmedValue = newTextValue.trim();
|
|
258
|
+
const exists = resolvedValue.some((v, i) => i !== index && v === trimmedValue);
|
|
259
|
+
if (exists) {
|
|
260
|
+
setIsInvalidInput(true);
|
|
261
|
+
onTagInvalid?.(trimmedValue);
|
|
262
|
+
return;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
if (onValidate && !onValidate(trimmedValue)) {
|
|
266
|
+
setIsInvalidInput(true);
|
|
267
|
+
onTagInvalid?.(trimmedValue);
|
|
268
|
+
return;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
const nextValue = [...resolvedValue];
|
|
272
|
+
nextValue[index] = displayValue(trimmedValue);
|
|
273
|
+
if (valueProp === undefined) setValue(nextValue);
|
|
274
|
+
onChange?.(nextValue);
|
|
275
|
+
setHighlightedIndex(index);
|
|
276
|
+
setEditingIndex(null);
|
|
277
|
+
setIsInvalidInput(false);
|
|
278
|
+
|
|
279
|
+
requestAnimationFrame(() => inputRef.current?.focus());
|
|
280
|
+
},
|
|
281
|
+
[resolvedValue, onChange, displayValue, onTagInvalid, onValidate, disabled, readOnly, valueProp]
|
|
282
|
+
);
|
|
283
|
+
|
|
284
|
+
const onItemRemove = React.useCallback(
|
|
285
|
+
(index: number) => {
|
|
286
|
+
if (disabled || readOnly) return;
|
|
287
|
+
if (index === -1) return;
|
|
288
|
+
|
|
289
|
+
const nextValue = [...resolvedValue];
|
|
290
|
+
nextValue.splice(index, 1);
|
|
291
|
+
if (valueProp === undefined) setValue(nextValue);
|
|
292
|
+
onChange?.(nextValue);
|
|
293
|
+
setHighlightedIndex(null);
|
|
294
|
+
setEditingIndex(null);
|
|
295
|
+
inputRef.current?.focus();
|
|
296
|
+
},
|
|
297
|
+
[resolvedValue, onChange, disabled, readOnly, valueProp]
|
|
298
|
+
);
|
|
299
|
+
|
|
300
|
+
const onItemLeave = React.useCallback(() => {
|
|
301
|
+
setHighlightedIndex(null);
|
|
302
|
+
setEditingIndex(null);
|
|
303
|
+
inputRef.current?.focus();
|
|
304
|
+
}, []);
|
|
305
|
+
|
|
306
|
+
const findNextEnabledIndex = React.useCallback(
|
|
307
|
+
(currentIndex: number | null, direction: "next" | "prev"): number | null => {
|
|
308
|
+
const enabledItems = getEnabledItems();
|
|
309
|
+
const enabledIndices = enabledItems.map((_, index) => index);
|
|
310
|
+
if (enabledIndices.length === 0) return null;
|
|
311
|
+
|
|
312
|
+
if (currentIndex === null) {
|
|
313
|
+
return direction === "prev"
|
|
314
|
+
? (enabledIndices[enabledIndices.length - 1] ?? null)
|
|
315
|
+
: (enabledIndices[0] ?? null);
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
const currentEnabledIndex = enabledIndices.indexOf(currentIndex);
|
|
319
|
+
if (direction === "next") {
|
|
320
|
+
return currentEnabledIndex >= enabledIndices.length - 1
|
|
321
|
+
? loop
|
|
322
|
+
? (enabledIndices[0] ?? null)
|
|
323
|
+
: null
|
|
324
|
+
: (enabledIndices[currentEnabledIndex + 1] ?? null);
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
return currentEnabledIndex <= 0
|
|
328
|
+
? loop
|
|
329
|
+
? (enabledIndices[enabledIndices.length - 1] ?? null)
|
|
330
|
+
: null
|
|
331
|
+
: (enabledIndices[currentEnabledIndex - 1] ?? null);
|
|
332
|
+
},
|
|
333
|
+
[getEnabledItems, loop]
|
|
334
|
+
);
|
|
335
|
+
|
|
336
|
+
const onInputKeydown = React.useCallback(
|
|
337
|
+
(event: React.KeyboardEvent) => {
|
|
338
|
+
const target = event.target;
|
|
339
|
+
if (!(target instanceof HTMLInputElement)) return;
|
|
340
|
+
|
|
341
|
+
const isArrowLeft = event.key === "ArrowLeft";
|
|
342
|
+
const isArrowRight = event.key === "ArrowRight";
|
|
343
|
+
|
|
344
|
+
if (target.value && target.selectionStart !== 0) {
|
|
345
|
+
setHighlightedIndex(null);
|
|
346
|
+
setEditingIndex(null);
|
|
347
|
+
return;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
switch (event.key) {
|
|
351
|
+
case "Delete":
|
|
352
|
+
case "Backspace": {
|
|
353
|
+
if (target.selectionStart !== 0 || target.selectionEnd !== 0) break;
|
|
354
|
+
if (highlightedIndex !== null) {
|
|
355
|
+
const newIndex = findNextEnabledIndex(highlightedIndex, "prev");
|
|
356
|
+
onItemRemove(highlightedIndex);
|
|
357
|
+
setHighlightedIndex(newIndex);
|
|
358
|
+
event.preventDefault();
|
|
359
|
+
} else if (event.key === "Backspace" && resolvedValue.length > 0) {
|
|
360
|
+
const lastIndex = findNextEnabledIndex(null, "prev");
|
|
361
|
+
setHighlightedIndex(lastIndex);
|
|
362
|
+
event.preventDefault();
|
|
363
|
+
}
|
|
364
|
+
break;
|
|
365
|
+
}
|
|
366
|
+
case "Enter": {
|
|
367
|
+
if (highlightedIndex !== null && editable && !disabled) {
|
|
368
|
+
setEditingIndex(highlightedIndex);
|
|
369
|
+
event.preventDefault();
|
|
370
|
+
}
|
|
371
|
+
break;
|
|
372
|
+
}
|
|
373
|
+
case "ArrowLeft":
|
|
374
|
+
case "ArrowRight": {
|
|
375
|
+
if (target.selectionStart === 0 && isArrowLeft && highlightedIndex === null && resolvedValue.length > 0) {
|
|
376
|
+
const lastIndex = findNextEnabledIndex(null, "prev");
|
|
377
|
+
setHighlightedIndex(lastIndex);
|
|
378
|
+
event.preventDefault();
|
|
379
|
+
} else if (highlightedIndex !== null) {
|
|
380
|
+
const nextIndex = findNextEnabledIndex(highlightedIndex, isArrowLeft ? "prev" : "next");
|
|
381
|
+
if (nextIndex !== null) {
|
|
382
|
+
setHighlightedIndex(nextIndex);
|
|
383
|
+
event.preventDefault();
|
|
384
|
+
} else if (isArrowRight) {
|
|
385
|
+
setHighlightedIndex(null);
|
|
386
|
+
requestAnimationFrame(() => target.setSelectionRange(0, 0));
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
break;
|
|
390
|
+
}
|
|
391
|
+
case "Home": {
|
|
392
|
+
if (highlightedIndex !== null) {
|
|
393
|
+
const firstIndex = findNextEnabledIndex(null, "next");
|
|
394
|
+
setHighlightedIndex(firstIndex);
|
|
395
|
+
event.preventDefault();
|
|
396
|
+
}
|
|
397
|
+
break;
|
|
398
|
+
}
|
|
399
|
+
case "End": {
|
|
400
|
+
if (highlightedIndex !== null) {
|
|
401
|
+
const lastIndex = findNextEnabledIndex(null, "prev");
|
|
402
|
+
setHighlightedIndex(lastIndex);
|
|
403
|
+
event.preventDefault();
|
|
404
|
+
}
|
|
405
|
+
break;
|
|
406
|
+
}
|
|
407
|
+
case "Escape": {
|
|
408
|
+
if (highlightedIndex !== null) setHighlightedIndex(null);
|
|
409
|
+
if (editingIndex !== null) setEditingIndex(null);
|
|
410
|
+
requestAnimationFrame(() => target.setSelectionRange(0, 0));
|
|
411
|
+
break;
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
},
|
|
415
|
+
[resolvedValue, highlightedIndex, editingIndex, onItemRemove, findNextEnabledIndex, editable, disabled]
|
|
416
|
+
);
|
|
417
|
+
|
|
418
|
+
const getIsClickedInEmptyRoot = React.useCallback(
|
|
419
|
+
(target: HTMLElement) => {
|
|
420
|
+
return (
|
|
421
|
+
collectionRef.current?.contains(target) &&
|
|
422
|
+
!target.hasAttribute(DATA_ITEM_ATTR) &&
|
|
423
|
+
target.tagName !== "INPUT"
|
|
424
|
+
);
|
|
425
|
+
},
|
|
426
|
+
[]
|
|
427
|
+
);
|
|
428
|
+
|
|
429
|
+
return (
|
|
430
|
+
<TagsInputContext.Provider
|
|
431
|
+
value={{
|
|
432
|
+
value: resolvedValue,
|
|
433
|
+
onValueChange: onChange ?? (() => {}),
|
|
434
|
+
onItemAdd,
|
|
435
|
+
onItemRemove,
|
|
436
|
+
onItemUpdate,
|
|
437
|
+
onInputKeydown,
|
|
438
|
+
highlightedIndex,
|
|
439
|
+
setHighlightedIndex,
|
|
440
|
+
editingIndex,
|
|
441
|
+
setEditingIndex,
|
|
442
|
+
displayValue,
|
|
443
|
+
onItemLeave,
|
|
444
|
+
inputRef,
|
|
445
|
+
isInvalidInput,
|
|
446
|
+
addOnPaste,
|
|
447
|
+
addOnTab,
|
|
448
|
+
disabled,
|
|
449
|
+
editable,
|
|
450
|
+
loop,
|
|
451
|
+
readOnly,
|
|
452
|
+
blurBehavior,
|
|
453
|
+
delimiter,
|
|
454
|
+
max,
|
|
455
|
+
id,
|
|
456
|
+
inputId,
|
|
457
|
+
labelId,
|
|
458
|
+
}}
|
|
459
|
+
>
|
|
460
|
+
<div
|
|
461
|
+
id={id}
|
|
462
|
+
ref={(node) => {
|
|
463
|
+
collectionRef.current = node;
|
|
464
|
+
if (typeof ref === "function") {
|
|
465
|
+
ref(node);
|
|
466
|
+
} else if (ref) {
|
|
467
|
+
(ref as React.MutableRefObject<HTMLDivElement | null>).current = node;
|
|
468
|
+
}
|
|
469
|
+
}}
|
|
470
|
+
data-disabled={disabled ? "" : undefined}
|
|
471
|
+
data-invalid={isInvalidInput ? "" : undefined}
|
|
472
|
+
data-readonly={readOnly ? "" : undefined}
|
|
473
|
+
className={cn(
|
|
474
|
+
"flex flex-wrap items-center gap-1.5 rounded-[var(--radius)] border border-input bg-transparent px-2 py-1.5 text-sm shadow-sm transition-colors",
|
|
475
|
+
"focus-within:ring-1 focus-within:ring-ring",
|
|
476
|
+
disabled && "cursor-not-allowed opacity-50",
|
|
477
|
+
className
|
|
478
|
+
)}
|
|
479
|
+
onClick={(event) => {
|
|
480
|
+
rootProps.onClick?.(event);
|
|
481
|
+
const target = event.target;
|
|
482
|
+
if (!(target instanceof HTMLElement)) return;
|
|
483
|
+
if (
|
|
484
|
+
getIsClickedInEmptyRoot(target) &&
|
|
485
|
+
document.activeElement !== inputRef.current
|
|
486
|
+
) {
|
|
487
|
+
event.currentTarget.focus();
|
|
488
|
+
inputRef.current?.focus();
|
|
489
|
+
}
|
|
490
|
+
}}
|
|
491
|
+
onMouseDown={(event) => {
|
|
492
|
+
rootProps.onMouseDown?.(event);
|
|
493
|
+
const target = event.target;
|
|
494
|
+
if (!(target instanceof HTMLElement)) return;
|
|
495
|
+
if (getIsClickedInEmptyRoot(target)) {
|
|
496
|
+
event.preventDefault();
|
|
497
|
+
}
|
|
498
|
+
}}
|
|
499
|
+
onBlur={(event) => {
|
|
500
|
+
rootProps.onBlur?.(event);
|
|
501
|
+
if (
|
|
502
|
+
event.relatedTarget !== inputRef.current &&
|
|
503
|
+
!collectionRef.current?.contains(event.relatedTarget as Node)
|
|
504
|
+
) {
|
|
505
|
+
requestAnimationFrame(() => setHighlightedIndex(null));
|
|
506
|
+
}
|
|
507
|
+
}}
|
|
508
|
+
{...rootProps}
|
|
509
|
+
>
|
|
510
|
+
{typeof children === "function" ? children({ value: resolvedValue }) : children}
|
|
511
|
+
{isFormControl.current && name && (
|
|
512
|
+
<input type="hidden" name={name} value={resolvedValue.join(delimiter)} disabled={disabled} required={required} />
|
|
513
|
+
)}
|
|
514
|
+
</div>
|
|
515
|
+
</TagsInputContext.Provider>
|
|
516
|
+
);
|
|
517
|
+
}
|
|
518
|
+
);
|
|
519
|
+
|
|
520
|
+
TagsInput.displayName = "TagsInput";
|
|
521
|
+
|
|
522
|
+
// =============================================================================
|
|
523
|
+
// Input
|
|
524
|
+
// =============================================================================
|
|
525
|
+
|
|
526
|
+
const TagsInputInput = React.forwardRef<HTMLInputElement, TagsInputInputProps>(
|
|
527
|
+
(props, ref) => {
|
|
528
|
+
const { autoFocus, ...inputProps } = props;
|
|
529
|
+
const context = useTagsInput("TagsInputInput");
|
|
530
|
+
|
|
531
|
+
const onCustomKeydown = React.useCallback(
|
|
532
|
+
(event: React.KeyboardEvent<HTMLInputElement>) => {
|
|
533
|
+
if (event.defaultPrevented) return;
|
|
534
|
+
const value = event.currentTarget.value;
|
|
535
|
+
if (!value) return;
|
|
536
|
+
const isAdded = context.onItemAdd(value);
|
|
537
|
+
if (isAdded) {
|
|
538
|
+
event.currentTarget.value = "";
|
|
539
|
+
context.setHighlightedIndex(null);
|
|
540
|
+
}
|
|
541
|
+
event.preventDefault();
|
|
542
|
+
},
|
|
543
|
+
[context.onItemAdd, context.setHighlightedIndex]
|
|
544
|
+
);
|
|
545
|
+
|
|
546
|
+
const onTab = React.useCallback(
|
|
547
|
+
(event: React.KeyboardEvent<HTMLInputElement>) => {
|
|
548
|
+
if (!context.addOnTab) return;
|
|
549
|
+
onCustomKeydown(event);
|
|
550
|
+
},
|
|
551
|
+
[context.addOnTab, onCustomKeydown]
|
|
552
|
+
);
|
|
553
|
+
|
|
554
|
+
React.useEffect(() => {
|
|
555
|
+
if (!autoFocus) return;
|
|
556
|
+
const id = requestAnimationFrame(() => context.inputRef.current?.focus());
|
|
557
|
+
return () => cancelAnimationFrame(id);
|
|
558
|
+
}, [autoFocus, context.inputRef]);
|
|
559
|
+
|
|
560
|
+
const composedRef = React.useCallback(
|
|
561
|
+
(node: HTMLInputElement | null) => {
|
|
562
|
+
context.inputRef.current = node;
|
|
563
|
+
if (typeof ref === "function") {
|
|
564
|
+
ref(node);
|
|
565
|
+
} else if (ref) {
|
|
566
|
+
(ref as React.MutableRefObject<HTMLInputElement | null>).current = node;
|
|
567
|
+
}
|
|
568
|
+
},
|
|
569
|
+
[ref, context.inputRef]
|
|
570
|
+
);
|
|
571
|
+
|
|
572
|
+
return (
|
|
573
|
+
<input
|
|
574
|
+
type="text"
|
|
575
|
+
id={context.inputId}
|
|
576
|
+
autoCapitalize="off"
|
|
577
|
+
autoComplete="off"
|
|
578
|
+
autoCorrect="off"
|
|
579
|
+
spellCheck="false"
|
|
580
|
+
autoFocus={autoFocus}
|
|
581
|
+
aria-labelledby={context.labelId}
|
|
582
|
+
aria-readonly={context.readOnly}
|
|
583
|
+
data-invalid={context.isInvalidInput ? "" : undefined}
|
|
584
|
+
disabled={context.disabled}
|
|
585
|
+
readOnly={context.readOnly}
|
|
586
|
+
{...inputProps}
|
|
587
|
+
ref={composedRef}
|
|
588
|
+
className={cn(
|
|
589
|
+
"flex-1 bg-transparent outline-none placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50 min-w-[60px]",
|
|
590
|
+
inputProps.className
|
|
591
|
+
)}
|
|
592
|
+
onBlur={(event) => {
|
|
593
|
+
inputProps.onBlur?.(event);
|
|
594
|
+
if (context.readOnly) return;
|
|
595
|
+
if (context.blurBehavior === "add") {
|
|
596
|
+
const val = event.target.value;
|
|
597
|
+
if (val) {
|
|
598
|
+
const isAdded = context.onItemAdd(val);
|
|
599
|
+
if (isAdded) event.target.value = "";
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
if (context.blurBehavior === "clear") {
|
|
603
|
+
event.target.value = "";
|
|
604
|
+
}
|
|
605
|
+
}}
|
|
606
|
+
onChange={(event) => {
|
|
607
|
+
inputProps.onChange?.(event);
|
|
608
|
+
if (context.readOnly) return;
|
|
609
|
+
const target = event.target;
|
|
610
|
+
if (!(target instanceof HTMLInputElement)) return;
|
|
611
|
+
const delimiter = context.delimiter;
|
|
612
|
+
if (delimiter === target.value.slice(-1)) {
|
|
613
|
+
const val = target.value.slice(0, -1);
|
|
614
|
+
target.value = "";
|
|
615
|
+
if (val) {
|
|
616
|
+
context.onItemAdd(val);
|
|
617
|
+
context.setHighlightedIndex(null);
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
}}
|
|
621
|
+
onKeyDown={(event) => {
|
|
622
|
+
inputProps.onKeyDown?.(event);
|
|
623
|
+
if (context.readOnly) return;
|
|
624
|
+
if (event.key === "Enter") onCustomKeydown(event);
|
|
625
|
+
if (event.key === "Tab") onTab(event);
|
|
626
|
+
context.onInputKeydown(event);
|
|
627
|
+
if (event.key.length === 1) context.setHighlightedIndex(null);
|
|
628
|
+
}}
|
|
629
|
+
onPaste={(event) => {
|
|
630
|
+
inputProps.onPaste?.(event);
|
|
631
|
+
if (context.readOnly) return;
|
|
632
|
+
if (context.addOnPaste) {
|
|
633
|
+
event.preventDefault();
|
|
634
|
+
const val = event.clipboardData.getData("text");
|
|
635
|
+
context.onItemAdd(val, { viaPaste: true });
|
|
636
|
+
context.setHighlightedIndex(null);
|
|
637
|
+
}
|
|
638
|
+
}}
|
|
639
|
+
/>
|
|
640
|
+
);
|
|
641
|
+
}
|
|
642
|
+
);
|
|
643
|
+
|
|
644
|
+
TagsInputInput.displayName = "TagsInputInput";
|
|
645
|
+
|
|
646
|
+
// =============================================================================
|
|
647
|
+
// Item
|
|
648
|
+
// =============================================================================
|
|
649
|
+
|
|
650
|
+
const TagsInputItem = React.forwardRef<HTMLDivElement, TagsInputItemProps>(
|
|
651
|
+
(props, ref) => {
|
|
652
|
+
const { value, disabled: itemDisabledProp, ...itemProps } = props;
|
|
653
|
+
const pointerTypeRef = React.useRef<React.PointerEvent["pointerType"]>("touch");
|
|
654
|
+
const context = useTagsInput("TagsInputItem");
|
|
655
|
+
const id = React.useId();
|
|
656
|
+
const textId = `${id}text`;
|
|
657
|
+
const index = context.value.indexOf(value);
|
|
658
|
+
const isHighlighted = index === context.highlightedIndex;
|
|
659
|
+
const isEditing = index === context.editingIndex;
|
|
660
|
+
const itemDisabled = itemDisabledProp || context.disabled;
|
|
661
|
+
const displayValue = context.displayValue(value);
|
|
662
|
+
|
|
663
|
+
const onItemSelect = React.useCallback(() => {
|
|
664
|
+
context.setHighlightedIndex(index);
|
|
665
|
+
context.inputRef.current?.focus();
|
|
666
|
+
}, [context.setHighlightedIndex, context.inputRef, index]);
|
|
667
|
+
|
|
668
|
+
return (
|
|
669
|
+
<TagsInputItemContext.Provider
|
|
670
|
+
value={{
|
|
671
|
+
id,
|
|
672
|
+
value,
|
|
673
|
+
index,
|
|
674
|
+
isHighlighted,
|
|
675
|
+
isEditing,
|
|
676
|
+
disabled: itemDisabled,
|
|
677
|
+
textId,
|
|
678
|
+
displayValue,
|
|
679
|
+
}}
|
|
680
|
+
>
|
|
681
|
+
<div
|
|
682
|
+
id={id}
|
|
683
|
+
ref={ref}
|
|
684
|
+
aria-labelledby={textId}
|
|
685
|
+
aria-current={isHighlighted}
|
|
686
|
+
aria-disabled={itemDisabled}
|
|
687
|
+
{...{ [DATA_ITEM_ATTR]: "" }}
|
|
688
|
+
data-state={isHighlighted ? "active" : "inactive"}
|
|
689
|
+
data-highlighted={isHighlighted ? "" : undefined}
|
|
690
|
+
data-editing={isEditing ? "" : undefined}
|
|
691
|
+
data-editable={context.editable ? "" : undefined}
|
|
692
|
+
data-disabled={itemDisabled ? "" : undefined}
|
|
693
|
+
className={cn(
|
|
694
|
+
"inline-flex items-center gap-1 rounded-[var(--radius)] border bg-secondary px-2 py-0.5 text-sm text-secondary-foreground transition-colors",
|
|
695
|
+
isHighlighted && "ring-1 ring-ring",
|
|
696
|
+
itemDisabled && "opacity-50 cursor-not-allowed",
|
|
697
|
+
itemProps.className
|
|
698
|
+
)}
|
|
699
|
+
onClick={(event) => {
|
|
700
|
+
itemProps.onClick?.(event);
|
|
701
|
+
event.stopPropagation();
|
|
702
|
+
if (!isEditing && pointerTypeRef.current !== "mouse") {
|
|
703
|
+
onItemSelect();
|
|
704
|
+
}
|
|
705
|
+
}}
|
|
706
|
+
onDoubleClick={() => {
|
|
707
|
+
itemProps.onDoubleClick?.(undefined as unknown as React.MouseEvent<HTMLDivElement>);
|
|
708
|
+
if (context.editable && !itemDisabled) {
|
|
709
|
+
requestAnimationFrame(() => context.setEditingIndex(index));
|
|
710
|
+
}
|
|
711
|
+
}}
|
|
712
|
+
onPointerUp={() => {
|
|
713
|
+
itemProps.onPointerUp?.(undefined as unknown as React.PointerEvent<HTMLDivElement>);
|
|
714
|
+
if (pointerTypeRef.current === "mouse") onItemSelect();
|
|
715
|
+
}}
|
|
716
|
+
onPointerDown={(event) => {
|
|
717
|
+
itemProps.onPointerDown?.(event);
|
|
718
|
+
pointerTypeRef.current = event.pointerType;
|
|
719
|
+
}}
|
|
720
|
+
onPointerMove={(event) => {
|
|
721
|
+
itemProps.onPointerMove?.(event);
|
|
722
|
+
pointerTypeRef.current = event.pointerType;
|
|
723
|
+
if (itemDisabledProp) {
|
|
724
|
+
context.onItemLeave();
|
|
725
|
+
} else if (pointerTypeRef.current === "mouse") {
|
|
726
|
+
event.currentTarget.focus({ preventScroll: true });
|
|
727
|
+
}
|
|
728
|
+
}}
|
|
729
|
+
onPointerLeave={(event) => {
|
|
730
|
+
itemProps.onPointerLeave?.(event);
|
|
731
|
+
if (event.currentTarget === document.activeElement) {
|
|
732
|
+
context.onItemLeave();
|
|
733
|
+
}
|
|
734
|
+
}}
|
|
735
|
+
{...itemProps}
|
|
736
|
+
/>
|
|
737
|
+
</TagsInputItemContext.Provider>
|
|
738
|
+
);
|
|
739
|
+
}
|
|
740
|
+
);
|
|
741
|
+
|
|
742
|
+
TagsInputItem.displayName = "TagsInputItem";
|
|
743
|
+
|
|
744
|
+
// =============================================================================
|
|
745
|
+
// ItemText
|
|
746
|
+
// =============================================================================
|
|
747
|
+
|
|
748
|
+
function TagsInputEditableItemText() {
|
|
749
|
+
const context = useTagsInput("TagsInputEditableItemText");
|
|
750
|
+
const itemContext = useTagsInputItem("TagsInputEditableItemText");
|
|
751
|
+
const [editValue, setEditValue] = React.useState(itemContext.displayValue);
|
|
752
|
+
|
|
753
|
+
const onBlur = React.useCallback(() => {
|
|
754
|
+
setEditValue(itemContext.displayValue);
|
|
755
|
+
context.setEditingIndex(null);
|
|
756
|
+
}, [context.setEditingIndex, itemContext.displayValue]);
|
|
757
|
+
|
|
758
|
+
const onChange = React.useCallback((event: React.ChangeEvent<HTMLInputElement>) => {
|
|
759
|
+
const target = event.target;
|
|
760
|
+
target.style.width = "0";
|
|
761
|
+
target.style.width = `${target.scrollWidth + 4}px`;
|
|
762
|
+
setEditValue(event.target.value);
|
|
763
|
+
}, []);
|
|
764
|
+
|
|
765
|
+
const onFocus = React.useCallback((event: React.FocusEvent<HTMLInputElement>) => {
|
|
766
|
+
event.target.select();
|
|
767
|
+
event.target.style.width = "0";
|
|
768
|
+
event.target.style.width = `${event.target.scrollWidth + 4}px`;
|
|
769
|
+
}, []);
|
|
770
|
+
|
|
771
|
+
const onKeyDown = React.useCallback(
|
|
772
|
+
(event: React.KeyboardEvent<HTMLInputElement>) => {
|
|
773
|
+
if (event.key === "Enter") {
|
|
774
|
+
const index = context.value.indexOf(itemContext.value);
|
|
775
|
+
context.onItemUpdate(index, editValue);
|
|
776
|
+
} else if (event.key === "Escape") {
|
|
777
|
+
setEditValue(itemContext.displayValue);
|
|
778
|
+
context.setEditingIndex(null);
|
|
779
|
+
context.setHighlightedIndex(itemContext.index);
|
|
780
|
+
context.inputRef.current?.focus();
|
|
781
|
+
}
|
|
782
|
+
event.stopPropagation();
|
|
783
|
+
},
|
|
784
|
+
[
|
|
785
|
+
context.value,
|
|
786
|
+
context.onItemUpdate,
|
|
787
|
+
context.setEditingIndex,
|
|
788
|
+
itemContext.displayValue,
|
|
789
|
+
editValue,
|
|
790
|
+
itemContext.value,
|
|
791
|
+
context.setHighlightedIndex,
|
|
792
|
+
itemContext.index,
|
|
793
|
+
context.inputRef,
|
|
794
|
+
]
|
|
795
|
+
);
|
|
796
|
+
|
|
797
|
+
return (
|
|
798
|
+
<input
|
|
799
|
+
type="text"
|
|
800
|
+
autoCapitalize="off"
|
|
801
|
+
autoComplete="off"
|
|
802
|
+
autoCorrect="off"
|
|
803
|
+
spellCheck="false"
|
|
804
|
+
autoFocus
|
|
805
|
+
aria-describedby={itemContext.textId}
|
|
806
|
+
value={editValue}
|
|
807
|
+
onChange={onChange}
|
|
808
|
+
onKeyDown={onKeyDown}
|
|
809
|
+
onFocus={onFocus}
|
|
810
|
+
onBlur={onBlur}
|
|
811
|
+
className="bg-transparent outline-none min-w-[1ch]"
|
|
812
|
+
style={{
|
|
813
|
+
font: "inherit",
|
|
814
|
+
color: "inherit",
|
|
815
|
+
padding: 0,
|
|
816
|
+
border: "none",
|
|
817
|
+
}}
|
|
818
|
+
/>
|
|
819
|
+
);
|
|
820
|
+
}
|
|
821
|
+
|
|
822
|
+
const TagsInputItemText = React.forwardRef<HTMLSpanElement, TagsInputItemTextProps>(
|
|
823
|
+
(props, ref) => {
|
|
824
|
+
const { children, ...itemTextProps } = props;
|
|
825
|
+
const context = useTagsInput("TagsInputItemText");
|
|
826
|
+
const itemContext = useTagsInputItem("TagsInputItemText");
|
|
827
|
+
|
|
828
|
+
if (itemContext.isEditing && context.editable && !itemContext.disabled) {
|
|
829
|
+
return <TagsInputEditableItemText />;
|
|
830
|
+
}
|
|
831
|
+
|
|
832
|
+
return (
|
|
833
|
+
<span id={itemContext.textId} {...itemTextProps} ref={ref}>
|
|
834
|
+
{children ?? itemContext.displayValue}
|
|
835
|
+
</span>
|
|
836
|
+
);
|
|
837
|
+
}
|
|
838
|
+
);
|
|
839
|
+
|
|
840
|
+
TagsInputItemText.displayName = "TagsInputItemText";
|
|
841
|
+
|
|
842
|
+
// =============================================================================
|
|
843
|
+
// ItemDelete
|
|
844
|
+
// =============================================================================
|
|
845
|
+
|
|
846
|
+
const TagsInputItemDelete = React.forwardRef<HTMLButtonElement, TagsInputItemDeleteProps>(
|
|
847
|
+
(props, ref) => {
|
|
848
|
+
const context = useTagsInput("TagsInputItemDelete");
|
|
849
|
+
const itemContext = useTagsInputItem("TagsInputItemDelete");
|
|
850
|
+
const disabled = itemContext.disabled || context.disabled;
|
|
851
|
+
|
|
852
|
+
if (itemContext.isEditing) return null;
|
|
853
|
+
|
|
854
|
+
return (
|
|
855
|
+
<button
|
|
856
|
+
type="button"
|
|
857
|
+
ref={ref}
|
|
858
|
+
tabIndex={disabled ? undefined : -1}
|
|
859
|
+
aria-labelledby={itemContext.textId}
|
|
860
|
+
aria-controls={itemContext.id}
|
|
861
|
+
aria-current={itemContext.isHighlighted}
|
|
862
|
+
data-state={itemContext.isHighlighted ? "active" : "inactive"}
|
|
863
|
+
data-disabled={disabled ? "" : undefined}
|
|
864
|
+
className={cn(
|
|
865
|
+
"inline-flex items-center justify-center rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
|
|
866
|
+
disabled && "pointer-events-none opacity-50",
|
|
867
|
+
props.className
|
|
868
|
+
)}
|
|
869
|
+
onClick={(event) => {
|
|
870
|
+
props.onClick?.(event);
|
|
871
|
+
if (disabled) return;
|
|
872
|
+
const index = context.value.indexOf(itemContext.value);
|
|
873
|
+
context.onItemRemove(index);
|
|
874
|
+
}}
|
|
875
|
+
{...props}
|
|
876
|
+
>
|
|
877
|
+
{props.children ?? <X className="h-3 w-3" />}
|
|
878
|
+
</button>
|
|
879
|
+
);
|
|
880
|
+
}
|
|
881
|
+
);
|
|
882
|
+
|
|
883
|
+
TagsInputItemDelete.displayName = "TagsInputItemDelete";
|
|
884
|
+
|
|
885
|
+
// =============================================================================
|
|
886
|
+
// Exports
|
|
887
|
+
// =============================================================================
|
|
888
|
+
|
|
889
|
+
export {
|
|
890
|
+
TagsInput,
|
|
891
|
+
TagsInputInput,
|
|
892
|
+
TagsInputItem,
|
|
893
|
+
TagsInputItemText,
|
|
894
|
+
TagsInputItemDelete,
|
|
895
|
+
};
|
|
896
|
+
|