@petnow/ui 5.2.0 → 5.3.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 CHANGED
@@ -1,5 +1,34 @@
1
1
  # Changelog
2
2
 
3
+ ## 5.3.0
4
+
5
+ ### Added
6
+
7
+ - **`Select`** (`@petnow/ui/select`): pick one option from a list. It is a native `<select>` styled like
8
+ `TextField`, so it keeps the mobile picker, form validation (`required`) and keyboard behavior of the
9
+ browser. Props: `label`, `options` (`string` or `{ value, label, disabled }`), `value` / `defaultValue`,
10
+ `onChange(value)`, `placeholder`, `errorText`, `assistiveText`, `size` (`large` | `small`), plus every
11
+ `<select>` attribute and `ref`.
12
+ - **`Combobox`** (`@petnow/ui/combobox`): type to search and pick one option (WAI-ARIA combobox with list
13
+ autocomplete). Focus stays in the input and the active option is announced with
14
+ `aria-activedescendant`. Only a picked option becomes `value`; typing after a pick calls
15
+ `onChange("")`, and text equal to an option label (for example from browser autofill) picks it.
16
+ Escape and blur discard the query, and a new `value` from outside (a form reset) replaces it. IME
17
+ composition keys (Korean, Japanese) never pick an option. With `required`, text that is not a picked
18
+ option fails browser validation (`invalidSelectionText`). It is controlled only (`value` and
19
+ `onChange`; no `defaultValue`). Other props as `Select`, plus `filter`, `emptyText`, `toggleLabel` and
20
+ `name` (submitted through a hidden input with the picked value). The list is positioned below the input,
21
+ so a parent with `overflow: hidden` clips it.
22
+ - Both are tagged `source-dev` in Storybook: they follow `TextField`, not a separate design baseline.
23
+
24
+ ### Changes
25
+
26
+ - `Checkbox` accepts `ref` (React 19 ref prop), which reaches the `<input>`. Without `isChecked` it is
27
+ uncontrolled and `defaultChecked` sets the first state.
28
+ - `Checkbox` is dimmed (`opacity-50`, box and check mark) and shows a not-allowed cursor when
29
+ `disabled`. A consumer `disabled:opacity-*` class still wins for the box.
30
+ - `TextField` shares its field styles with `Select` and `Combobox`. Its classes are unchanged.
31
+
3
32
  ## 5.2.0
4
33
 
5
34
  ### Changes
package/dist/checkbox.js CHANGED
@@ -3,20 +3,20 @@ import { t as e } from "./TailwindUtil-DiNvXuNl.js";
3
3
  import { jsx as t, jsxs as n } from "react/jsx-runtime";
4
4
  //#region src/components/checkbox/Checkbox.tsx
5
5
  var r = ({ className: r, id: i, onChange: a, disabled: o, isChecked: s, ...c }) => /* @__PURE__ */ n("label", {
6
- className: "relative flex h-min w-min cursor-pointer items-center rounded-full",
6
+ className: "relative flex h-min w-min cursor-pointer items-center rounded-full has-disabled:cursor-not-allowed",
7
7
  htmlFor: i,
8
8
  children: [/* @__PURE__ */ t("input", {
9
9
  ...c,
10
10
  type: "checkbox",
11
11
  disabled: o,
12
- className: e("peer bg-petnow-grey-primary checked:bg-surface-primary-accent focus-visible:outline-border-prominent-accent relative size-6 cursor-pointer appearance-none rounded-xl border border-transparent transition-all focus-visible:outline-2 focus-visible:outline-offset-2", r),
12
+ className: e("peer bg-petnow-grey-primary checked:bg-surface-primary-accent focus-visible:outline-border-prominent-accent relative size-6 cursor-pointer appearance-none rounded-xl border border-transparent transition-all focus-visible:outline-2 focus-visible:outline-offset-2 disabled:cursor-not-allowed disabled:opacity-50", r),
13
13
  id: i,
14
14
  checked: s,
15
15
  onChange: (e) => {
16
16
  a?.(e.target.checked);
17
17
  }
18
18
  }), /* @__PURE__ */ t("span", {
19
- className: "text-petnow-grey-secondary peer-checked:text-icon-prominent-neutral pointer-events-none absolute top-2/4 left-2/4 -translate-x-2/4 -translate-y-2/4 transition",
19
+ className: "text-petnow-grey-secondary peer-checked:text-icon-prominent-neutral pointer-events-none absolute top-2/4 left-2/4 -translate-x-2/4 -translate-y-2/4 transition peer-disabled:opacity-50",
20
20
  children: /* @__PURE__ */ t("svg", {
21
21
  xmlns: "http://www.w3.org/2000/svg",
22
22
  className: "h-4 w-4",
@@ -1 +1 @@
1
- {"version":3,"file":"checkbox.js","names":[],"sources":["../src/components/checkbox/Checkbox.tsx"],"sourcesContent":["\"use client\";\n\nimport { cn } from \"../../utils/TailwindUtil\";\n\nimport type { ComponentPropsWithoutRef } from \"react\";\n\ninterface Props extends Omit<ComponentPropsWithoutRef<\"input\">, \"type\" | \"checked\" | \"onChange\"> {\n className?: string;\n id?: string;\n onChange?: (newValue: boolean) => void;\n disabled?: boolean;\n isChecked?: boolean;\n}\n\nconst Checkbox = ({ className, id, onChange, disabled, isChecked, ...inputProps }: Props) => {\n return (\n <label className=\"relative flex h-min w-min cursor-pointer items-center rounded-full\" htmlFor={id}>\n <input\n {...inputProps}\n type=\"checkbox\"\n disabled={disabled}\n className={cn(\n \"peer bg-petnow-grey-primary checked:bg-surface-primary-accent focus-visible:outline-border-prominent-accent relative size-6 cursor-pointer appearance-none rounded-xl border border-transparent transition-all focus-visible:outline-2 focus-visible:outline-offset-2\",\n className,\n )}\n id={id}\n checked={isChecked}\n onChange={(event) => {\n onChange?.(event.target.checked);\n }}\n />\n <span className=\"text-petnow-grey-secondary peer-checked:text-icon-prominent-neutral pointer-events-none absolute top-2/4 left-2/4 -translate-x-2/4 -translate-y-2/4 transition\">\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n className=\"h-4 w-4\"\n viewBox=\"0 0 20 20\"\n fill=\"currentColor\"\n stroke=\"currentColor\"\n strokeWidth=\"1\"\n >\n <path\n fillRule=\"evenodd\"\n d=\"M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z\"\n clipRule=\"evenodd\"\n ></path>\n </svg>\n </span>\n </label>\n );\n};\n\nexport default Checkbox;\n"],"mappings":";;;;AAcA,IAAM,KAAY,EAAE,cAAW,OAAI,aAAU,aAAU,cAAW,GAAG,QAEjE,kBAAC,SAAD;CAAO,WAAU;CAAqE,SAAS;WAA/F,CACE,kBAAC,SAAD;EACE,GAAI;EACJ,MAAK;EACK;EACV,WAAW,EACT,yQACA,CACF;EACI;EACJ,SAAS;EACT,WAAW,MAAU;GACnB,IAAW,EAAM,OAAO,OAAO;EACjC;CACD,CAAA,GACD,kBAAC,QAAD;EAAM,WAAU;YACd,kBAAC,OAAD;GACE,OAAM;GACN,WAAU;GACV,SAAQ;GACR,MAAK;GACL,QAAO;GACP,aAAY;aAEZ,kBAAC,QAAD;IACE,UAAS;IACT,GAAE;IACF,UAAS;GACJ,CAAA;EACJ,CAAA;CACD,CAAA,CACD"}
1
+ {"version":3,"file":"checkbox.js","names":[],"sources":["../src/components/checkbox/Checkbox.tsx"],"sourcesContent":["\"use client\";\n\nimport { cn } from \"../../utils/TailwindUtil\";\n\nimport type { ComponentPropsWithRef } from \"react\";\n\ninterface Props extends Omit<ComponentPropsWithRef<\"input\">, \"type\" | \"checked\" | \"onChange\"> {\n className?: string;\n id?: string;\n onChange?: (newValue: boolean) => void;\n disabled?: boolean;\n /** 제어 컴포넌트로 쓸 때의 체크 상태. 넘기지 않으면 비제어 컴포넌트가 되고, 처음 상태는 `defaultChecked`로 정한다. */\n isChecked?: boolean;\n}\n\nconst Checkbox = ({ className, id, onChange, disabled, isChecked, ...inputProps }: Props) => {\n return (\n <label\n className=\"relative flex h-min w-min cursor-pointer items-center rounded-full has-disabled:cursor-not-allowed\"\n htmlFor={id}\n >\n <input\n {...inputProps}\n type=\"checkbox\"\n disabled={disabled}\n className={cn(\n \"peer bg-petnow-grey-primary checked:bg-surface-primary-accent focus-visible:outline-border-prominent-accent relative size-6 cursor-pointer appearance-none rounded-xl border border-transparent transition-all focus-visible:outline-2 focus-visible:outline-offset-2 disabled:cursor-not-allowed disabled:opacity-50\",\n className,\n )}\n id={id}\n checked={isChecked}\n onChange={(event) => {\n onChange?.(event.target.checked);\n }}\n />\n <span className=\"text-petnow-grey-secondary peer-checked:text-icon-prominent-neutral pointer-events-none absolute top-2/4 left-2/4 -translate-x-2/4 -translate-y-2/4 transition peer-disabled:opacity-50\">\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n className=\"h-4 w-4\"\n viewBox=\"0 0 20 20\"\n fill=\"currentColor\"\n stroke=\"currentColor\"\n strokeWidth=\"1\"\n >\n <path\n fillRule=\"evenodd\"\n d=\"M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z\"\n clipRule=\"evenodd\"\n ></path>\n </svg>\n </span>\n </label>\n );\n};\n\nexport default Checkbox;\n"],"mappings":";;;;AAeA,IAAM,KAAY,EAAE,cAAW,OAAI,aAAU,aAAU,cAAW,GAAG,QAEjE,kBAAC,SAAD;CACE,WAAU;CACV,SAAS;WAFX,CAIE,kBAAC,SAAD;EACE,GAAI;EACJ,MAAK;EACK;EACV,WAAW,EACT,yTACA,CACF;EACI;EACJ,SAAS;EACT,WAAW,MAAU;GACnB,IAAW,EAAM,OAAO,OAAO;EACjC;CACD,CAAA,GACD,kBAAC,QAAD;EAAM,WAAU;YACd,kBAAC,OAAD;GACE,OAAM;GACN,WAAU;GACV,SAAQ;GACR,MAAK;GACL,QAAO;GACP,aAAY;aAEZ,kBAAC,QAAD;IACE,UAAS;IACT,GAAE;IACF,UAAS;GACJ,CAAA;EACJ,CAAA;CACD,CAAA,CACD"}
@@ -0,0 +1,201 @@
1
+ "use client";
2
+ import { t as e } from "./TailwindUtil-DiNvXuNl.js";
3
+ import { a as t, n, o as r, t as ee } from "./fieldStyles-DyZu__hI.js";
4
+ import { ChevronDownIcon as te } from "./icons/action/chevron-down.js";
5
+ import { n as ne, r as re, t as ie } from "./mergeRefs-BHwdBwNQ.js";
6
+ import { jsx as i, jsxs as a } from "react/jsx-runtime";
7
+ import { useEffect as o, useId as s, useMemo as c, useRef as l, useState as u } from "react";
8
+ //#region src/components/combobox/Combobox.tsx
9
+ var d = (e) => e.toLocaleLowerCase().replace(/\s+/g, ""), f = (e, t) => d(e.label).includes(d(t)), p = ({ id: p, label: m, options: ae, value: h, onChange: oe, filter: g = f, emptyText: _, toggleLabel: v, invalidSelectionText: y = "Select an option from the list.", errorText: b, assistiveText: x, size: S = "large", name: C, required: w, disabled: T = !1, placeholder: se, className: ce, autoComplete: le = "off", "aria-describedby": ue, onBlur: de, onKeyDown: fe, onClick: pe, ref: E, ...D }) => {
10
+ let me = s(), O = p ?? me, k = `${O}-listbox`, A = `${O}-message`, j = (e) => `${O}-option-${e}`, M = !!b, he = !!(b || x), N = `${O}-label`, P = l(null), F = c(() => ie(P, E), [E]), I = l(null), L = l(!1), R = ae.map((e) => typeof e == "string" ? {
11
+ value: e,
12
+ label: e
13
+ } : e), z = R.find((e) => e.value === h), [B, V] = u(null), [H, U] = u(!1), [W, G] = u(-1), K = R.map((e, t) => ({
14
+ option: e,
15
+ index: t
16
+ })).filter(({ option: e }) => !B || g(e, B)), q = H && !T && (K.length > 0 || _ !== void 0), J = K.filter(({ option: e }) => !e.disabled);
17
+ o(() => {
18
+ let e = L.current;
19
+ L.current = !1, !(!q || W < 0 || !e) && document.getElementById(j(W))?.scrollIntoView?.({ block: "nearest" });
20
+ }, [q, W]), o(() => {
21
+ h !== I.current && V(null), I.current = null;
22
+ }, [h]), o(() => {
23
+ I.current = null;
24
+ });
25
+ let Y = !!(w && !z && B);
26
+ o(() => {
27
+ P.current?.setCustomValidity(Y ? y : "");
28
+ }, [Y, y]);
29
+ let X = (e) => {
30
+ I.current = e, oe(e);
31
+ }, Z = () => {
32
+ L.current = !0, U(!0), G(z && !z.disabled ? R.indexOf(z) : -1);
33
+ }, Q = () => {
34
+ U(!1), V(null), G(-1);
35
+ }, $ = (e) => {
36
+ e.disabled || (X(e.value), Q());
37
+ }, ge = (e) => {
38
+ if (J.length === 0) return;
39
+ let t = J.findIndex(({ index: e }) => e === W), n = t === -1 ? e === 1 ? 0 : J.length - 1 : (t + e + J.length) % J.length;
40
+ L.current = !0, G(J[n].index);
41
+ }, _e = (e) => {
42
+ if (fe?.(e), !(e.defaultPrevented || e.nativeEvent.isComposing || e.keyCode === 229)) switch (e.key) {
43
+ case "ArrowDown":
44
+ case "ArrowUp":
45
+ if (e.preventDefault(), H && e.altKey && e.key === "ArrowUp") {
46
+ Q();
47
+ return;
48
+ }
49
+ if (!H) {
50
+ U(!0);
51
+ let t = z && !z.disabled ? R.indexOf(z) : -1, n = e.key === "ArrowDown" ? J[0] : J[J.length - 1];
52
+ L.current = !0, G(t >= 0 || e.altKey ? t : n?.index ?? -1);
53
+ return;
54
+ }
55
+ ge(e.key === "ArrowDown" ? 1 : -1);
56
+ return;
57
+ case "Enter":
58
+ q && W >= 0 && (e.preventDefault(), $(R[W]));
59
+ return;
60
+ case "Escape":
61
+ (q || B !== null) && (e.preventDefault(), Q());
62
+ return;
63
+ }
64
+ }, ve = (e) => {
65
+ let t = e.trim() ? R.find((t) => !t.disabled && d(t.label) === d(e)) : void 0;
66
+ if (t ? t.value !== h && X(t.value) : h !== "" && X(""), document.activeElement !== P.current) {
67
+ V(t ? null : e);
68
+ return;
69
+ }
70
+ V(e), U(!0);
71
+ let n = R.findIndex((t) => !t.disabled && (!e || g(t, e)));
72
+ L.current = !0, G(t ? R.indexOf(t) : e ? n : -1);
73
+ };
74
+ return /* @__PURE__ */ a("div", {
75
+ className: e("flex w-full min-w-0 flex-col gap-2", ce),
76
+ children: [
77
+ m && /* @__PURE__ */ i(ne, {
78
+ id: N,
79
+ htmlFor: O,
80
+ size: S,
81
+ required: w,
82
+ children: m
83
+ }),
84
+ /* @__PURE__ */ a("div", {
85
+ className: "relative",
86
+ children: [
87
+ /* @__PURE__ */ a("div", {
88
+ className: "relative",
89
+ children: [/* @__PURE__ */ i("input", {
90
+ ...D,
91
+ ref: F,
92
+ id: O,
93
+ type: "text",
94
+ role: "combobox",
95
+ "aria-expanded": q,
96
+ "aria-controls": k,
97
+ "aria-autocomplete": "list",
98
+ "aria-activedescendant": q && W >= 0 ? j(W) : void 0,
99
+ "aria-invalid": M || void 0,
100
+ "aria-describedby": r(ue, he && A),
101
+ autoComplete: le,
102
+ required: w,
103
+ disabled: T,
104
+ placeholder: se,
105
+ value: B ?? z?.label ?? "",
106
+ onChange: (e) => ve(e.target.value),
107
+ onKeyDown: _e,
108
+ onClick: (e) => {
109
+ pe?.(e), !H && !e.defaultPrevented && Z();
110
+ },
111
+ onBlur: (e) => {
112
+ de?.(e), Q();
113
+ },
114
+ className: e(n({
115
+ size: S,
116
+ hasError: M,
117
+ disabled: T
118
+ }), "text-text-base-neutral-5 placeholder:text-text-base-neutral-2 w-full min-w-0 truncate outline-none disabled:cursor-not-allowed", !T && !M && "focus:border-border-base-neutral-4", S === "large" ? "pr-14" : "pr-12", t(S))
119
+ }), /* @__PURE__ */ i("button", {
120
+ type: "button",
121
+ tabIndex: -1,
122
+ "aria-label": v ?? m ?? D["aria-label"],
123
+ "aria-controls": k,
124
+ "aria-expanded": q,
125
+ disabled: T,
126
+ onMouseDown: (e) => e.preventDefault(),
127
+ onClick: () => {
128
+ q ? Q() : Z(), P.current?.focus();
129
+ },
130
+ className: e("absolute inset-y-0 right-0 flex cursor-pointer items-center justify-center disabled:cursor-not-allowed", S === "large" ? "w-14" : "w-12"),
131
+ children: /* @__PURE__ */ i(te, {
132
+ "aria-hidden": "true",
133
+ className: e("size-6 transition-transform duration-200", q ? "text-icon-prominent-accent-1 rotate-180" : "text-icon-base-neutral-3")
134
+ })
135
+ })]
136
+ }),
137
+ C && /* @__PURE__ */ i("input", {
138
+ type: "hidden",
139
+ name: C,
140
+ value: h,
141
+ disabled: T,
142
+ form: D.form
143
+ }),
144
+ /* @__PURE__ */ a("ul", {
145
+ id: k,
146
+ role: "listbox",
147
+ "aria-labelledby": m ? N : void 0,
148
+ "aria-label": m ? void 0 : D["aria-label"],
149
+ hidden: !q,
150
+ onMouseDown: (e) => e.preventDefault(),
151
+ className: e("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", ee),
152
+ children: [K.map(({ option: t, index: n }) => {
153
+ let r = t.value === h;
154
+ return /* @__PURE__ */ a("li", {
155
+ id: j(n),
156
+ role: "option",
157
+ "aria-selected": r,
158
+ "aria-disabled": t.disabled || void 0,
159
+ onMouseMove: () => {
160
+ L.current = !1, t.disabled || G(n);
161
+ },
162
+ onClick: () => $(t),
163
+ className: e("text-text-base-neutral-5 flex cursor-pointer items-center gap-3 px-3 py-2.5 select-none", S === "large" ? "text-body-16-regular" : "text-body-14-regular", n === W && "bg-surface-base-neutral-2", r && "font-medium", t.disabled && "cursor-not-allowed opacity-50"),
164
+ children: [/* @__PURE__ */ i("span", {
165
+ className: "min-w-0 flex-1 truncate",
166
+ children: t.label
167
+ }), r && /* @__PURE__ */ i("svg", {
168
+ "aria-hidden": "true",
169
+ viewBox: "0 0 20 20",
170
+ fill: "none",
171
+ className: "text-icon-prominent-accent-1 size-4 shrink-0",
172
+ children: /* @__PURE__ */ i("path", {
173
+ d: "m4 10 3.5 3.5L16 5",
174
+ stroke: "currentColor",
175
+ strokeWidth: "2",
176
+ strokeLinecap: "round",
177
+ strokeLinejoin: "round"
178
+ })
179
+ })]
180
+ }, t.value);
181
+ }), K.length === 0 && _ !== void 0 && /* @__PURE__ */ i("li", {
182
+ role: "presentation",
183
+ className: "text-text-base-neutral-2 text-body-14-regular px-3 py-2.5",
184
+ children: _
185
+ })]
186
+ })
187
+ ]
188
+ }),
189
+ /* @__PURE__ */ i(re, {
190
+ id: A,
191
+ size: S,
192
+ errorText: b,
193
+ assistiveText: x
194
+ })
195
+ ]
196
+ });
197
+ };
198
+ //#endregion
199
+ export { p as Combobox };
200
+
201
+ //# sourceMappingURL=combobox.js.map
@@ -0,0 +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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"}
@@ -0,0 +1,42 @@
1
+ import { cva as e } from "class-variance-authority";
2
+ //#region src/components/ui/fieldStyles.ts
3
+ var t = "rounded-[4px]", n = e(`flex items-center ${t} bg-surface-base-neutral-2 border transition-colors duration-200`, {
4
+ variants: {
5
+ size: {
6
+ large: "gap-2 p-[15px] min-h-[56px]",
7
+ small: "gap-3 p-[11px] min-h-[48px]"
8
+ },
9
+ hasError: {
10
+ true: "border-border-prominent-error",
11
+ false: "border-transparent"
12
+ },
13
+ disabled: {
14
+ true: "opacity-50",
15
+ false: ""
16
+ }
17
+ },
18
+ defaultVariants: {
19
+ size: "large",
20
+ disabled: !1,
21
+ hasError: !1
22
+ }
23
+ }), r = e("", {
24
+ variants: {
25
+ size: {
26
+ large: "text-body-16-regular",
27
+ small: "text-caption-12-medium"
28
+ },
29
+ hasError: {
30
+ true: "text-text-prominent-error",
31
+ false: "text-text-base-neutral-2"
32
+ }
33
+ },
34
+ defaultVariants: {
35
+ size: "large",
36
+ hasError: !1
37
+ }
38
+ }), i = (e) => e === "large" ? "text-title-18-bold" : "text-title-14-bold", a = (e) => e === "large" ? "text-title-20-regular" : "text-body-16-regular", o = (...e) => e.filter(Boolean).join(" ") || void 0;
39
+ //#endregion
40
+ export { a, r as i, n, o, i as r, t };
41
+
42
+ //# sourceMappingURL=fieldStyles-DyZu__hI.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fieldStyles-DyZu__hI.js","names":[],"sources":["../src/components/ui/fieldStyles.ts"],"sourcesContent":["import { cva } from \"class-variance-authority\";\n\n// TextField, Select, Combobox가 함께 쓰는 입력 칸 스타일\n// 입력 칸과 목록 팝업이 같은 모서리를 쓴다. 소비 레포의 --radius-* 설정과 무관하게 4px로 고정한다.\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":";;AAIA,IAAa,IAAe,iBAEf,IAAyB,EACpC,qBAAqB,EAAa,mEAClC;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
@@ -7,6 +7,8 @@ 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
9
  export { TextFieldNormal, TextFieldSelect, TextFieldWithButton } from './src/components/text-field/TextField';
10
+ export { Select, type SelectOption } from './src/components/select/Select';
11
+ export { Combobox, type ComboboxOption } from './src/components/combobox/Combobox';
10
12
  export { CancelFilledIcon } from './src/components/icons/action/CancelFilledIcon';
11
13
  export { CancelIcon } from './src/components/icons/action/CancelIcon';
12
14
  export { ChevronDownIcon } from './src/components/icons/action/ChevronDownIcon';
package/dist/index.es.js CHANGED
@@ -9,16 +9,18 @@ import { t as s } from "./Popup-DRf-ERpL.js";
9
9
  import { CancelFilledIcon as c } from "./icons/action/cancel-filled.js";
10
10
  import { ChevronLeftIcon as l } from "./icons/action/chevron-left.js";
11
11
  import { TextFieldNormal as u, TextFieldSelect as d, TextFieldWithButton as f } from "./text-field.js";
12
- import { CancelIcon as p } from "./icons/action/cancel.js";
13
- import { ChevronDownIcon as m } from "./icons/action/chevron-down.js";
14
- import { ChevronRightIcon as h } from "./icons/action/chevron-right.js";
15
- import { ChevronUpIcon as g } from "./icons/action/chevron-up.js";
16
- import { FilterIcon as _ } from "./icons/action/filter.js";
17
- import { RetryIcon as v } from "./icons/action/retry.js";
18
- import { SortingIcon as y } from "./icons/action/sorting.js";
19
- import { CloseIcon as b } from "./icons/static/close.js";
20
- import { DeleteIcon as x } from "./icons/static/delete.js";
21
- import { RecentIcon as S } from "./icons/static/recent.js";
22
- import { ReportIcon as C } from "./icons/static/report.js";
23
- import { ShieldDoneIcon as w } from "./icons/static/shield-done.js";
24
- export { i as Badge, o as CTAButton, c as CancelFilledIcon, p as CancelIcon, n as Checkbox, m as ChevronDownIcon, l as ChevronLeftIcon, h as ChevronRightIcon, g as ChevronUpIcon, a as Chip, b as CloseIcon, x as DeleteIcon, _ as FilterIcon, s as Popup, e as Radio, S as RecentIcon, C as ReportIcon, v as RetryIcon, w as ShieldDoneIcon, y as SortingIcon, t as Tabs, u as TextFieldNormal, d as TextFieldSelect, f as TextFieldWithButton, r as Toggle };
12
+ import { ChevronDownIcon as p } from "./icons/action/chevron-down.js";
13
+ import { Select as m } from "./select.js";
14
+ import { Combobox as h } from "./combobox.js";
15
+ import { CancelIcon as g } from "./icons/action/cancel.js";
16
+ import { ChevronRightIcon as _ } from "./icons/action/chevron-right.js";
17
+ import { ChevronUpIcon as v } from "./icons/action/chevron-up.js";
18
+ import { FilterIcon as y } from "./icons/action/filter.js";
19
+ import { RetryIcon as b } from "./icons/action/retry.js";
20
+ import { SortingIcon as x } from "./icons/action/sorting.js";
21
+ import { CloseIcon as S } from "./icons/static/close.js";
22
+ import { DeleteIcon as C } from "./icons/static/delete.js";
23
+ import { RecentIcon as w } from "./icons/static/recent.js";
24
+ import { ReportIcon as T } from "./icons/static/report.js";
25
+ import { ShieldDoneIcon as E } from "./icons/static/shield-done.js";
26
+ export { i as Badge, o as CTAButton, c as CancelFilledIcon, g as CancelIcon, n as Checkbox, p as ChevronDownIcon, l as ChevronLeftIcon, _ as ChevronRightIcon, v as ChevronUpIcon, a as Chip, S as CloseIcon, h as Combobox, C as DeleteIcon, y as FilterIcon, s as Popup, e as Radio, w as RecentIcon, T as ReportIcon, b as RetryIcon, m as Select, E as ShieldDoneIcon, x as SortingIcon, t as Tabs, u as TextFieldNormal, d as TextFieldSelect, f as TextFieldWithButton, r as Toggle };
@@ -0,0 +1,27 @@
1
+ import { t as e } from "./TailwindUtil-DiNvXuNl.js";
2
+ import { i as t, r as n } from "./fieldStyles-DyZu__hI.js";
3
+ import { jsx as r, jsxs as i } from "react/jsx-runtime";
4
+ //#region src/components/ui/field.tsx
5
+ var a = ({ id: t, htmlFor: a, size: o, required: s, children: c }) => /* @__PURE__ */ i("label", {
6
+ id: t,
7
+ htmlFor: a,
8
+ className: e(n(o), "text-text-base-neutral-5"),
9
+ children: [c, s && /* @__PURE__ */ r("span", {
10
+ className: "text-text-prominent-accent-1",
11
+ "aria-hidden": "true",
12
+ children: "*"
13
+ })]
14
+ }), o = ({ id: e, size: n, errorText: i, assistiveText: a }) => !i && !a ? null : /* @__PURE__ */ r("span", {
15
+ id: e,
16
+ className: t({
17
+ size: n,
18
+ hasError: !!i
19
+ }),
20
+ children: i || a
21
+ }), s = (...e) => (t) => {
22
+ for (let n of e) typeof n == "function" ? n(t) : n && (n.current = t);
23
+ };
24
+ //#endregion
25
+ export { a as n, o as r, s as t };
26
+
27
+ //# sourceMappingURL=mergeRefs-BHwdBwNQ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mergeRefs-BHwdBwNQ.js","names":[],"sources":["../src/components/ui/field.tsx","../src/utils/mergeRefs.ts"],"sourcesContent":["import { fieldLabelFontSize, fieldMessageVariants, type FieldSize } from \"./fieldStyles\";\n\nimport type { ReactNode } from \"react\";\n\nimport { cn } from \"@/utils/TailwindUtil\";\n\nexport const FieldLabel = ({\n id,\n htmlFor,\n size,\n required,\n children,\n}: {\n id?: string;\n htmlFor: string;\n size: FieldSize;\n required?: boolean;\n children: ReactNode;\n}) => (\n <label id={id} htmlFor={htmlFor} className={cn(fieldLabelFontSize(size), \"text-text-base-neutral-5\")}>\n {children}\n {required && (\n <span className=\"text-text-prominent-accent-1\" aria-hidden=\"true\">\n *\n </span>\n )}\n </label>\n);\n\nexport const FieldMessage = ({\n id,\n size,\n errorText,\n assistiveText,\n}: {\n id: string;\n size: FieldSize;\n errorText?: string;\n assistiveText?: ReactNode;\n}) => {\n if (!errorText && !assistiveText) return null;\n return (\n <span id={id} className={fieldMessageVariants({ size, hasError: Boolean(errorText) })}>\n {errorText || assistiveText}\n </span>\n );\n};\n","import type { Ref, RefCallback } from \"react\";\n\n// 내부 ref와 소비 레포가 넘긴 ref(React 19 ref prop)를 함께 연결한다.\nexport const mergeRefs =\n <T>(...refs: (Ref<T> | undefined)[]): RefCallback<T> =>\n (node) => {\n for (const ref of refs) {\n if (typeof ref === \"function\") ref(node);\n else if (ref) ref.current = node;\n }\n };\n"],"mappings":";;;;AAMA,IAAa,KAAc,EACzB,OACA,YACA,SACA,aACA,kBAQA,kBAAC,SAAD;CAAW;CAAa;CAAS,WAAW,EAAG,EAAmB,CAAI,GAAG,0BAA0B;WAAnG,CACG,GACA,KACC,kBAAC,QAAD;EAAM,WAAU;EAA+B,eAAY;YAAO;CAE5D,CAAA,CAEH;IAGI,KAAgB,EAC3B,OACA,SACA,cACA,uBAOI,CAAC,KAAa,CAAC,IAAsB,OAEvC,kBAAC,QAAD;CAAU;CAAI,WAAW,EAAqB;EAAE;EAAM,UAAU,EAAQ;CAAW,CAAC;WACjF,KAAa;AACV,CAAA,GCzCG,KACP,GAAG,OACN,MAAS;CACR,KAAK,IAAM,KAAO,GAChB,AAAI,OAAO,KAAQ,aAAY,EAAI,CAAI,IAC9B,MAAK,EAAI,UAAU;AAEhC"}
package/dist/select.js ADDED
@@ -0,0 +1,82 @@
1
+ "use client";
2
+ import { t as e } from "./TailwindUtil-DiNvXuNl.js";
3
+ import { a as t, n, o as r } from "./fieldStyles-DyZu__hI.js";
4
+ import { ChevronDownIcon as i } from "./icons/action/chevron-down.js";
5
+ import { n as a, r as o, t as s } from "./mergeRefs-BHwdBwNQ.js";
6
+ import { jsx as c, jsxs as l } from "react/jsx-runtime";
7
+ import { useEffect as u, useId as d, useMemo as f, useRef as p, useState as m } from "react";
8
+ //#region src/components/select/Select.tsx
9
+ var h = ({ id: h, label: g, options: _, value: v, defaultValue: y, onChange: b, placeholder: x, errorText: S, assistiveText: C, size: w = "large", required: T, disabled: E = !1, className: D, "aria-describedby": O, ref: k, ...A }) => {
10
+ let j = d(), M = h ?? j, N = `${M}-message`, P = !!S, F = !!(S || C), [I, L] = m(y ?? ""), R = v ?? I, z = _.map((e) => typeof e == "string" ? {
11
+ value: e,
12
+ label: e
13
+ } : e), B = x === void 0 && !z.some((e) => e.value === R), V = p(null), H = f(() => s(V, k), [k]);
14
+ return u(() => {
15
+ let e = V.current?.form;
16
+ if (v !== void 0 || !e) return;
17
+ let t = () => setTimeout(() => L(V.current?.value ?? ""));
18
+ return e.addEventListener("reset", t), () => e.removeEventListener("reset", t);
19
+ }, [v]), /* @__PURE__ */ l("div", {
20
+ className: e("flex w-full min-w-0 flex-col gap-2", D),
21
+ children: [
22
+ g && /* @__PURE__ */ c(a, {
23
+ htmlFor: M,
24
+ size: w,
25
+ required: T,
26
+ children: g
27
+ }),
28
+ /* @__PURE__ */ l("div", {
29
+ className: "relative",
30
+ children: [/* @__PURE__ */ l("select", {
31
+ ...A,
32
+ ref: H,
33
+ id: M,
34
+ value: v,
35
+ defaultValue: v === void 0 ? y ?? "" : void 0,
36
+ required: T,
37
+ disabled: E,
38
+ "aria-invalid": P || void 0,
39
+ "aria-describedby": r(O, F && N),
40
+ onChange: (e) => {
41
+ L(e.target.value), b?.(e.target.value);
42
+ },
43
+ className: e(n({
44
+ size: w,
45
+ hasError: P,
46
+ disabled: E
47
+ }), "w-full min-w-0 cursor-pointer appearance-none truncate outline-none disabled:cursor-not-allowed", !E && !P && "focus:border-border-base-neutral-4", w === "large" ? "pr-12" : "pr-11", t(w), R ? "text-text-base-neutral-5" : "text-text-base-neutral-2"),
48
+ children: [
49
+ x !== void 0 && /* @__PURE__ */ c("option", {
50
+ value: "",
51
+ children: x
52
+ }),
53
+ B && /* @__PURE__ */ c("option", {
54
+ value: R,
55
+ hidden: !0,
56
+ disabled: !0
57
+ }),
58
+ z.map((e) => /* @__PURE__ */ c("option", {
59
+ value: e.value,
60
+ disabled: e.disabled,
61
+ className: "text-text-base-neutral-5",
62
+ children: e.label
63
+ }, e.value))
64
+ ]
65
+ }), /* @__PURE__ */ c(i, {
66
+ "aria-hidden": "true",
67
+ className: e("text-icon-base-neutral-3 pointer-events-none absolute top-1/2 size-6 -translate-y-1/2", w === "large" ? "right-4" : "right-3")
68
+ })]
69
+ }),
70
+ /* @__PURE__ */ c(o, {
71
+ id: N,
72
+ size: w,
73
+ errorText: S,
74
+ assistiveText: C
75
+ })
76
+ ]
77
+ });
78
+ };
79
+ //#endregion
80
+ export { h as Select };
81
+
82
+ //# sourceMappingURL=select.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"select.js","names":[],"sources":["../src/components/select/Select.tsx"],"sourcesContent":["\"use client\";\n\nimport { useEffect, useId, useMemo, useRef, useState } from \"react\";\n\nimport { ChevronDownIcon } from \"../icons/action/ChevronDownIcon\";\n\nimport type { ComponentPropsWithRef, ReactNode } from \"react\";\n\nimport { FieldLabel, FieldMessage } from \"@/components/ui/field\";\nimport { fieldContainerVariants, fieldValueFontSize, joinIds, type FieldSize } from \"@/components/ui/fieldStyles\";\nimport { mergeRefs } from \"@/utils/mergeRefs\";\nimport { cn } from \"@/utils/TailwindUtil\";\n\nexport interface SelectOption {\n value: string;\n label: string;\n disabled?: boolean;\n}\n\ninterface SelectProps\n extends Omit<ComponentPropsWithRef<\"select\">, \"size\" | \"value\" | \"defaultValue\" | \"onChange\" | \"children\"> {\n /** 칸 위에 보이는 이름. `<label>`로 `<select>`에 연결된다. */\n label?: string;\n /** 문자열 옵션은 값과 표시 이름이 같다. */\n options: (string | SelectOption)[];\n value?: string;\n defaultValue?: string;\n onChange?: (value: string) => void;\n /** 값이 없을 때 보이는 첫 옵션(값 `\"\"`). `required`이면 이 옵션으로는 제출되지 않는다. */\n placeholder?: string;\n errorText?: string;\n assistiveText?: ReactNode;\n size?: FieldSize;\n}\n\n/** 목록에서 하나를 고르는 입력. 브라우저 기본 `<select>`라 모바일 선택기·폼 검증·키보드 조작을 그대로 쓴다. */\nconst Select = ({\n id,\n label,\n options,\n value,\n defaultValue,\n onChange,\n placeholder,\n errorText,\n assistiveText,\n size = \"large\",\n required,\n disabled = false,\n className,\n \"aria-describedby\": ariaDescribedBy,\n ref,\n ...selectProps\n}: SelectProps) => {\n const generatedId = useId();\n const selectId = id ?? generatedId;\n const messageId = `${selectId}-message`;\n const hasError = Boolean(errorText);\n const hasMessage = Boolean(errorText || assistiveText);\n const [uncontrolledValue, setUncontrolledValue] = useState(defaultValue ?? \"\");\n const currentValue = value ?? uncontrolledValue;\n const normalized = options.map((option) => (typeof option === \"string\" ? { value: option, label: option } : option));\n // placeholder 없이 옵션에 없는 값(예: \"\")이면 브라우저가 첫 옵션을 보여 주므로, 숨긴 빈 옵션을 고른 상태로 둔다.\n const needsEmptyOption = placeholder === undefined && !normalized.some((option) => option.value === currentValue);\n const selectRef = useRef<HTMLSelectElement | null>(null);\n const setRef = useMemo(() => mergeRefs(selectRef, ref), [ref]);\n\n // 비제어일 때 form.reset()은 onChange 없이 값을 되돌리므로 placeholder 색을 다시 맞춘다.\n useEffect(() => {\n const form = selectRef.current?.form;\n if (value !== undefined || !form) return;\n const sync = () => setTimeout(() => setUncontrolledValue(selectRef.current?.value ?? \"\"));\n form.addEventListener(\"reset\", sync);\n return () => form.removeEventListener(\"reset\", sync);\n }, [value]);\n\n return (\n <div className={cn(\"flex w-full min-w-0 flex-col gap-2\", className)}>\n {label && (\n <FieldLabel htmlFor={selectId} size={size} required={required}>\n {label}\n </FieldLabel>\n )}\n <div className=\"relative\">\n <select\n {...selectProps}\n ref={setRef}\n id={selectId}\n value={value}\n defaultValue={value === undefined ? (defaultValue ?? \"\") : undefined}\n required={required}\n disabled={disabled}\n aria-invalid={hasError || undefined}\n aria-describedby={joinIds(ariaDescribedBy, hasMessage && messageId)}\n onChange={(event) => {\n setUncontrolledValue(event.target.value);\n onChange?.(event.target.value);\n }}\n className={cn(\n fieldContainerVariants({ size, hasError, disabled }),\n \"w-full min-w-0 cursor-pointer appearance-none truncate outline-none disabled:cursor-not-allowed\",\n !disabled && !hasError && \"focus:border-border-base-neutral-4\",\n size === \"large\" ? \"pr-12\" : \"pr-11\",\n fieldValueFontSize(size),\n currentValue ? \"text-text-base-neutral-5\" : \"text-text-base-neutral-2\",\n )}\n >\n {placeholder !== undefined && <option value=\"\">{placeholder}</option>}\n {needsEmptyOption && <option value={currentValue} hidden disabled />}\n {normalized.map((option) => (\n <option\n key={option.value}\n value={option.value}\n disabled={option.disabled}\n className=\"text-text-base-neutral-5\"\n >\n {option.label}\n </option>\n ))}\n </select>\n <ChevronDownIcon\n aria-hidden=\"true\"\n className={cn(\n \"text-icon-base-neutral-3 pointer-events-none absolute top-1/2 size-6 -translate-y-1/2\",\n size === \"large\" ? \"right-4\" : \"right-3\",\n )}\n />\n </div>\n <FieldMessage id={messageId} size={size} errorText={errorText} assistiveText={assistiveText} />\n </div>\n );\n};\n\nexport { Select };\n"],"mappings":";;;;;;;;AAoCA,IAAM,KAAU,EACd,OACA,UACA,YACA,UACA,iBACA,aACA,gBACA,cACA,kBACA,UAAO,SACP,aACA,cAAW,IACX,cACA,oBAAoB,GACpB,QACA,GAAG,QACc;CACjB,IAAM,IAAc,EAAM,GACpB,IAAW,KAAM,GACjB,IAAY,GAAG,EAAS,WACxB,IAAW,EAAQ,GACnB,IAAa,GAAQ,KAAa,IAClC,CAAC,GAAmB,KAAwB,EAAS,KAAgB,EAAE,GACvE,IAAe,KAAS,GACxB,IAAa,EAAQ,KAAK,MAAY,OAAO,KAAW,WAAW;EAAE,OAAO;EAAQ,OAAO;CAAO,IAAI,CAAO,GAE7G,IAAmB,MAAgB,KAAA,KAAa,CAAC,EAAW,MAAM,MAAW,EAAO,UAAU,CAAY,GAC1G,IAAY,EAAiC,IAAI,GACjD,IAAS,QAAc,EAAU,GAAW,CAAG,GAAG,CAAC,CAAG,CAAC;CAW7D,OARA,QAAgB;EACd,IAAM,IAAO,EAAU,SAAS;EAChC,IAAI,MAAU,KAAA,KAAa,CAAC,GAAM;EAClC,IAAM,UAAa,iBAAiB,EAAqB,EAAU,SAAS,SAAS,EAAE,CAAC;EAExF,OADA,EAAK,iBAAiB,SAAS,CAAI,SACtB,EAAK,oBAAoB,SAAS,CAAI;CACrD,GAAG,CAAC,CAAK,CAAC,GAGR,kBAAC,OAAD;EAAK,WAAW,EAAG,sCAAsC,CAAS;YAAlE;GACG,KACC,kBAAC,GAAD;IAAY,SAAS;IAAgB;IAAgB;cAClD;GACS,CAAA;GAEd,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,UAAD;KACE,GAAI;KACJ,KAAK;KACL,IAAI;KACG;KACP,cAAc,MAAU,KAAA,IAAa,KAAgB,KAAM,KAAA;KACjD;KACA;KACV,gBAAc,KAAY,KAAA;KAC1B,oBAAkB,EAAQ,GAAiB,KAAc,CAAS;KAClE,WAAW,MAAU;MAEnB,AADA,EAAqB,EAAM,OAAO,KAAK,GACvC,IAAW,EAAM,OAAO,KAAK;KAC/B;KACA,WAAW,EACT,EAAuB;MAAE;MAAM;MAAU;KAAS,CAAC,GACnD,mGACA,CAAC,KAAY,CAAC,KAAY,sCAC1B,MAAS,UAAU,UAAU,SAC7B,EAAmB,CAAI,GACvB,IAAe,6BAA6B,0BAC9C;eArBF;MAuBG,MAAgB,KAAA,KAAa,kBAAC,UAAD;OAAQ,OAAM;iBAAI;MAAoB,CAAA;MACnE,KAAoB,kBAAC,UAAD;OAAQ,OAAO;OAAc,QAAA;OAAO,UAAA;MAAU,CAAA;MAClE,EAAW,KAAK,MACf,kBAAC,UAAD;OAEE,OAAO,EAAO;OACd,UAAU,EAAO;OACjB,WAAU;iBAET,EAAO;MACF,GAND,EAAO,KAMN,CACT;KACK;QACR,kBAAC,GAAD;KACE,eAAY;KACZ,WAAW,EACT,yFACA,MAAS,UAAU,YAAY,SACjC;IACD,CAAA,CACE;;GACL,kBAAC,GAAD;IAAc,IAAI;IAAiB;IAAiB;IAA0B;GAAgB,CAAA;EAC3F;;AAET"}
@@ -1,9 +1,10 @@
1
- import { ComponentPropsWithoutRef } from 'react';
2
- interface Props extends Omit<ComponentPropsWithoutRef<"input">, "type" | "checked" | "onChange"> {
1
+ import { ComponentPropsWithRef } from 'react';
2
+ interface Props extends Omit<ComponentPropsWithRef<"input">, "type" | "checked" | "onChange"> {
3
3
  className?: string;
4
4
  id?: string;
5
5
  onChange?: (newValue: boolean) => void;
6
6
  disabled?: boolean;
7
+ /** 제어 컴포넌트로 쓸 때의 체크 상태. 넘기지 않으면 비제어 컴포넌트가 되고, 처음 상태는 `defaultChecked`로 정한다. */
7
8
  isChecked?: boolean;
8
9
  }
9
10
  declare const Checkbox: ({ className, id, onChange, disabled, isChecked, ...inputProps }: Props) => import("react").JSX.Element;
@@ -0,0 +1,35 @@
1
+ import { ComponentPropsWithRef, ReactNode } from 'react';
2
+ import { FieldSize } from '../ui/fieldStyles';
3
+ export interface ComboboxOption {
4
+ value: string;
5
+ label: string;
6
+ disabled?: boolean;
7
+ }
8
+ interface ComboboxProps extends Omit<ComponentPropsWithRef<"input">, "size" | "value" | "defaultValue" | "onChange" | "type" | "role" | "children"> {
9
+ /** 칸 위에 보이는 이름. `<label>`로 입력에 연결된다. */
10
+ label?: string;
11
+ /** 문자열 옵션은 값과 표시 이름이 같다. */
12
+ options: (string | ComboboxOption)[];
13
+ /** 고른 옵션의 값. 고르지 않았으면 `""`. */
14
+ value: string;
15
+ /** 옵션을 고르면 그 값으로, 고른 뒤 다시 입력하기 시작하면 `""`로 호출된다. */
16
+ onChange: (value: string) => void;
17
+ /** 입력한 검색어로 옵션을 거른다. 기본은 대소문자·공백을 무시한 부분 일치. */
18
+ filter?: (option: ComboboxOption, query: string) => boolean;
19
+ /** 맞는 옵션이 없을 때 목록에 보이는 문구. 없으면 목록을 닫는다. */
20
+ emptyText?: ReactNode;
21
+ /** 목록 열기 버튼의 접근 가능한 이름. 기본은 `label`. */
22
+ toggleLabel?: string;
23
+ /** `required`인데 입력한 글자가 옵션으로 골라지지 않았을 때 브라우저 검증 메시지. 다국어 페이지에서는 번역해 넘긴다. */
24
+ invalidSelectionText?: string;
25
+ errorText?: string;
26
+ assistiveText?: ReactNode;
27
+ size?: FieldSize;
28
+ }
29
+ /**
30
+ * 입력해서 검색하고 목록에서 하나를 고르는 입력(WAI-ARIA combobox, list autocomplete).
31
+ * 포커스는 입력에 남고 목록의 현재 항목은 `aria-activedescendant`로 알린다.
32
+ * 고른 값만 `value`가 되고, 입력한 글자는 고르기 전까지 값이 아니다.
33
+ */
34
+ declare const Combobox: ({ id, label, options, value, onChange, filter, emptyText, toggleLabel, invalidSelectionText, errorText, assistiveText, size, name, required, disabled, placeholder, className, autoComplete, "aria-describedby": ariaDescribedBy, onBlur, onKeyDown, onClick, ref, ...inputProps }: ComboboxProps) => import("react").JSX.Element;
35
+ export { Combobox };
@@ -0,0 +1,24 @@
1
+ import { ComponentPropsWithRef, ReactNode } from 'react';
2
+ import { FieldSize } from '../ui/fieldStyles';
3
+ export interface SelectOption {
4
+ value: string;
5
+ label: string;
6
+ disabled?: boolean;
7
+ }
8
+ interface SelectProps extends Omit<ComponentPropsWithRef<"select">, "size" | "value" | "defaultValue" | "onChange" | "children"> {
9
+ /** 칸 위에 보이는 이름. `<label>`로 `<select>`에 연결된다. */
10
+ label?: string;
11
+ /** 문자열 옵션은 값과 표시 이름이 같다. */
12
+ options: (string | SelectOption)[];
13
+ value?: string;
14
+ defaultValue?: string;
15
+ onChange?: (value: string) => void;
16
+ /** 값이 없을 때 보이는 첫 옵션(값 `""`). `required`이면 이 옵션으로는 제출되지 않는다. */
17
+ placeholder?: string;
18
+ errorText?: string;
19
+ assistiveText?: ReactNode;
20
+ size?: FieldSize;
21
+ }
22
+ /** 목록에서 하나를 고르는 입력. 브라우저 기본 `<select>`라 모바일 선택기·폼 검증·키보드 조작을 그대로 쓴다. */
23
+ declare const Select: ({ id, label, options, value, defaultValue, onChange, placeholder, errorText, assistiveText, size, required, disabled, className, "aria-describedby": ariaDescribedBy, ref, ...selectProps }: SelectProps) => import("react").JSX.Element;
24
+ export { Select };
@@ -0,0 +1,15 @@
1
+ import { FieldSize } from './fieldStyles';
2
+ import { ReactNode } from 'react';
3
+ export declare const FieldLabel: ({ id, htmlFor, size, required, children, }: {
4
+ id?: string;
5
+ htmlFor: string;
6
+ size: FieldSize;
7
+ required?: boolean;
8
+ children: ReactNode;
9
+ }) => import("react").JSX.Element;
10
+ export declare const FieldMessage: ({ id, size, errorText, assistiveText, }: {
11
+ id: string;
12
+ size: FieldSize;
13
+ errorText?: string;
14
+ assistiveText?: ReactNode;
15
+ }) => import("react").JSX.Element | null;
@@ -0,0 +1,14 @@
1
+ export declare const FIELD_RADIUS = "rounded-[4px]";
2
+ export declare const fieldContainerVariants: (props?: ({
3
+ size?: "small" | "large" | null | undefined;
4
+ hasError?: boolean | null | undefined;
5
+ disabled?: boolean | null | undefined;
6
+ } & import('class-variance-authority/types').ClassProp) | undefined) => string;
7
+ export declare const fieldMessageVariants: (props?: ({
8
+ size?: "small" | "large" | null | undefined;
9
+ hasError?: boolean | null | undefined;
10
+ } & import('class-variance-authority/types').ClassProp) | undefined) => string;
11
+ export type FieldSize = "large" | "small";
12
+ export declare const fieldLabelFontSize: (size: FieldSize) => "text-title-18-bold" | "text-title-14-bold";
13
+ export declare const fieldValueFontSize: (size: FieldSize) => "text-title-20-regular" | "text-body-16-regular";
14
+ export declare const joinIds: (...ids: (string | false | undefined)[]) => string | undefined;
@@ -0,0 +1,2 @@
1
+ import { Ref, RefCallback } from 'react';
2
+ export declare const mergeRefs: <T>(...refs: (Ref<T> | undefined)[]) => RefCallback<T>;
@@ -3,70 +3,36 @@ import { t as e } from "./TailwindUtil-DiNvXuNl.js";
3
3
  import { CTAButton as t } from "./cta-button.js";
4
4
  import { CancelFilledIcon as n } from "./icons/action/cancel-filled.js";
5
5
  import { ChevronLeftIcon as r } from "./icons/action/chevron-left.js";
6
- import { jsx as i, jsxs as a } from "react/jsx-runtime";
7
- import * as o from "react";
8
- import { cva as s } from "class-variance-authority";
6
+ import { i, n as a } from "./fieldStyles-DyZu__hI.js";
7
+ import { jsx as o, jsxs as s } from "react/jsx-runtime";
8
+ import * as c from "react";
9
+ import { cva as l } from "class-variance-authority";
9
10
  //#region src/components/text-field/TextField.tsx
10
- var c = s("flex items-center rounded-[4px] bg-surface-base-neutral-2 border transition-colors duration-200", {
11
- variants: {
12
- size: {
13
- large: "gap-2 p-[15px] min-h-[56px]",
14
- small: "gap-3 p-[11px] min-h-[48px]"
15
- },
16
- hasError: {
17
- true: "border-border-prominent-error",
18
- false: "border-transparent"
19
- },
20
- disabled: {
21
- true: "opacity-50",
22
- false: ""
23
- }
24
- },
25
- defaultVariants: {
26
- size: "large",
27
- disabled: !1,
28
- hasError: !1
29
- }
30
- }), l = s("", {
31
- variants: {
32
- size: {
33
- large: "text-body-16-regular",
34
- small: "text-caption-12-medium"
35
- },
36
- hasError: {
37
- true: "text-text-prominent-error",
38
- false: "text-text-base-neutral-2"
39
- }
40
- },
41
- defaultVariants: {
42
- size: "large",
43
- hasError: !1
44
- }
45
- }), u = o.forwardRef(({ onChange: t, subtitle: r, isRequired: s = !1, assistiveText: u, errorText: d, disabled: f = !1, multiLine: p = !1, className: m, rightElement: h, showCounter: g = !1, value: _, maxLength: v, size: y = "large", ...b }, x) => {
46
- let S = !!d, C = o.useRef(null);
47
- o.useImperativeHandle(x, () => C.current);
48
- let w = o.useCallback(() => {
11
+ var u = c.forwardRef(({ onChange: t, subtitle: r, isRequired: l = !1, assistiveText: u, errorText: d, disabled: f = !1, multiLine: p = !1, className: m, rightElement: h, showCounter: g = !1, value: _, maxLength: v, size: y = "large", ...b }, x) => {
12
+ let S = !!d, C = c.useRef(null);
13
+ c.useImperativeHandle(x, () => C.current);
14
+ let w = c.useCallback(() => {
49
15
  C.current?.focus();
50
16
  }, []), T = (typeof _ == "string" ? _ : "").length, E = b, D = b, O = y === "large" ? "text-title-20-regular" : "text-body-16-regular", k = y === "large" ? "text-title-18-bold" : "text-title-14-bold", A = y === "large" ? "text-body-16-regular" : "text-caption-12-medium";
51
- return /* @__PURE__ */ a("div", {
17
+ return /* @__PURE__ */ s("div", {
52
18
  className: e("flex flex-col gap-2", m),
53
19
  children: [
54
- r && /* @__PURE__ */ a("span", {
20
+ r && /* @__PURE__ */ s("span", {
55
21
  className: e(k, "text-text-base-neutral-5"),
56
- children: [r, s && /* @__PURE__ */ i("span", {
22
+ children: [r, l && /* @__PURE__ */ o("span", {
57
23
  className: "text-text-prominent-accent-1",
58
24
  children: "*"
59
25
  })]
60
26
  }),
61
- /* @__PURE__ */ a("div", {
27
+ /* @__PURE__ */ s("div", {
62
28
  onClick: f ? void 0 : w,
63
- className: e("group overflow-hidden", c({
29
+ className: e("group overflow-hidden", a({
64
30
  size: y,
65
31
  disabled: f,
66
32
  hasError: S
67
33
  }), !f && !S && "focus-within:border-border-base-neutral-4", p && "h-[120px] items-start"),
68
34
  children: [
69
- p ? /* @__PURE__ */ i("textarea", {
35
+ p ? /* @__PURE__ */ o("textarea", {
70
36
  ...D,
71
37
  onChange: t,
72
38
  ref: C,
@@ -74,7 +40,7 @@ var c = s("flex items-center rounded-[4px] bg-surface-base-neutral-2 border tran
74
40
  maxLength: v,
75
41
  disabled: f,
76
42
  className: e("text-text-base-neutral-5 placeholder:text-text-base-neutral-2 h-full min-h-[24px] min-w-0 flex-1 border-none bg-transparent py-0 outline-none", O, "resize-none overflow-y-auto")
77
- }) : /* @__PURE__ */ i("input", {
43
+ }) : /* @__PURE__ */ o("input", {
78
44
  ...E,
79
45
  onChange: t,
80
46
  ref: C,
@@ -83,11 +49,11 @@ var c = s("flex items-center rounded-[4px] bg-surface-base-neutral-2 border tran
83
49
  disabled: f,
84
50
  className: e("text-text-base-neutral-5 placeholder:text-text-base-neutral-2 min-h-[24px] min-w-0 flex-1 border-none bg-transparent py-0 outline-none", O, "overflow-hidden text-ellipsis whitespace-nowrap")
85
51
  }),
86
- h && /* @__PURE__ */ i("div", {
52
+ h && /* @__PURE__ */ o("div", {
87
53
  className: "shrink-0",
88
54
  children: h
89
55
  }),
90
- /* @__PURE__ */ i("button", {
56
+ /* @__PURE__ */ o("button", {
91
57
  type: "button",
92
58
  onClick: () => t && t({ target: { value: "" } }),
93
59
  tabIndex: -1,
@@ -96,20 +62,20 @@ var c = s("flex items-center rounded-[4px] bg-surface-base-neutral-2 border tran
96
62
  flex: S,
97
63
  "group-focus-within:flex": !f && T > 0
98
64
  }),
99
- children: /* @__PURE__ */ i(n, { className: "text-icon-base-neutral-3 h-6 w-6 shrink-0 cursor-pointer" })
65
+ children: /* @__PURE__ */ o(n, { className: "text-icon-base-neutral-3 h-6 w-6 shrink-0 cursor-pointer" })
100
66
  })
101
67
  ]
102
68
  }),
103
- (u || d || v && g) && /* @__PURE__ */ a("div", {
69
+ (u || d || v && g) && /* @__PURE__ */ s("div", {
104
70
  className: "flex items-center justify-between gap-2",
105
- children: [(u || d) && /* @__PURE__ */ i("span", {
106
- className: e(A, l({
71
+ children: [(u || d) && /* @__PURE__ */ o("span", {
72
+ className: e(A, i({
107
73
  size: y,
108
74
  hasError: S
109
75
  })),
110
76
  children: d || u
111
- }), v && g && /* @__PURE__ */ a("span", {
112
- className: e("ml-auto", A, l({
77
+ }), v && g && /* @__PURE__ */ s("span", {
78
+ className: e("ml-auto", A, i({
113
79
  size: y,
114
80
  hasError: S
115
81
  })),
@@ -124,7 +90,7 @@ var c = s("flex items-center rounded-[4px] bg-surface-base-neutral-2 border tran
124
90
  });
125
91
  });
126
92
  u.displayName = "TextFieldNormal";
127
- var d = s("flex items-center justify-between rounded-[4px] bg-surface-base-neutral-2 border transition-colors duration-200", {
93
+ var d = l("flex items-center justify-between rounded-[4px] bg-surface-base-neutral-2 border transition-colors duration-200", {
128
94
  variants: {
129
95
  size: {
130
96
  large: "gap-2 p-[15px] min-h-[56px]",
@@ -140,44 +106,44 @@ var d = s("flex items-center justify-between rounded-[4px] bg-surface-base-neutr
140
106
  }
141
107
  },
142
108
  defaultVariants: { size: "large" }
143
- }), f = ({ label: t, placeholder: n, value: o, isRequired: s = !1, disabled: c = !1, assistiveText: u, errorText: f, onPress: p, className: m, size: h = "large" }) => {
109
+ }), f = ({ label: t, placeholder: n, value: a, isRequired: c = !1, disabled: l = !1, assistiveText: u, errorText: f, onPress: p, className: m, size: h = "large" }) => {
144
110
  let g = !!f, _ = h === "large" ? "text-title-18-bold" : "text-title-14-bold", v = h === "large" ? "text-title-20-regular" : "text-body-16-regular";
145
- return /* @__PURE__ */ a("div", {
111
+ return /* @__PURE__ */ s("div", {
146
112
  className: e("flex w-full flex-col gap-2", m),
147
113
  children: [
148
- t && /* @__PURE__ */ a("div", {
114
+ t && /* @__PURE__ */ s("div", {
149
115
  className: "flex items-start",
150
- children: [/* @__PURE__ */ i("span", {
116
+ children: [/* @__PURE__ */ o("span", {
151
117
  className: e(_, "text-text-base-neutral-5"),
152
118
  children: t
153
- }), s && /* @__PURE__ */ i("span", {
119
+ }), c && /* @__PURE__ */ o("span", {
154
120
  className: e(_, "text-text-prominent-accent-1"),
155
121
  children: "*"
156
122
  })]
157
123
  }),
158
- /* @__PURE__ */ a("button", {
124
+ /* @__PURE__ */ s("button", {
159
125
  type: "button",
160
126
  className: d({
161
127
  size: h,
162
128
  hasError: g,
163
- disabled: c
129
+ disabled: l
164
130
  }),
165
131
  onClick: p,
166
- disabled: c,
167
- children: [/* @__PURE__ */ i("span", {
168
- className: e(v, "flex-1", o ? "text-text-base-neutral-5" : "text-text-base-neutral-2"),
132
+ disabled: l,
133
+ children: [/* @__PURE__ */ o("span", {
134
+ className: e(v, "flex-1", a ? "text-text-base-neutral-5" : "text-text-base-neutral-2"),
169
135
  style: {
170
136
  textAlign: "left",
171
137
  overflow: "hidden",
172
138
  textOverflow: "ellipsis"
173
139
  },
174
- children: o || n
175
- }), /* @__PURE__ */ i(r, { className: "text-icon-base-neutral-3 rotate-270" })]
140
+ children: a || n
141
+ }), /* @__PURE__ */ o(r, { className: "text-icon-base-neutral-3 rotate-270" })]
176
142
  }),
177
- (u || f) && /* @__PURE__ */ i("div", {
143
+ (u || f) && /* @__PURE__ */ o("div", {
178
144
  className: "flex items-start gap-2",
179
- children: /* @__PURE__ */ i("span", {
180
- className: l({
145
+ children: /* @__PURE__ */ o("span", {
146
+ className: i({
181
147
  size: h,
182
148
  hasError: g
183
149
  }),
@@ -186,33 +152,33 @@ var d = s("flex items-center justify-between rounded-[4px] bg-surface-base-neutr
186
152
  })
187
153
  ]
188
154
  });
189
- }, p = o.forwardRef(({ onChange: r, subtitle: s, isRequired: u = !1, assistiveText: d, errorText: f, disabled: p = !1, multiLine: m = !1, className: h, showCounter: g = !1, value: _, maxLength: v, size: y = "large", fieldRightElement: b, buttonText: x, onButtonClick: S, buttonDisabled: C = !1, ...w }, T) => {
190
- let E = !!f, D = o.useRef(null);
191
- o.useImperativeHandle(T, () => D.current);
192
- let O = o.useCallback(() => {
155
+ }, p = c.forwardRef(({ onChange: r, subtitle: l, isRequired: u = !1, assistiveText: d, errorText: f, disabled: p = !1, multiLine: m = !1, className: h, showCounter: g = !1, value: _, maxLength: v, size: y = "large", fieldRightElement: b, buttonText: x, onButtonClick: S, buttonDisabled: C = !1, ...w }, T) => {
156
+ let E = !!f, D = c.useRef(null);
157
+ c.useImperativeHandle(T, () => D.current);
158
+ let O = c.useCallback(() => {
193
159
  D.current?.focus();
194
160
  }, []), k = (typeof _ == "string" ? _ : "").length, A = w, j = w, M = y === "large" ? "text-title-20-regular" : "text-body-16-regular", N = y === "large" ? "text-title-18-bold" : "text-title-14-bold", P = y === "large" ? "text-body-16-regular" : "text-caption-12-medium";
195
- return /* @__PURE__ */ a("div", {
161
+ return /* @__PURE__ */ s("div", {
196
162
  className: e("flex flex-col gap-2", h),
197
163
  children: [
198
- s && /* @__PURE__ */ a("span", {
164
+ l && /* @__PURE__ */ s("span", {
199
165
  className: e(N, "text-text-base-neutral-5"),
200
- children: [s, u && /* @__PURE__ */ i("span", {
166
+ children: [l, u && /* @__PURE__ */ o("span", {
201
167
  className: "text-text-prominent-accent-1",
202
168
  children: "*"
203
169
  })]
204
170
  }),
205
- /* @__PURE__ */ a("div", {
171
+ /* @__PURE__ */ s("div", {
206
172
  className: "flex items-center gap-2",
207
- children: [/* @__PURE__ */ a("div", {
173
+ children: [/* @__PURE__ */ s("div", {
208
174
  onClick: p ? void 0 : O,
209
- className: e("group flex-1 overflow-hidden", c({
175
+ className: e("group flex-1 overflow-hidden", a({
210
176
  size: y,
211
177
  disabled: p,
212
178
  hasError: E
213
179
  }), !p && !E && "focus-within:border-border-base-neutral-4", m && "h-[120px] items-start"),
214
180
  children: [
215
- m ? /* @__PURE__ */ i("textarea", {
181
+ m ? /* @__PURE__ */ o("textarea", {
216
182
  ...j,
217
183
  onChange: r,
218
184
  ref: D,
@@ -220,7 +186,7 @@ var d = s("flex items-center justify-between rounded-[4px] bg-surface-base-neutr
220
186
  maxLength: v,
221
187
  disabled: p,
222
188
  className: e("text-text-base-neutral-5 placeholder:text-text-base-neutral-2 min-h-[24px] min-w-0 flex-1 border-none bg-transparent py-0 outline-none", M, "resize-none overflow-y-auto")
223
- }) : /* @__PURE__ */ i("input", {
189
+ }) : /* @__PURE__ */ o("input", {
224
190
  ...A,
225
191
  onChange: r,
226
192
  ref: D,
@@ -229,11 +195,11 @@ var d = s("flex items-center justify-between rounded-[4px] bg-surface-base-neutr
229
195
  disabled: p,
230
196
  className: e("text-text-base-neutral-5 placeholder:text-text-base-neutral-2 min-h-[24px] min-w-0 flex-1 border-none bg-transparent py-0 outline-none", M, "overflow-hidden text-ellipsis whitespace-nowrap")
231
197
  }),
232
- b && /* @__PURE__ */ i("div", {
198
+ b && /* @__PURE__ */ o("div", {
233
199
  className: "shrink-0",
234
200
  children: b
235
201
  }),
236
- /* @__PURE__ */ i("button", {
202
+ /* @__PURE__ */ o("button", {
237
203
  type: "button",
238
204
  onClick: () => r && r({ target: { value: "" } }),
239
205
  tabIndex: -1,
@@ -242,12 +208,12 @@ var d = s("flex items-center justify-between rounded-[4px] bg-surface-base-neutr
242
208
  flex: E,
243
209
  "group-focus-within:flex": !p && k > 0
244
210
  }),
245
- children: /* @__PURE__ */ i(n, { className: "text-icon-base-neutral-3 h-6 w-6 shrink-0 cursor-pointer" })
211
+ children: /* @__PURE__ */ o(n, { className: "text-icon-base-neutral-3 h-6 w-6 shrink-0 cursor-pointer" })
246
212
  })
247
213
  ]
248
- }), /* @__PURE__ */ i("div", {
214
+ }), /* @__PURE__ */ o("div", {
249
215
  className: "shrink-0",
250
- children: /* @__PURE__ */ i(t, {
216
+ children: /* @__PURE__ */ o(t, {
251
217
  label: x,
252
218
  onClick: S,
253
219
  size: "medium",
@@ -256,16 +222,16 @@ var d = s("flex items-center justify-between rounded-[4px] bg-surface-base-neutr
256
222
  })
257
223
  })]
258
224
  }),
259
- (d || f || v && g) && /* @__PURE__ */ a("div", {
225
+ (d || f || v && g) && /* @__PURE__ */ s("div", {
260
226
  className: "flex items-center justify-between gap-2",
261
- children: [(d || f) && /* @__PURE__ */ i("span", {
262
- className: e(P, l({
227
+ children: [(d || f) && /* @__PURE__ */ o("span", {
228
+ className: e(P, i({
263
229
  size: y,
264
230
  hasError: E
265
231
  })),
266
232
  children: f || d
267
- }), v && g && /* @__PURE__ */ a("span", {
268
- className: e("ml-auto", P, l({
233
+ }), v && g && /* @__PURE__ */ s("span", {
234
+ className: e("ml-auto", P, i({
269
235
  size: y,
270
236
  hasError: E
271
237
  })),
@@ -1 +1 @@
1
- {"version":3,"file":"text-field.js","names":[],"sources":["../src/components/text-field/TextField.tsx"],"sourcesContent":["\"use client\";\n\nimport { cva, VariantProps } from \"class-variance-authority\";\nimport * as React from \"react\";\n\nimport { CTAButton } from \"../cta-button/CTAButton\";\nimport { CancelFilledIcon } from \"../icons/action/CancelFilledIcon\";\nimport { ChevronLeftIcon } from \"../icons/action/ChevronLeftIcon\";\n\nimport { cn } from \"@/utils/TailwindUtil\";\n\n// TextField Normal 컴포넌트 스타일 정의\nconst textFieldNormalContainerVariants = cva(\n \"flex items-center rounded-[4px] 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\nconst textFieldAssistiveAreaVariants = 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\ninterface TextFieldNormalProps extends Omit<React.InputHTMLAttributes<HTMLInputElement | HTMLTextAreaElement>, \"size\"> {\n subtitle?: string;\n isRequired?: boolean;\n assistiveText?: string | React.ReactNode;\n errorText?: string;\n disabled?: boolean;\n multiLine?: boolean;\n className?: string;\n rightElement?: React.ReactNode;\n showCounter?: boolean;\n size?: \"large\" | \"small\";\n}\n\nconst TextFieldNormal = React.forwardRef<HTMLInputElement | HTMLTextAreaElement, TextFieldNormalProps>(\n (\n {\n onChange,\n subtitle,\n isRequired = false,\n assistiveText,\n errorText,\n disabled = false,\n multiLine = false,\n className,\n rightElement,\n showCounter = false,\n value,\n maxLength,\n size = \"large\",\n ...inputProps\n },\n ref,\n ) => {\n const hasError = !!errorText;\n const inputRef = React.useRef<HTMLInputElement | HTMLTextAreaElement>(null);\n\n React.useImperativeHandle(ref, () => inputRef.current as HTMLInputElement | HTMLTextAreaElement);\n\n const handleContainerClick = React.useCallback(() => {\n inputRef.current?.focus();\n }, []);\n\n const stringValue = typeof value === \"string\" ? value : \"\";\n const currentLength = stringValue.length;\n\n const inputPropsForInput = inputProps as React.ComponentPropsWithoutRef<\"input\">;\n const inputPropsForTextarea = inputProps as React.ComponentPropsWithoutRef<\"textarea\">;\n\n // 사이즈별 폰트 크기\n const inputFontSize = size === \"large\" ? \"text-title-20-regular\" : \"text-body-16-regular\";\n const subtitleFontSize = size === \"large\" ? \"text-title-18-bold\" : \"text-title-14-bold\";\n const assistiveFontSize = size === \"large\" ? \"text-body-16-regular\" : \"text-caption-12-medium\";\n\n return (\n <div className={cn(\"flex flex-col gap-2\", className)}>\n {subtitle && (\n <span className={cn(subtitleFontSize, \"text-text-base-neutral-5\")}>\n {subtitle}\n {isRequired && <span className=\"text-text-prominent-accent-1\">*</span>}\n </span>\n )}\n\n <div\n onClick={disabled ? undefined : handleContainerClick}\n className={cn(\n \"group overflow-hidden\",\n textFieldNormalContainerVariants({ size, disabled, hasError }),\n !disabled && !hasError && \"focus-within:border-border-base-neutral-4\",\n multiLine && \"h-[120px] items-start\",\n )}\n >\n {multiLine ? (\n <textarea\n {...inputPropsForTextarea}\n onChange={onChange}\n ref={inputRef as React.Ref<HTMLTextAreaElement>}\n value={value}\n maxLength={maxLength}\n disabled={disabled}\n className={cn(\n \"text-text-base-neutral-5 placeholder:text-text-base-neutral-2 h-full min-h-[24px] min-w-0 flex-1 border-none bg-transparent py-0 outline-none\",\n inputFontSize,\n \"resize-none overflow-y-auto\",\n )}\n />\n ) : (\n <input\n {...inputPropsForInput}\n onChange={onChange}\n ref={inputRef as React.Ref<HTMLInputElement>}\n value={value}\n maxLength={maxLength}\n disabled={disabled}\n className={cn(\n \"text-text-base-neutral-5 placeholder:text-text-base-neutral-2 min-h-[24px] min-w-0 flex-1 border-none bg-transparent py-0 outline-none\",\n inputFontSize,\n \"overflow-hidden text-ellipsis whitespace-nowrap\",\n )}\n />\n )}\n\n {rightElement && <div className=\"shrink-0\">{rightElement}</div>}\n\n <button\n type=\"button\"\n onClick={() =>\n onChange &&\n onChange({ target: { value: \"\" } } as React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>)\n }\n tabIndex={-1}\n disabled={disabled}\n className={cn(\"hidden shrink-0\", {\n flex: hasError,\n \"group-focus-within:flex\": !disabled && currentLength > 0,\n })}\n >\n <CancelFilledIcon className=\"text-icon-base-neutral-3 h-6 w-6 shrink-0 cursor-pointer\" />\n </button>\n </div>\n\n {(assistiveText || errorText || (maxLength && showCounter)) && (\n <div className=\"flex items-center justify-between gap-2\">\n {(assistiveText || errorText) && (\n <span className={cn(assistiveFontSize, textFieldAssistiveAreaVariants({ size, hasError }))}>\n {errorText || assistiveText}\n </span>\n )}\n {maxLength && showCounter && (\n <span className={cn(\"ml-auto\", assistiveFontSize, textFieldAssistiveAreaVariants({ size, hasError }))}>\n {currentLength}/{maxLength}\n </span>\n )}\n </div>\n )}\n </div>\n );\n },\n);\nTextFieldNormal.displayName = \"TextFieldNormal\";\n\n// TextField Select 컴포넌트\nconst textFieldSelectVariants = cva(\n \"flex items-center justify-between rounded-[4px] 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 },\n },\n);\n\ninterface TextFieldSelectProps extends VariantProps<typeof textFieldSelectVariants> {\n label?: string;\n placeholder: string;\n value?: string;\n isRequired?: boolean;\n disabled?: boolean;\n assistiveText?: string | React.ReactNode;\n errorText?: string;\n onPress?: () => void;\n className?: string;\n size?: \"large\" | \"small\";\n}\n\nconst TextFieldSelect = ({\n label,\n placeholder,\n value,\n isRequired = false,\n disabled = false,\n assistiveText,\n errorText,\n onPress,\n className,\n size = \"large\",\n}: TextFieldSelectProps) => {\n const hasError = !!errorText;\n\n // 사이즈별 폰트 크기\n const labelFontSize = size === \"large\" ? \"text-title-18-bold\" : \"text-title-14-bold\";\n const valueFontSize = size === \"large\" ? \"text-title-20-regular\" : \"text-body-16-regular\";\n\n return (\n <div className={cn(\"flex w-full flex-col gap-2\", className)}>\n {/* Label with optional asterisk */}\n {label && (\n <div className=\"flex items-start\">\n <span className={cn(labelFontSize, \"text-text-base-neutral-5\")}>{label}</span>\n {isRequired && <span className={cn(labelFontSize, \"text-text-prominent-accent-1\")}>*</span>}\n </div>\n )}\n {/* Select Field */}\n <button\n type=\"button\"\n className={textFieldSelectVariants({ size, hasError, disabled })}\n onClick={onPress}\n disabled={disabled}\n >\n <span\n className={cn(valueFontSize, \"flex-1\", value ? \"text-text-base-neutral-5\" : \"text-text-base-neutral-2\")}\n style={{ textAlign: \"left\", overflow: \"hidden\", textOverflow: \"ellipsis\" }}\n >\n {value || placeholder}\n </span>\n <ChevronLeftIcon className=\"text-icon-base-neutral-3 rotate-270\" />\n </button>\n {/* Assistive/Error Text */}\n {(assistiveText || errorText) && (\n <div className=\"flex items-start gap-2\">\n <span className={textFieldAssistiveAreaVariants({ size, hasError })}>{errorText || assistiveText}</span>\n </div>\n )}\n </div>\n );\n};\n\n// TextField With Button 컴포넌트\ninterface TextFieldWithButtonProps\n extends Omit<React.InputHTMLAttributes<HTMLInputElement | HTMLTextAreaElement>, \"size\"> {\n subtitle?: string;\n isRequired?: boolean;\n assistiveText?: string | React.ReactNode;\n errorText?: string;\n disabled?: boolean;\n multiLine?: boolean;\n className?: string;\n showCounter?: boolean;\n size?: \"large\" | \"small\";\n fieldRightElement?: React.ReactNode;\n buttonText: string;\n onButtonClick: () => void;\n buttonDisabled?: boolean;\n}\n\nconst TextFieldWithButton = React.forwardRef<HTMLInputElement | HTMLTextAreaElement, TextFieldWithButtonProps>(\n (\n {\n onChange,\n subtitle,\n isRequired = false,\n assistiveText,\n errorText,\n disabled = false,\n multiLine = false,\n className,\n showCounter = false,\n value,\n maxLength,\n size = \"large\",\n fieldRightElement,\n buttonText,\n onButtonClick,\n buttonDisabled = false,\n ...inputProps\n },\n ref,\n ) => {\n const hasError = !!errorText;\n const inputRef = React.useRef<HTMLInputElement | HTMLTextAreaElement>(null);\n\n React.useImperativeHandle(ref, () => inputRef.current as HTMLInputElement | HTMLTextAreaElement);\n\n const handleContainerClick = React.useCallback(() => {\n inputRef.current?.focus();\n }, []);\n\n const stringValue = typeof value === \"string\" ? value : \"\";\n const currentLength = stringValue.length;\n\n const inputPropsForInput = inputProps as React.ComponentPropsWithoutRef<\"input\">;\n const inputPropsForTextarea = inputProps as React.ComponentPropsWithoutRef<\"textarea\">;\n\n // 사이즈별 폰트 크기\n const inputFontSize = size === \"large\" ? \"text-title-20-regular\" : \"text-body-16-regular\";\n const subtitleFontSize = size === \"large\" ? \"text-title-18-bold\" : \"text-title-14-bold\";\n const assistiveFontSize = size === \"large\" ? \"text-body-16-regular\" : \"text-caption-12-medium\";\n\n return (\n <div className={cn(\"flex flex-col gap-2\", className)}>\n {subtitle && (\n <span className={cn(subtitleFontSize, \"text-text-base-neutral-5\")}>\n {subtitle}\n {isRequired && <span className=\"text-text-prominent-accent-1\">*</span>}\n </span>\n )}\n\n <div className=\"flex items-center gap-2\">\n <div\n onClick={disabled ? undefined : handleContainerClick}\n className={cn(\n \"group flex-1 overflow-hidden\",\n textFieldNormalContainerVariants({ size, disabled, hasError }),\n !disabled && !hasError && \"focus-within:border-border-base-neutral-4\",\n multiLine && \"h-[120px] items-start\",\n )}\n >\n {multiLine ? (\n <textarea\n {...inputPropsForTextarea}\n onChange={onChange}\n ref={inputRef as React.Ref<HTMLTextAreaElement>}\n value={value}\n maxLength={maxLength}\n disabled={disabled}\n className={cn(\n \"text-text-base-neutral-5 placeholder:text-text-base-neutral-2 min-h-[24px] min-w-0 flex-1 border-none bg-transparent py-0 outline-none\",\n inputFontSize,\n \"resize-none overflow-y-auto\",\n )}\n />\n ) : (\n <input\n {...inputPropsForInput}\n onChange={onChange}\n ref={inputRef as React.Ref<HTMLInputElement>}\n value={value}\n maxLength={maxLength}\n disabled={disabled}\n className={cn(\n \"text-text-base-neutral-5 placeholder:text-text-base-neutral-2 min-h-[24px] min-w-0 flex-1 border-none bg-transparent py-0 outline-none\",\n inputFontSize,\n \"overflow-hidden text-ellipsis whitespace-nowrap\",\n )}\n />\n )}\n\n {fieldRightElement && <div className=\"shrink-0\">{fieldRightElement}</div>}\n\n <button\n type=\"button\"\n onClick={() =>\n onChange &&\n onChange({ target: { value: \"\" } } as React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>)\n }\n tabIndex={-1}\n disabled={disabled}\n className={cn(\"hidden shrink-0\", {\n flex: hasError,\n \"group-focus-within:flex\": !disabled && currentLength > 0,\n })}\n >\n <CancelFilledIcon className=\"text-icon-base-neutral-3 h-6 w-6 shrink-0 cursor-pointer\" />\n </button>\n </div>\n\n <div className=\"shrink-0\">\n <CTAButton\n label={buttonText}\n onClick={onButtonClick}\n size=\"medium\"\n hierarchy=\"tertiary\"\n disabled={buttonDisabled || disabled}\n />\n </div>\n </div>\n\n {(assistiveText || errorText || (maxLength && showCounter)) && (\n <div className=\"flex items-center justify-between gap-2\">\n {(assistiveText || errorText) && (\n <span className={cn(assistiveFontSize, textFieldAssistiveAreaVariants({ size, hasError }))}>\n {errorText || assistiveText}\n </span>\n )}\n {maxLength && showCounter && (\n <span className={cn(\"ml-auto\", assistiveFontSize, textFieldAssistiveAreaVariants({ size, hasError }))}>\n {currentLength}/{maxLength}\n </span>\n )}\n </div>\n )}\n </div>\n );\n },\n);\nTextFieldWithButton.displayName = \"TextFieldWithButton\";\n\nexport { TextFieldNormal, TextFieldSelect, TextFieldWithButton };\n"],"mappings":"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"}
1
+ {"version":3,"file":"text-field.js","names":[],"sources":["../src/components/text-field/TextField.tsx"],"sourcesContent":["\"use client\";\n\nimport { cva, VariantProps } from \"class-variance-authority\";\nimport * as React from \"react\";\n\nimport { CTAButton } from \"../cta-button/CTAButton\";\nimport { CancelFilledIcon } from \"../icons/action/CancelFilledIcon\";\nimport { ChevronLeftIcon } from \"../icons/action/ChevronLeftIcon\";\n\nimport {\n fieldContainerVariants as textFieldNormalContainerVariants,\n fieldMessageVariants as textFieldAssistiveAreaVariants,\n} from \"@/components/ui/fieldStyles\";\nimport { cn } from \"@/utils/TailwindUtil\";\n\ninterface TextFieldNormalProps extends Omit<React.InputHTMLAttributes<HTMLInputElement | HTMLTextAreaElement>, \"size\"> {\n subtitle?: string;\n isRequired?: boolean;\n assistiveText?: string | React.ReactNode;\n errorText?: string;\n disabled?: boolean;\n multiLine?: boolean;\n className?: string;\n rightElement?: React.ReactNode;\n showCounter?: boolean;\n size?: \"large\" | \"small\";\n}\n\nconst TextFieldNormal = React.forwardRef<HTMLInputElement | HTMLTextAreaElement, TextFieldNormalProps>(\n (\n {\n onChange,\n subtitle,\n isRequired = false,\n assistiveText,\n errorText,\n disabled = false,\n multiLine = false,\n className,\n rightElement,\n showCounter = false,\n value,\n maxLength,\n size = \"large\",\n ...inputProps\n },\n ref,\n ) => {\n const hasError = !!errorText;\n const inputRef = React.useRef<HTMLInputElement | HTMLTextAreaElement>(null);\n\n React.useImperativeHandle(ref, () => inputRef.current as HTMLInputElement | HTMLTextAreaElement);\n\n const handleContainerClick = React.useCallback(() => {\n inputRef.current?.focus();\n }, []);\n\n const stringValue = typeof value === \"string\" ? value : \"\";\n const currentLength = stringValue.length;\n\n const inputPropsForInput = inputProps as React.ComponentPropsWithoutRef<\"input\">;\n const inputPropsForTextarea = inputProps as React.ComponentPropsWithoutRef<\"textarea\">;\n\n // 사이즈별 폰트 크기\n const inputFontSize = size === \"large\" ? \"text-title-20-regular\" : \"text-body-16-regular\";\n const subtitleFontSize = size === \"large\" ? \"text-title-18-bold\" : \"text-title-14-bold\";\n const assistiveFontSize = size === \"large\" ? \"text-body-16-regular\" : \"text-caption-12-medium\";\n\n return (\n <div className={cn(\"flex flex-col gap-2\", className)}>\n {subtitle && (\n <span className={cn(subtitleFontSize, \"text-text-base-neutral-5\")}>\n {subtitle}\n {isRequired && <span className=\"text-text-prominent-accent-1\">*</span>}\n </span>\n )}\n\n <div\n onClick={disabled ? undefined : handleContainerClick}\n className={cn(\n \"group overflow-hidden\",\n textFieldNormalContainerVariants({ size, disabled, hasError }),\n !disabled && !hasError && \"focus-within:border-border-base-neutral-4\",\n multiLine && \"h-[120px] items-start\",\n )}\n >\n {multiLine ? (\n <textarea\n {...inputPropsForTextarea}\n onChange={onChange}\n ref={inputRef as React.Ref<HTMLTextAreaElement>}\n value={value}\n maxLength={maxLength}\n disabled={disabled}\n className={cn(\n \"text-text-base-neutral-5 placeholder:text-text-base-neutral-2 h-full min-h-[24px] min-w-0 flex-1 border-none bg-transparent py-0 outline-none\",\n inputFontSize,\n \"resize-none overflow-y-auto\",\n )}\n />\n ) : (\n <input\n {...inputPropsForInput}\n onChange={onChange}\n ref={inputRef as React.Ref<HTMLInputElement>}\n value={value}\n maxLength={maxLength}\n disabled={disabled}\n className={cn(\n \"text-text-base-neutral-5 placeholder:text-text-base-neutral-2 min-h-[24px] min-w-0 flex-1 border-none bg-transparent py-0 outline-none\",\n inputFontSize,\n \"overflow-hidden text-ellipsis whitespace-nowrap\",\n )}\n />\n )}\n\n {rightElement && <div className=\"shrink-0\">{rightElement}</div>}\n\n <button\n type=\"button\"\n onClick={() =>\n onChange &&\n onChange({ target: { value: \"\" } } as React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>)\n }\n tabIndex={-1}\n disabled={disabled}\n className={cn(\"hidden shrink-0\", {\n flex: hasError,\n \"group-focus-within:flex\": !disabled && currentLength > 0,\n })}\n >\n <CancelFilledIcon className=\"text-icon-base-neutral-3 h-6 w-6 shrink-0 cursor-pointer\" />\n </button>\n </div>\n\n {(assistiveText || errorText || (maxLength && showCounter)) && (\n <div className=\"flex items-center justify-between gap-2\">\n {(assistiveText || errorText) && (\n <span className={cn(assistiveFontSize, textFieldAssistiveAreaVariants({ size, hasError }))}>\n {errorText || assistiveText}\n </span>\n )}\n {maxLength && showCounter && (\n <span className={cn(\"ml-auto\", assistiveFontSize, textFieldAssistiveAreaVariants({ size, hasError }))}>\n {currentLength}/{maxLength}\n </span>\n )}\n </div>\n )}\n </div>\n );\n },\n);\nTextFieldNormal.displayName = \"TextFieldNormal\";\n\n// TextField Select 컴포넌트\nconst textFieldSelectVariants = cva(\n \"flex items-center justify-between rounded-[4px] 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 },\n },\n);\n\ninterface TextFieldSelectProps extends VariantProps<typeof textFieldSelectVariants> {\n label?: string;\n placeholder: string;\n value?: string;\n isRequired?: boolean;\n disabled?: boolean;\n assistiveText?: string | React.ReactNode;\n errorText?: string;\n onPress?: () => void;\n className?: string;\n size?: \"large\" | \"small\";\n}\n\nconst TextFieldSelect = ({\n label,\n placeholder,\n value,\n isRequired = false,\n disabled = false,\n assistiveText,\n errorText,\n onPress,\n className,\n size = \"large\",\n}: TextFieldSelectProps) => {\n const hasError = !!errorText;\n\n // 사이즈별 폰트 크기\n const labelFontSize = size === \"large\" ? \"text-title-18-bold\" : \"text-title-14-bold\";\n const valueFontSize = size === \"large\" ? \"text-title-20-regular\" : \"text-body-16-regular\";\n\n return (\n <div className={cn(\"flex w-full flex-col gap-2\", className)}>\n {/* Label with optional asterisk */}\n {label && (\n <div className=\"flex items-start\">\n <span className={cn(labelFontSize, \"text-text-base-neutral-5\")}>{label}</span>\n {isRequired && <span className={cn(labelFontSize, \"text-text-prominent-accent-1\")}>*</span>}\n </div>\n )}\n {/* Select Field */}\n <button\n type=\"button\"\n className={textFieldSelectVariants({ size, hasError, disabled })}\n onClick={onPress}\n disabled={disabled}\n >\n <span\n className={cn(valueFontSize, \"flex-1\", value ? \"text-text-base-neutral-5\" : \"text-text-base-neutral-2\")}\n style={{ textAlign: \"left\", overflow: \"hidden\", textOverflow: \"ellipsis\" }}\n >\n {value || placeholder}\n </span>\n <ChevronLeftIcon className=\"text-icon-base-neutral-3 rotate-270\" />\n </button>\n {/* Assistive/Error Text */}\n {(assistiveText || errorText) && (\n <div className=\"flex items-start gap-2\">\n <span className={textFieldAssistiveAreaVariants({ size, hasError })}>{errorText || assistiveText}</span>\n </div>\n )}\n </div>\n );\n};\n\n// TextField With Button 컴포넌트\ninterface TextFieldWithButtonProps\n extends Omit<React.InputHTMLAttributes<HTMLInputElement | HTMLTextAreaElement>, \"size\"> {\n subtitle?: string;\n isRequired?: boolean;\n assistiveText?: string | React.ReactNode;\n errorText?: string;\n disabled?: boolean;\n multiLine?: boolean;\n className?: string;\n showCounter?: boolean;\n size?: \"large\" | \"small\";\n fieldRightElement?: React.ReactNode;\n buttonText: string;\n onButtonClick: () => void;\n buttonDisabled?: boolean;\n}\n\nconst TextFieldWithButton = React.forwardRef<HTMLInputElement | HTMLTextAreaElement, TextFieldWithButtonProps>(\n (\n {\n onChange,\n subtitle,\n isRequired = false,\n assistiveText,\n errorText,\n disabled = false,\n multiLine = false,\n className,\n showCounter = false,\n value,\n maxLength,\n size = \"large\",\n fieldRightElement,\n buttonText,\n onButtonClick,\n buttonDisabled = false,\n ...inputProps\n },\n ref,\n ) => {\n const hasError = !!errorText;\n const inputRef = React.useRef<HTMLInputElement | HTMLTextAreaElement>(null);\n\n React.useImperativeHandle(ref, () => inputRef.current as HTMLInputElement | HTMLTextAreaElement);\n\n const handleContainerClick = React.useCallback(() => {\n inputRef.current?.focus();\n }, []);\n\n const stringValue = typeof value === \"string\" ? value : \"\";\n const currentLength = stringValue.length;\n\n const inputPropsForInput = inputProps as React.ComponentPropsWithoutRef<\"input\">;\n const inputPropsForTextarea = inputProps as React.ComponentPropsWithoutRef<\"textarea\">;\n\n // 사이즈별 폰트 크기\n const inputFontSize = size === \"large\" ? \"text-title-20-regular\" : \"text-body-16-regular\";\n const subtitleFontSize = size === \"large\" ? \"text-title-18-bold\" : \"text-title-14-bold\";\n const assistiveFontSize = size === \"large\" ? \"text-body-16-regular\" : \"text-caption-12-medium\";\n\n return (\n <div className={cn(\"flex flex-col gap-2\", className)}>\n {subtitle && (\n <span className={cn(subtitleFontSize, \"text-text-base-neutral-5\")}>\n {subtitle}\n {isRequired && <span className=\"text-text-prominent-accent-1\">*</span>}\n </span>\n )}\n\n <div className=\"flex items-center gap-2\">\n <div\n onClick={disabled ? undefined : handleContainerClick}\n className={cn(\n \"group flex-1 overflow-hidden\",\n textFieldNormalContainerVariants({ size, disabled, hasError }),\n !disabled && !hasError && \"focus-within:border-border-base-neutral-4\",\n multiLine && \"h-[120px] items-start\",\n )}\n >\n {multiLine ? (\n <textarea\n {...inputPropsForTextarea}\n onChange={onChange}\n ref={inputRef as React.Ref<HTMLTextAreaElement>}\n value={value}\n maxLength={maxLength}\n disabled={disabled}\n className={cn(\n \"text-text-base-neutral-5 placeholder:text-text-base-neutral-2 min-h-[24px] min-w-0 flex-1 border-none bg-transparent py-0 outline-none\",\n inputFontSize,\n \"resize-none overflow-y-auto\",\n )}\n />\n ) : (\n <input\n {...inputPropsForInput}\n onChange={onChange}\n ref={inputRef as React.Ref<HTMLInputElement>}\n value={value}\n maxLength={maxLength}\n disabled={disabled}\n className={cn(\n \"text-text-base-neutral-5 placeholder:text-text-base-neutral-2 min-h-[24px] min-w-0 flex-1 border-none bg-transparent py-0 outline-none\",\n inputFontSize,\n \"overflow-hidden text-ellipsis whitespace-nowrap\",\n )}\n />\n )}\n\n {fieldRightElement && <div className=\"shrink-0\">{fieldRightElement}</div>}\n\n <button\n type=\"button\"\n onClick={() =>\n onChange &&\n onChange({ target: { value: \"\" } } as React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>)\n }\n tabIndex={-1}\n disabled={disabled}\n className={cn(\"hidden shrink-0\", {\n flex: hasError,\n \"group-focus-within:flex\": !disabled && currentLength > 0,\n })}\n >\n <CancelFilledIcon className=\"text-icon-base-neutral-3 h-6 w-6 shrink-0 cursor-pointer\" />\n </button>\n </div>\n\n <div className=\"shrink-0\">\n <CTAButton\n label={buttonText}\n onClick={onButtonClick}\n size=\"medium\"\n hierarchy=\"tertiary\"\n disabled={buttonDisabled || disabled}\n />\n </div>\n </div>\n\n {(assistiveText || errorText || (maxLength && showCounter)) && (\n <div className=\"flex items-center justify-between gap-2\">\n {(assistiveText || errorText) && (\n <span className={cn(assistiveFontSize, textFieldAssistiveAreaVariants({ size, hasError }))}>\n {errorText || assistiveText}\n </span>\n )}\n {maxLength && showCounter && (\n <span className={cn(\"ml-auto\", assistiveFontSize, textFieldAssistiveAreaVariants({ size, hasError }))}>\n {currentLength}/{maxLength}\n </span>\n )}\n </div>\n )}\n </div>\n );\n },\n);\nTextFieldWithButton.displayName = \"TextFieldWithButton\";\n\nexport { TextFieldNormal, TextFieldSelect, TextFieldWithButton };\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@petnow/ui",
3
- "version": "5.2.0",
3
+ "version": "5.3.0",
4
4
  "type": "module",
5
5
  "packageManager": "pnpm@11.1.3",
6
6
  "scripts": {
@@ -117,6 +117,14 @@
117
117
  "types": "./dist/src/components/text-field/TextField.d.ts",
118
118
  "import": "./dist/text-field.js"
119
119
  },
120
+ "./select": {
121
+ "types": "./dist/src/components/select/Select.d.ts",
122
+ "import": "./dist/select.js"
123
+ },
124
+ "./combobox": {
125
+ "types": "./dist/src/components/combobox/Combobox.d.ts",
126
+ "import": "./dist/combobox.js"
127
+ },
120
128
  "./icons/action/cancel-filled": {
121
129
  "types": "./dist/src/components/icons/action/CancelFilledIcon.d.ts",
122
130
  "import": "./dist/icons/action/cancel-filled.js"