@pylerai/design-system 2.3.1-rc.1 → 2.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -1
- package/dist/components/ui/badge.d.ts +3 -0
- package/dist/components/ui/badge.js +28 -20
- package/dist/components/ui/button.d.ts +1 -1
- package/dist/components/ui/button.js +38 -25
- package/dist/components/ui/dropdown.js +89 -86
- package/dist/components/ui/input.js +30 -30
- package/dist/components/ui/label.d.ts +1 -1
- package/dist/components/ui/label.js +11 -10
- package/dist/components/ui/media-slider.d.ts +20 -0
- package/dist/components/ui/media-slider.js +90 -0
- package/dist/components/ui/progress-bar.d.ts +21 -0
- package/dist/components/ui/progress-bar.js +45 -0
- package/dist/components/ui/search-field.js +45 -43
- package/dist/components/ui/table.js +33 -33
- package/dist/components/ui/tabs.js +76 -69
- package/dist/components/ui/timeline-tracks.d.ts +66 -0
- package/dist/components/ui/timeline-tracks.js +80 -0
- package/dist/components/ui/timeline.d.ts +31 -0
- package/dist/components/ui/timeline.js +119 -0
- package/dist/components/ui/toast.js +5 -5
- package/dist/components/ui/video-player.d.ts +29 -0
- package/dist/components/ui/video-player.js +174 -0
- package/dist/index.d.ts +7 -1
- package/dist/index.js +38 -32
- package/dist/lib/field-box.d.ts +13 -0
- package/dist/lib/field-box.js +11 -0
- package/dist/lib/format-time.d.ts +5 -0
- package/dist/lib/format-time.js +8 -0
- package/dist/lib/icons.d.ts +6 -0
- package/dist/lib/icons.js +68 -44
- package/dist/lib/utils.d.ts +1 -0
- package/dist/lib/utils.js +14 -7
- package/dist/style.css +1 -1
- package/dist/tokens/pyler-light.d.ts +12 -0
- package/dist/tokens/pyler-light.js +27 -23
- package/dist/tokens/shadow-radius.d.ts +3 -3
- package/dist/tokens/shadow-radius.js +3 -3
- package/dist/tokens/typography.d.ts +8 -2
- package/dist/tokens/typography.js +6 -0
- package/package.json +1 -1
- package/pyler-tailwind-colors.cjs +4 -0
- package/pyler-typography.cjs +7 -0
package/README.md
CHANGED
|
@@ -207,6 +207,7 @@ flatPylerLightTokens.forEach(({ path, hex, cssVar }) => { /* 문서화·순회
|
|
|
207
207
|
| Body 4 M | 12px | 500 | `text-body-4-m` | `.ds-type-body-4-m` |
|
|
208
208
|
| Body 4 R | 12px | 400 | `text-body-4-r` | `.ds-type-body-4-r` |
|
|
209
209
|
| Label 1 R | 11px | 400 | `text-label-1-r` | `.ds-type-label-1-r` |
|
|
210
|
+
| Label 1 SB | 11px | 600 | `text-label-1-sb` | `.ds-type-label-1-sb` |
|
|
210
211
|
|
|
211
212
|
폰트는 Pretendard JP(`--ds-font-sans`)를 사용합니다. `@pylerai/design-system/fonts.css`를 함께 로드하면 등록됩니다(위 "폰트" 절 참고).
|
|
212
213
|
|
|
@@ -274,7 +275,7 @@ Dim은 `var(--ds-dim-default)`로 제공됩니다. Storybook **Foundation → Ra
|
|
|
274
275
|
- `Checkbox`·`Radio`는 자체적으로 `<label>`을 렌더합니다. **밖에서 `<label>`로 감싸지 마세요** — label이 중첩되어 연결이 깨집니다. 텍스트는 children으로 넘기고(`<Checkbox>동의</Checkbox>`), 텍스트가 없으면 `aria-label`을 지정하세요.
|
|
275
276
|
- `Toggle`, 아이콘 전용 `Button`, 라벨 없는 `Dropdown`/`SearchField`에는 `aria-label`이 필요합니다.
|
|
276
277
|
|
|
277
|
-
> ⚠️ **컬러 대비는 아직 WCAG AA를 충족하지 못합니다.** `--ds-text-normal`(#969696)은 흰 배경에서 2.
|
|
278
|
+
> ⚠️ **컬러 대비는 아직 WCAG AA를 충족하지 못합니다.** `--ds-text-normal`(#afafaf, 이전 #969696)은 흰 배경에서 2.2:1, `--ds-text-disabled`(#cccccc)는 1.6:1, 흰 글자 + `--ds-primary-blue-normal`(#2c80f5)은 3.8:1로 기준(4.5:1)에 미달합니다. 토큰을 바꾸면 제품 전체 색이 바뀌므로 디자이너 결정이 필요한 사안이며, 그때까지 axe의 `color-contrast` 규칙만 꺼 둔 상태입니다(`.storybook/preview.ts`).
|
|
278
279
|
|
|
279
280
|
## 📁 프로젝트 구조
|
|
280
281
|
|
|
@@ -2,9 +2,12 @@ import { DsSize } from '../../lib/size';
|
|
|
2
2
|
import * as React from "react";
|
|
3
3
|
export type BadgeColor = "normal" | "neutral" | "negative" | "positive";
|
|
4
4
|
export type BadgeSize = Extract<DsSize, "md" | "sm">;
|
|
5
|
+
/** `outline` 흰 바탕 + 테두리(기본) · `surface` 테두리 없이 연한 면(Figma 2026-11 추가) */
|
|
6
|
+
export type BadgeVariant = "outline" | "surface";
|
|
5
7
|
export interface BadgeProps extends React.HTMLAttributes<HTMLSpanElement> {
|
|
6
8
|
color?: BadgeColor;
|
|
7
9
|
size?: BadgeSize;
|
|
10
|
+
variant?: BadgeVariant;
|
|
8
11
|
}
|
|
9
12
|
declare const Badge: React.ForwardRefExoticComponent<BadgeProps & React.RefAttributes<HTMLSpanElement>>;
|
|
10
13
|
export { Badge };
|
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { jsx as
|
|
3
|
-
import { cn as
|
|
4
|
-
import * as
|
|
5
|
-
const
|
|
2
|
+
import { jsx as l } from "react/jsx-runtime";
|
|
3
|
+
import { cn as b } from "../../lib/utils.js";
|
|
4
|
+
import * as c from "react";
|
|
5
|
+
const g = {
|
|
6
|
+
normal: "bg-ds-primary-blue-muted",
|
|
7
|
+
neutral: "bg-ds-surface-normal",
|
|
8
|
+
negative: "bg-ds-primary-red-muted",
|
|
9
|
+
positive: "bg-ds-primary-green-muted"
|
|
10
|
+
}, u = {
|
|
6
11
|
normal: {
|
|
7
12
|
border: "border-ds-primary-blue-normal",
|
|
8
13
|
text: "text-ds-primary-blue-normal"
|
|
@@ -19,31 +24,34 @@ const b = {
|
|
|
19
24
|
border: "border-ds-primary-green-normal",
|
|
20
25
|
text: "text-ds-primary-green-normal"
|
|
21
26
|
}
|
|
22
|
-
},
|
|
27
|
+
}, f = {
|
|
23
28
|
md: { padding: "px-2 pt-[5px] pb-[3px]", text: "text-body-3-m" },
|
|
24
29
|
sm: { padding: "px-1 pt-[3px] pb-[1px]", text: "text-label-1-r" }
|
|
25
|
-
},
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
30
|
+
}, y = {
|
|
31
|
+
md: { padding: "px-2 pt-[5px] pb-[3px]", text: "text-body-3-m" },
|
|
32
|
+
sm: { padding: "px-1.5 pt-[5px] pb-[3px]", text: "text-label-1-sb" }
|
|
33
|
+
}, C = c.forwardRef(
|
|
34
|
+
({ color: e = "normal", size: t = "md", variant: s = "outline", className: a, children: d, ...o }, n) => {
|
|
35
|
+
const r = s === "surface", { border: p, text: m } = u[e], { padding: i, text: x } = (r ? y : f)[t];
|
|
36
|
+
return /* @__PURE__ */ l(
|
|
29
37
|
"span",
|
|
30
38
|
{
|
|
31
|
-
ref:
|
|
32
|
-
className:
|
|
33
|
-
"inline-flex items-center justify-center
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
n,
|
|
39
|
+
ref: n,
|
|
40
|
+
className: b(
|
|
41
|
+
"inline-flex items-center justify-center rounded-ds-ss whitespace-nowrap",
|
|
42
|
+
r ? g[e] : ["border bg-ds-background-default", p],
|
|
43
|
+
i,
|
|
37
44
|
m,
|
|
38
|
-
|
|
45
|
+
x,
|
|
46
|
+
a
|
|
39
47
|
),
|
|
40
|
-
...
|
|
41
|
-
children:
|
|
48
|
+
...o,
|
|
49
|
+
children: d
|
|
42
50
|
}
|
|
43
51
|
);
|
|
44
52
|
}
|
|
45
53
|
);
|
|
46
|
-
|
|
54
|
+
C.displayName = "Badge";
|
|
47
55
|
export {
|
|
48
|
-
|
|
56
|
+
C as Badge
|
|
49
57
|
};
|
|
@@ -3,7 +3,7 @@ import { VariantProps } from 'class-variance-authority';
|
|
|
3
3
|
import * as React from "react";
|
|
4
4
|
declare const buttonVariants: (props?: ({
|
|
5
5
|
variant?: "text" | "icon" | "filled" | "outlined" | "indicator" | null | undefined;
|
|
6
|
-
color?: "blue" | "red" | "green" | null | undefined;
|
|
6
|
+
color?: "blue" | "red" | "green" | "neutral" | null | undefined;
|
|
7
7
|
size?: "sm" | "md" | "lg" | "xl" | null | undefined;
|
|
8
8
|
} & import('class-variance-authority/types').ClassProp) | undefined) => string;
|
|
9
9
|
export interface ButtonProps extends Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, "color">, VariantProps<typeof buttonVariants> {
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { jsx as
|
|
2
|
+
import { jsx as s, jsxs as O, Fragment as k } from "react/jsx-runtime";
|
|
3
3
|
import { cn as m } from "../../lib/utils.js";
|
|
4
4
|
import { Slot as B } from "@radix-ui/react-slot";
|
|
5
5
|
import { cva as R } from "class-variance-authority";
|
|
6
|
-
import * as
|
|
6
|
+
import * as p from "react";
|
|
7
7
|
const v = R(
|
|
8
8
|
"inline-flex items-center justify-center whitespace-nowrap font-sans transition-colors duration-150 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 disabled:pointer-events-none select-none",
|
|
9
9
|
{
|
|
@@ -12,13 +12,16 @@ const v = R(
|
|
|
12
12
|
filled: "rounded-ds-md",
|
|
13
13
|
outlined: "rounded-ds-md border border-ds-line-normal bg-ds-background-default text-ds-text-strong hover:bg-ds-surface-normal active:bg-ds-surface-strong disabled:bg-ds-surface-normal disabled:text-ds-text-disabled",
|
|
14
14
|
text: "rounded-ds-md bg-transparent text-ds-text-strong disabled:text-ds-text-disabled",
|
|
15
|
-
|
|
15
|
+
// radius 는 size compound 에서 — buttonVariants 를 cn 없이 직접 쓰는 앱에서도 sm 4px 이 나오도록
|
|
16
|
+
icon: "",
|
|
16
17
|
indicator: "rounded-full inline-block"
|
|
17
18
|
},
|
|
18
19
|
color: {
|
|
19
20
|
blue: "",
|
|
20
21
|
red: "",
|
|
21
|
-
green: ""
|
|
22
|
+
green: "",
|
|
23
|
+
/** 회색 버튼 — Figma 2026-11 추가. filled·icon 에서 쓴다(indicator 는 대상 아님) */
|
|
24
|
+
neutral: ""
|
|
22
25
|
},
|
|
23
26
|
size: {
|
|
24
27
|
sm: "h-7 gap-1",
|
|
@@ -44,6 +47,11 @@ const v = R(
|
|
|
44
47
|
color: "green",
|
|
45
48
|
className: "bg-ds-primary-green-normal text-white hover:bg-ds-primary-green-hovered active:bg-ds-primary-green-hovered disabled:bg-ds-primary-green-disabled disabled:text-white focus-visible:ring-ds-primary-green-normal"
|
|
46
49
|
},
|
|
50
|
+
{
|
|
51
|
+
variant: "filled",
|
|
52
|
+
color: "neutral",
|
|
53
|
+
className: "bg-ds-surface-strong text-ds-text-strong hover:bg-ds-surface-hovered active:bg-ds-surface-hovered disabled:bg-ds-surface-disabled disabled:text-ds-text-disabled focus-visible:ring-ds-text-strong"
|
|
54
|
+
},
|
|
47
55
|
// ── icon filled × color ───────────────────────────────────────────────
|
|
48
56
|
{
|
|
49
57
|
variant: "icon",
|
|
@@ -60,17 +68,22 @@ const v = R(
|
|
|
60
68
|
color: "green",
|
|
61
69
|
className: "bg-ds-primary-green-normal text-white hover:bg-ds-primary-green-hovered active:bg-ds-primary-green-hovered disabled:bg-ds-primary-green-disabled disabled:text-white focus-visible:ring-ds-primary-green-normal"
|
|
62
70
|
},
|
|
71
|
+
{
|
|
72
|
+
variant: "icon",
|
|
73
|
+
color: "neutral",
|
|
74
|
+
className: "bg-ds-surface-strong text-ds-text-strong hover:bg-ds-surface-hovered active:bg-ds-surface-hovered disabled:bg-ds-surface-disabled disabled:text-ds-text-disabled focus-visible:ring-ds-text-strong"
|
|
75
|
+
},
|
|
63
76
|
// ── icon × size (fixed square dimensions + SVG sizing) ──────────────
|
|
64
|
-
{ variant: "icon", size: "sm", className: "h-7 w-7 p-0 rounded [&_svg]:size-3" },
|
|
65
|
-
{ variant: "icon", size: "md", className: "h-8 w-8 p-0 [&_svg]:size-5" },
|
|
66
|
-
{ variant: "icon", size: "lg", className: "h-9 w-9 p-0 [&_svg]:size-5" },
|
|
67
|
-
{ variant: "icon", size: "xl", className: "h-11 w-11 p-0 [&_svg]:size-7" },
|
|
77
|
+
{ variant: "icon", size: "sm", className: "h-7 w-7 p-0 rounded-ds-ss [&_svg]:size-3" },
|
|
78
|
+
{ variant: "icon", size: "md", className: "h-8 w-8 p-0 rounded-ds-md [&_svg]:size-5" },
|
|
79
|
+
{ variant: "icon", size: "lg", className: "h-9 w-9 p-0 rounded-ds-md [&_svg]:size-5" },
|
|
80
|
+
{ variant: "icon", size: "xl", className: "h-11 w-11 p-0 rounded-ds-md [&_svg]:size-7" },
|
|
68
81
|
// ── filled/outlined/text size → padding + typography ─────────────────
|
|
69
|
-
{ variant: "filled", size: "sm", className: "px-2 text-body-4-m
|
|
82
|
+
{ variant: "filled", size: "sm", className: "px-2 text-body-4-m" },
|
|
70
83
|
{ variant: "filled", size: "md", className: "px-3 text-body-3-m" },
|
|
71
84
|
{ variant: "filled", size: "lg", className: "px-3.5 text-body-2-m" },
|
|
72
85
|
{ variant: "filled", size: "xl", className: "px-[18px] text-body-2-m" },
|
|
73
|
-
{ variant: "outlined", size: "sm", className: "px-2 text-body-4-m
|
|
86
|
+
{ variant: "outlined", size: "sm", className: "px-2 text-body-4-m" },
|
|
74
87
|
{ variant: "outlined", size: "md", className: "px-3 text-body-3-m" },
|
|
75
88
|
{ variant: "outlined", size: "lg", className: "px-3.5 text-body-2-m" },
|
|
76
89
|
{ variant: "outlined", size: "xl", className: "px-[18px] text-body-2-m" },
|
|
@@ -107,9 +120,9 @@ const v = R(
|
|
|
107
120
|
size: "md"
|
|
108
121
|
}
|
|
109
122
|
}
|
|
110
|
-
), _ =
|
|
123
|
+
), _ = p.forwardRef(
|
|
111
124
|
({
|
|
112
|
-
className:
|
|
125
|
+
className: a,
|
|
113
126
|
variant: e,
|
|
114
127
|
color: i,
|
|
115
128
|
size: r,
|
|
@@ -120,44 +133,44 @@ const v = R(
|
|
|
120
133
|
rightIcon: t,
|
|
121
134
|
children: b,
|
|
122
135
|
...h
|
|
123
|
-
},
|
|
124
|
-
const
|
|
125
|
-
const
|
|
136
|
+
}, f) => {
|
|
137
|
+
const y = c ? B : "button", N = e === "icon" && x === "outlined" ? "bg-ds-background-default border border-ds-line-normal text-ds-text-strong hover:bg-ds-surface-normal active:bg-ds-surface-strong disabled:text-ds-text-disabled" : void 0, l = !!d, g = !!t, z = e !== "icon" && e !== "indicator" && (l || g) ? (() => {
|
|
138
|
+
const o = r ?? "md", n = {
|
|
126
139
|
sm: { leftOnly: "pl-1 pr-2", rightOnly: "pl-2 pr-1", both: "px-1" },
|
|
127
140
|
md: { leftOnly: "pl-2 pr-3", rightOnly: "pl-3 pr-2", both: "px-2" },
|
|
128
141
|
lg: { leftOnly: "pl-2.5 pr-3.5", rightOnly: "pl-3.5 pr-2.5", both: "px-2.5" },
|
|
129
142
|
xl: { leftOnly: "pl-3.5 pr-[18px]", rightOnly: "pl-[18px] pr-3.5", both: "px-3.5" }
|
|
130
|
-
}, w = l &&
|
|
143
|
+
}, w = l && g ? n[o].both : l ? n[o].leftOnly : n[o].rightOnly;
|
|
131
144
|
return m(w, "gap-1");
|
|
132
145
|
})() : void 0;
|
|
133
|
-
return /* @__PURE__ */
|
|
134
|
-
|
|
146
|
+
return /* @__PURE__ */ s(
|
|
147
|
+
y,
|
|
135
148
|
{
|
|
136
149
|
className: m(
|
|
137
150
|
v({ variant: e, color: i, size: r }),
|
|
138
151
|
e === "icon" && u === "round" && "rounded-full",
|
|
139
152
|
N,
|
|
140
153
|
z,
|
|
141
|
-
|
|
154
|
+
a
|
|
142
155
|
),
|
|
143
|
-
ref:
|
|
156
|
+
ref: f,
|
|
144
157
|
...h,
|
|
145
158
|
children: c ? b : /* @__PURE__ */ O(k, { children: [
|
|
146
|
-
d && /* @__PURE__ */
|
|
159
|
+
d && /* @__PURE__ */ s("span", { className: "shrink-0", children: d }),
|
|
147
160
|
b,
|
|
148
|
-
t && /* @__PURE__ */
|
|
161
|
+
t && /* @__PURE__ */ s("span", { className: "shrink-0", children: t })
|
|
149
162
|
] })
|
|
150
163
|
}
|
|
151
164
|
);
|
|
152
165
|
}
|
|
153
166
|
);
|
|
154
167
|
_.displayName = "Button";
|
|
155
|
-
const j =
|
|
156
|
-
({ color:
|
|
168
|
+
const j = p.forwardRef(
|
|
169
|
+
({ color: a = "blue", size: e = "md", className: i }, r) => /* @__PURE__ */ s(
|
|
157
170
|
"span",
|
|
158
171
|
{
|
|
159
172
|
className: m(
|
|
160
|
-
v({ variant: "indicator", color:
|
|
173
|
+
v({ variant: "indicator", color: a, size: e }),
|
|
161
174
|
i
|
|
162
175
|
),
|
|
163
176
|
ref: r
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { jsxs as
|
|
3
|
-
import { DropdownArrowIcon as V, SelectCheckIcon as ee, CheckboxCheckedIcon as
|
|
4
|
-
import { cn as
|
|
2
|
+
import { jsxs as m, jsx as n } from "react/jsx-runtime";
|
|
3
|
+
import { DropdownArrowIcon as V, SelectCheckIcon as ee, CheckboxCheckedIcon as te, CheckboxUncheckedIcon as re, RadioCheckedIcon as ne, RadioUncheckedIcon as se } from "../../lib/icons.js";
|
|
4
|
+
import { cn as x } from "../../lib/utils.js";
|
|
5
5
|
import * as s from "react";
|
|
6
6
|
import { Button as U } from "./button.js";
|
|
7
7
|
const k = {
|
|
@@ -22,20 +22,20 @@ const k = {
|
|
|
22
22
|
}
|
|
23
23
|
}, ae = s.forwardRef(
|
|
24
24
|
({
|
|
25
|
-
type:
|
|
25
|
+
type: o = "default",
|
|
26
26
|
options: i,
|
|
27
27
|
value: R,
|
|
28
28
|
onValueChange: K,
|
|
29
|
-
values:
|
|
29
|
+
values: b,
|
|
30
30
|
onValuesChange: T,
|
|
31
31
|
placeholder: D = "선택해주세요",
|
|
32
|
-
label:
|
|
32
|
+
label: p,
|
|
33
33
|
showLabel: v = !0,
|
|
34
34
|
require: E,
|
|
35
|
-
size:
|
|
36
|
-
disabled:
|
|
35
|
+
size: h = "lg",
|
|
36
|
+
disabled: d,
|
|
37
37
|
readOnly: w,
|
|
38
|
-
error:
|
|
38
|
+
error: u,
|
|
39
39
|
hint: N,
|
|
40
40
|
leftIcon: j,
|
|
41
41
|
showButtons: q,
|
|
@@ -47,181 +47,184 @@ const k = {
|
|
|
47
47
|
className: J,
|
|
48
48
|
"aria-label": P
|
|
49
49
|
}, Q) => {
|
|
50
|
-
const [
|
|
50
|
+
const [t, c] = s.useState(!1), [a, g] = s.useState(-1), [$, A] = s.useState("keyboard"), y = s.useRef(null), L = s.useRef(null), M = s.useRef(null), f = s.useId(), I = (e) => `${f}-option-${e}`;
|
|
51
51
|
s.useEffect(() => {
|
|
52
52
|
const e = (r) => {
|
|
53
|
-
y.current && !y.current.contains(r.target) &&
|
|
53
|
+
y.current && !y.current.contains(r.target) && c(!1);
|
|
54
54
|
};
|
|
55
55
|
return document.addEventListener("mousedown", e), () => document.removeEventListener("mousedown", e);
|
|
56
56
|
}, []), s.useEffect(() => {
|
|
57
|
-
|
|
58
|
-
}, [
|
|
59
|
-
!
|
|
60
|
-
}, [
|
|
61
|
-
const W = (e) =>
|
|
62
|
-
|
|
57
|
+
t || g(-1);
|
|
58
|
+
}, [t]), s.useEffect(() => {
|
|
59
|
+
!t || a < 0 || L.current?.querySelector(`#${CSS.escape(I(a))}`)?.scrollIntoView({ block: "nearest" });
|
|
60
|
+
}, [t, a]);
|
|
61
|
+
const W = (e) => o === "checkbox" ? b?.includes(e) ?? !1 : e === R, C = i.find((e) => e.value === R), X = () => o === "checkbox" ? !b || b.length === 0 ? D : i.filter((r) => b.includes(r.value)).map((r) => r.label).join(", ") : C ? C.label : D, Y = () => {
|
|
62
|
+
d || w || c((e) => !e);
|
|
63
63
|
}, O = (e) => {
|
|
64
|
-
if (
|
|
65
|
-
const r =
|
|
66
|
-
T?.(
|
|
64
|
+
if (o === "checkbox") {
|
|
65
|
+
const r = b ?? [], l = r.includes(e) ? r.filter((S) => S !== e) : [...r, e];
|
|
66
|
+
T?.(l);
|
|
67
67
|
} else
|
|
68
|
-
K?.(e),
|
|
69
|
-
}, Z =
|
|
70
|
-
if (
|
|
71
|
-
const r = (
|
|
72
|
-
e.preventDefault(),
|
|
68
|
+
K?.(e), c(!1);
|
|
69
|
+
}, Z = o === "checkbox" ? (b?.length ?? 0) > 0 : !!C, _ = (e) => {
|
|
70
|
+
if (d || w) return;
|
|
71
|
+
const r = (l) => {
|
|
72
|
+
e.preventDefault(), t || c(!0), A("keyboard"), g(l);
|
|
73
73
|
};
|
|
74
74
|
switch (e.key) {
|
|
75
75
|
case "ArrowDown":
|
|
76
|
-
return r(
|
|
76
|
+
return r(t ? Math.min(a + 1, i.length - 1) : 0);
|
|
77
77
|
case "ArrowUp":
|
|
78
|
-
return r(
|
|
78
|
+
return r(t ? Math.max(a - 1, 0) : i.length - 1);
|
|
79
79
|
case "Home":
|
|
80
|
-
return
|
|
80
|
+
return t ? r(0) : void 0;
|
|
81
81
|
case "End":
|
|
82
|
-
return
|
|
82
|
+
return t ? r(i.length - 1) : void 0;
|
|
83
83
|
case "Enter":
|
|
84
84
|
case " ":
|
|
85
|
-
e.preventDefault(),
|
|
85
|
+
e.preventDefault(), t ? a >= 0 && O(i[a].value) : c(!0);
|
|
86
86
|
return;
|
|
87
87
|
case "Escape":
|
|
88
|
-
|
|
88
|
+
t && (e.preventDefault(), c(!1), M.current?.focus());
|
|
89
89
|
return;
|
|
90
90
|
case "Tab":
|
|
91
|
-
|
|
91
|
+
c(!1);
|
|
92
92
|
return;
|
|
93
93
|
default:
|
|
94
94
|
return;
|
|
95
95
|
}
|
|
96
96
|
};
|
|
97
|
-
return /* @__PURE__ */
|
|
98
|
-
v &&
|
|
99
|
-
/* @__PURE__ */
|
|
100
|
-
E && /* @__PURE__ */
|
|
97
|
+
return /* @__PURE__ */ m("div", { ref: Q, className: x("flex flex-col gap-2", G), children: [
|
|
98
|
+
v && p && /* @__PURE__ */ m("div", { className: "flex items-start gap-0.5 whitespace-nowrap", children: [
|
|
99
|
+
/* @__PURE__ */ n("span", { id: `${f}-label`, className: "text-body-2-r text-ds-text-strong", children: p }),
|
|
100
|
+
E && /* @__PURE__ */ n("span", { "aria-hidden": "true", className: "text-body-2-r text-ds-primary-red-normal", children: "*" })
|
|
101
101
|
] }),
|
|
102
|
-
/* @__PURE__ */
|
|
103
|
-
/* @__PURE__ */
|
|
102
|
+
/* @__PURE__ */ m("div", { ref: y, className: "relative", children: [
|
|
103
|
+
/* @__PURE__ */ m(
|
|
104
104
|
"button",
|
|
105
105
|
{
|
|
106
106
|
ref: M,
|
|
107
107
|
type: "button",
|
|
108
108
|
role: "combobox",
|
|
109
|
-
"aria-expanded":
|
|
109
|
+
"aria-expanded": t,
|
|
110
110
|
"aria-haspopup": "listbox",
|
|
111
|
-
"aria-controls":
|
|
112
|
-
"aria-activedescendant":
|
|
113
|
-
"aria-labelledby": v &&
|
|
114
|
-
"aria-label": v &&
|
|
115
|
-
"aria-invalid":
|
|
116
|
-
"aria-describedby":
|
|
111
|
+
"aria-controls": t ? f : void 0,
|
|
112
|
+
"aria-activedescendant": t && a >= 0 ? I(a) : void 0,
|
|
113
|
+
"aria-labelledby": v && p ? `${f}-label` : void 0,
|
|
114
|
+
"aria-label": v && p ? void 0 : P,
|
|
115
|
+
"aria-invalid": u ? !0 : void 0,
|
|
116
|
+
"aria-describedby": u || N ? `${f}-description` : void 0,
|
|
117
117
|
"aria-required": E || void 0,
|
|
118
|
-
disabled:
|
|
118
|
+
disabled: d,
|
|
119
119
|
onClick: Y,
|
|
120
120
|
onKeyDown: _,
|
|
121
|
-
className:
|
|
121
|
+
className: x(
|
|
122
122
|
"flex items-center w-full rounded-ds-md border transition-colors",
|
|
123
|
-
|
|
124
|
-
|
|
123
|
+
// SearchField·InputField(src/lib/field-box.ts)와 같은 규칙. 트리거는 버튼이라 focus-within 대신
|
|
124
|
+
// 열림 상태와 focus-visible 로 표현한다. 배경은 disabled·readOnly 우선, 테두리는 error 우선
|
|
125
|
+
d ? "bg-ds-surface-strong cursor-not-allowed" : w ? "bg-ds-surface-strong cursor-default" : u || t ? "bg-ds-background-default" : "bg-ds-surface-normal",
|
|
126
|
+
u ? "border-ds-primary-red-normal" : d ? "border-transparent" : t ? "border-ds-primary-blue-normal" : "border-transparent focus-visible:outline-none focus-visible:border-ds-primary-blue-normal",
|
|
127
|
+
k[h].trigger,
|
|
125
128
|
J
|
|
126
129
|
),
|
|
127
130
|
children: [
|
|
128
|
-
j && /* @__PURE__ */
|
|
129
|
-
/* @__PURE__ */
|
|
131
|
+
j && /* @__PURE__ */ n("span", { className: "shrink-0 flex items-center justify-center text-ds-icon-normal", children: j }),
|
|
132
|
+
/* @__PURE__ */ n(
|
|
130
133
|
"span",
|
|
131
134
|
{
|
|
132
|
-
className:
|
|
135
|
+
className: x(
|
|
133
136
|
"flex-1 min-w-0 text-left truncate font-sans",
|
|
134
|
-
k[
|
|
135
|
-
Z ? "text-ds-text-strong" :
|
|
137
|
+
k[h].text,
|
|
138
|
+
Z ? "text-ds-text-strong" : d ? "text-ds-text-disabled" : "text-ds-text-normal"
|
|
136
139
|
),
|
|
137
140
|
children: X()
|
|
138
141
|
}
|
|
139
142
|
),
|
|
140
|
-
/* @__PURE__ */
|
|
143
|
+
/* @__PURE__ */ n(
|
|
141
144
|
"span",
|
|
142
145
|
{
|
|
143
|
-
className:
|
|
146
|
+
className: x(
|
|
144
147
|
"shrink-0 flex items-center justify-center transition-transform",
|
|
145
|
-
|
|
146
|
-
|
|
148
|
+
t && "rotate-180",
|
|
149
|
+
d ? "text-ds-text-disabled" : "text-ds-icon-strong"
|
|
147
150
|
),
|
|
148
|
-
children: /* @__PURE__ */
|
|
151
|
+
children: /* @__PURE__ */ n(V, {})
|
|
149
152
|
}
|
|
150
153
|
)
|
|
151
154
|
]
|
|
152
155
|
}
|
|
153
156
|
),
|
|
154
|
-
|
|
157
|
+
t && /* @__PURE__ */ m(
|
|
155
158
|
"ul",
|
|
156
159
|
{
|
|
157
160
|
ref: L,
|
|
158
|
-
id:
|
|
161
|
+
id: f,
|
|
159
162
|
role: "listbox",
|
|
160
|
-
"aria-multiselectable":
|
|
163
|
+
"aria-multiselectable": o === "checkbox" ? !0 : void 0,
|
|
161
164
|
className: "absolute top-full left-0 z-50 mt-1 min-w-full w-max max-w-sm bg-ds-background-default border border-ds-line-normal rounded-ds-md p-1 shadow-ds-default",
|
|
162
165
|
onMouseLeave: () => {
|
|
163
|
-
$ === "mouse" &&
|
|
166
|
+
$ === "mouse" && g(-1);
|
|
164
167
|
},
|
|
165
168
|
children: [
|
|
166
169
|
i.map((e, r) => {
|
|
167
|
-
const
|
|
170
|
+
const l = W(e.value), S = r === a;
|
|
168
171
|
return (
|
|
169
172
|
// 키보드 조작은 aria-activedescendant 패턴에 따라 combobox 트리거의
|
|
170
173
|
// onKeyDown이 전담한다(옵션은 포커스를 받지 않는 것이 WAI-ARIA 권고).
|
|
171
174
|
// 이 규칙은 그 패턴을 인식하지 못하므로 해당 줄만 해제한다.
|
|
172
175
|
// eslint-disable-next-line jsx-a11y/click-events-have-key-events
|
|
173
|
-
/* @__PURE__ */
|
|
176
|
+
/* @__PURE__ */ m(
|
|
174
177
|
"li",
|
|
175
178
|
{
|
|
176
179
|
id: I(r),
|
|
177
180
|
role: "option",
|
|
178
|
-
"aria-selected":
|
|
181
|
+
"aria-selected": l,
|
|
179
182
|
onClick: () => O(e.value),
|
|
180
183
|
onMouseEnter: () => {
|
|
181
|
-
A("mouse"),
|
|
184
|
+
A("mouse"), g(r);
|
|
182
185
|
},
|
|
183
|
-
className:
|
|
186
|
+
className: x(
|
|
184
187
|
"flex items-center rounded-ds-md cursor-pointer gap-1.5 transition-colors",
|
|
185
|
-
k[
|
|
186
|
-
|
|
188
|
+
k[h].option,
|
|
189
|
+
l ? "bg-ds-surface-normal" : "bg-ds-background-default hover:bg-ds-surface-normal",
|
|
187
190
|
// 키보드 이동 위치 표시 (마우스 hover와 구분되도록 링으로, 키보드 이동에만)
|
|
188
191
|
S && $ === "keyboard" && "ring-1 ring-inset ring-ds-primary-blue-normal"
|
|
189
192
|
),
|
|
190
193
|
children: [
|
|
191
|
-
/* @__PURE__ */
|
|
194
|
+
/* @__PURE__ */ n(
|
|
192
195
|
"span",
|
|
193
196
|
{
|
|
194
|
-
className:
|
|
197
|
+
className: x(
|
|
195
198
|
"flex-1 min-w-0 truncate font-sans",
|
|
196
|
-
k[
|
|
199
|
+
k[h].text,
|
|
197
200
|
"text-ds-text-strong"
|
|
198
201
|
),
|
|
199
202
|
children: e.label
|
|
200
203
|
}
|
|
201
204
|
),
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
+
o === "default" && l && /* @__PURE__ */ n("span", { className: "shrink-0 flex items-center justify-center text-ds-text-strong", children: /* @__PURE__ */ n(ee, {}) }),
|
|
206
|
+
o === "checkbox" && /* @__PURE__ */ n("span", { className: "shrink-0 flex items-center justify-center", children: l ? /* @__PURE__ */ n(te, {}) : /* @__PURE__ */ n(re, {}) }),
|
|
207
|
+
o === "radio" && /* @__PURE__ */ n("span", { className: "shrink-0 flex items-center justify-center", children: l ? /* @__PURE__ */ n(ne, {}) : /* @__PURE__ */ n(se, {}) })
|
|
205
208
|
]
|
|
206
209
|
},
|
|
207
210
|
e.value
|
|
208
211
|
)
|
|
209
212
|
);
|
|
210
213
|
}),
|
|
211
|
-
q && /* @__PURE__ */
|
|
212
|
-
/* @__PURE__ */
|
|
214
|
+
q && /* @__PURE__ */ m("li", { className: "flex gap-2 pt-1", children: [
|
|
215
|
+
/* @__PURE__ */ n(
|
|
213
216
|
U,
|
|
214
217
|
{
|
|
215
218
|
variant: "outlined",
|
|
216
219
|
size: "lg",
|
|
217
220
|
className: "flex-1",
|
|
218
221
|
onClick: () => {
|
|
219
|
-
H?.(),
|
|
222
|
+
H?.(), c(!1);
|
|
220
223
|
},
|
|
221
224
|
children: z
|
|
222
225
|
}
|
|
223
226
|
),
|
|
224
|
-
/* @__PURE__ */
|
|
227
|
+
/* @__PURE__ */ n(
|
|
225
228
|
U,
|
|
226
229
|
{
|
|
227
230
|
variant: "filled",
|
|
@@ -229,7 +232,7 @@ const k = {
|
|
|
229
232
|
size: "lg",
|
|
230
233
|
className: "flex-1",
|
|
231
234
|
onClick: () => {
|
|
232
|
-
F?.(),
|
|
235
|
+
F?.(), c(!1);
|
|
233
236
|
},
|
|
234
237
|
children: B
|
|
235
238
|
}
|
|
@@ -239,15 +242,15 @@ const k = {
|
|
|
239
242
|
}
|
|
240
243
|
)
|
|
241
244
|
] }),
|
|
242
|
-
(
|
|
245
|
+
(u || N) && /* @__PURE__ */ n(
|
|
243
246
|
"p",
|
|
244
247
|
{
|
|
245
|
-
id: `${
|
|
246
|
-
className:
|
|
248
|
+
id: `${f}-description`,
|
|
249
|
+
className: x(
|
|
247
250
|
"text-body-4-r",
|
|
248
|
-
|
|
251
|
+
u ? "text-ds-primary-red-normal" : "text-ds-text-normal"
|
|
249
252
|
),
|
|
250
|
-
children:
|
|
253
|
+
children: u ?? N
|
|
251
254
|
}
|
|
252
255
|
)
|
|
253
256
|
] });
|