@tipp/ui 1.4.21 → 1.4.23

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/dist/atoms/index.cjs +20 -4
  2. package/dist/atoms/index.cjs.map +1 -1
  3. package/dist/atoms/index.js +2 -2
  4. package/dist/atoms/text-area.cjs +10 -2
  5. package/dist/atoms/text-area.cjs.map +1 -1
  6. package/dist/atoms/text-area.js +1 -1
  7. package/dist/atoms/text-field.cjs +10 -2
  8. package/dist/atoms/text-field.cjs.map +1 -1
  9. package/dist/atoms/text-field.js +1 -1
  10. package/dist/chunk-23P5I2JR.js +192 -0
  11. package/dist/chunk-23P5I2JR.js.map +1 -0
  12. package/dist/chunk-52V6NWNP.js +350 -0
  13. package/dist/chunk-52V6NWNP.js.map +1 -0
  14. package/dist/chunk-7SOSBA77.js +40 -0
  15. package/dist/chunk-7SOSBA77.js.map +1 -0
  16. package/dist/chunk-IYEPU2SQ.js +350 -0
  17. package/dist/chunk-IYEPU2SQ.js.map +1 -0
  18. package/dist/chunk-SNHHJCBS.js +36 -0
  19. package/dist/chunk-SNHHJCBS.js.map +1 -0
  20. package/dist/chunk-SQ7HPPKD.js +360 -0
  21. package/dist/chunk-SQ7HPPKD.js.map +1 -0
  22. package/dist/chunk-UEELYPYP.js +40 -0
  23. package/dist/chunk-UEELYPYP.js.map +1 -0
  24. package/dist/chunk-WNYDAKKK.js +350 -0
  25. package/dist/chunk-WNYDAKKK.js.map +1 -0
  26. package/dist/chunk-XBI2YSRP.js +350 -0
  27. package/dist/chunk-XBI2YSRP.js.map +1 -0
  28. package/dist/chunk-YKVMEKVZ.js +36 -0
  29. package/dist/chunk-YKVMEKVZ.js.map +1 -0
  30. package/dist/chunk-ZCAMAL6P.js +360 -0
  31. package/dist/chunk-ZCAMAL6P.js.map +1 -0
  32. package/dist/index.cjs +35 -29
  33. package/dist/index.cjs.map +1 -1
  34. package/dist/index.js +3 -3
  35. package/dist/molecules/expand-table/index.js +2 -2
  36. package/dist/molecules/expand-table/row.js +2 -2
  37. package/dist/molecules/index.cjs +15 -25
  38. package/dist/molecules/index.cjs.map +1 -1
  39. package/dist/molecules/index.js +3 -3
  40. package/dist/molecules/navigation.js +2 -2
  41. package/dist/molecules/tag-selector.cjs +15 -25
  42. package/dist/molecules/tag-selector.cjs.map +1 -1
  43. package/dist/molecules/tag-selector.js +3 -3
  44. package/package.json +1 -1
  45. package/src/atoms/text-area.tsx +11 -2
  46. package/src/atoms/text-field.tsx +12 -2
  47. package/src/molecules/tag-selector.tsx +15 -30
@@ -0,0 +1,360 @@
1
+ import {
2
+ ScrollArea
3
+ } from "./chunk-ZKZDVS7G.js";
4
+ import {
5
+ Popover
6
+ } from "./chunk-5AVBYDPB.js";
7
+ import {
8
+ Flex
9
+ } from "./chunk-25HMMI7R.js";
10
+ import {
11
+ Grid
12
+ } from "./chunk-EGEQY3KT.js";
13
+ import {
14
+ Typo
15
+ } from "./chunk-52MVZ6AN.js";
16
+ import {
17
+ Button
18
+ } from "./chunk-U7JPP7WJ.js";
19
+ import {
20
+ CheckIcon
21
+ } from "./chunk-F77ES5Y3.js";
22
+ import {
23
+ AutoSizingInput
24
+ } from "./chunk-YJCCE5WP.js";
25
+ import {
26
+ Badge
27
+ } from "./chunk-EWD4AO5N.js";
28
+
29
+ // src/molecules/tag-selector.tsx
30
+ import {
31
+ useCallback,
32
+ useEffect,
33
+ useMemo,
34
+ useRef,
35
+ useState
36
+ } from "react";
37
+ import {
38
+ ChevronRightIcon,
39
+ Cross1Icon,
40
+ MagnifyingGlassIcon
41
+ } from "@radix-ui/react-icons";
42
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
43
+ var OPTION_HEIGHT = 32;
44
+ var DefaultDropdownContainer = (props) => {
45
+ return props.children;
46
+ };
47
+ function TagSelector(props) {
48
+ const {
49
+ options = [],
50
+ tagRender,
51
+ placeholder,
52
+ maxCount = 0,
53
+ DropdownContainer = DefaultDropdownContainer,
54
+ onChange,
55
+ size = "medium",
56
+ readOnly,
57
+ error
58
+ } = props;
59
+ const [selected, setSelected] = useState(props.selected || []);
60
+ const [value, setValue] = useState("");
61
+ const [focusIndex, setFocusIndex] = useState(null);
62
+ const [open, setOpen] = useState(false);
63
+ const [focus, setFocus] = useState(false);
64
+ const isInit = useRef(true);
65
+ const fieldRef = useRef(null);
66
+ const scrollRef = useRef(null);
67
+ const closeRef = useRef();
68
+ const onFocus = useCallback(() => {
69
+ clearTimeout(closeRef.current);
70
+ setFocus(true);
71
+ setOpen(true);
72
+ }, []);
73
+ const onBlur = useCallback(() => {
74
+ closeRef.current = setTimeout(() => {
75
+ setFocus(false);
76
+ setOpen(false);
77
+ setTimeout(() => {
78
+ setValue("");
79
+ }, 100);
80
+ }, 300);
81
+ }, []);
82
+ const onChangeValue = useCallback(
83
+ (e) => {
84
+ setValue(e.target.value);
85
+ },
86
+ []
87
+ );
88
+ const filteredOptions = useMemo(() => {
89
+ return options.filter((option) => {
90
+ return option.name.includes(value);
91
+ });
92
+ }, [options, value]);
93
+ const selectedItem = useMemo(() => {
94
+ const result = [];
95
+ selected.forEach((id) => {
96
+ const maybeItem = options.find((option) => option.id === id);
97
+ if (maybeItem)
98
+ result.push(maybeItem);
99
+ });
100
+ return result;
101
+ }, [options, selected]);
102
+ const onDelete = useCallback((id) => {
103
+ setSelected((prev) => prev.filter((el) => el !== id));
104
+ }, []);
105
+ const onSelect = useCallback(
106
+ (id) => {
107
+ if (maxCount && selected.length + 1 > maxCount)
108
+ return;
109
+ setSelected((prev) => [...prev, id]);
110
+ },
111
+ [maxCount, selected]
112
+ );
113
+ const toggleItem = useCallback(
114
+ (id) => {
115
+ if (selected.includes(id)) {
116
+ onDelete(id);
117
+ } else {
118
+ onSelect(id);
119
+ }
120
+ },
121
+ [onDelete, onSelect, selected]
122
+ );
123
+ const setScroll = useCallback((index) => {
124
+ if (scrollRef.current) {
125
+ const scrollTop = scrollRef.current.scrollTop;
126
+ const clientHeight = scrollRef.current.clientHeight;
127
+ const focusTop = index * OPTION_HEIGHT;
128
+ if (focusTop < scrollTop) {
129
+ scrollRef.current.scrollTop = focusTop;
130
+ } else if (focusTop + OPTION_HEIGHT > scrollTop + clientHeight) {
131
+ scrollRef.current.scrollTop = focusTop + OPTION_HEIGHT - clientHeight;
132
+ }
133
+ }
134
+ }, []);
135
+ const handleKeyDown = useCallback(
136
+ (e) => {
137
+ var _a;
138
+ const key = e.key;
139
+ switch (key) {
140
+ case "ArrowDown": {
141
+ e.preventDefault();
142
+ let newFocus = 0;
143
+ if (typeof focusIndex === "number") {
144
+ newFocus = focusIndex + 1 >= options.length ? 0 : focusIndex + 1;
145
+ }
146
+ setFocusIndex(newFocus);
147
+ setScroll(newFocus);
148
+ break;
149
+ }
150
+ case "ArrowUp": {
151
+ e.preventDefault();
152
+ let newFocus = 0;
153
+ if (typeof focusIndex === "number") {
154
+ newFocus = focusIndex - 1 < 0 ? options.length - 1 : focusIndex - 1;
155
+ }
156
+ setFocusIndex(newFocus);
157
+ setScroll(newFocus);
158
+ break;
159
+ }
160
+ case "Enter":
161
+ setValue("");
162
+ if (focusIndex !== null) {
163
+ toggleItem((_a = filteredOptions[focusIndex]) == null ? void 0 : _a.id);
164
+ }
165
+ break;
166
+ case "Backspace":
167
+ if (value.length === 0) {
168
+ setSelected((prev) => prev.slice(0, -1));
169
+ }
170
+ break;
171
+ case "Escape":
172
+ setOpen(false);
173
+ break;
174
+ }
175
+ },
176
+ [
177
+ focusIndex,
178
+ value.length,
179
+ setScroll,
180
+ options.length,
181
+ toggleItem,
182
+ filteredOptions
183
+ ]
184
+ );
185
+ const placeholderVisible = useMemo(() => {
186
+ if (selectedItem.length)
187
+ return false;
188
+ if (value)
189
+ return false;
190
+ if (focus)
191
+ return false;
192
+ return true;
193
+ }, [focus, selectedItem.length, value]);
194
+ useEffect(() => {
195
+ if (isInit.current) {
196
+ return;
197
+ }
198
+ onChange == null ? void 0 : onChange(selected);
199
+ }, [[...selected].sort().join(",")]);
200
+ useEffect(() => {
201
+ if (isInit.current) {
202
+ return;
203
+ }
204
+ setSelected(props.selected || []);
205
+ }, [[...props.selected || []].sort().join(",")]);
206
+ useEffect(() => {
207
+ setFocusIndex(null);
208
+ }, [open]);
209
+ useEffect(() => {
210
+ setFocusIndex(0);
211
+ }, [filteredOptions]);
212
+ useEffect(() => {
213
+ isInit.current = false;
214
+ }, []);
215
+ const badgeSize = useMemo(() => {
216
+ switch (size) {
217
+ case "large":
218
+ return "large";
219
+ case "medium":
220
+ case "small":
221
+ default:
222
+ return "small";
223
+ }
224
+ }, [size]);
225
+ const placeholderVariant = useMemo(() => {
226
+ switch (size) {
227
+ case "large":
228
+ return "subtitle";
229
+ case "medium":
230
+ default:
231
+ return "body";
232
+ }
233
+ }, [size]);
234
+ const focusClassName = focus ? " focused" : "";
235
+ const readOnlyClassName = readOnly ? " read-only" : "";
236
+ const errorClassName = error ? " error" : "";
237
+ const readonlyContent = /* @__PURE__ */ jsx(Flex, { gap: "1", children: selectedItem.map((item) => {
238
+ return tagRender ? tagRender(item) : /* @__PURE__ */ jsx(Badge, { size: badgeSize, children: item.name }, item.id);
239
+ }) });
240
+ const editContent = /* @__PURE__ */ jsxs(Fragment, { children: [
241
+ /* @__PURE__ */ jsxs(
242
+ Flex,
243
+ {
244
+ align: "center",
245
+ gap: "1",
246
+ maxWidth: "100%",
247
+ overflow: "hidden",
248
+ wrap: "wrap",
249
+ children: [
250
+ selectedItem.map((item) => {
251
+ return tagRender ? tagRender(item) : /* @__PURE__ */ jsxs(Badge, { size: badgeSize, children: [
252
+ item.name,
253
+ /* @__PURE__ */ jsx(
254
+ Button,
255
+ {
256
+ onClick: () => {
257
+ onDelete(item.id);
258
+ },
259
+ variant: "transparent",
260
+ children: /* @__PURE__ */ jsx(Cross1Icon, {})
261
+ }
262
+ )
263
+ ] }, item.id);
264
+ }),
265
+ placeholderVisible ? /* @__PURE__ */ jsx(Typo, { color: "gray", variant: placeholderVariant, children: placeholder }) : null,
266
+ /* @__PURE__ */ jsx(
267
+ AutoSizingInput,
268
+ {
269
+ onBlur,
270
+ onChange: onChangeValue,
271
+ onFocus,
272
+ onKeyDown: handleKeyDown,
273
+ ref: fieldRef,
274
+ value
275
+ }
276
+ )
277
+ ]
278
+ }
279
+ ),
280
+ focus ? /* @__PURE__ */ jsx(MagnifyingGlassIcon, {}) : /* @__PURE__ */ jsx(ChevronRightIcon, {})
281
+ ] });
282
+ return /* @__PURE__ */ jsxs(Popover.Root, { open: Boolean(filteredOptions.length) && open, children: [
283
+ /* @__PURE__ */ jsx(Popover.Trigger, { style: { position: "relative" }, children: /* @__PURE__ */ jsx(
284
+ Grid,
285
+ {
286
+ align: "center",
287
+ className: `tag-selector ${size} ${focusClassName}${readOnlyClassName}${errorClassName}`,
288
+ columns: "1fr auto",
289
+ gap: "1",
290
+ onClick: () => {
291
+ var _a;
292
+ return !readOnly && ((_a = fieldRef.current) == null ? void 0 : _a.focus());
293
+ },
294
+ children: readOnly ? readonlyContent : editContent
295
+ }
296
+ ) }),
297
+ /* @__PURE__ */ jsx(
298
+ Popover.Content,
299
+ {
300
+ onOpenAutoFocus: (e) => {
301
+ e.preventDefault();
302
+ },
303
+ children: /* @__PURE__ */ jsx(
304
+ DropdownContainer,
305
+ {
306
+ currentItem: filteredOptions[focusIndex || 0],
307
+ items: filteredOptions,
308
+ children: /* @__PURE__ */ jsx(
309
+ ScrollArea,
310
+ {
311
+ ref: scrollRef,
312
+ scrollbars: "vertical",
313
+ style: { height: OPTION_HEIGHT * 8 },
314
+ type: "auto",
315
+ children: /* @__PURE__ */ jsx(Flex, { direction: "column", children: filteredOptions.map((item, i) => {
316
+ const isSelected = Boolean(
317
+ selected.find((el) => el === item.id)
318
+ );
319
+ const isFocus = i === focusIndex;
320
+ return /* @__PURE__ */ jsx(
321
+ Button,
322
+ {
323
+ className: `tag-selector-button ${isFocus ? "focus" : ""}`,
324
+ onClick: () => {
325
+ var _a;
326
+ toggleItem(item.id);
327
+ (_a = fieldRef.current) == null ? void 0 : _a.focus();
328
+ },
329
+ onMouseEnter: () => {
330
+ setFocusIndex(i);
331
+ },
332
+ style: {
333
+ width: "100%",
334
+ background: isFocus ? "var(--accent-a3)" : void 0,
335
+ color: "var(--gray-12)",
336
+ fontWeight: 400,
337
+ boxShadow: "none"
338
+ },
339
+ variant: isSelected ? "soft" : "outline",
340
+ children: /* @__PURE__ */ jsxs(Flex, { justify: "between", width: "100%", children: [
341
+ item.name,
342
+ isSelected ? /* @__PURE__ */ jsx(CheckIcon, {}) : null
343
+ ] })
344
+ },
345
+ item.id
346
+ );
347
+ }) })
348
+ }
349
+ )
350
+ }
351
+ )
352
+ }
353
+ )
354
+ ] });
355
+ }
356
+
357
+ export {
358
+ TagSelector
359
+ };
360
+ //# sourceMappingURL=chunk-ZCAMAL6P.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/molecules/tag-selector.tsx"],"sourcesContent":["import React, {\n useCallback,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from 'react';\nimport {\n ChevronRightIcon,\n Cross1Icon,\n MagnifyingGlassIcon,\n} from '@radix-ui/react-icons';\nimport { Popover } from '../atoms/popover';\nimport { Flex } from '../atoms/flex';\nimport { Button } from '../atoms/button';\nimport type { BadgeProps } from '../atoms/badge';\nimport { Badge } from '../atoms/badge';\nimport { CheckIcon } from '../icon';\nimport { ScrollArea } from '../atoms/scroll-area';\nimport { Grid } from '../atoms/grid';\nimport type { TypoProps } from '../atoms';\nimport { AutoSizingInput, Typo } from '../atoms';\n\ntype ID = string;\ninterface Item {\n name: string;\n id: ID;\n}\n\nconst OPTION_HEIGHT = 32;\n\nexport interface TagSelectorProps<T extends Item> {\n options?: T[];\n selected?: ID[];\n placeholder?: string;\n disabled?: boolean;\n maxCount?: number;\n style?: React.CSSProperties;\n size?: 'large' | 'medium' | 'small';\n readOnly?: boolean;\n error?: boolean;\n\n onChange?: (selected: ID[]) => void;\n tagRender?: (item: T) => React.ReactElement;\n DropdownContainer?: DropdownContainer<T>;\n}\ntype DropdownContainer<T> = (props: {\n items: T[];\n children: React.ReactElement;\n currentItem?: T;\n}) => React.ReactElement;\n\nconst DefaultDropdownContainer = (props: {\n children: React.ReactElement;\n}): React.ReactElement => {\n return props.children;\n};\n\nexport function TagSelector<T extends Item>(\n props: TagSelectorProps<T>\n): React.ReactElement {\n const {\n options = [],\n tagRender,\n placeholder,\n maxCount = 0,\n DropdownContainer = DefaultDropdownContainer,\n onChange,\n size = 'medium',\n readOnly,\n error,\n } = props;\n const [selected, setSelected] = useState<string[]>(props.selected || []);\n const [value, setValue] = useState<string>('');\n const [focusIndex, setFocusIndex] = useState<number | null>(null);\n const [open, setOpen] = useState(false);\n const [focus, setFocus] = useState(false);\n const isInit = useRef(true);\n\n const fieldRef = useRef<HTMLInputElement>(null);\n const scrollRef = useRef<HTMLDivElement>(null);\n\n const closeRef = useRef<ReturnType<typeof setTimeout>>();\n\n const onFocus = useCallback(() => {\n clearTimeout(closeRef.current);\n setFocus(true);\n setOpen(true);\n }, []);\n\n const onBlur = useCallback(() => {\n closeRef.current = setTimeout(() => {\n setFocus(false);\n setOpen(false);\n setTimeout(() => {\n setValue('');\n }, 100);\n }, 300);\n }, []);\n\n const onChangeValue = useCallback<React.ChangeEventHandler<HTMLInputElement>>(\n (e) => {\n setValue(e.target.value);\n },\n []\n );\n\n const filteredOptions = useMemo<T[]>(() => {\n return options.filter((option) => {\n return option.name.includes(value);\n });\n }, [options, value]);\n\n const selectedItem = useMemo<T[]>(() => {\n const result = [] as T[];\n selected.forEach((id) => {\n const maybeItem = options.find((option) => option.id === id);\n if (maybeItem) result.push(maybeItem);\n });\n return result;\n }, [options, selected]);\n\n const onDelete = useCallback((id: string) => {\n setSelected((prev) => prev.filter((el) => el !== id));\n }, []);\n\n const onSelect = useCallback(\n (id: string) => {\n if (maxCount && selected.length + 1 > maxCount) return;\n setSelected((prev) => [...prev, id]);\n },\n [maxCount, selected]\n );\n\n const toggleItem = useCallback(\n (id: string) => {\n if (selected.includes(id)) {\n onDelete(id);\n } else {\n onSelect(id);\n }\n },\n [onDelete, onSelect, selected]\n );\n\n const setScroll = useCallback((index: number): void => {\n if (scrollRef.current) {\n const scrollTop = scrollRef.current.scrollTop;\n const clientHeight = scrollRef.current.clientHeight;\n const focusTop = index * OPTION_HEIGHT;\n if (focusTop < scrollTop) {\n scrollRef.current.scrollTop = focusTop;\n } else if (focusTop + OPTION_HEIGHT > scrollTop + clientHeight) {\n scrollRef.current.scrollTop = focusTop + OPTION_HEIGHT - clientHeight;\n }\n }\n }, []);\n\n const handleKeyDown = useCallback(\n (e: React.KeyboardEvent) => {\n const key = e.key;\n\n switch (key) {\n case 'ArrowDown': {\n e.preventDefault();\n let newFocus = 0;\n if (typeof focusIndex === 'number') {\n newFocus = focusIndex + 1 >= options.length ? 0 : focusIndex + 1;\n }\n setFocusIndex(newFocus);\n setScroll(newFocus);\n break;\n }\n\n case 'ArrowUp': {\n e.preventDefault();\n let newFocus = 0;\n if (typeof focusIndex === 'number') {\n newFocus = focusIndex - 1 < 0 ? options.length - 1 : focusIndex - 1;\n }\n setFocusIndex(newFocus);\n setScroll(newFocus);\n break;\n }\n\n case 'Enter':\n setValue('');\n if (focusIndex !== null) {\n toggleItem(filteredOptions[focusIndex]?.id);\n }\n break;\n\n case 'Backspace':\n if (value.length === 0) {\n setSelected((prev) => prev.slice(0, -1));\n }\n break;\n\n case 'Escape':\n setOpen(false);\n break;\n }\n },\n [\n focusIndex,\n value.length,\n setScroll,\n options.length,\n toggleItem,\n filteredOptions,\n ]\n );\n\n const placeholderVisible = useMemo(() => {\n if (selectedItem.length) return false;\n if (value) return false;\n if (focus) return false;\n return true;\n }, [focus, selectedItem.length, value]);\n\n useEffect(() => {\n if (isInit.current) {\n return;\n }\n onChange?.(selected);\n // eslint-disable-next-line react-hooks/exhaustive-deps -- 주소값 변경 방지\n }, [[...selected].sort().join(',')]);\n\n useEffect(() => {\n if (isInit.current) {\n return;\n }\n setSelected(props.selected || []);\n // eslint-disable-next-line react-hooks/exhaustive-deps -- 주소값 변경 방지\n }, [[...(props.selected || [])].sort().join(',')]);\n\n useEffect(() => {\n setFocusIndex(null);\n }, [open]);\n\n useEffect(() => {\n setFocusIndex(0);\n }, [filteredOptions]);\n\n useEffect(() => {\n isInit.current = false;\n }, []);\n\n const badgeSize = useMemo<BadgeProps['size']>(() => {\n switch (size) {\n case 'large':\n return 'large';\n case 'medium':\n case 'small':\n default:\n return 'small';\n }\n }, [size]);\n\n const placeholderVariant = useMemo<TypoProps['variant']>(() => {\n switch (size) {\n case 'large':\n return 'subtitle';\n case 'medium':\n default:\n return 'body';\n }\n }, [size]);\n\n const focusClassName = focus ? ' focused' : '';\n const readOnlyClassName = readOnly ? ' read-only' : '';\n const errorClassName = error ? ' error' : '';\n\n const readonlyContent = (\n <Flex gap=\"1\">\n {selectedItem.map((item) => {\n return tagRender ? (\n tagRender(item)\n ) : (\n <Badge key={item.id} size={badgeSize}>\n {item.name}\n </Badge>\n );\n })}\n </Flex>\n );\n\n const editContent = (\n <>\n <Flex\n align=\"center\"\n gap=\"1\"\n maxWidth=\"100%\"\n overflow=\"hidden\"\n wrap=\"wrap\"\n >\n {selectedItem.map((item) => {\n return tagRender ? (\n tagRender(item)\n ) : (\n <Badge key={item.id} size={badgeSize}>\n {item.name}\n <Button\n onClick={() => {\n onDelete(item.id);\n }}\n variant=\"transparent\"\n >\n <Cross1Icon />\n </Button>\n </Badge>\n );\n })}\n {placeholderVisible ? (\n <Typo color=\"gray\" variant={placeholderVariant}>\n {placeholder}\n </Typo>\n ) : null}\n <AutoSizingInput\n onBlur={onBlur}\n onChange={onChangeValue}\n onFocus={onFocus}\n onKeyDown={handleKeyDown}\n ref={fieldRef}\n value={value}\n />\n </Flex>\n {focus ? <MagnifyingGlassIcon /> : <ChevronRightIcon />}\n </>\n );\n return (\n <Popover.Root open={Boolean(filteredOptions.length) && open}>\n <Popover.Trigger style={{ position: 'relative' }}>\n <Grid\n align=\"center\"\n className={`tag-selector ${size} ${focusClassName}${readOnlyClassName}${errorClassName}`}\n columns=\"1fr auto\"\n gap=\"1\"\n onClick={() => !readOnly && fieldRef.current?.focus()}\n >\n {readOnly ? readonlyContent : editContent}\n </Grid>\n </Popover.Trigger>\n\n <Popover.Content\n onOpenAutoFocus={(e) => {\n e.preventDefault();\n }}\n >\n <DropdownContainer\n currentItem={filteredOptions[focusIndex || 0]}\n items={filteredOptions}\n >\n <ScrollArea\n ref={scrollRef}\n scrollbars=\"vertical\"\n style={{ height: OPTION_HEIGHT * 8 }}\n type=\"auto\"\n >\n <Flex direction=\"column\">\n {filteredOptions.map((item, i) => {\n const isSelected = Boolean(\n selected.find((el) => el === item.id)\n );\n const isFocus = i === focusIndex;\n\n return (\n <Button\n className={`tag-selector-button ${isFocus ? 'focus' : ''}`}\n key={item.id}\n onClick={() => {\n toggleItem(item.id);\n fieldRef.current?.focus();\n }}\n onMouseEnter={() => {\n setFocusIndex(i);\n }}\n style={{\n width: '100%',\n background: isFocus ? 'var(--accent-a3)' : undefined,\n color: 'var(--gray-12)',\n fontWeight: 400,\n boxShadow: 'none',\n }}\n variant={isSelected ? 'soft' : 'outline'}\n >\n <Flex justify=\"between\" width=\"100%\">\n {item.name}\n {isSelected ? <CheckIcon /> : null}\n </Flex>\n </Button>\n );\n })}\n </Flex>\n </ScrollArea>\n </DropdownContainer>\n </Popover.Content>\n </Popover.Root>\n );\n}\n"],"mappings":"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","names":[]}
package/dist/index.cjs CHANGED
@@ -656,7 +656,7 @@ var import_react12 = require("react");
656
656
  var import_jsx_runtime12 = require("react/jsx-runtime");
657
657
  var TextArea = (0, import_react12.forwardRef)(
658
658
  (props, ref) => {
659
- const _a = props, { error, style } = _a, rest = __objRest(_a, ["error", "style"]);
659
+ const _a = props, { error, style, className } = _a, rest = __objRest(_a, ["error", "style", "className"]);
660
660
  const fieldStyle = (0, import_react12.useMemo)(() => {
661
661
  if (!style && !error)
662
662
  return void 0;
@@ -665,7 +665,15 @@ var TextArea = (0, import_react12.forwardRef)(
665
665
  } : {};
666
666
  return __spreadValues(__spreadValues({}, style || {}), errorStyle);
667
667
  }, [error, style]);
668
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_themes45.TextArea, __spreadProps(__spreadValues({}, rest), { ref, style: fieldStyle }));
668
+ const classNameStr = error ? `error ${className}` : className;
669
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
670
+ import_themes45.TextArea,
671
+ __spreadProps(__spreadValues({}, rest), {
672
+ className: classNameStr,
673
+ ref,
674
+ style: fieldStyle
675
+ })
676
+ );
669
677
  }
670
678
  );
671
679
  TextArea.displayName = "TextArea";
@@ -700,7 +708,7 @@ var import_themes47 = require("@radix-ui/themes");
700
708
  var import_react14 = require("react");
701
709
  var import_jsx_runtime14 = require("react/jsx-runtime");
702
710
  var Root3 = (0, import_react14.forwardRef)((props, ref) => {
703
- const _a = props, { error, style } = _a, rest = __objRest(_a, ["error", "style"]);
711
+ const _a = props, { error, style, className } = _a, rest = __objRest(_a, ["error", "style", "className"]);
704
712
  const fieldStyle = (0, import_react14.useMemo)(() => {
705
713
  if (!style && !error)
706
714
  return void 0;
@@ -709,7 +717,15 @@ var Root3 = (0, import_react14.forwardRef)((props, ref) => {
709
717
  };
710
718
  return __spreadValues(__spreadValues({}, style || {}), errorStyle);
711
719
  }, [error, style]);
712
- return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_themes47.TextField.Root, __spreadValues({ ref, style: fieldStyle }, rest));
720
+ const classNameStr = error ? `error ${className}` : className;
721
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
722
+ import_themes47.TextField.Root,
723
+ __spreadValues({
724
+ className: classNameStr,
725
+ ref,
726
+ style: fieldStyle
727
+ }, rest)
728
+ );
713
729
  });
714
730
  Root3.displayName = "TextField.Root";
715
731
  var TextField = { Root: Root3, Slot: import_themes47.TextField.Slot };
@@ -1729,17 +1745,17 @@ function TagSelector(props) {
1729
1745
  placeholder,
1730
1746
  maxCount = 0,
1731
1747
  DropdownContainer = DefaultDropdownContainer,
1732
- onChange,
1733
1748
  size = "medium",
1734
1749
  readOnly,
1735
1750
  error
1736
1751
  } = props;
1737
- const [selected, setSelected] = (0, import_react26.useState)(props.selected || []);
1752
+ const [_selected, _setSelected] = (0, import_react26.useState)(props.selected || []);
1753
+ const selected = props.selected || _selected;
1754
+ const setSelected = props.onChange || _setSelected;
1738
1755
  const [value, setValue] = (0, import_react26.useState)("");
1739
1756
  const [focusIndex, setFocusIndex] = (0, import_react26.useState)(null);
1740
1757
  const [open, setOpen] = (0, import_react26.useState)(false);
1741
1758
  const [focus, setFocus] = (0, import_react26.useState)(false);
1742
- const isInit = (0, import_react26.useRef)(true);
1743
1759
  const fieldRef = (0, import_react26.useRef)(null);
1744
1760
  const scrollRef = (0, import_react26.useRef)(null);
1745
1761
  const closeRef = (0, import_react26.useRef)();
@@ -1777,16 +1793,19 @@ function TagSelector(props) {
1777
1793
  });
1778
1794
  return result;
1779
1795
  }, [options, selected]);
1780
- const onDelete = (0, import_react26.useCallback)((id) => {
1781
- setSelected((prev) => prev.filter((el) => el !== id));
1782
- }, []);
1796
+ const onDelete = (0, import_react26.useCallback)(
1797
+ (id) => {
1798
+ setSelected(selected.filter((el) => el !== id));
1799
+ },
1800
+ [selected, setSelected]
1801
+ );
1783
1802
  const onSelect = (0, import_react26.useCallback)(
1784
1803
  (id) => {
1785
1804
  if (maxCount && selected.length + 1 > maxCount)
1786
1805
  return;
1787
- setSelected((prev) => [...prev, id]);
1806
+ setSelected([...selected, id]);
1788
1807
  },
1789
- [maxCount, selected]
1808
+ [maxCount, selected, setSelected]
1790
1809
  );
1791
1810
  const toggleItem = (0, import_react26.useCallback)(
1792
1811
  (id) => {
@@ -1843,7 +1862,7 @@ function TagSelector(props) {
1843
1862
  break;
1844
1863
  case "Backspace":
1845
1864
  if (value.length === 0) {
1846
- setSelected((prev) => prev.slice(0, -1));
1865
+ setSelected(selected.slice(0, -1));
1847
1866
  }
1848
1867
  break;
1849
1868
  case "Escape":
@@ -1857,7 +1876,9 @@ function TagSelector(props) {
1857
1876
  setScroll,
1858
1877
  options.length,
1859
1878
  toggleItem,
1860
- filteredOptions
1879
+ filteredOptions,
1880
+ setSelected,
1881
+ selected
1861
1882
  ]
1862
1883
  );
1863
1884
  const placeholderVisible = (0, import_react26.useMemo)(() => {
@@ -1869,27 +1890,12 @@ function TagSelector(props) {
1869
1890
  return false;
1870
1891
  return true;
1871
1892
  }, [focus, selectedItem.length, value]);
1872
- (0, import_react26.useEffect)(() => {
1873
- if (isInit.current) {
1874
- return;
1875
- }
1876
- onChange == null ? void 0 : onChange(selected);
1877
- }, [[...selected].sort().join(",")]);
1878
- (0, import_react26.useEffect)(() => {
1879
- if (isInit.current) {
1880
- return;
1881
- }
1882
- setSelected(props.selected || []);
1883
- }, [[...props.selected || []].sort().join(",")]);
1884
1893
  (0, import_react26.useEffect)(() => {
1885
1894
  setFocusIndex(null);
1886
1895
  }, [open]);
1887
1896
  (0, import_react26.useEffect)(() => {
1888
1897
  setFocusIndex(0);
1889
1898
  }, [filteredOptions]);
1890
- (0, import_react26.useEffect)(() => {
1891
- isInit.current = false;
1892
- }, []);
1893
1899
  const badgeSize = (0, import_react26.useMemo)(() => {
1894
1900
  switch (size) {
1895
1901
  case "large":