@petnow/ui 5.4.0 โ 6.0.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 +30 -0
- package/dist/combobox.js +166 -152
- package/dist/combobox.js.map +1 -1
- package/dist/{fieldStyles-DyZu__hI.js โ fieldStyles-DXyDrzxD.js} +5 -6
- package/dist/fieldStyles-DXyDrzxD.js.map +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.es.js +17 -17
- package/dist/mergeRefs-CsNeQOZ6.js +39 -0
- package/dist/mergeRefs-CsNeQOZ6.js.map +1 -0
- package/dist/select.js +86 -66
- package/dist/select.js.map +1 -1
- package/dist/src/components/combobox/Combobox.d.ts +1 -0
- package/dist/src/components/select/Select.d.ts +13 -6
- package/dist/src/components/text-field/TextField.d.ts +1 -20
- package/dist/src/components/ui/listbox.d.ts +1 -0
- package/dist/src/components/ui/listboxStyles.d.ts +8 -0
- package/dist/text-field.js +50 -114
- package/dist/text-field.js.map +1 -1
- package/dist/v1/text-field.js +1 -1
- package/docs/migration/v5-to-v6.md +59 -0
- package/package.json +1 -1
- package/dist/fieldStyles-DyZu__hI.js.map +0 -1
- package/dist/mergeRefs-BHwdBwNQ.js +0 -27
- package/dist/mergeRefs-BHwdBwNQ.js.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,35 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 6.0.0
|
|
4
|
+
|
|
5
|
+
### Breaking
|
|
6
|
+
|
|
7
|
+
- **`Select`** is no longer a native `<select>`. It is built on Radix Select, like the Select in
|
|
8
|
+
petify-admin-console: the trigger keeps the `TextField` look, and the options open in a panel below it
|
|
9
|
+
(portal to `body`, 8px radius, border, shadow) with a check in front of the picked option and the
|
|
10
|
+
picked option in the accent color. Keyboard (arrows, Enter, Escape, typeahead) and ARIA come from Radix.
|
|
11
|
+
- `ref` reaches the trigger `<button>` (`HTMLButtonElement`), and the rest props are button attributes,
|
|
12
|
+
not `<select>` attributes. `name`, `form` and `required` still work: inside a form, Radix renders a
|
|
13
|
+
hidden `<select>`, so the value reaches `FormData` and browser validation.
|
|
14
|
+
- Mobile browsers show this panel instead of the system picker.
|
|
15
|
+
- An option value cannot be `""`; `""` means nothing is picked and shows `placeholder`. Options with
|
|
16
|
+
`""` are left out. Once picked, the user cannot go back to "nothing picked"; the native `<select>`
|
|
17
|
+
let them pick the placeholder again. Set `value` to `""` in code if a form needs that.
|
|
18
|
+
- A form `reset` restores `defaultValue` (or nothing) on screen and in the hidden `<select>`; a
|
|
19
|
+
controlled `Select` gets it through `onChange`.
|
|
20
|
+
- **`TextFieldSelect`** is removed. It was only a button that called `onPress`, without a list. Use
|
|
21
|
+
`Select` (or `Combobox` to search). See [`docs/migration/v5-to-v6.md`](docs/migration/v5-to-v6.md).
|
|
22
|
+
|
|
23
|
+
### Changes
|
|
24
|
+
|
|
25
|
+
- `Combobox` opens its list in the same panel as `Select`, through a Radix Popover portal, so a parent with
|
|
26
|
+
`overflow: hidden` or a scroll container no longer clips it, and the list flips above the input when
|
|
27
|
+
there is no room below. Its options use the `Select` row (left check slot, accent color for the picked
|
|
28
|
+
option). The list is in the DOM only while it is open, so the input and toggle set `aria-controls` only
|
|
29
|
+
then. Escape on an open list closes only the list, not a surrounding dialog.
|
|
30
|
+
- TextField stories no longer show select-like fields: the `SelectField` and `SelectFieldSmallError`
|
|
31
|
+
stories are removed, and `NormalWithErrorAndCounter` no longer shows a dropdown chevron.
|
|
32
|
+
|
|
3
33
|
## 5.4.0
|
|
4
34
|
|
|
5
35
|
### Added
|
package/dist/combobox.js
CHANGED
|
@@ -1,201 +1,215 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { t as e } from "./TailwindUtil-DiNvXuNl.js";
|
|
3
|
-
import { a as t,
|
|
4
|
-
import { ChevronDownIcon as
|
|
5
|
-
import {
|
|
3
|
+
import { a as t, i as n, t as r } from "./fieldStyles-DXyDrzxD.js";
|
|
4
|
+
import { ChevronDownIcon as ee } from "./icons/action/chevron-down.js";
|
|
5
|
+
import { c as te, d as ne, l as re, n as ie, o as ae, t as oe, u as se } from "./mergeRefs-CsNeQOZ6.js";
|
|
6
6
|
import { jsx as i, jsxs as a } from "react/jsx-runtime";
|
|
7
|
-
import { useEffect as o, useId as
|
|
7
|
+
import { useCallback as ce, useEffect as o, useId as le, useMemo as ue, useRef as s, useState as c } from "react";
|
|
8
|
+
import { Popover as l } from "radix-ui";
|
|
8
9
|
//#region src/components/combobox/Combobox.tsx
|
|
9
|
-
var
|
|
10
|
-
let
|
|
10
|
+
var u = (e) => e.toLocaleLowerCase().replace(/\s+/g, ""), de = (e, t) => u(e.label).includes(u(t)), d = ({ id: d, label: f, options: fe, value: p, onChange: pe, filter: m = de, emptyText: h, toggleLabel: g, invalidSelectionText: _ = "Select an option from the list.", errorText: v, assistiveText: y, size: b = "large", name: x, required: S, disabled: C = !1, placeholder: me, className: he, autoComplete: ge = "off", "aria-describedby": _e, onBlur: ve, onKeyDown: ye, onClick: w, ref: T, ...E }) => {
|
|
11
|
+
let be = le(), D = d ?? be, O = `${D}-listbox`, k = `${D}-message`, A = (e) => `${D}-option-${e}`, j = !!v, M = !!(v || y), N = `${D}-label`, P = s(null), xe = ue(() => oe(P, T), [T]), F = s(null), I = s(!1), L = fe.map((e) => typeof e == "string" ? {
|
|
11
12
|
value: e,
|
|
12
13
|
label: e
|
|
13
|
-
} : e),
|
|
14
|
+
} : e), R = L.find((e) => e.value === p), [z, B] = c(null), [V, H] = c(!1), [U, W] = c(-1), G = L.map((e, t) => ({
|
|
14
15
|
option: e,
|
|
15
16
|
index: t
|
|
16
|
-
})).filter(({ option: e }) => !
|
|
17
|
+
})).filter(({ option: e }) => !z || m(e, z)), K = V && !C && (G.length > 0 || h !== void 0), q = G.filter(({ option: e }) => !e.disabled), J = s(-1);
|
|
17
18
|
o(() => {
|
|
18
|
-
let e =
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
}, [
|
|
23
|
-
|
|
19
|
+
let e = I.current;
|
|
20
|
+
if (I.current = !1, !K || U < 0 || !e) return;
|
|
21
|
+
let t = document.getElementById(A(U));
|
|
22
|
+
t ? t.scrollIntoView?.({ block: "nearest" }) : J.current = U;
|
|
23
|
+
}, [K, U]);
|
|
24
|
+
let Se = ce((e) => {
|
|
25
|
+
!e || J.current < 0 || (document.getElementById(`${D}-option-${J.current}`)?.scrollIntoView?.({ block: "nearest" }), J.current = -1);
|
|
26
|
+
}, [D]);
|
|
27
|
+
o(() => {
|
|
28
|
+
p !== F.current && B(null), F.current = null;
|
|
29
|
+
}, [p]), o(() => {
|
|
30
|
+
F.current = null;
|
|
24
31
|
});
|
|
25
|
-
let Y = !!(
|
|
32
|
+
let Y = !!(S && !R && z);
|
|
26
33
|
o(() => {
|
|
27
|
-
P.current?.setCustomValidity(Y ?
|
|
28
|
-
}, [Y,
|
|
34
|
+
P.current?.setCustomValidity(Y ? _ : "");
|
|
35
|
+
}, [Y, _]);
|
|
29
36
|
let X = (e) => {
|
|
30
|
-
|
|
37
|
+
F.current = e, pe(e);
|
|
31
38
|
}, Z = () => {
|
|
32
|
-
|
|
39
|
+
I.current = !0, H(!0), W(R && !R.disabled ? L.indexOf(R) : -1);
|
|
33
40
|
}, Q = () => {
|
|
34
|
-
|
|
41
|
+
H(!1), B(null), W(-1);
|
|
35
42
|
}, $ = (e) => {
|
|
36
43
|
e.disabled || (X(e.value), Q());
|
|
37
|
-
},
|
|
38
|
-
if (
|
|
39
|
-
let t =
|
|
40
|
-
|
|
41
|
-
},
|
|
42
|
-
if (
|
|
44
|
+
}, Ce = (e) => {
|
|
45
|
+
if (q.length === 0) return;
|
|
46
|
+
let t = q.findIndex(({ index: e }) => e === U), n = t === -1 ? e === 1 ? 0 : q.length - 1 : (t + e + q.length) % q.length;
|
|
47
|
+
I.current = !0, W(q[n].index);
|
|
48
|
+
}, we = (e) => {
|
|
49
|
+
if (ye?.(e), !(e.defaultPrevented || e.nativeEvent.isComposing || e.keyCode === 229)) switch (e.key) {
|
|
43
50
|
case "ArrowDown":
|
|
44
51
|
case "ArrowUp":
|
|
45
|
-
if (e.preventDefault(),
|
|
52
|
+
if (e.preventDefault(), V && e.altKey && e.key === "ArrowUp") {
|
|
46
53
|
Q();
|
|
47
54
|
return;
|
|
48
55
|
}
|
|
49
|
-
if (!
|
|
50
|
-
|
|
51
|
-
let t =
|
|
52
|
-
|
|
56
|
+
if (!V) {
|
|
57
|
+
H(!0);
|
|
58
|
+
let t = R && !R.disabled ? L.indexOf(R) : -1, n = e.key === "ArrowDown" ? q[0] : q[q.length - 1];
|
|
59
|
+
I.current = !0, W(t >= 0 || e.altKey ? t : n?.index ?? -1);
|
|
53
60
|
return;
|
|
54
61
|
}
|
|
55
|
-
|
|
62
|
+
Ce(e.key === "ArrowDown" ? 1 : -1);
|
|
56
63
|
return;
|
|
57
64
|
case "Enter":
|
|
58
|
-
|
|
65
|
+
K && U >= 0 && (e.preventDefault(), $(L[U]));
|
|
59
66
|
return;
|
|
60
67
|
case "Escape":
|
|
61
|
-
(
|
|
68
|
+
(K || z !== null) && (e.preventDefault(), Q());
|
|
62
69
|
return;
|
|
63
70
|
}
|
|
64
|
-
},
|
|
65
|
-
let t = e.trim() ?
|
|
66
|
-
if (t ? t.value !==
|
|
67
|
-
|
|
71
|
+
}, Te = (e) => {
|
|
72
|
+
let t = e.trim() ? L.find((t) => !t.disabled && u(t.label) === u(e)) : void 0;
|
|
73
|
+
if (t ? t.value !== p && X(t.value) : p !== "" && X(""), document.activeElement !== P.current) {
|
|
74
|
+
B(t ? null : e);
|
|
68
75
|
return;
|
|
69
76
|
}
|
|
70
|
-
|
|
71
|
-
let n =
|
|
72
|
-
|
|
77
|
+
B(e), H(!0);
|
|
78
|
+
let n = L.findIndex((t) => !t.disabled && (!e || m(t, e)));
|
|
79
|
+
I.current = !0;
|
|
80
|
+
let r = t && m(t, e);
|
|
81
|
+
W(r ? L.indexOf(t) : e ? n : -1);
|
|
73
82
|
};
|
|
74
83
|
return /* @__PURE__ */ a("div", {
|
|
75
|
-
className: e("flex w-full min-w-0 flex-col gap-2",
|
|
84
|
+
className: e("flex w-full min-w-0 flex-col gap-2", he),
|
|
76
85
|
children: [
|
|
77
|
-
|
|
86
|
+
f && /* @__PURE__ */ i(se, {
|
|
78
87
|
id: N,
|
|
79
|
-
htmlFor:
|
|
80
|
-
size:
|
|
81
|
-
required:
|
|
82
|
-
children:
|
|
88
|
+
htmlFor: D,
|
|
89
|
+
size: b,
|
|
90
|
+
required: S,
|
|
91
|
+
children: f
|
|
83
92
|
}),
|
|
84
|
-
/* @__PURE__ */ a(
|
|
85
|
-
|
|
93
|
+
/* @__PURE__ */ a(l.Root, {
|
|
94
|
+
open: K,
|
|
86
95
|
children: [
|
|
87
|
-
/* @__PURE__ */
|
|
88
|
-
|
|
89
|
-
children:
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
"
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
96
|
+
/* @__PURE__ */ i(l.Anchor, {
|
|
97
|
+
asChild: !0,
|
|
98
|
+
children: /* @__PURE__ */ a("div", {
|
|
99
|
+
className: "relative",
|
|
100
|
+
children: [/* @__PURE__ */ i("input", {
|
|
101
|
+
...E,
|
|
102
|
+
ref: xe,
|
|
103
|
+
id: D,
|
|
104
|
+
type: "text",
|
|
105
|
+
role: "combobox",
|
|
106
|
+
"aria-expanded": K,
|
|
107
|
+
"aria-controls": K ? O : void 0,
|
|
108
|
+
"aria-autocomplete": "list",
|
|
109
|
+
"aria-activedescendant": K && U >= 0 ? A(U) : void 0,
|
|
110
|
+
"aria-invalid": j || void 0,
|
|
111
|
+
"aria-describedby": t(_e, M && k),
|
|
112
|
+
autoComplete: ge,
|
|
113
|
+
required: S,
|
|
114
|
+
disabled: C,
|
|
115
|
+
placeholder: me,
|
|
116
|
+
value: z ?? R?.label ?? "",
|
|
117
|
+
onChange: (e) => Te(e.target.value),
|
|
118
|
+
onKeyDown: we,
|
|
119
|
+
onClick: (e) => {
|
|
120
|
+
w?.(e), !V && !e.defaultPrevented && Z();
|
|
121
|
+
},
|
|
122
|
+
onBlur: (e) => {
|
|
123
|
+
ve?.(e), Q();
|
|
124
|
+
},
|
|
125
|
+
className: e(r({
|
|
126
|
+
size: b,
|
|
127
|
+
hasError: j,
|
|
128
|
+
disabled: C
|
|
129
|
+
}), "text-text-base-neutral-5 placeholder:text-text-base-neutral-2 w-full min-w-0 truncate outline-none disabled:cursor-not-allowed", !C && !j && "focus:border-border-base-neutral-4", b === "large" ? "pr-14" : "pr-12", n(b))
|
|
130
|
+
}), /* @__PURE__ */ i("button", {
|
|
131
|
+
type: "button",
|
|
132
|
+
tabIndex: -1,
|
|
133
|
+
"aria-label": g ?? f ?? E["aria-label"],
|
|
134
|
+
"aria-controls": K ? O : void 0,
|
|
135
|
+
"aria-expanded": K,
|
|
136
|
+
disabled: C,
|
|
137
|
+
onMouseDown: (e) => e.preventDefault(),
|
|
138
|
+
onClick: () => {
|
|
139
|
+
K ? Q() : Z(), P.current?.focus();
|
|
140
|
+
},
|
|
141
|
+
className: e("absolute inset-y-0 right-0 flex cursor-pointer items-center justify-center disabled:cursor-not-allowed", b === "large" ? "w-14" : "w-12"),
|
|
142
|
+
children: /* @__PURE__ */ i(ee, {
|
|
143
|
+
"aria-hidden": "true",
|
|
144
|
+
className: e("size-6 transition-transform duration-200", K ? "text-icon-prominent-accent-1 rotate-180" : "text-icon-base-neutral-3")
|
|
145
|
+
})
|
|
146
|
+
})]
|
|
147
|
+
})
|
|
136
148
|
}),
|
|
137
|
-
|
|
149
|
+
x && /* @__PURE__ */ i("input", {
|
|
138
150
|
type: "hidden",
|
|
139
|
-
name:
|
|
140
|
-
value:
|
|
141
|
-
disabled:
|
|
142
|
-
form:
|
|
151
|
+
name: x,
|
|
152
|
+
value: p,
|
|
153
|
+
disabled: C,
|
|
154
|
+
form: E.form
|
|
143
155
|
}),
|
|
144
|
-
/* @__PURE__ */
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
"aria-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
156
|
+
/* @__PURE__ */ i(l.Portal, { children: /* @__PURE__ */ i(l.Content, {
|
|
157
|
+
asChild: !0,
|
|
158
|
+
align: "start",
|
|
159
|
+
sideOffset: 8,
|
|
160
|
+
onOpenAutoFocus: (e) => e.preventDefault(),
|
|
161
|
+
onCloseAutoFocus: (e) => e.preventDefault(),
|
|
162
|
+
onInteractOutside: (e) => e.preventDefault(),
|
|
163
|
+
onEscapeKeyDown: (e) => {
|
|
164
|
+
e.isComposing || e.keyCode === 229 || (e.preventDefault(), Q());
|
|
165
|
+
},
|
|
166
|
+
children: /* @__PURE__ */ a("ul", {
|
|
167
|
+
ref: Se,
|
|
168
|
+
id: O,
|
|
169
|
+
role: "listbox",
|
|
170
|
+
"aria-labelledby": f ? N : void 0,
|
|
171
|
+
"aria-label": f ? void 0 : E["aria-label"],
|
|
172
|
+
onMouseDown: (e) => e.preventDefault(),
|
|
173
|
+
className: e(ae, "w-(--radix-popover-trigger-width)"),
|
|
174
|
+
children: [G.map(({ option: t, index: n }) => {
|
|
175
|
+
let r = t.value === p;
|
|
176
|
+
return /* @__PURE__ */ a("li", {
|
|
177
|
+
id: A(n),
|
|
178
|
+
role: "option",
|
|
179
|
+
"aria-selected": r,
|
|
180
|
+
"aria-disabled": t.disabled || void 0,
|
|
181
|
+
onMouseMove: () => {
|
|
182
|
+
I.current = !1, t.disabled || W(n);
|
|
183
|
+
},
|
|
184
|
+
onClick: () => $(t),
|
|
185
|
+
className: e(te(b), n === U && "bg-surface-base-neutral-2", r && "text-text-prominent-accent-1 font-medium", t.disabled && "cursor-not-allowed opacity-50"),
|
|
186
|
+
children: [/* @__PURE__ */ i("span", {
|
|
187
|
+
className: ie,
|
|
188
|
+
children: r && /* @__PURE__ */ i(re, {})
|
|
189
|
+
}), /* @__PURE__ */ i("span", {
|
|
190
|
+
className: "min-w-0 flex-1 truncate",
|
|
191
|
+
children: t.label
|
|
192
|
+
})]
|
|
193
|
+
}, t.value);
|
|
194
|
+
}), G.length === 0 && h !== void 0 && /* @__PURE__ */ i("li", {
|
|
195
|
+
role: "presentation",
|
|
196
|
+
className: "text-text-base-neutral-2 text-body-14-regular px-4 py-3",
|
|
197
|
+
children: h
|
|
198
|
+
})]
|
|
199
|
+
})
|
|
200
|
+
}) })
|
|
187
201
|
]
|
|
188
202
|
}),
|
|
189
|
-
/* @__PURE__ */ i(
|
|
190
|
-
id:
|
|
191
|
-
size:
|
|
192
|
-
errorText:
|
|
193
|
-
assistiveText:
|
|
203
|
+
/* @__PURE__ */ i(ne, {
|
|
204
|
+
id: k,
|
|
205
|
+
size: b,
|
|
206
|
+
errorText: v,
|
|
207
|
+
assistiveText: y
|
|
194
208
|
})
|
|
195
209
|
]
|
|
196
210
|
});
|
|
197
211
|
};
|
|
198
212
|
//#endregion
|
|
199
|
-
export {
|
|
213
|
+
export { d as Combobox };
|
|
200
214
|
|
|
201
215
|
//# sourceMappingURL=combobox.js.map
|
package/dist/combobox.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"combobox.js","names":[],"sources":["../src/components/combobox/Combobox.tsx"],"sourcesContent":["\"use client\";\n\nimport { useEffect, useId, useMemo, useRef, useState } from \"react\";\n\nimport { ChevronDownIcon } from \"../icons/action/ChevronDownIcon\";\n\nimport type { ComponentPropsWithRef, KeyboardEvent, ReactNode } from \"react\";\n\nimport { FieldLabel, FieldMessage } from \"@/components/ui/field\";\nimport {\n FIELD_RADIUS,\n fieldContainerVariants,\n fieldValueFontSize,\n joinIds,\n type FieldSize,\n} from \"@/components/ui/fieldStyles\";\nimport { mergeRefs } from \"@/utils/mergeRefs\";\nimport { cn } from \"@/utils/TailwindUtil\";\n\nexport interface ComboboxOption {\n value: string;\n label: string;\n disabled?: boolean;\n}\n\ninterface ComboboxProps\n extends Omit<\n ComponentPropsWithRef<\"input\">,\n \"size\" | \"value\" | \"defaultValue\" | \"onChange\" | \"type\" | \"role\" | \"children\"\n > {\n /** ์นธ ์์ ๋ณด์ด๋ ์ด๋ฆ. `<label>`๋ก ์
๋ ฅ์ ์ฐ๊ฒฐ๋๋ค. */\n label?: string;\n /** ๋ฌธ์์ด ์ต์
์ ๊ฐ๊ณผ ํ์ ์ด๋ฆ์ด ๊ฐ๋ค. */\n options: (string | ComboboxOption)[];\n /** ๊ณ ๋ฅธ ์ต์
์ ๊ฐ. ๊ณ ๋ฅด์ง ์์์ผ๋ฉด `\"\"`. */\n value: string;\n /** ์ต์
์ ๊ณ ๋ฅด๋ฉด ๊ทธ ๊ฐ์ผ๋ก, ๊ณ ๋ฅธ ๋ค ๋ค์ ์
๋ ฅํ๊ธฐ ์์ํ๋ฉด `\"\"`๋ก ํธ์ถ๋๋ค. */\n onChange: (value: string) => void;\n /** ์
๋ ฅํ ๊ฒ์์ด๋ก ์ต์
์ ๊ฑฐ๋ฅธ๋ค. ๊ธฐ๋ณธ์ ๋์๋ฌธ์ยท๊ณต๋ฐฑ์ ๋ฌด์ํ ๋ถ๋ถ ์ผ์น. */\n filter?: (option: ComboboxOption, query: string) => boolean;\n /** ๋ง๋ ์ต์
์ด ์์ ๋ ๋ชฉ๋ก์ ๋ณด์ด๋ ๋ฌธ๊ตฌ. ์์ผ๋ฉด ๋ชฉ๋ก์ ๋ซ๋๋ค. */\n emptyText?: ReactNode;\n /** ๋ชฉ๋ก ์ด๊ธฐ ๋ฒํผ์ ์ ๊ทผ ๊ฐ๋ฅํ ์ด๋ฆ. ๊ธฐ๋ณธ์ `label`. */\n toggleLabel?: string;\n /** `required`์ธ๋ฐ ์
๋ ฅํ ๊ธ์๊ฐ ์ต์
์ผ๋ก ๊ณจ๋ผ์ง์ง ์์์ ๋ ๋ธ๋ผ์ฐ์ ๊ฒ์ฆ ๋ฉ์์ง. ๋ค๊ตญ์ด ํ์ด์ง์์๋ ๋ฒ์ญํด ๋๊ธด๋ค. */\n invalidSelectionText?: string;\n errorText?: string;\n assistiveText?: ReactNode;\n size?: FieldSize;\n}\n\nconst normalize = (text: string) => text.toLocaleLowerCase().replace(/\\s+/g, \"\");\n\nconst defaultFilter = (option: ComboboxOption, query: string) => normalize(option.label).includes(normalize(query));\n\n/**\n * ์
๋ ฅํด์ ๊ฒ์ํ๊ณ ๋ชฉ๋ก์์ ํ๋๋ฅผ ๊ณ ๋ฅด๋ ์
๋ ฅ(WAI-ARIA combobox, list autocomplete).\n * ํฌ์ปค์ค๋ ์
๋ ฅ์ ๋จ๊ณ ๋ชฉ๋ก์ ํ์ฌ ํญ๋ชฉ์ `aria-activedescendant`๋ก ์๋ฆฐ๋ค.\n * ๊ณ ๋ฅธ ๊ฐ๋ง `value`๊ฐ ๋๊ณ , ์
๋ ฅํ ๊ธ์๋ ๊ณ ๋ฅด๊ธฐ ์ ๊น์ง ๊ฐ์ด ์๋๋ค.\n */\nconst Combobox = ({\n id,\n label,\n options,\n value,\n onChange,\n filter = defaultFilter,\n emptyText,\n toggleLabel,\n invalidSelectionText = \"Select an option from the list.\",\n errorText,\n assistiveText,\n size = \"large\",\n name,\n required,\n disabled = false,\n placeholder,\n className,\n autoComplete = \"off\",\n \"aria-describedby\": ariaDescribedBy,\n onBlur,\n onKeyDown,\n onClick,\n ref,\n ...inputProps\n}: ComboboxProps) => {\n const generatedId = useId();\n const inputId = id ?? generatedId;\n const listboxId = `${inputId}-listbox`;\n const messageId = `${inputId}-message`;\n const optionId = (index: number) => `${inputId}-option-${index}`;\n const hasError = Boolean(errorText);\n const hasMessage = Boolean(errorText || assistiveText);\n const labelId = `${inputId}-label`;\n const inputRef = useRef<HTMLInputElement | null>(null);\n const setRef = useMemo(() => mergeRefs(inputRef, ref), [ref]);\n // ์ด ์ปดํฌ๋ํธ๊ฐ onChange๋ก ๋ธ ๊ฐ. ๋ฐ๊นฅ์์ value๋ฅผ ๋ฐ๊ฟจ๋์ง ๊ตฌ๋ถํ๋ค.\n const emittedValue = useRef<string | null>(null);\n // ํค๋ณด๋๋ก ์ฎ๊ธธ ๋๋ง ํ์ฌ ํญ๋ชฉ์ ์คํฌ๋กคํ๋ค. ๋ง์ฐ์ค hover๋ก ์คํฌ๋กคํ๋ฉด ๋ชฉ๋ก์ด ํ๋ค๋ฆฐ๋ค.\n const scrollOnMove = useRef(false);\n\n const normalized = options.map((option) => (typeof option === \"string\" ? { value: option, label: option } : option));\n const selected = normalized.find((option) => option.value === value);\n // null์ด๋ฉด ๊ฒ์ ์ค์ด ์๋๊ณ , ์
๋ ฅ์๋ ๊ณ ๋ฅธ ์ต์
์ด๋ฆ์ด ๋ณด์ธ๋ค.\n const [query, setQuery] = useState<string | null>(null);\n const [open, setOpen] = useState(false);\n const [activeIndex, setActiveIndex] = useState(-1);\n\n // ์ธ๋ฑ์ค๋ ์ ์ฒด ์ต์
๊ธฐ์ค์ด๋ผ ๊ฑฐ๋ฅด๋๋ผ๋ id๊ฐ ๋ค๋ฅธ ์ต์
์ผ๋ก ๋์ด๊ฐ์ง ์๋๋ค.\n const matches = normalized\n .map((option, index) => ({ option, index }))\n .filter(({ option }) => (query ? filter(option, query) : true));\n const expanded = open && !disabled && (matches.length > 0 || emptyText !== undefined);\n const enabledMatches = matches.filter(({ option }) => !option.disabled);\n\n useEffect(() => {\n const shouldScroll = scrollOnMove.current;\n scrollOnMove.current = false;\n if (!expanded || activeIndex < 0 || !shouldScroll) return;\n document.getElementById(optionId(activeIndex))?.scrollIntoView?.({ block: \"nearest\" });\n // optionId๋ inputId์์๋ง ๋ฐ๋๋ค.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [expanded, activeIndex]);\n\n // ๋ฐ๊นฅ์์ value๊ฐ ๋ฐ๋๋ฉด(ํผ reset ๋ฑ) ์
๋ ฅ ์ค์ธ ๊ฒ์์ด๋ฅผ ๋ฒ๋ฆฌ๊ณ ์ ๊ฐ์ ์ด๋ฆ์ ๋ณด์ธ๋ค.\n useEffect(() => {\n if (value !== emittedValue.current) setQuery(null);\n emittedValue.current = null;\n }, [value]);\n\n // ๋ถ๋ชจ๊ฐ ๋ธ ๊ฐ์ ๋ฐ์๋ค์ด์ง ์์ value๊ฐ ๊ทธ๋๋ก๋ฉด, ๋ค์ ๋ฐ๊นฅ ๋ณ๊ฒฝ์ ์ฐ๋ฆฌ ๊ฐ์ผ๋ก ์ค์ธํ์ง ์๊ฒ ๋น์ด๋ค.\n useEffect(() => {\n emittedValue.current = null;\n });\n\n // ๋ธ๋ผ์ฐ์ ๊ฒ์ฆ์ ๋ณด์ด๋ ๊ธ์๋ฅผ ๋ณธ๋ค. ๊ธ์๋ ์๋๋ฐ ๊ณ ๋ฅธ ์ต์
์ด ์์ผ๋ฉด ํต๊ณผํ์ง ์๊ฒ ํ๋ค.\n const selectionMissing = Boolean(required && !selected && query);\n useEffect(() => {\n inputRef.current?.setCustomValidity(selectionMissing ? invalidSelectionText : \"\");\n }, [selectionMissing, invalidSelectionText]);\n\n const emit = (next: string) => {\n emittedValue.current = next;\n onChange(next);\n };\n\n const openList = () => {\n scrollOnMove.current = true;\n setOpen(true);\n setActiveIndex(selected && !selected.disabled ? normalized.indexOf(selected) : -1);\n };\n\n const close = () => {\n setOpen(false);\n setQuery(null);\n setActiveIndex(-1);\n };\n\n const pick = (option: ComboboxOption) => {\n if (option.disabled) return;\n emit(option.value);\n close();\n };\n\n const move = (step: 1 | -1) => {\n if (enabledMatches.length === 0) return;\n const position = enabledMatches.findIndex(({ index }) => index === activeIndex);\n const next =\n position === -1\n ? step === 1\n ? 0\n : enabledMatches.length - 1\n : (position + step + enabledMatches.length) % enabledMatches.length;\n scrollOnMove.current = true;\n setActiveIndex(enabledMatches[next].index);\n };\n\n const handleKeyDown = (event: KeyboardEvent<HTMLInputElement>) => {\n onKeyDown?.(event);\n // IME ์กฐํฉ(ํ๊ธยท์ผ๋ณธ์ด) ์ค Enterยท๋ฐฉํฅํค๋ ์กฐํฉ์ ํ์ ํ๋ ํค๋ค. keyCode 229๋ Safari์ฉ.\n if (event.defaultPrevented || event.nativeEvent.isComposing || event.keyCode === 229) return;\n\n switch (event.key) {\n case \"ArrowDown\":\n case \"ArrowUp\":\n event.preventDefault();\n if (open && event.altKey && event.key === \"ArrowUp\") {\n close();\n return;\n }\n if (!open) {\n // ์ด ๋๋ ๊ณ ๋ฅธ ์ต์
์, ๊ณ ๋ฅธ ๊ฒ ์์ผ๋ฉด ๋ฐฉํฅ์ ๋ง๋ ์ฒซ ์ต์
์ ๊ฐ๋ฆฌํจ๋ค. Alt๋ ์ด๊ธฐ๋ง ํ๋ค.\n setOpen(true);\n const selectedIndex = selected && !selected.disabled ? normalized.indexOf(selected) : -1;\n const edge = event.key === \"ArrowDown\" ? enabledMatches[0] : enabledMatches[enabledMatches.length - 1];\n scrollOnMove.current = true;\n setActiveIndex(selectedIndex >= 0 || event.altKey ? selectedIndex : (edge?.index ?? -1));\n return;\n }\n move(event.key === \"ArrowDown\" ? 1 : -1);\n return;\n case \"Enter\":\n if (expanded && activeIndex >= 0) {\n event.preventDefault();\n pick(normalized[activeIndex]);\n }\n return;\n case \"Escape\":\n if (expanded || query !== null) {\n event.preventDefault();\n close();\n }\n return;\n }\n };\n\n const handleInput = (text: string) => {\n // ๋ธ๋ผ์ฐ์ ์๋์์ฑ์ฒ๋ผ ์ต์
์ด๋ฆ๊ณผ ๋๊ฐ์ด ์
๋ ฅ๋๋ฉด ๊ทธ ์ต์
์ ๊ณ ๋ฅธ ๊ฒ์ผ๋ก ๋ณธ๋ค.\n const exact = text.trim()\n ? normalized.find((option) => !option.disabled && normalize(option.label) === normalize(text))\n : undefined;\n if (exact) {\n if (exact.value !== value) emit(exact.value);\n } else if (value !== \"\") {\n emit(\"\");\n }\n\n // ํฌ์ปค์ค๊ฐ ์์ ๋ ๋ค์ด์จ ์
๋ ฅ(๋ค๋ฅธ ์นธ์์ ์์ํ ์๋์์ฑ)์ ๋ชฉ๋ก์ ์ด์ง ์๋๋ค.\n if (document.activeElement !== inputRef.current) {\n setQuery(exact ? null : text);\n return;\n }\n\n setQuery(text);\n setOpen(true);\n const firstMatch = normalized.findIndex((option) => !option.disabled && (text ? filter(option, text) : true));\n scrollOnMove.current = true;\n setActiveIndex(exact ? normalized.indexOf(exact) : text ? firstMatch : -1);\n };\n\n return (\n <div className={cn(\"flex w-full min-w-0 flex-col gap-2\", className)}>\n {label && (\n <FieldLabel id={labelId} htmlFor={inputId} size={size} required={required}>\n {label}\n </FieldLabel>\n )}\n <div className=\"relative\">\n <div className=\"relative\">\n <input\n {...inputProps}\n ref={setRef}\n id={inputId}\n type=\"text\"\n role=\"combobox\"\n aria-expanded={expanded}\n aria-controls={listboxId}\n aria-autocomplete=\"list\"\n aria-activedescendant={expanded && activeIndex >= 0 ? optionId(activeIndex) : undefined}\n aria-invalid={hasError || undefined}\n aria-describedby={joinIds(ariaDescribedBy, hasMessage && messageId)}\n autoComplete={autoComplete}\n required={required}\n disabled={disabled}\n placeholder={placeholder}\n value={query ?? selected?.label ?? \"\"}\n onChange={(event) => handleInput(event.target.value)}\n onKeyDown={handleKeyDown}\n onClick={(event) => {\n onClick?.(event);\n if (!open && !event.defaultPrevented) openList();\n }}\n onBlur={(event) => {\n onBlur?.(event);\n close();\n }}\n className={cn(\n fieldContainerVariants({ size, hasError, disabled }),\n \"text-text-base-neutral-5 placeholder:text-text-base-neutral-2 w-full min-w-0 truncate outline-none disabled:cursor-not-allowed\",\n !disabled && !hasError && \"focus:border-border-base-neutral-4\",\n size === \"large\" ? \"pr-14\" : \"pr-12\",\n fieldValueFontSize(size),\n )}\n />\n <button\n type=\"button\"\n tabIndex={-1}\n aria-label={toggleLabel ?? label ?? inputProps[\"aria-label\"]}\n aria-controls={listboxId}\n aria-expanded={expanded}\n disabled={disabled}\n // ์
๋ ฅ์ ํฌ์ปค์ค๋ฅผ ์ ์งํด์ผ blur๋ก ๋ชฉ๋ก์ด ๋ซํ์ง ์๋๋ค.\n onMouseDown={(event) => event.preventDefault()}\n onClick={() => {\n if (expanded) close();\n else openList();\n inputRef.current?.focus();\n }}\n className={cn(\n \"absolute inset-y-0 right-0 flex cursor-pointer items-center justify-center disabled:cursor-not-allowed\",\n size === \"large\" ? \"w-14\" : \"w-12\",\n )}\n >\n <ChevronDownIcon\n aria-hidden=\"true\"\n className={cn(\n \"size-6 transition-transform duration-200\",\n expanded ? \"text-icon-prominent-accent-1 rotate-180\" : \"text-icon-base-neutral-3\",\n )}\n />\n </button>\n </div>\n {name && <input type=\"hidden\" name={name} value={value} disabled={disabled} form={inputProps.form} />}\n <ul\n id={listboxId}\n role=\"listbox\"\n aria-labelledby={label ? labelId : undefined}\n aria-label={label ? undefined : inputProps[\"aria-label\"]}\n hidden={!expanded}\n onMouseDown={(event) => event.preventDefault()}\n className={cn(\n \"border-border-base-neutral-2 bg-surface-base-neutral-1 absolute z-20 mt-2 max-h-72 w-full overflow-y-auto border py-1 shadow-lg\",\n FIELD_RADIUS,\n )}\n >\n {matches.map(({ option, index }) => {\n const isSelected = option.value === value;\n return (\n <li\n key={option.value}\n id={optionId(index)}\n role=\"option\"\n aria-selected={isSelected}\n aria-disabled={option.disabled || undefined}\n onMouseMove={() => {\n scrollOnMove.current = false;\n if (!option.disabled) setActiveIndex(index);\n }}\n onClick={() => pick(option)}\n className={cn(\n \"text-text-base-neutral-5 flex cursor-pointer items-center gap-3 px-3 py-2.5 select-none\",\n size === \"large\" ? \"text-body-16-regular\" : \"text-body-14-regular\",\n index === activeIndex && \"bg-surface-base-neutral-2\",\n isSelected && \"font-medium\",\n option.disabled && \"cursor-not-allowed opacity-50\",\n )}\n >\n <span className=\"min-w-0 flex-1 truncate\">{option.label}</span>\n {isSelected && (\n <svg\n aria-hidden=\"true\"\n viewBox=\"0 0 20 20\"\n fill=\"none\"\n className=\"text-icon-prominent-accent-1 size-4 shrink-0\"\n >\n <path\n d=\"m4 10 3.5 3.5L16 5\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n />\n </svg>\n )}\n </li>\n );\n })}\n {matches.length === 0 && emptyText !== undefined && (\n <li role=\"presentation\" className=\"text-text-base-neutral-2 text-body-14-regular px-3 py-2.5\">\n {emptyText}\n </li>\n )}\n </ul>\n </div>\n <FieldMessage id={messageId} size={size} errorText={errorText} assistiveText={assistiveText} />\n </div>\n );\n};\n\nexport { Combobox };\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"combobox.js","names":[],"sources":["../src/components/combobox/Combobox.tsx"],"sourcesContent":["\"use client\";\n\nimport { Popover as PopoverPrimitive } from \"radix-ui\";\nimport { useCallback, useEffect, useId, useMemo, useRef, useState } from \"react\";\n\nimport { ChevronDownIcon } from \"../icons/action/ChevronDownIcon\";\n\nimport type { ComponentPropsWithRef, KeyboardEvent, ReactNode } from \"react\";\n\nimport { FieldLabel, FieldMessage } from \"@/components/ui/field\";\nimport { fieldContainerVariants, fieldValueFontSize, joinIds, type FieldSize } from \"@/components/ui/fieldStyles\";\nimport { ListboxCheckIcon } from \"@/components/ui/listbox\";\nimport {\n LISTBOX_CHECK_SLOT,\n LISTBOX_OPTION_ACTIVE,\n LISTBOX_OPTION_DISABLED,\n LISTBOX_OPTION_SELECTED,\n LISTBOX_PANEL,\n listboxOptionClassName,\n} from \"@/components/ui/listboxStyles\";\nimport { mergeRefs } from \"@/utils/mergeRefs\";\nimport { cn } from \"@/utils/TailwindUtil\";\n\nexport interface ComboboxOption {\n value: string;\n label: string;\n disabled?: boolean;\n}\n\ninterface ComboboxProps\n extends Omit<\n ComponentPropsWithRef<\"input\">,\n \"size\" | \"value\" | \"defaultValue\" | \"onChange\" | \"type\" | \"role\" | \"children\"\n > {\n /** ์นธ ์์ ๋ณด์ด๋ ์ด๋ฆ. `<label>`๋ก ์
๋ ฅ์ ์ฐ๊ฒฐ๋๋ค. */\n label?: string;\n /** ๋ฌธ์์ด ์ต์
์ ๊ฐ๊ณผ ํ์ ์ด๋ฆ์ด ๊ฐ๋ค. */\n options: (string | ComboboxOption)[];\n /** ๊ณ ๋ฅธ ์ต์
์ ๊ฐ. ๊ณ ๋ฅด์ง ์์์ผ๋ฉด `\"\"`. */\n value: string;\n /** ์ต์
์ ๊ณ ๋ฅด๋ฉด ๊ทธ ๊ฐ์ผ๋ก, ๊ณ ๋ฅธ ๋ค ๋ค์ ์
๋ ฅํ๊ธฐ ์์ํ๋ฉด `\"\"`๋ก ํธ์ถ๋๋ค. */\n onChange: (value: string) => void;\n /** ์
๋ ฅํ ๊ฒ์์ด๋ก ์ต์
์ ๊ฑฐ๋ฅธ๋ค. ๊ธฐ๋ณธ์ ๋์๋ฌธ์ยท๊ณต๋ฐฑ์ ๋ฌด์ํ ๋ถ๋ถ ์ผ์น. */\n filter?: (option: ComboboxOption, query: string) => boolean;\n /** ๋ง๋ ์ต์
์ด ์์ ๋ ๋ชฉ๋ก์ ๋ณด์ด๋ ๋ฌธ๊ตฌ. ์์ผ๋ฉด ๋ชฉ๋ก์ ๋ซ๋๋ค. */\n emptyText?: ReactNode;\n /** ๋ชฉ๋ก ์ด๊ธฐ ๋ฒํผ์ ์ ๊ทผ ๊ฐ๋ฅํ ์ด๋ฆ. ๊ธฐ๋ณธ์ `label`. */\n toggleLabel?: string;\n /** `required`์ธ๋ฐ ์
๋ ฅํ ๊ธ์๊ฐ ์ต์
์ผ๋ก ๊ณจ๋ผ์ง์ง ์์์ ๋ ๋ธ๋ผ์ฐ์ ๊ฒ์ฆ ๋ฉ์์ง. ๋ค๊ตญ์ด ํ์ด์ง์์๋ ๋ฒ์ญํด ๋๊ธด๋ค. */\n invalidSelectionText?: string;\n errorText?: string;\n assistiveText?: ReactNode;\n size?: FieldSize;\n}\n\nconst normalize = (text: string) => text.toLocaleLowerCase().replace(/\\s+/g, \"\");\n\nconst defaultFilter = (option: ComboboxOption, query: string) => normalize(option.label).includes(normalize(query));\n\n/**\n * ์
๋ ฅํด์ ๊ฒ์ํ๊ณ ๋ชฉ๋ก์์ ํ๋๋ฅผ ๊ณ ๋ฅด๋ ์
๋ ฅ(WAI-ARIA combobox, list autocomplete).\n * ํฌ์ปค์ค๋ ์
๋ ฅ์ ๋จ๊ณ ๋ชฉ๋ก์ ํ์ฌ ํญ๋ชฉ์ `aria-activedescendant`๋ก ์๋ฆฐ๋ค.\n * ๊ณ ๋ฅธ ๊ฐ๋ง `value`๊ฐ ๋๊ณ , ์
๋ ฅํ ๊ธ์๋ ๊ณ ๋ฅด๊ธฐ ์ ๊น์ง ๊ฐ์ด ์๋๋ค.\n * ๋ชฉ๋ก์ `Select`์ ๊ฐ์ ํฌํธ ํจ๋(Radix Popover)์ด๋ผ ์คํฌ๋กคยทoverflow ์กฐ์์ ์๋ฆฌ์ง ์๋๋ค.\n */\nconst Combobox = ({\n id,\n label,\n options,\n value,\n onChange,\n filter = defaultFilter,\n emptyText,\n toggleLabel,\n invalidSelectionText = \"Select an option from the list.\",\n errorText,\n assistiveText,\n size = \"large\",\n name,\n required,\n disabled = false,\n placeholder,\n className,\n autoComplete = \"off\",\n \"aria-describedby\": ariaDescribedBy,\n onBlur,\n onKeyDown,\n onClick,\n ref,\n ...inputProps\n}: ComboboxProps) => {\n const generatedId = useId();\n const inputId = id ?? generatedId;\n const listboxId = `${inputId}-listbox`;\n const messageId = `${inputId}-message`;\n const optionId = (index: number) => `${inputId}-option-${index}`;\n const hasError = Boolean(errorText);\n const hasMessage = Boolean(errorText || assistiveText);\n const labelId = `${inputId}-label`;\n const inputRef = useRef<HTMLInputElement | null>(null);\n const setRef = useMemo(() => mergeRefs(inputRef, ref), [ref]);\n // ์ด ์ปดํฌ๋ํธ๊ฐ onChange๋ก ๋ธ ๊ฐ. ๋ฐ๊นฅ์์ value๋ฅผ ๋ฐ๊ฟจ๋์ง ๊ตฌ๋ถํ๋ค.\n const emittedValue = useRef<string | null>(null);\n // ํค๋ณด๋๋ก ์ฎ๊ธธ ๋๋ง ํ์ฌ ํญ๋ชฉ์ ์คํฌ๋กคํ๋ค. ๋ง์ฐ์ค hover๋ก ์คํฌ๋กคํ๋ฉด ๋ชฉ๋ก์ด ํ๋ค๋ฆฐ๋ค.\n const scrollOnMove = useRef(false);\n\n const normalized = options.map((option) => (typeof option === \"string\" ? { value: option, label: option } : option));\n const selected = normalized.find((option) => option.value === value);\n // null์ด๋ฉด ๊ฒ์ ์ค์ด ์๋๊ณ , ์
๋ ฅ์๋ ๊ณ ๋ฅธ ์ต์
์ด๋ฆ์ด ๋ณด์ธ๋ค.\n const [query, setQuery] = useState<string | null>(null);\n const [open, setOpen] = useState(false);\n const [activeIndex, setActiveIndex] = useState(-1);\n\n // ์ธ๋ฑ์ค๋ ์ ์ฒด ์ต์
๊ธฐ์ค์ด๋ผ ๊ฑฐ๋ฅด๋๋ผ๋ id๊ฐ ๋ค๋ฅธ ์ต์
์ผ๋ก ๋์ด๊ฐ์ง ์๋๋ค.\n const matches = normalized\n .map((option, index) => ({ option, index }))\n .filter(({ option }) => (query ? filter(option, query) : true));\n const expanded = open && !disabled && (matches.length > 0 || emptyText !== undefined);\n const enabledMatches = matches.filter(({ option }) => !option.disabled);\n\n // ๋ชฉ๋ก์ ๋ง ์ด๋ฉด ํฌํธ์ด ๋ค์ ๋ ๋์ ๋ถ์ผ๋ฏ๋ก, ๊ทธ๋ ๋ชฉ๋ก ref๊ฐ ๋์ ์คํฌ๋กคํ๋ค.\n const pendingScroll = useRef(-1);\n useEffect(() => {\n const shouldScroll = scrollOnMove.current;\n scrollOnMove.current = false;\n if (!expanded || activeIndex < 0 || !shouldScroll) return;\n const option = document.getElementById(optionId(activeIndex));\n if (option) option.scrollIntoView?.({ block: \"nearest\" });\n else pendingScroll.current = activeIndex;\n // optionId๋ inputId์์๋ง ๋ฐ๋๋ค.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [expanded, activeIndex]);\n const scrollPending = useCallback(\n (list: HTMLUListElement | null) => {\n if (!list || pendingScroll.current < 0) return;\n document.getElementById(`${inputId}-option-${pendingScroll.current}`)?.scrollIntoView?.({ block: \"nearest\" });\n pendingScroll.current = -1;\n },\n [inputId],\n );\n\n // ๋ฐ๊นฅ์์ value๊ฐ ๋ฐ๋๋ฉด(ํผ reset ๋ฑ) ์
๋ ฅ ์ค์ธ ๊ฒ์์ด๋ฅผ ๋ฒ๋ฆฌ๊ณ ์ ๊ฐ์ ์ด๋ฆ์ ๋ณด์ธ๋ค.\n useEffect(() => {\n if (value !== emittedValue.current) setQuery(null);\n emittedValue.current = null;\n }, [value]);\n\n // ๋ถ๋ชจ๊ฐ ๋ธ ๊ฐ์ ๋ฐ์๋ค์ด์ง ์์ value๊ฐ ๊ทธ๋๋ก๋ฉด, ๋ค์ ๋ฐ๊นฅ ๋ณ๊ฒฝ์ ์ฐ๋ฆฌ ๊ฐ์ผ๋ก ์ค์ธํ์ง ์๊ฒ ๋น์ด๋ค.\n useEffect(() => {\n emittedValue.current = null;\n });\n\n // ๋ธ๋ผ์ฐ์ ๊ฒ์ฆ์ ๋ณด์ด๋ ๊ธ์๋ฅผ ๋ณธ๋ค. ๊ธ์๋ ์๋๋ฐ ๊ณ ๋ฅธ ์ต์
์ด ์์ผ๋ฉด ํต๊ณผํ์ง ์๊ฒ ํ๋ค.\n const selectionMissing = Boolean(required && !selected && query);\n useEffect(() => {\n inputRef.current?.setCustomValidity(selectionMissing ? invalidSelectionText : \"\");\n }, [selectionMissing, invalidSelectionText]);\n\n const emit = (next: string) => {\n emittedValue.current = next;\n onChange(next);\n };\n\n const openList = () => {\n scrollOnMove.current = true;\n setOpen(true);\n setActiveIndex(selected && !selected.disabled ? normalized.indexOf(selected) : -1);\n };\n\n const close = () => {\n setOpen(false);\n setQuery(null);\n setActiveIndex(-1);\n };\n\n const pick = (option: ComboboxOption) => {\n if (option.disabled) return;\n emit(option.value);\n close();\n };\n\n const move = (step: 1 | -1) => {\n if (enabledMatches.length === 0) return;\n const position = enabledMatches.findIndex(({ index }) => index === activeIndex);\n const next =\n position === -1\n ? step === 1\n ? 0\n : enabledMatches.length - 1\n : (position + step + enabledMatches.length) % enabledMatches.length;\n scrollOnMove.current = true;\n setActiveIndex(enabledMatches[next].index);\n };\n\n const handleKeyDown = (event: KeyboardEvent<HTMLInputElement>) => {\n onKeyDown?.(event);\n // IME ์กฐํฉ(ํ๊ธยท์ผ๋ณธ์ด) ์ค Enterยท๋ฐฉํฅํค๋ ์กฐํฉ์ ํ์ ํ๋ ํค๋ค. keyCode 229๋ Safari์ฉ.\n if (event.defaultPrevented || event.nativeEvent.isComposing || event.keyCode === 229) return;\n\n switch (event.key) {\n case \"ArrowDown\":\n case \"ArrowUp\":\n event.preventDefault();\n if (open && event.altKey && event.key === \"ArrowUp\") {\n close();\n return;\n }\n if (!open) {\n // ์ด ๋๋ ๊ณ ๋ฅธ ์ต์
์, ๊ณ ๋ฅธ ๊ฒ ์์ผ๋ฉด ๋ฐฉํฅ์ ๋ง๋ ์ฒซ ์ต์
์ ๊ฐ๋ฆฌํจ๋ค. Alt๋ ์ด๊ธฐ๋ง ํ๋ค.\n setOpen(true);\n const selectedIndex = selected && !selected.disabled ? normalized.indexOf(selected) : -1;\n const edge = event.key === \"ArrowDown\" ? enabledMatches[0] : enabledMatches[enabledMatches.length - 1];\n scrollOnMove.current = true;\n setActiveIndex(selectedIndex >= 0 || event.altKey ? selectedIndex : (edge?.index ?? -1));\n return;\n }\n move(event.key === \"ArrowDown\" ? 1 : -1);\n return;\n case \"Enter\":\n if (expanded && activeIndex >= 0) {\n event.preventDefault();\n pick(normalized[activeIndex]);\n }\n return;\n case \"Escape\":\n if (expanded || query !== null) {\n event.preventDefault();\n close();\n }\n return;\n }\n };\n\n const handleInput = (text: string) => {\n // ๋ธ๋ผ์ฐ์ ์๋์์ฑ์ฒ๋ผ ์ต์
์ด๋ฆ๊ณผ ๋๊ฐ์ด ์
๋ ฅ๋๋ฉด ๊ทธ ์ต์
์ ๊ณ ๋ฅธ ๊ฒ์ผ๋ก ๋ณธ๋ค.\n const exact = text.trim()\n ? normalized.find((option) => !option.disabled && normalize(option.label) === normalize(text))\n : undefined;\n if (exact) {\n if (exact.value !== value) emit(exact.value);\n } else if (value !== \"\") {\n emit(\"\");\n }\n\n // ํฌ์ปค์ค๊ฐ ์์ ๋ ๋ค์ด์จ ์
๋ ฅ(๋ค๋ฅธ ์นธ์์ ์์ํ ์๋์์ฑ)์ ๋ชฉ๋ก์ ์ด์ง ์๋๋ค.\n if (document.activeElement !== inputRef.current) {\n setQuery(exact ? null : text);\n return;\n }\n\n setQuery(text);\n setOpen(true);\n const firstMatch = normalized.findIndex((option) => !option.disabled && (text ? filter(option, text) : true));\n scrollOnMove.current = true;\n // ์ฌ์ฉ์ filter๊ฐ ๊ฑธ๋ฌ๋ธ ์ต์
์ ๋ชฉ๋ก์ ์์ผ๋ฏ๋ก ํ์ฌ ํญ๋ชฉ์ผ๋ก ์ผ์ง ์๋๋ค.\n const exactShown = exact && filter(exact, text);\n setActiveIndex(exactShown ? normalized.indexOf(exact) : text ? firstMatch : -1);\n };\n\n return (\n <div className={cn(\"flex w-full min-w-0 flex-col gap-2\", className)}>\n {label && (\n <FieldLabel id={labelId} htmlFor={inputId} size={size} required={required}>\n {label}\n </FieldLabel>\n )}\n <PopoverPrimitive.Root open={expanded}>\n <PopoverPrimitive.Anchor asChild>\n <div className=\"relative\">\n <input\n {...inputProps}\n ref={setRef}\n id={inputId}\n type=\"text\"\n role=\"combobox\"\n aria-expanded={expanded}\n // ๋ชฉ๋ก์ ์ด๋ ธ์ ๋๋ง DOM์ ์๋ค.\n aria-controls={expanded ? listboxId : undefined}\n aria-autocomplete=\"list\"\n aria-activedescendant={expanded && activeIndex >= 0 ? optionId(activeIndex) : undefined}\n aria-invalid={hasError || undefined}\n aria-describedby={joinIds(ariaDescribedBy, hasMessage && messageId)}\n autoComplete={autoComplete}\n required={required}\n disabled={disabled}\n placeholder={placeholder}\n value={query ?? selected?.label ?? \"\"}\n onChange={(event) => handleInput(event.target.value)}\n onKeyDown={handleKeyDown}\n onClick={(event) => {\n onClick?.(event);\n if (!open && !event.defaultPrevented) openList();\n }}\n onBlur={(event) => {\n onBlur?.(event);\n close();\n }}\n className={cn(\n fieldContainerVariants({ size, hasError, disabled }),\n \"text-text-base-neutral-5 placeholder:text-text-base-neutral-2 w-full min-w-0 truncate outline-none disabled:cursor-not-allowed\",\n !disabled && !hasError && \"focus:border-border-base-neutral-4\",\n size === \"large\" ? \"pr-14\" : \"pr-12\",\n fieldValueFontSize(size),\n )}\n />\n <button\n type=\"button\"\n tabIndex={-1}\n aria-label={toggleLabel ?? label ?? inputProps[\"aria-label\"]}\n aria-controls={expanded ? listboxId : undefined}\n aria-expanded={expanded}\n disabled={disabled}\n // ์
๋ ฅ์ ํฌ์ปค์ค๋ฅผ ์ ์งํด์ผ blur๋ก ๋ชฉ๋ก์ด ๋ซํ์ง ์๋๋ค.\n onMouseDown={(event) => event.preventDefault()}\n onClick={() => {\n if (expanded) close();\n else openList();\n inputRef.current?.focus();\n }}\n className={cn(\n \"absolute inset-y-0 right-0 flex cursor-pointer items-center justify-center disabled:cursor-not-allowed\",\n size === \"large\" ? \"w-14\" : \"w-12\",\n )}\n >\n <ChevronDownIcon\n aria-hidden=\"true\"\n className={cn(\n \"size-6 transition-transform duration-200\",\n expanded ? \"text-icon-prominent-accent-1 rotate-180\" : \"text-icon-base-neutral-3\",\n )}\n />\n </button>\n </div>\n </PopoverPrimitive.Anchor>\n {name && <input type=\"hidden\" name={name} value={value} disabled={disabled} form={inputProps.form} />}\n <PopoverPrimitive.Portal>\n <PopoverPrimitive.Content\n asChild\n align=\"start\"\n sideOffset={8}\n // ํฌ์ปค์ค๋ ์
๋ ฅ์ ๋จ๊ธด๋ค. ๋ฐ๊นฅ ํด๋ฆญ์ ์
๋ ฅ์ blur๊ฐ ๋ซ๋๋ค(ํ ๊ธ ๋ฒํผ ํด๋ฆญ์ด ๋ซ๊ณ ๋ค์ ์ฌ๋ ๊ฒ์ ๋ง๋๋ค).\n onOpenAutoFocus={(event) => event.preventDefault()}\n onCloseAutoFocus={(event) => event.preventDefault()}\n onInteractOutside={(event) => event.preventDefault()}\n // ์ด๋ฆฐ ๋ชฉ๋ก์ Esc๋ ๋ชฉ๋ก๋ง ๋ซ๋๋ค. ๊ฐ์ผ Dialog ๋ฑ์ ๋ซํ์ง ์๋๋ค.\n onEscapeKeyDown={(event) => {\n // IME ์กฐํฉ ์ค Esc๋ ์กฐํฉ์ ์ทจ์ํ๋ ํค๋ค. ์
๋ ฅ์ keydown๊ณผ ๊ฐ์ ๊ธฐ์ค.\n if (event.isComposing || event.keyCode === 229) return;\n event.preventDefault();\n close();\n }}\n >\n <ul\n ref={scrollPending}\n id={listboxId}\n role=\"listbox\"\n aria-labelledby={label ? labelId : undefined}\n aria-label={label ? undefined : inputProps[\"aria-label\"]}\n onMouseDown={(event) => event.preventDefault()}\n className={cn(LISTBOX_PANEL, \"w-(--radix-popover-trigger-width)\")}\n >\n {matches.map(({ option, index }) => {\n const isSelected = option.value === value;\n return (\n <li\n key={option.value}\n id={optionId(index)}\n role=\"option\"\n aria-selected={isSelected}\n aria-disabled={option.disabled || undefined}\n onMouseMove={() => {\n scrollOnMove.current = false;\n if (!option.disabled) setActiveIndex(index);\n }}\n onClick={() => pick(option)}\n className={cn(\n listboxOptionClassName(size),\n index === activeIndex && LISTBOX_OPTION_ACTIVE,\n isSelected && LISTBOX_OPTION_SELECTED,\n option.disabled && LISTBOX_OPTION_DISABLED,\n )}\n >\n <span className={LISTBOX_CHECK_SLOT}>{isSelected && <ListboxCheckIcon />}</span>\n <span className=\"min-w-0 flex-1 truncate\">{option.label}</span>\n </li>\n );\n })}\n {matches.length === 0 && emptyText !== undefined && (\n <li role=\"presentation\" className=\"text-text-base-neutral-2 text-body-14-regular px-4 py-3\">\n {emptyText}\n </li>\n )}\n </ul>\n </PopoverPrimitive.Content>\n </PopoverPrimitive.Portal>\n </PopoverPrimitive.Root>\n <FieldMessage id={messageId} size={size} errorText={errorText} assistiveText={assistiveText} />\n </div>\n );\n};\n\nexport { Combobox };\n"],"mappings":";;;;;;;;;AAuDA,IAAM,KAAa,MAAiB,EAAK,kBAAkB,CAAC,CAAC,QAAQ,QAAQ,EAAE,GAEzE,MAAiB,GAAwB,MAAkB,EAAU,EAAO,KAAK,CAAC,CAAC,SAAS,EAAU,CAAK,CAAC,GAQ5G,KAAY,EAChB,OACA,UACA,aACA,UACA,cACA,YAAS,IACT,cACA,gBACA,0BAAuB,mCACvB,cACA,kBACA,UAAO,SACP,SACA,aACA,cAAW,IACX,iBACA,eACA,mBAAe,OACf,oBAAoB,IACpB,YACA,eACA,YACA,QACA,GAAG,QACgB;CACnB,IAAM,KAAc,GAAM,GACpB,IAAU,KAAM,IAChB,IAAY,GAAG,EAAQ,WACvB,IAAY,GAAG,EAAQ,WACvB,KAAY,MAAkB,GAAG,EAAQ,UAAU,KACnD,IAAW,EAAQ,GACnB,IAAa,GAAQ,KAAa,IAClC,IAAU,GAAG,EAAQ,SACrB,IAAW,EAAgC,IAAI,GAC/C,KAAS,SAAc,GAAU,GAAU,CAAG,GAAG,CAAC,CAAG,CAAC,GAEtD,IAAe,EAAsB,IAAI,GAEzC,IAAe,EAAO,EAAK,GAE3B,IAAa,GAAQ,KAAK,MAAY,OAAO,KAAW,WAAW;EAAE,OAAO;EAAQ,OAAO;CAAO,IAAI,CAAO,GAC7G,IAAW,EAAW,MAAM,MAAW,EAAO,UAAU,CAAK,GAE7D,CAAC,GAAO,KAAY,EAAwB,IAAI,GAChD,CAAC,GAAM,KAAW,EAAS,EAAK,GAChC,CAAC,GAAa,KAAkB,EAAS,EAAE,GAG3C,IAAU,EACb,KAAK,GAAQ,OAAW;EAAE;EAAQ;CAAM,EAAE,CAAC,CAC3C,QAAQ,EAAE,gBAAc,MAAQ,EAAO,GAAQ,CAAK,CAAS,GAC1D,IAAW,KAAQ,CAAC,MAAa,EAAQ,SAAS,KAAK,MAAc,KAAA,IACrE,IAAiB,EAAQ,QAAQ,EAAE,gBAAa,CAAC,EAAO,QAAQ,GAGhE,IAAgB,EAAO,EAAE;CAC/B,QAAgB;EACd,IAAM,IAAe,EAAa;EAElC,IADA,EAAa,UAAU,IACnB,CAAC,KAAY,IAAc,KAAK,CAAC,GAAc;EACnD,IAAM,IAAS,SAAS,eAAe,EAAS,CAAW,CAAC;EAC5D,AAAI,IAAQ,EAAO,iBAAiB,EAAE,OAAO,UAAU,CAAC,IACnD,EAAc,UAAU;CAG/B,GAAG,CAAC,GAAU,CAAW,CAAC;CAC1B,IAAM,KAAgB,IACnB,MAAkC;EAC7B,CAAC,KAAQ,EAAc,UAAU,MACrC,SAAS,eAAe,GAAG,EAAQ,UAAU,EAAc,SAAS,CAAC,EAAE,iBAAiB,EAAE,OAAO,UAAU,CAAC,GAC5G,EAAc,UAAU;CAC1B,GACA,CAAC,CAAO,CACV;CASA,AANA,QAAgB;EAEd,AADI,MAAU,EAAa,WAAS,EAAS,IAAI,GACjD,EAAa,UAAU;CACzB,GAAG,CAAC,CAAK,CAAC,GAGV,QAAgB;EACd,EAAa,UAAU;CACzB,CAAC;CAGD,IAAM,IAAmB,GAAQ,KAAY,CAAC,KAAY;CAC1D,QAAgB;EACd,EAAS,SAAS,kBAAkB,IAAmB,IAAuB,EAAE;CAClF,GAAG,CAAC,GAAkB,CAAoB,CAAC;CAE3C,IAAM,KAAQ,MAAiB;EAE7B,AADA,EAAa,UAAU,GACvB,GAAS,CAAI;CACf,GAEM,UAAiB;EAGrB,AAFA,EAAa,UAAU,IACvB,EAAQ,EAAI,GACZ,EAAe,KAAY,CAAC,EAAS,WAAW,EAAW,QAAQ,CAAQ,IAAI,EAAE;CACnF,GAEM,UAAc;EAGlB,AAFA,EAAQ,EAAK,GACb,EAAS,IAAI,GACb,EAAe,EAAE;CACnB,GAEM,KAAQ,MAA2B;EACnC,EAAO,aACX,EAAK,EAAO,KAAK,GACjB,EAAM;CACR,GAEM,MAAQ,MAAiB;EAC7B,IAAI,EAAe,WAAW,GAAG;EACjC,IAAM,IAAW,EAAe,WAAW,EAAE,eAAY,MAAU,CAAW,GACxE,IACJ,MAAa,KACT,MAAS,IACP,IACA,EAAe,SAAS,KACzB,IAAW,IAAO,EAAe,UAAU,EAAe;EAEjE,AADA,EAAa,UAAU,IACvB,EAAe,EAAe,EAAK,CAAC,KAAK;CAC3C,GAEM,MAAiB,MAA2C;EAChE,SAAY,CAAK,GAEb,IAAM,oBAAoB,EAAM,YAAY,eAAe,EAAM,YAAY,MAEjF,QAAQ,EAAM,KAAd;GACE,KAAK;GACL,KAAK;IAEH,IADA,EAAM,eAAe,GACjB,KAAQ,EAAM,UAAU,EAAM,QAAQ,WAAW;KACnD,EAAM;KACN;IACF;IACA,IAAI,CAAC,GAAM;KAET,EAAQ,EAAI;KACZ,IAAM,IAAgB,KAAY,CAAC,EAAS,WAAW,EAAW,QAAQ,CAAQ,IAAI,IAChF,IAAO,EAAM,QAAQ,cAAc,EAAe,KAAK,EAAe,EAAe,SAAS;KAEpG,AADA,EAAa,UAAU,IACvB,EAAe,KAAiB,KAAK,EAAM,SAAS,IAAiB,GAAM,SAAS,EAAG;KACvF;IACF;IACA,GAAK,EAAM,QAAQ,cAAc,IAAI,EAAE;IACvC;GACF,KAAK;IACH,AAAI,KAAY,KAAe,MAC7B,EAAM,eAAe,GACrB,EAAK,EAAW,EAAY;IAE9B;GACF,KAAK;IACH,CAAI,KAAY,MAAU,UACxB,EAAM,eAAe,GACrB,EAAM;IAER;EACJ;CACF,GAEM,MAAe,MAAiB;EAEpC,IAAM,IAAQ,EAAK,KAAK,IACpB,EAAW,MAAM,MAAW,CAAC,EAAO,YAAY,EAAU,EAAO,KAAK,MAAM,EAAU,CAAI,CAAC,IAC3F,KAAA;EAQJ,IAPI,IACE,EAAM,UAAU,KAAO,EAAK,EAAM,KAAK,IAClC,MAAU,MACnB,EAAK,EAAE,GAIL,SAAS,kBAAkB,EAAS,SAAS;GAC/C,EAAS,IAAQ,OAAO,CAAI;GAC5B;EACF;EAGA,AADA,EAAS,CAAI,GACb,EAAQ,EAAI;EACZ,IAAM,IAAa,EAAW,WAAW,MAAW,CAAC,EAAO,aAAa,MAAO,EAAO,GAAQ,CAAI,EAAS;EAC5G,EAAa,UAAU;EAEvB,IAAM,IAAa,KAAS,EAAO,GAAO,CAAI;EAC9C,EAAe,IAAa,EAAW,QAAQ,CAAK,IAAI,IAAO,IAAa,EAAE;CAChF;CAEA,OACE,kBAAC,OAAD;EAAK,WAAW,EAAG,sCAAsC,EAAS;YAAlE;GACG,KACC,kBAAC,IAAD;IAAY,IAAI;IAAS,SAAS;IAAe;IAAgB;cAC9D;GACS,CAAA;GAEd,kBAAC,EAAiB,MAAlB;IAAuB,MAAM;cAA7B;KACE,kBAAC,EAAiB,QAAlB;MAAyB,SAAA;gBACvB,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,SAAD;QACE,GAAI;QACJ,KAAK;QACL,IAAI;QACJ,MAAK;QACL,MAAK;QACL,iBAAe;QAEf,iBAAe,IAAW,IAAY,KAAA;QACtC,qBAAkB;QAClB,yBAAuB,KAAY,KAAe,IAAI,EAAS,CAAW,IAAI,KAAA;QAC9E,gBAAc,KAAY,KAAA;QAC1B,oBAAkB,EAAQ,IAAiB,KAAc,CAAS;QACpD;QACJ;QACA;QACG;QACb,OAAO,KAAS,GAAU,SAAS;QACnC,WAAW,MAAU,GAAY,EAAM,OAAO,KAAK;QACnD,WAAW;QACX,UAAU,MAAU;SAElB,AADA,IAAU,CAAK,GACX,CAAC,KAAQ,CAAC,EAAM,oBAAkB,EAAS;QACjD;QACA,SAAS,MAAU;SAEjB,AADA,KAAS,CAAK,GACd,EAAM;QACR;QACA,WAAW,EACT,EAAuB;SAAE;SAAM;SAAU;QAAS,CAAC,GACnD,kIACA,CAAC,KAAY,CAAC,KAAY,sCAC1B,MAAS,UAAU,UAAU,SAC7B,EAAmB,CAAI,CACzB;OACD,CAAA,GACD,kBAAC,UAAD;QACE,MAAK;QACL,UAAU;QACV,cAAY,KAAe,KAAS,EAAW;QAC/C,iBAAe,IAAW,IAAY,KAAA;QACtC,iBAAe;QACL;QAEV,cAAc,MAAU,EAAM,eAAe;QAC7C,eAAe;SAGb,AAFI,IAAU,EAAM,IACf,EAAS,GACd,EAAS,SAAS,MAAM;QAC1B;QACA,WAAW,EACT,0GACA,MAAS,UAAU,SAAS,MAC9B;kBAEA,kBAAC,IAAD;SACE,eAAY;SACZ,WAAW,EACT,4CACA,IAAW,4CAA4C,0BACzD;QACD,CAAA;OACK,CAAA,CACL;;KACkB,CAAA;KACxB,KAAQ,kBAAC,SAAD;MAAO,MAAK;MAAe;MAAa;MAAiB;MAAU,MAAM,EAAW;KAAO,CAAA;KACpG,kBAAC,EAAiB,QAAlB,EAAA,UACE,kBAAC,EAAiB,SAAlB;MACE,SAAA;MACA,OAAM;MACN,YAAY;MAEZ,kBAAkB,MAAU,EAAM,eAAe;MACjD,mBAAmB,MAAU,EAAM,eAAe;MAClD,oBAAoB,MAAU,EAAM,eAAe;MAEnD,kBAAkB,MAAU;OAEtB,EAAM,eAAe,EAAM,YAAY,QAC3C,EAAM,eAAe,GACrB,EAAM;MACR;gBAEA,kBAAC,MAAD;OACE,KAAK;OACL,IAAI;OACJ,MAAK;OACL,mBAAiB,IAAQ,IAAU,KAAA;OACnC,cAAY,IAAQ,KAAA,IAAY,EAAW;OAC3C,cAAc,MAAU,EAAM,eAAe;OAC7C,WAAW,EAAG,IAAe,mCAAmC;iBAPlE,CASG,EAAQ,KAAK,EAAE,WAAQ,eAAY;QAClC,IAAM,IAAa,EAAO,UAAU;QACpC,OACE,kBAAC,MAAD;SAEE,IAAI,EAAS,CAAK;SAClB,MAAK;SACL,iBAAe;SACf,iBAAe,EAAO,YAAY,KAAA;SAClC,mBAAmB;UAEjB,AADA,EAAa,UAAU,IAClB,EAAO,YAAU,EAAe,CAAK;SAC5C;SACA,eAAe,EAAK,CAAM;SAC1B,WAAW,EACT,GAAuB,CAAI,GAC3B,MAAU,KAAA,6BACV,KAAA,4CACA,EAAO,YAAA,+BACT;mBAhBF,CAkBE,kBAAC,QAAD;UAAM,WAAW;oBAAqB,KAAc,kBAAC,IAAD,CAAmB,CAAA;SAAQ,CAAA,GAC/E,kBAAC,QAAD;UAAM,WAAU;oBAA2B,EAAO;SAAY,CAAA,CAC5D;WAnBG,EAAO,KAmBV;OAER,CAAC,GACA,EAAQ,WAAW,KAAK,MAAc,KAAA,KACrC,kBAAC,MAAD;QAAI,MAAK;QAAe,WAAU;kBAC/B;OACC,CAAA,CAEJ;;KACoB,CAAA,EACH,CAAA;IACJ;;GACvB,kBAAC,IAAD;IAAc,IAAI;IAAiB;IAAiB;IAA0B;GAAgB,CAAA;EAC3F;;AAET"}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { cva as e } from "class-variance-authority";
|
|
2
|
-
|
|
3
|
-
var t = "rounded-[4px]", n = e(`flex items-center ${t} bg-surface-base-neutral-2 border transition-colors duration-200`, {
|
|
2
|
+
var t = e("flex items-center rounded-[4px] bg-surface-base-neutral-2 border transition-colors duration-200", {
|
|
4
3
|
variants: {
|
|
5
4
|
size: {
|
|
6
5
|
large: "gap-2 p-[15px] min-h-[56px]",
|
|
@@ -20,7 +19,7 @@ var t = "rounded-[4px]", n = e(`flex items-center ${t} bg-surface-base-neutral-2
|
|
|
20
19
|
disabled: !1,
|
|
21
20
|
hasError: !1
|
|
22
21
|
}
|
|
23
|
-
}),
|
|
22
|
+
}), n = e("", {
|
|
24
23
|
variants: {
|
|
25
24
|
size: {
|
|
26
25
|
large: "text-body-16-regular",
|
|
@@ -35,8 +34,8 @@ var t = "rounded-[4px]", n = e(`flex items-center ${t} bg-surface-base-neutral-2
|
|
|
35
34
|
size: "large",
|
|
36
35
|
hasError: !1
|
|
37
36
|
}
|
|
38
|
-
}),
|
|
37
|
+
}), r = (e) => e === "large" ? "text-title-18-bold" : "text-title-14-bold", i = (e) => e === "large" ? "text-title-20-regular" : "text-body-16-regular", a = (...e) => e.filter(Boolean).join(" ") || void 0;
|
|
39
38
|
//#endregion
|
|
40
|
-
export { a, r as
|
|
39
|
+
export { a, i, r as n, n as r, t };
|
|
41
40
|
|
|
42
|
-
//# sourceMappingURL=fieldStyles-
|
|
41
|
+
//# sourceMappingURL=fieldStyles-DXyDrzxD.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fieldStyles-DXyDrzxD.js","names":[],"sources":["../src/components/ui/fieldStyles.ts"],"sourcesContent":["import { cva } from \"class-variance-authority\";\n\n// TextField, Select, Combobox๊ฐ ํจ๊ป ์ฐ๋ ์
๋ ฅ ์นธ ์คํ์ผ\n// ์
๋ ฅ ์นธ ๋ชจ์๋ฆฌ. ์๋น ๋ ํฌ์ --radius-* ์ค์ ๊ณผ ๋ฌด๊ดํ๊ฒ 4px๋ก ๊ณ ์ ํ๋ค(๋ชฉ๋ก ํจ๋์ listboxStyles.ts).\nexport const FIELD_RADIUS = \"rounded-[4px]\";\n\nexport const fieldContainerVariants = cva(\n `flex items-center ${FIELD_RADIUS} bg-surface-base-neutral-2 border transition-colors duration-200`,\n {\n variants: {\n size: {\n large: \"gap-2 p-[15px] min-h-[56px]\",\n small: \"gap-3 p-[11px] min-h-[48px]\",\n },\n hasError: {\n true: \"border-border-prominent-error\",\n false: \"border-transparent\",\n },\n disabled: {\n true: \"opacity-50\",\n false: \"\",\n },\n },\n defaultVariants: {\n size: \"large\",\n disabled: false,\n hasError: false,\n },\n },\n);\n\nexport const fieldMessageVariants = cva(\"\", {\n variants: {\n size: {\n large: \"text-body-16-regular\",\n small: \"text-caption-12-medium\",\n },\n hasError: {\n true: \"text-text-prominent-error\",\n false: \"text-text-base-neutral-2\",\n },\n },\n defaultVariants: {\n size: \"large\",\n hasError: false,\n },\n});\n\nexport type FieldSize = \"large\" | \"small\";\n\nexport const fieldLabelFontSize = (size: FieldSize) => (size === \"large\" ? \"text-title-18-bold\" : \"text-title-14-bold\");\nexport const fieldValueFontSize = (size: FieldSize) =>\n size === \"large\" ? \"text-title-20-regular\" : \"text-body-16-regular\";\n\n// ์๋น ๋ ํฌ๊ฐ ๋๊ธด aria-describedby์ ํ๋ ๋ฉ์์ง id๋ฅผ ํฉ์น๋ค.\nexport const joinIds = (...ids: (string | false | undefined)[]) => ids.filter(Boolean).join(\" \") || undefined;\n"],"mappings":";AAMA,IAAa,IAAyB,EACpC,mGACA;CACE,UAAU;EACR,MAAM;GACJ,OAAO;GACP,OAAO;EACT;EACA,UAAU;GACR,MAAM;GACN,OAAO;EACT;EACA,UAAU;GACR,MAAM;GACN,OAAO;EACT;CACF;CACA,iBAAiB;EACf,MAAM;EACN,UAAU;EACV,UAAU;CACZ;AACF,CACF,GAEa,IAAuB,EAAI,IAAI;CAC1C,UAAU;EACR,MAAM;GACJ,OAAO;GACP,OAAO;EACT;EACA,UAAU;GACR,MAAM;GACN,OAAO;EACT;CACF;CACA,iBAAiB;EACf,MAAM;EACN,UAAU;CACZ;AACF,CAAC,GAIY,KAAsB,MAAqB,MAAS,UAAU,uBAAuB,sBACrF,KAAsB,MACjC,MAAS,UAAU,0BAA0B,wBAGlC,KAAW,GAAG,MAAwC,EAAI,OAAO,OAAO,CAAC,CAAC,KAAK,GAAG,KAAK,KAAA"}
|
package/dist/index.d.ts
CHANGED
|
@@ -6,7 +6,7 @@ export { Badge } from './src/components/badge/Badge';
|
|
|
6
6
|
export { Chip } from './src/components/chip/Chip';
|
|
7
7
|
export { CTAButton } from './src/components/cta-button/CTAButton';
|
|
8
8
|
export { Popup } from './src/components/popup/Popup';
|
|
9
|
-
export { TextFieldNormal,
|
|
9
|
+
export { TextFieldNormal, TextFieldWithButton } from './src/components/text-field/TextField';
|
|
10
10
|
export { Select, type SelectOption } from './src/components/select/Select';
|
|
11
11
|
export { Combobox, type ComboboxOption } from './src/components/combobox/Combobox';
|
|
12
12
|
export { CancelFilledIcon } from './src/components/icons/action/CancelFilledIcon';
|
package/dist/index.es.js
CHANGED
|
@@ -7,20 +7,20 @@ import { t as a } from "./Chip-Bu5q0mhe.js";
|
|
|
7
7
|
import { CTAButton as o } from "./cta-button.js";
|
|
8
8
|
import { t as s } from "./Popup-DRf-ERpL.js";
|
|
9
9
|
import { CancelFilledIcon as c } from "./icons/action/cancel-filled.js";
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
16
|
-
import { ChevronRightIcon as
|
|
17
|
-
import { ChevronUpIcon as
|
|
18
|
-
import { FilterIcon as
|
|
19
|
-
import { RetryIcon as
|
|
20
|
-
import { SortingIcon as
|
|
21
|
-
import { CloseIcon as
|
|
22
|
-
import { DeleteIcon as
|
|
23
|
-
import { RecentIcon as
|
|
24
|
-
import { ReportIcon as
|
|
25
|
-
import { ShieldDoneIcon as
|
|
26
|
-
export { i as Badge, o as CTAButton, c as CancelFilledIcon,
|
|
10
|
+
import { TextFieldNormal as l, TextFieldWithButton as u } from "./text-field.js";
|
|
11
|
+
import { ChevronDownIcon as d } from "./icons/action/chevron-down.js";
|
|
12
|
+
import { Select as f } from "./select.js";
|
|
13
|
+
import { Combobox as p } from "./combobox.js";
|
|
14
|
+
import { CancelIcon as m } from "./icons/action/cancel.js";
|
|
15
|
+
import { ChevronLeftIcon as h } from "./icons/action/chevron-left.js";
|
|
16
|
+
import { ChevronRightIcon as g } from "./icons/action/chevron-right.js";
|
|
17
|
+
import { ChevronUpIcon as _ } from "./icons/action/chevron-up.js";
|
|
18
|
+
import { FilterIcon as v } from "./icons/action/filter.js";
|
|
19
|
+
import { RetryIcon as y } from "./icons/action/retry.js";
|
|
20
|
+
import { SortingIcon as b } from "./icons/action/sorting.js";
|
|
21
|
+
import { CloseIcon as x } from "./icons/static/close.js";
|
|
22
|
+
import { DeleteIcon as S } from "./icons/static/delete.js";
|
|
23
|
+
import { RecentIcon as C } from "./icons/static/recent.js";
|
|
24
|
+
import { ReportIcon as w } from "./icons/static/report.js";
|
|
25
|
+
import { ShieldDoneIcon as T } from "./icons/static/shield-done.js";
|
|
26
|
+
export { i as Badge, o as CTAButton, c as CancelFilledIcon, m as CancelIcon, n as Checkbox, d as ChevronDownIcon, h as ChevronLeftIcon, g as ChevronRightIcon, _ as ChevronUpIcon, a as Chip, x as CloseIcon, p as Combobox, S as DeleteIcon, v as FilterIcon, s as Popup, e as Radio, C as RecentIcon, w as ReportIcon, y as RetryIcon, f as Select, T as ShieldDoneIcon, b as SortingIcon, t as Tabs, l as TextFieldNormal, u as TextFieldWithButton, r as Toggle };
|