@3w-witim/ui 0.1.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/dist/index.mjs ADDED
@@ -0,0 +1,3584 @@
1
+ import { clsx } from 'clsx';
2
+ import { twMerge } from 'tailwind-merge';
3
+ import * as React16 from 'react';
4
+ import { Slot } from '@radix-ui/react-slot';
5
+ import { cva } from 'class-variance-authority';
6
+ import { jsx, jsxs } from 'react/jsx-runtime';
7
+ import * as ScrollAreaPrimitive from '@radix-ui/react-scroll-area';
8
+
9
+ // src/utils.ts
10
+ function cn(...inputs) {
11
+ return twMerge(clsx(inputs));
12
+ }
13
+ var buttonVariants = cva(
14
+ "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded text-base font-semibold transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-border-focus focus-visible:ring-offset-2 focus-visible:ring-offset-bg-base disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-5 [&_svg]:shrink-0",
15
+ {
16
+ variants: {
17
+ variant: {
18
+ primary: "bg-brand-solid text-text-on-brand hover:bg-brand-solid-hover active:bg-brand-solid-pressed",
19
+ neutral: "border border-border-default bg-surface-default text-text-default hover:bg-surface-hover active:bg-surface-pressed",
20
+ ghost: "text-text-default hover:bg-surface-hover active:bg-surface-pressed",
21
+ danger: "bg-danger-solid text-text-on-brand hover:bg-danger-solid-hover",
22
+ "danger-outline": "border border-danger-border bg-transparent text-danger-text hover:bg-danger-subtle active:bg-danger-subtle"
23
+ },
24
+ size: {
25
+ sm: "h-9 px-3",
26
+ // 36px · px 12
27
+ md: "h-10 px-4",
28
+ // 40px · px 16 (기본)
29
+ lg: "h-12 px-5"
30
+ // 48px · px 20
31
+ }
32
+ },
33
+ defaultVariants: {
34
+ variant: "primary",
35
+ size: "md"
36
+ }
37
+ }
38
+ );
39
+ var Button = React16.forwardRef(
40
+ ({ className, variant, size, asChild = false, ...props }, ref) => {
41
+ const Comp = asChild ? Slot : "button";
42
+ return /* @__PURE__ */ jsx(
43
+ Comp,
44
+ {
45
+ className: cn(buttonVariants({ variant, size, className })),
46
+ ref,
47
+ ...props
48
+ }
49
+ );
50
+ }
51
+ );
52
+ Button.displayName = "Button";
53
+ var inputVariants = cva(
54
+ "flex w-full items-center rounded border bg-surface-default text-text-default transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-text-subtle focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:ring-offset-bg-base disabled:cursor-not-allowed disabled:bg-surface-sunken disabled:text-text-disabled disabled:placeholder:text-text-disabled",
55
+ {
56
+ variants: {
57
+ state: {
58
+ default: "border-border-default hover:border-border-strong focus-visible:border-border-focus focus-visible:ring-brand-border",
59
+ error: "border-danger-border focus-visible:border-danger-border focus-visible:ring-danger-border"
60
+ },
61
+ inputSize: {
62
+ sm: "h-9 gap-1.5 px-2.5 text-sm",
63
+ md: "h-10 gap-2 px-3 text-base",
64
+ lg: "h-12 gap-2.5 px-3.5 text-base"
65
+ }
66
+ },
67
+ defaultVariants: {
68
+ state: "default",
69
+ inputSize: "md"
70
+ }
71
+ }
72
+ );
73
+ var Input = React16.forwardRef(
74
+ ({
75
+ className,
76
+ type = "text",
77
+ state,
78
+ inputSize,
79
+ startIcon,
80
+ endIcon,
81
+ helperText,
82
+ id,
83
+ "aria-describedby": ariaDescribedby,
84
+ ...props
85
+ }, ref) => {
86
+ const isError = state === "error";
87
+ const reactId = React16.useId();
88
+ const helperId = helperText ? `${id ?? reactId}-helper` : void 0;
89
+ const describedBy = [ariaDescribedby, helperId].filter(Boolean).join(" ") || void 0;
90
+ return /* @__PURE__ */ jsxs("div", { className: "flex w-full flex-col gap-1.5", children: [
91
+ /* @__PURE__ */ jsxs(
92
+ "div",
93
+ {
94
+ className: cn(
95
+ inputVariants({ state, inputSize }),
96
+ (startIcon || endIcon) && "cursor-text",
97
+ className
98
+ ),
99
+ children: [
100
+ startIcon ? /* @__PURE__ */ jsx(
101
+ "span",
102
+ {
103
+ "aria-hidden": "true",
104
+ className: cn(
105
+ "flex size-5 shrink-0 items-center justify-center [&_svg]:size-5",
106
+ isError ? "text-danger-text" : "text-text-subtle"
107
+ ),
108
+ children: startIcon
109
+ }
110
+ ) : null,
111
+ /* @__PURE__ */ jsx(
112
+ "input",
113
+ {
114
+ id: id ?? reactId,
115
+ type,
116
+ ref,
117
+ "aria-invalid": isError || void 0,
118
+ "aria-describedby": describedBy,
119
+ className: "h-full w-full min-w-0 bg-transparent outline-none placeholder:text-text-subtle disabled:cursor-not-allowed disabled:text-text-disabled",
120
+ ...props
121
+ }
122
+ ),
123
+ endIcon ? /* @__PURE__ */ jsx(
124
+ "span",
125
+ {
126
+ "aria-hidden": "true",
127
+ className: cn(
128
+ "flex size-5 shrink-0 items-center justify-center [&_svg]:size-5",
129
+ isError ? "text-danger-text" : "text-text-subtle"
130
+ ),
131
+ children: endIcon
132
+ }
133
+ ) : null
134
+ ]
135
+ }
136
+ ),
137
+ helperText ? /* @__PURE__ */ jsxs(
138
+ "p",
139
+ {
140
+ id: helperId,
141
+ className: cn(
142
+ "flex items-center gap-1 text-sm [&_svg]:size-4",
143
+ isError ? "text-danger-text" : "text-text-subtle"
144
+ ),
145
+ children: [
146
+ isError ? /* @__PURE__ */ jsx(
147
+ "svg",
148
+ {
149
+ "aria-hidden": "true",
150
+ viewBox: "0 0 20 20",
151
+ fill: "currentColor",
152
+ className: "shrink-0",
153
+ children: /* @__PURE__ */ jsx(
154
+ "path",
155
+ {
156
+ fillRule: "evenodd",
157
+ d: "M10 18a8 8 0 1 0 0-16 8 8 0 0 0 0 16Zm-.75-11.5a.75.75 0 0 1 1.5 0v4a.75.75 0 0 1-1.5 0v-4ZM10 14.5a1 1 0 1 0 0-2 1 1 0 0 0 0 2Z",
158
+ clipRule: "evenodd"
159
+ }
160
+ )
161
+ }
162
+ ) : null,
163
+ helperText
164
+ ]
165
+ }
166
+ ) : null
167
+ ] });
168
+ }
169
+ );
170
+ Input.displayName = "Input";
171
+ var textareaVariants = cva(
172
+ "flex w-full resize-none rounded border bg-surface-default text-base text-text-default transition-colors placeholder:text-text-subtle focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:ring-offset-bg-base disabled:cursor-not-allowed disabled:opacity-50",
173
+ {
174
+ variants: {
175
+ state: {
176
+ default: "border-border-default focus-visible:border-border-focus focus-visible:ring-brand-border",
177
+ error: "border-danger-border focus-visible:border-danger-border focus-visible:ring-danger-border"
178
+ },
179
+ size: {
180
+ sm: "min-h-[64px] p-2 text-sm",
181
+ md: "min-h-[80px] p-3 text-base",
182
+ lg: "min-h-[120px] p-4 text-base"
183
+ }
184
+ },
185
+ defaultVariants: {
186
+ state: "default",
187
+ size: "md"
188
+ }
189
+ }
190
+ );
191
+ var Textarea = React16.forwardRef(
192
+ ({ className, state, size, "aria-invalid": ariaInvalid, ...props }, ref) => {
193
+ return /* @__PURE__ */ jsx(
194
+ "textarea",
195
+ {
196
+ ref,
197
+ "aria-invalid": ariaInvalid ?? (state === "error" ? true : void 0),
198
+ className: cn(textareaVariants({ state, size, className })),
199
+ ...props
200
+ }
201
+ );
202
+ }
203
+ );
204
+ Textarea.displayName = "Textarea";
205
+ var cardVariants = cva("rounded-lg text-text-default transition-colors", {
206
+ variants: {
207
+ variant: {
208
+ default: "border border-border-subtle bg-surface-default shadow-card",
209
+ raised: "border border-border-subtle bg-surface-raised shadow-md",
210
+ outline: "border border-border-default bg-transparent"
211
+ }
212
+ },
213
+ defaultVariants: {
214
+ variant: "default"
215
+ }
216
+ });
217
+ var Card = React16.forwardRef(
218
+ ({ className, variant, ...props }, ref) => /* @__PURE__ */ jsx(
219
+ "div",
220
+ {
221
+ ref,
222
+ className: cn(cardVariants({ variant, className })),
223
+ ...props
224
+ }
225
+ )
226
+ );
227
+ Card.displayName = "Card";
228
+ var CardHeader = React16.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
229
+ "div",
230
+ {
231
+ ref,
232
+ className: cn("flex flex-col gap-3 p-5", className),
233
+ ...props
234
+ }
235
+ ));
236
+ CardHeader.displayName = "CardHeader";
237
+ var CardTitle = React16.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
238
+ "div",
239
+ {
240
+ ref,
241
+ className: cn(
242
+ "font-semibold leading-none tracking-tight text-text-strong",
243
+ className
244
+ ),
245
+ ...props
246
+ }
247
+ ));
248
+ CardTitle.displayName = "CardTitle";
249
+ var CardDescription = React16.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
250
+ "div",
251
+ {
252
+ ref,
253
+ className: cn("text-sm text-text-subtle", className),
254
+ ...props
255
+ }
256
+ ));
257
+ CardDescription.displayName = "CardDescription";
258
+ var CardContent = React16.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx("div", { ref, className: cn("p-5 pt-0", className), ...props }));
259
+ CardContent.displayName = "CardContent";
260
+ var CardFooter = React16.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
261
+ "div",
262
+ {
263
+ ref,
264
+ className: cn("flex items-center gap-3 p-5 pt-0", className),
265
+ ...props
266
+ }
267
+ ));
268
+ CardFooter.displayName = "CardFooter";
269
+ var ScrollArea = React16.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxs(
270
+ ScrollAreaPrimitive.Root,
271
+ {
272
+ ref,
273
+ className: cn("relative overflow-hidden", className),
274
+ ...props,
275
+ children: [
276
+ /* @__PURE__ */ jsx(ScrollAreaPrimitive.Viewport, { className: "h-full w-full rounded-[inherit]", children }),
277
+ /* @__PURE__ */ jsx(ScrollBar, {}),
278
+ /* @__PURE__ */ jsx(ScrollAreaPrimitive.Corner, {})
279
+ ]
280
+ }
281
+ ));
282
+ ScrollArea.displayName = ScrollAreaPrimitive.Root.displayName;
283
+ var ScrollBar = React16.forwardRef(({ className, orientation = "vertical", ...props }, ref) => /* @__PURE__ */ jsx(
284
+ ScrollAreaPrimitive.ScrollAreaScrollbar,
285
+ {
286
+ ref,
287
+ orientation,
288
+ className: cn(
289
+ "flex touch-none select-none transition-colors",
290
+ orientation === "vertical" && "h-full w-2.5 border-l border-l-transparent p-[1px]",
291
+ orientation === "horizontal" && "h-2.5 flex-col border-t border-t-transparent p-[1px]",
292
+ className
293
+ ),
294
+ ...props,
295
+ children: /* @__PURE__ */ jsx(ScrollAreaPrimitive.ScrollAreaThumb, { className: "relative flex-1 rounded-full bg-slate-300 hover:bg-slate-400" })
296
+ }
297
+ ));
298
+ ScrollBar.displayName = ScrollAreaPrimitive.ScrollAreaScrollbar.displayName;
299
+ var labelVariants = cva(
300
+ "inline-flex items-center justify-center gap-1 rounded-full px-2 py-0.5 text-xs font-medium whitespace-nowrap [&_svg]:pointer-events-none [&_svg]:size-3 [&_svg]:shrink-0",
301
+ {
302
+ variants: {
303
+ variant: {
304
+ brand: "bg-brand-subtle text-brand-text",
305
+ danger: "bg-danger-subtle text-danger-text",
306
+ neutral: "bg-[var(--comp-label-neutral-bg)] text-[var(--comp-label-neutral-text)]"
307
+ }
308
+ },
309
+ defaultVariants: {
310
+ variant: "neutral"
311
+ }
312
+ }
313
+ );
314
+ var Label = React16.forwardRef(
315
+ ({ className, variant, ...props }, ref) => {
316
+ return /* @__PURE__ */ jsx(
317
+ "span",
318
+ {
319
+ ref,
320
+ className: cn(labelVariants({ variant, className })),
321
+ ...props
322
+ }
323
+ );
324
+ }
325
+ );
326
+ Label.displayName = "Label";
327
+ var skeletonVariants = cva("animate-pulse bg-border-subtle", {
328
+ variants: {
329
+ variant: {
330
+ text: "rounded-lg h-4 w-full",
331
+ circle: "rounded-full",
332
+ rect: "rounded-lg"
333
+ }
334
+ },
335
+ defaultVariants: {
336
+ variant: "rect"
337
+ }
338
+ });
339
+ var Skeleton = React16.forwardRef(
340
+ ({ className, variant, ...props }, ref) => {
341
+ return /* @__PURE__ */ jsx(
342
+ "div",
343
+ {
344
+ ref,
345
+ role: "status",
346
+ "aria-busy": "true",
347
+ "aria-live": "polite",
348
+ "aria-label": "\uB85C\uB529 \uC911",
349
+ className: cn(skeletonVariants({ variant, className })),
350
+ ...props,
351
+ children: /* @__PURE__ */ jsx("span", { className: "sr-only", children: "\uB85C\uB529 \uC911" })
352
+ }
353
+ );
354
+ }
355
+ );
356
+ Skeleton.displayName = "Skeleton";
357
+ var progressTrackVariants = cva(
358
+ "relative w-full overflow-hidden rounded-full bg-border-subtle",
359
+ {
360
+ variants: {
361
+ size: {
362
+ md: "h-1.5"
363
+ }
364
+ },
365
+ defaultVariants: {
366
+ size: "md"
367
+ }
368
+ }
369
+ );
370
+ var progressFillVariants = cva(
371
+ "h-full rounded-full bg-brand-solid transition-[width] duration-300 ease-out",
372
+ {
373
+ variants: {
374
+ indeterminate: {
375
+ true: "w-full animate-pulse rounded-full",
376
+ false: ""
377
+ }
378
+ },
379
+ defaultVariants: {
380
+ indeterminate: false
381
+ }
382
+ }
383
+ );
384
+ function clamp(value) {
385
+ if (Number.isNaN(value)) return 0;
386
+ return Math.min(100, Math.max(0, value));
387
+ }
388
+ var Progress = React16.forwardRef(
389
+ ({
390
+ className,
391
+ size,
392
+ value = 0,
393
+ indeterminate = false,
394
+ label = "Loading",
395
+ ...props
396
+ }, ref) => {
397
+ const pct = clamp(value);
398
+ return /* @__PURE__ */ jsx(
399
+ "div",
400
+ {
401
+ ref,
402
+ role: "progressbar",
403
+ "aria-label": label,
404
+ "aria-valuemin": indeterminate ? void 0 : 0,
405
+ "aria-valuemax": indeterminate ? void 0 : 100,
406
+ "aria-valuenow": indeterminate ? void 0 : pct,
407
+ "aria-valuetext": indeterminate ? "Loading" : `${pct}%`,
408
+ className: cn(progressTrackVariants({ size, className })),
409
+ ...props,
410
+ children: /* @__PURE__ */ jsx(
411
+ "div",
412
+ {
413
+ className: cn(progressFillVariants({ indeterminate })),
414
+ style: indeterminate ? void 0 : { width: `${pct}%` }
415
+ }
416
+ )
417
+ }
418
+ );
419
+ }
420
+ );
421
+ Progress.displayName = "Progress";
422
+ var checkboxVariants = cva(
423
+ "relative flex size-5 shrink-0 items-center justify-center rounded border-2 transition-colors peer-focus-visible:outline-none peer-focus-visible:ring-2 peer-focus-visible:ring-offset-2 peer-focus-visible:ring-offset-bg-base peer-disabled:cursor-not-allowed peer-disabled:opacity-50",
424
+ {
425
+ variants: {
426
+ state: {
427
+ default: "border-border-strong bg-surface-default peer-focus-visible:ring-border-focus peer-checked:border-brand-solid peer-checked:bg-brand-solid",
428
+ error: "border-danger-border bg-surface-default peer-focus-visible:ring-danger-solid peer-checked:border-danger-border peer-checked:bg-danger-solid"
429
+ }
430
+ },
431
+ defaultVariants: {
432
+ state: "default"
433
+ }
434
+ }
435
+ );
436
+ var CheckIcon = ({ className }) => /* @__PURE__ */ jsx(
437
+ "svg",
438
+ {
439
+ viewBox: "0 0 12 12",
440
+ fill: "none",
441
+ "aria-hidden": "true",
442
+ className: cn("size-3", className),
443
+ children: /* @__PURE__ */ jsx(
444
+ "path",
445
+ {
446
+ d: "M2.5 6.2 5 8.7l4.5-5",
447
+ stroke: "currentColor",
448
+ strokeWidth: 2,
449
+ strokeLinecap: "round",
450
+ strokeLinejoin: "round"
451
+ }
452
+ )
453
+ }
454
+ );
455
+ var Checkbox = React16.forwardRef(
456
+ ({ className, state, error, label, id, disabled, "aria-invalid": ariaInvalid, ...props }, ref) => {
457
+ const reactId = React16.useId();
458
+ const inputId = id ?? reactId;
459
+ const resolvedState = error ? "error" : state ?? "default";
460
+ const isInvalid = resolvedState === "error";
461
+ return /* @__PURE__ */ jsxs(
462
+ "label",
463
+ {
464
+ htmlFor: inputId,
465
+ className: cn(
466
+ "group inline-flex items-start gap-2 text-sm text-text-default",
467
+ disabled ? "cursor-not-allowed text-text-disabled" : "cursor-pointer"
468
+ ),
469
+ children: [
470
+ /* @__PURE__ */ jsx(
471
+ "input",
472
+ {
473
+ ref,
474
+ id: inputId,
475
+ type: "checkbox",
476
+ disabled,
477
+ "aria-invalid": ariaInvalid ?? (isInvalid || void 0),
478
+ className: "peer sr-only",
479
+ ...props
480
+ }
481
+ ),
482
+ /* @__PURE__ */ jsx(
483
+ "span",
484
+ {
485
+ className: cn(
486
+ checkboxVariants({ state: resolvedState }),
487
+ // 여러 줄 라벨에서 첫 줄 글자와 광학적으로 맞추기 위한 미세 보정(2px)
488
+ "mt-0.5 peer-checked:[&>svg]:opacity-100",
489
+ className
490
+ ),
491
+ children: /* @__PURE__ */ jsx(CheckIcon, { className: "text-text-on-brand opacity-0 transition-opacity" })
492
+ }
493
+ ),
494
+ label != null && /* @__PURE__ */ jsx("span", { className: "select-none", children: label })
495
+ ]
496
+ }
497
+ );
498
+ }
499
+ );
500
+ Checkbox.displayName = "Checkbox";
501
+ var RadioGroupContext = React16.createContext(
502
+ null
503
+ );
504
+ var RadioGroup = React16.forwardRef(
505
+ ({
506
+ className,
507
+ name,
508
+ value,
509
+ defaultValue,
510
+ onValueChange,
511
+ disabled,
512
+ error,
513
+ children,
514
+ ...props
515
+ }, ref) => {
516
+ const [internalValue, setInternalValue] = React16.useState(defaultValue);
517
+ const isControlled = value !== void 0;
518
+ const currentValue = isControlled ? value : internalValue;
519
+ const handleValueChange = React16.useCallback(
520
+ (next) => {
521
+ if (!isControlled) setInternalValue(next);
522
+ onValueChange?.(next);
523
+ },
524
+ [isControlled, onValueChange]
525
+ );
526
+ const ctx = React16.useMemo(
527
+ () => ({
528
+ name,
529
+ value: currentValue,
530
+ onValueChange: handleValueChange,
531
+ disabled,
532
+ error
533
+ }),
534
+ [name, currentValue, handleValueChange, disabled, error]
535
+ );
536
+ return /* @__PURE__ */ jsx(RadioGroupContext.Provider, { value: ctx, children: /* @__PURE__ */ jsx(
537
+ "div",
538
+ {
539
+ ref,
540
+ role: "radiogroup",
541
+ "aria-invalid": error || void 0,
542
+ className: cn("flex flex-col gap-3", className),
543
+ ...props,
544
+ children
545
+ }
546
+ ) });
547
+ }
548
+ );
549
+ RadioGroup.displayName = "RadioGroup";
550
+ var radioVariants = cva(
551
+ "relative grid h-5 w-5 shrink-0 place-items-center rounded-full border-2 bg-surface-default transition-colors peer-focus-visible:outline-none peer-focus-visible:ring-2 peer-focus-visible:ring-offset-2 peer-focus-visible:ring-offset-bg-base disabled:cursor-not-allowed",
552
+ {
553
+ variants: {
554
+ state: {
555
+ default: "border-border-strong peer-focus-visible:ring-border-focus peer-checked:border-brand-border",
556
+ error: "border-danger-border peer-focus-visible:ring-danger-solid peer-checked:border-danger-border"
557
+ }
558
+ },
559
+ defaultVariants: {
560
+ state: "default"
561
+ }
562
+ }
563
+ );
564
+ var radioDotVariants = cva(
565
+ "h-2 w-2 rounded-full scale-0 transition-transform peer-checked:scale-100",
566
+ {
567
+ variants: {
568
+ state: {
569
+ default: "bg-brand-solid",
570
+ error: "bg-danger-solid"
571
+ }
572
+ },
573
+ defaultVariants: {
574
+ state: "default"
575
+ }
576
+ }
577
+ );
578
+ var Radio = React16.forwardRef(
579
+ ({
580
+ className,
581
+ id,
582
+ value,
583
+ label,
584
+ description,
585
+ name: nameProp,
586
+ checked: checkedProp,
587
+ disabled: disabledProp,
588
+ error: errorProp,
589
+ onValueChange,
590
+ onChange,
591
+ onClick,
592
+ ...props
593
+ }, ref) => {
594
+ const group = React16.useContext(RadioGroupContext);
595
+ const reactId = React16.useId();
596
+ const inputId = id ?? reactId;
597
+ const isGroup = group != null;
598
+ const isControlled = checkedProp !== void 0;
599
+ const [internalChecked, setInternalChecked] = React16.useState(false);
600
+ const name = nameProp ?? group?.name;
601
+ const disabled = disabledProp ?? group?.disabled ?? false;
602
+ const error = errorProp ?? group?.error ?? false;
603
+ const checked = isControlled ? checkedProp : isGroup ? group.value === value : internalChecked;
604
+ const state = error ? "error" : "default";
605
+ const handleChange = (event) => {
606
+ onChange?.(event);
607
+ if (event.target.checked) {
608
+ onValueChange?.(value);
609
+ group?.onValueChange?.(value);
610
+ }
611
+ };
612
+ const handleClick = (event) => {
613
+ onClick?.(event);
614
+ if (!isControlled && !isGroup) {
615
+ setInternalChecked((prev) => !prev);
616
+ }
617
+ };
618
+ const control = /* @__PURE__ */ jsxs("span", { className: "relative inline-flex", children: [
619
+ /* @__PURE__ */ jsx(
620
+ "input",
621
+ {
622
+ ref,
623
+ id: inputId,
624
+ type: "radio",
625
+ name,
626
+ value,
627
+ checked,
628
+ disabled,
629
+ "aria-invalid": error || void 0,
630
+ onChange: handleChange,
631
+ onClick: handleClick,
632
+ className: "peer sr-only",
633
+ ...props
634
+ }
635
+ ),
636
+ /* @__PURE__ */ jsx(
637
+ "span",
638
+ {
639
+ "aria-hidden": "true",
640
+ className: cn(
641
+ radioVariants({ state }),
642
+ disabled ? "cursor-not-allowed opacity-50" : "cursor-pointer"
643
+ )
644
+ }
645
+ ),
646
+ /* @__PURE__ */ jsx(
647
+ "span",
648
+ {
649
+ "aria-hidden": "true",
650
+ className: cn(
651
+ radioDotVariants({ state }),
652
+ "pointer-events-none absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2",
653
+ disabled && "opacity-50"
654
+ )
655
+ }
656
+ )
657
+ ] });
658
+ if (!label && !description) {
659
+ return control;
660
+ }
661
+ return /* @__PURE__ */ jsxs(
662
+ "label",
663
+ {
664
+ htmlFor: inputId,
665
+ className: cn(
666
+ "flex items-start gap-2",
667
+ disabled ? "cursor-not-allowed" : "cursor-pointer",
668
+ className
669
+ ),
670
+ children: [
671
+ control,
672
+ /* @__PURE__ */ jsxs("span", { className: "flex flex-col gap-0.5", children: [
673
+ label ? /* @__PURE__ */ jsx(
674
+ "span",
675
+ {
676
+ className: cn(
677
+ "text-sm leading-5",
678
+ disabled ? "text-text-disabled" : "text-text-default"
679
+ ),
680
+ children: label
681
+ }
682
+ ) : null,
683
+ description ? /* @__PURE__ */ jsx(
684
+ "span",
685
+ {
686
+ className: cn(
687
+ "text-xs leading-4",
688
+ error ? "text-danger-text" : "text-text-subtle"
689
+ ),
690
+ children: description
691
+ }
692
+ ) : null
693
+ ] })
694
+ ]
695
+ }
696
+ );
697
+ }
698
+ );
699
+ Radio.displayName = "Radio";
700
+ var toggleTrackVariants = cva(
701
+ "relative inline-flex shrink-0 cursor-pointer items-center rounded-full p-0.5 transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-border-focus focus-visible:ring-offset-2 focus-visible:ring-offset-bg-base disabled:cursor-not-allowed disabled:opacity-50 data-[state=off]:bg-surface-sunken data-[state=on]:bg-brand-solid",
702
+ {
703
+ variants: {
704
+ size: {
705
+ sm: "h-5 w-9",
706
+ md: "h-6 w-11"
707
+ }
708
+ },
709
+ defaultVariants: {
710
+ size: "md"
711
+ }
712
+ }
713
+ );
714
+ var toggleThumbVariants = cva(
715
+ "pointer-events-none block rounded-full bg-surface-default shadow-sm transition-transform data-[state=off]:translate-x-0",
716
+ {
717
+ variants: {
718
+ size: {
719
+ sm: "h-4 w-4 data-[state=on]:translate-x-4",
720
+ md: "h-5 w-5 data-[state=on]:translate-x-5"
721
+ }
722
+ },
723
+ defaultVariants: {
724
+ size: "md"
725
+ }
726
+ }
727
+ );
728
+ var Toggle = React16.forwardRef(
729
+ ({
730
+ className,
731
+ size,
732
+ checked,
733
+ defaultChecked = false,
734
+ onCheckedChange,
735
+ disabled,
736
+ onClick,
737
+ ...props
738
+ }, ref) => {
739
+ const isControlled = checked !== void 0;
740
+ const [internalChecked, setInternalChecked] = React16.useState(defaultChecked);
741
+ const isOn = isControlled ? checked : internalChecked;
742
+ const state = isOn ? "on" : "off";
743
+ const handleClick = (event) => {
744
+ onClick?.(event);
745
+ if (event.defaultPrevented || disabled) return;
746
+ const next = !isOn;
747
+ if (!isControlled) setInternalChecked(next);
748
+ onCheckedChange?.(next);
749
+ };
750
+ return /* @__PURE__ */ jsx(
751
+ "button",
752
+ {
753
+ ref,
754
+ type: "button",
755
+ role: "switch",
756
+ "aria-checked": isOn,
757
+ "data-state": state,
758
+ disabled,
759
+ onClick: handleClick,
760
+ className: cn(toggleTrackVariants({ size, className })),
761
+ ...props,
762
+ children: /* @__PURE__ */ jsx("span", { "data-state": state, className: cn(toggleThumbVariants({ size })) })
763
+ }
764
+ );
765
+ }
766
+ );
767
+ Toggle.displayName = "Toggle";
768
+ var chipVariants = cva(
769
+ "inline-flex h-8 items-center gap-1 rounded border px-3 text-sm font-semibold transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-border-focus focus-visible:ring-offset-2 focus-visible:ring-offset-bg-base disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
770
+ {
771
+ variants: {
772
+ variant: {
773
+ default: "border-border-default bg-surface-default text-text-default hover:bg-surface-hover active:bg-surface-pressed",
774
+ selected: "border-brand-border bg-brand-subtle text-brand-text font-bold hover:bg-brand-subtle active:bg-brand-subtle"
775
+ }
776
+ },
777
+ defaultVariants: {
778
+ variant: "default"
779
+ }
780
+ }
781
+ );
782
+ var CheckIcon2 = () => /* @__PURE__ */ jsx(
783
+ "svg",
784
+ {
785
+ viewBox: "0 0 16 16",
786
+ fill: "none",
787
+ stroke: "currentColor",
788
+ strokeWidth: "2",
789
+ strokeLinecap: "round",
790
+ strokeLinejoin: "round",
791
+ "aria-hidden": "true",
792
+ children: /* @__PURE__ */ jsx("path", { d: "M13 4.5 6.5 11.5 3 8" })
793
+ }
794
+ );
795
+ var CloseIcon = () => /* @__PURE__ */ jsx(
796
+ "svg",
797
+ {
798
+ viewBox: "0 0 16 16",
799
+ fill: "none",
800
+ stroke: "currentColor",
801
+ strokeWidth: "2",
802
+ strokeLinecap: "round",
803
+ strokeLinejoin: "round",
804
+ "aria-hidden": "true",
805
+ children: /* @__PURE__ */ jsx("path", { d: "M4 4l8 8M12 4l-8 8" })
806
+ }
807
+ );
808
+ var Chip = React16.forwardRef(
809
+ ({
810
+ className,
811
+ variant,
812
+ showSelectedIcon = false,
813
+ removable = false,
814
+ onRemove,
815
+ removeLabel = "Remove",
816
+ children,
817
+ disabled,
818
+ ...props
819
+ }, ref) => {
820
+ return /* @__PURE__ */ jsxs(
821
+ "button",
822
+ {
823
+ type: "button",
824
+ ref,
825
+ disabled,
826
+ "aria-pressed": variant === "selected",
827
+ className: cn(chipVariants({ variant }), removable && "pr-2", className),
828
+ ...props,
829
+ children: [
830
+ showSelectedIcon && variant === "selected" ? /* @__PURE__ */ jsx(CheckIcon2, {}) : null,
831
+ /* @__PURE__ */ jsx("span", { className: "truncate", children }),
832
+ removable ? /* @__PURE__ */ jsx(
833
+ "span",
834
+ {
835
+ role: "button",
836
+ "aria-label": removeLabel,
837
+ tabIndex: disabled ? -1 : 0,
838
+ onClick: (e) => {
839
+ e.stopPropagation();
840
+ if (!disabled) onRemove?.();
841
+ },
842
+ onKeyDown: (e) => {
843
+ if (e.key === "Enter" || e.key === " ") {
844
+ e.preventDefault();
845
+ e.stopPropagation();
846
+ if (!disabled) onRemove?.();
847
+ }
848
+ },
849
+ className: "-mr-1 ml-0.5 inline-flex size-4 items-center justify-center rounded-full text-text-subtle transition-colors hover:bg-surface-hover hover:text-text-default focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-border-focus focus-visible:ring-offset-2 focus-visible:ring-offset-bg-base",
850
+ children: /* @__PURE__ */ jsx(CloseIcon, {})
851
+ }
852
+ ) : null
853
+ ]
854
+ }
855
+ );
856
+ }
857
+ );
858
+ Chip.displayName = "Chip";
859
+ var alertVariants = cva(
860
+ "relative flex gap-3 rounded-lg border p-3 [&>svg]:size-5 [&>svg]:shrink-0 [&>svg]:mt-0.5",
861
+ {
862
+ variants: {
863
+ variant: {
864
+ info: "bg-surface-sunken border-border-default text-text-default [&>svg]:text-text-subtle",
865
+ danger: "bg-danger-subtle border-danger-border text-danger-text [&>svg]:text-danger-text"
866
+ }
867
+ },
868
+ defaultVariants: {
869
+ variant: "info"
870
+ }
871
+ }
872
+ );
873
+ var Alert = React16.forwardRef(
874
+ ({ className, variant, role = "alert", ...props }, ref) => /* @__PURE__ */ jsx(
875
+ "div",
876
+ {
877
+ ref,
878
+ role,
879
+ className: cn(alertVariants({ variant, className })),
880
+ ...props
881
+ }
882
+ )
883
+ );
884
+ Alert.displayName = "Alert";
885
+ var AlertTitle = React16.forwardRef(
886
+ ({ className, ...props }, ref) => /* @__PURE__ */ jsx(
887
+ "p",
888
+ {
889
+ ref,
890
+ className: cn(
891
+ "text-sm font-semibold leading-5 text-text-strong",
892
+ className
893
+ ),
894
+ ...props
895
+ }
896
+ )
897
+ );
898
+ AlertTitle.displayName = "AlertTitle";
899
+ var AlertDescription = React16.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
900
+ "p",
901
+ {
902
+ ref,
903
+ className: cn("mt-1 text-sm leading-5 [&_p]:leading-5", className),
904
+ ...props
905
+ }
906
+ ));
907
+ AlertDescription.displayName = "AlertDescription";
908
+ var SIZE_PX = { sm: 12, md: 16, lg: 20 };
909
+ var PRESENCE_LABEL = {
910
+ online: "\uC628\uB77C\uC778",
911
+ away: "\uC790\uB9AC\uBE44\uC6C0",
912
+ offline: "\uC624\uD504\uB77C\uC778",
913
+ remote: "\uC7AC\uD0DD"
914
+ };
915
+ function HouseMark({ px }) {
916
+ const s = Math.round(px * 0.64);
917
+ return /* @__PURE__ */ jsx(
918
+ "svg",
919
+ {
920
+ width: s,
921
+ height: s,
922
+ viewBox: "0 0 24 24",
923
+ fill: "currentColor",
924
+ "aria-hidden": true,
925
+ children: /* @__PURE__ */ jsx("path", { d: "M12 3 4 9v12h5v-7h6v7h5V9z" })
926
+ }
927
+ );
928
+ }
929
+ function Presence({
930
+ status = "online",
931
+ size = "md",
932
+ className,
933
+ ...props
934
+ }) {
935
+ const px = SIZE_PX[size];
936
+ const label = PRESENCE_LABEL[status];
937
+ const base = "inline-flex shrink-0 items-center justify-center rounded-full font-extrabold leading-none";
938
+ const STATUS_STYLE = {
939
+ online: "bg-brand-solid text-text-on-brand",
940
+ away: "bg-presence-away-bg text-presence-away-fg",
941
+ offline: "bg-presence-offline",
942
+ remote: "bg-brand-solid text-text-on-brand"
943
+ };
944
+ return /* @__PURE__ */ jsxs(
945
+ "span",
946
+ {
947
+ role: "img",
948
+ "aria-label": label,
949
+ title: label,
950
+ className: cn(base, STATUS_STYLE[status], className),
951
+ style: { width: px, height: px, fontSize: Math.round(px * 0.58) },
952
+ ...props,
953
+ children: [
954
+ status === "away" && "Z",
955
+ status === "remote" && /* @__PURE__ */ jsx(HouseMark, { px })
956
+ ]
957
+ }
958
+ );
959
+ }
960
+ var avatarVariants = cva(
961
+ "relative inline-flex shrink-0 items-center justify-center overflow-hidden rounded-full bg-surface-sunken align-middle ring-2 ring-border-subtle select-none",
962
+ {
963
+ variants: {
964
+ size: {
965
+ xs: "h-6 w-6 text-[10px]",
966
+ // 24px
967
+ sm: "h-8 w-8 text-sm",
968
+ // 32px · font14 기본
969
+ md: "h-10 w-10 text-base",
970
+ // 40px
971
+ lg: "h-12 w-12 text-lg",
972
+ // 48px
973
+ xl: "h-16 w-16 text-2xl"
974
+ // 64px
975
+ }
976
+ },
977
+ defaultVariants: {
978
+ size: "sm"
979
+ }
980
+ }
981
+ );
982
+ var presenceSizeForAvatar = {
983
+ xs: "sm",
984
+ sm: "sm",
985
+ md: "md",
986
+ lg: "md",
987
+ xl: "lg"
988
+ };
989
+ var MASCOTS = {};
990
+ try {
991
+ MASCOTS = import.meta.glob(
992
+ "../../design-system/avatars/avatar-mascot-*.svg",
993
+ { query: "?url", import: "default", eager: true }
994
+ );
995
+ } catch {
996
+ MASCOTS = {};
997
+ }
998
+ var MASCOT_BY_COLOR = {};
999
+ for (const [path, url] of Object.entries(MASCOTS)) {
1000
+ const m = path.match(/avatar-mascot-([a-z]+)\.svg$/);
1001
+ if (m) MASCOT_BY_COLOR[m[1]] = url;
1002
+ }
1003
+ var AVATAR_COLORS = Object.keys(
1004
+ MASCOT_BY_COLOR
1005
+ ).sort();
1006
+ function defaultColor(seed) {
1007
+ if (AVATAR_COLORS.length === 0) return "gray";
1008
+ const s = seed ?? "";
1009
+ let h = 0;
1010
+ for (let i = 0; i < s.length; i++) h = h * 31 + s.charCodeAt(i) >>> 0;
1011
+ return AVATAR_COLORS[h % AVATAR_COLORS.length];
1012
+ }
1013
+ function mascotUrl(color, seed) {
1014
+ const c = color ?? defaultColor(seed);
1015
+ return MASCOT_BY_COLOR[c] ?? MASCOT_BY_COLOR[AVATAR_COLORS[0]] ?? "";
1016
+ }
1017
+ function MascotImage({
1018
+ color,
1019
+ seed,
1020
+ alt
1021
+ }) {
1022
+ return /* @__PURE__ */ jsx(
1023
+ "img",
1024
+ {
1025
+ src: mascotUrl(color, seed),
1026
+ alt: alt ?? "",
1027
+ "aria-hidden": alt ? void 0 : true,
1028
+ className: "h-full w-full object-cover"
1029
+ }
1030
+ );
1031
+ }
1032
+ var Avatar = React16.forwardRef(
1033
+ ({ className, size = "sm", src, alt, color, presence, children, ...props }, ref) => {
1034
+ const resolvedSize = size ?? "sm";
1035
+ return (
1036
+ // presence badge가 원 밖으로 걸치도록 wrapper(relative)에 이미지 원 + badge를 형제로 배치.
1037
+ /* @__PURE__ */ jsxs(
1038
+ "span",
1039
+ {
1040
+ ref,
1041
+ className: cn("relative inline-flex shrink-0 align-middle", className),
1042
+ ...props,
1043
+ children: [
1044
+ /* @__PURE__ */ jsx(
1045
+ "span",
1046
+ {
1047
+ className: cn(
1048
+ avatarVariants({ size }),
1049
+ // 가이드: 오프라인이면 아바타를 흐리게(opacity .55)
1050
+ presence === "offline" && "opacity-[.55]"
1051
+ ),
1052
+ children: children ?? (src ? /* @__PURE__ */ jsx(AvatarImage, { src, alt, color, seed: alt }) : (
1053
+ // 기본: 무조건 마스코트 이미지. color 선택 시 그 색, 없으면 이름 기반 기본값.
1054
+ /* @__PURE__ */ jsx(MascotImage, { color, seed: alt, alt })
1055
+ ))
1056
+ }
1057
+ ),
1058
+ presence ? /* @__PURE__ */ jsx(
1059
+ Presence,
1060
+ {
1061
+ status: presence,
1062
+ size: presenceSizeForAvatar[resolvedSize],
1063
+ className: "absolute -bottom-0.5 -right-0.5 ring-2 ring-bg-base"
1064
+ }
1065
+ ) : null
1066
+ ]
1067
+ }
1068
+ )
1069
+ );
1070
+ }
1071
+ );
1072
+ Avatar.displayName = "Avatar";
1073
+ var AvatarImage = React16.forwardRef(
1074
+ ({ className, alt = "", color, seed, ...props }, ref) => {
1075
+ const [errored, setErrored] = React16.useState(false);
1076
+ if (errored) {
1077
+ return /* @__PURE__ */ jsx(MascotImage, { color, seed: seed ?? alt, alt });
1078
+ }
1079
+ return /* @__PURE__ */ jsx(
1080
+ "img",
1081
+ {
1082
+ ref,
1083
+ alt,
1084
+ className: cn("h-full w-full object-cover", className),
1085
+ onError: () => setErrored(true),
1086
+ ...props
1087
+ }
1088
+ );
1089
+ }
1090
+ );
1091
+ AvatarImage.displayName = "AvatarImage";
1092
+ var AvatarFallback = React16.forwardRef(
1093
+ ({ className, ...props }, ref) => /* @__PURE__ */ jsx(
1094
+ "span",
1095
+ {
1096
+ ref,
1097
+ className: cn(
1098
+ "flex h-full w-full items-center justify-center bg-surface-sunken font-medium text-text-subtle",
1099
+ className
1100
+ ),
1101
+ ...props
1102
+ }
1103
+ )
1104
+ );
1105
+ AvatarFallback.displayName = "AvatarFallback";
1106
+ var AvatarGroup = React16.forwardRef(
1107
+ ({ className, children, max, size = "sm", ...props }, ref) => {
1108
+ const items = React16.Children.toArray(children).filter(React16.isValidElement);
1109
+ const visible = typeof max === "number" ? items.slice(0, max) : items;
1110
+ const overflow = items.length - visible.length;
1111
+ return /* @__PURE__ */ jsxs("div", { ref, className: cn("flex items-center", className), ...props, children: [
1112
+ visible.map((child, i) => /* @__PURE__ */ jsx("span", { className: i === 0 ? "" : "-ml-2", children: child }, i)),
1113
+ overflow > 0 ? /* @__PURE__ */ jsxs(
1114
+ "span",
1115
+ {
1116
+ className: cn(
1117
+ avatarVariants({ size }),
1118
+ "-ml-2 font-medium text-text-subtle"
1119
+ ),
1120
+ "aria-label": `\uC678 ${overflow}\uBA85`,
1121
+ title: `\uC678 ${overflow}\uBA85`,
1122
+ children: [
1123
+ "+",
1124
+ overflow
1125
+ ]
1126
+ }
1127
+ ) : null
1128
+ ] });
1129
+ }
1130
+ );
1131
+ AvatarGroup.displayName = "AvatarGroup";
1132
+ var tooltipContentVariants = cva(
1133
+ "pointer-events-none absolute z-50 w-max max-w-xs whitespace-normal rounded bg-[var(--comp-tooltip-bg)] px-2 py-1 text-xs font-medium text-[var(--comp-tooltip-text)] shadow-popover",
1134
+ {
1135
+ variants: {
1136
+ placement: {
1137
+ top: "bottom-full left-1/2 mb-2 -translate-x-1/2",
1138
+ "top-left": "bottom-full left-0 mb-2",
1139
+ "top-right": "bottom-full right-0 mb-2",
1140
+ right: "left-full top-1/2 ml-2 -translate-y-1/2",
1141
+ bottom: "top-full left-1/2 mt-2 -translate-x-1/2",
1142
+ "bottom-left": "top-full left-0 mt-2",
1143
+ "bottom-right": "top-full right-0 mt-2",
1144
+ left: "right-full top-1/2 mr-2 -translate-y-1/2"
1145
+ }
1146
+ },
1147
+ defaultVariants: {
1148
+ placement: "top"
1149
+ }
1150
+ }
1151
+ );
1152
+ var tooltipArrowVariants = cva(
1153
+ "pointer-events-none absolute z-50 h-2 w-2 rotate-45 bg-[var(--comp-tooltip-bg)]",
1154
+ {
1155
+ variants: {
1156
+ placement: {
1157
+ top: "bottom-full left-1/2 -translate-x-1/2 mb-[5px]",
1158
+ "top-left": "bottom-full left-1/2 -translate-x-1/2 mb-[5px]",
1159
+ "top-right": "bottom-full left-1/2 -translate-x-1/2 mb-[5px]",
1160
+ right: "left-full top-1/2 -translate-y-1/2 ml-[5px]",
1161
+ bottom: "top-full left-1/2 -translate-x-1/2 mt-[5px]",
1162
+ "bottom-left": "top-full left-1/2 -translate-x-1/2 mt-[5px]",
1163
+ "bottom-right": "top-full left-1/2 -translate-x-1/2 mt-[5px]",
1164
+ left: "right-full top-1/2 -translate-y-1/2 mr-[5px]"
1165
+ }
1166
+ },
1167
+ defaultVariants: {
1168
+ placement: "top"
1169
+ }
1170
+ }
1171
+ );
1172
+ var Tooltip = React16.forwardRef(
1173
+ ({
1174
+ className,
1175
+ content,
1176
+ children,
1177
+ placement = "top",
1178
+ open,
1179
+ arrow = true,
1180
+ disabled = false,
1181
+ ...props
1182
+ }, ref) => {
1183
+ const [hovered, setHovered] = React16.useState(false);
1184
+ const isControlled = open !== void 0;
1185
+ const visible = !disabled && (isControlled ? open : hovered);
1186
+ const tooltipId = React16.useId();
1187
+ return /* @__PURE__ */ jsxs(
1188
+ "div",
1189
+ {
1190
+ ref,
1191
+ className: cn("relative inline-flex", className),
1192
+ onMouseEnter: isControlled ? void 0 : () => setHovered(true),
1193
+ onMouseLeave: isControlled ? void 0 : () => setHovered(false),
1194
+ onFocus: isControlled ? void 0 : () => setHovered(true),
1195
+ onBlur: isControlled ? void 0 : () => setHovered(false),
1196
+ ...props,
1197
+ children: [
1198
+ /* @__PURE__ */ jsx("span", { "aria-describedby": visible ? tooltipId : void 0, children }),
1199
+ /* @__PURE__ */ jsx(
1200
+ "div",
1201
+ {
1202
+ id: tooltipId,
1203
+ role: "tooltip",
1204
+ "aria-hidden": !visible,
1205
+ className: cn(
1206
+ tooltipContentVariants({ placement }),
1207
+ "transition-opacity duration-150",
1208
+ visible ? "opacity-100" : "opacity-0"
1209
+ ),
1210
+ children: content
1211
+ }
1212
+ ),
1213
+ arrow ? /* @__PURE__ */ jsx(
1214
+ "span",
1215
+ {
1216
+ "aria-hidden": true,
1217
+ className: cn(
1218
+ tooltipArrowVariants({ placement }),
1219
+ "transition-opacity duration-150",
1220
+ visible ? "opacity-100" : "opacity-0"
1221
+ )
1222
+ }
1223
+ ) : null
1224
+ ]
1225
+ }
1226
+ );
1227
+ }
1228
+ );
1229
+ Tooltip.displayName = "Tooltip";
1230
+ var DropdownMenuContext = React16.createContext(null);
1231
+ function useDropdownMenuContext(component) {
1232
+ const ctx = React16.useContext(DropdownMenuContext);
1233
+ if (!ctx) {
1234
+ throw new Error(`${component} must be used within <DropdownMenu>`);
1235
+ }
1236
+ return ctx;
1237
+ }
1238
+ var DropdownMenu = React16.forwardRef(
1239
+ ({ className, open: openProp, defaultOpen = false, onOpenChange, ...props }, ref) => {
1240
+ const [uncontrolledOpen, setUncontrolledOpen] = React16.useState(defaultOpen);
1241
+ const isControlled = openProp !== void 0;
1242
+ const open = isControlled ? openProp : uncontrolledOpen;
1243
+ const setOpen = React16.useCallback(
1244
+ (value) => {
1245
+ const next = typeof value === "function" ? value(open) : value;
1246
+ if (!isControlled) setUncontrolledOpen(next);
1247
+ onOpenChange?.(next);
1248
+ },
1249
+ [isControlled, onOpenChange, open]
1250
+ );
1251
+ const ctx = React16.useMemo(() => ({ open, setOpen }), [open, setOpen]);
1252
+ return /* @__PURE__ */ jsx(DropdownMenuContext.Provider, { value: ctx, children: /* @__PURE__ */ jsx(
1253
+ "div",
1254
+ {
1255
+ ref,
1256
+ className: cn("relative inline-block text-left", className),
1257
+ ...props
1258
+ }
1259
+ ) });
1260
+ }
1261
+ );
1262
+ DropdownMenu.displayName = "DropdownMenu";
1263
+ var DropdownMenuTrigger = React16.forwardRef(({ className, onClick, disabled, ...props }, ref) => {
1264
+ const { open, setOpen } = useDropdownMenuContext("DropdownMenuTrigger");
1265
+ return /* @__PURE__ */ jsx(
1266
+ "button",
1267
+ {
1268
+ ref,
1269
+ type: "button",
1270
+ "aria-haspopup": "menu",
1271
+ "aria-expanded": open,
1272
+ disabled,
1273
+ onClick: (event) => {
1274
+ onClick?.(event);
1275
+ if (!event.defaultPrevented) setOpen((prev) => !prev);
1276
+ },
1277
+ className: cn(
1278
+ "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded border border-border-default bg-surface-default px-4 py-2 text-sm font-medium text-text-default transition-colors hover:bg-surface-hover active:bg-surface-pressed focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-border-focus focus-visible:ring-offset-2 focus-visible:ring-offset-bg-base disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
1279
+ className
1280
+ ),
1281
+ ...props
1282
+ }
1283
+ );
1284
+ });
1285
+ DropdownMenuTrigger.displayName = "DropdownMenuTrigger";
1286
+ var dropdownMenuContentVariants = cva(
1287
+ // panel padding 4 · radius sm(8) · min-w 180 · shadow-dropdown
1288
+ "z-50 min-w-[180px] rounded-lg border border-border-subtle bg-surface-raised p-1 shadow-dropdown outline-none",
1289
+ {
1290
+ variants: {
1291
+ align: {
1292
+ start: "left-0",
1293
+ end: "right-0"
1294
+ }
1295
+ },
1296
+ defaultVariants: {
1297
+ align: "start"
1298
+ }
1299
+ }
1300
+ );
1301
+ var DropdownMenuContent = React16.forwardRef(({ className, align, forceMount = false, ...props }, ref) => {
1302
+ const { open } = useDropdownMenuContext("DropdownMenuContent");
1303
+ if (!open && !forceMount) return null;
1304
+ return /* @__PURE__ */ jsx(
1305
+ "div",
1306
+ {
1307
+ ref,
1308
+ role: "menu",
1309
+ "aria-orientation": "vertical",
1310
+ className: cn(
1311
+ "absolute top-[calc(100%+4px)]",
1312
+ dropdownMenuContentVariants({ align, className })
1313
+ ),
1314
+ ...props
1315
+ }
1316
+ );
1317
+ });
1318
+ DropdownMenuContent.displayName = "DropdownMenuContent";
1319
+ var dropdownMenuItemVariants = cva(
1320
+ // item px12 py8 · item radius xs(4) · hover bg-surface-hover
1321
+ "relative flex w-full cursor-pointer select-none items-center gap-2 rounded px-3 py-2 text-sm text-text-default outline-none transition-colors hover:bg-surface-hover focus-visible:bg-surface-hover focus-visible:outline-none data-[disabled=true]:pointer-events-none data-[disabled=true]:text-text-disabled data-[disabled=true]:opacity-60 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
1322
+ {
1323
+ variants: {
1324
+ variant: {
1325
+ default: "",
1326
+ danger: "text-danger-text hover:bg-danger-subtle focus-visible:bg-danger-subtle"
1327
+ }
1328
+ },
1329
+ defaultVariants: {
1330
+ variant: "default"
1331
+ }
1332
+ }
1333
+ );
1334
+ var DropdownMenuItem = React16.forwardRef(
1335
+ ({
1336
+ className,
1337
+ variant,
1338
+ disabled = false,
1339
+ icon,
1340
+ trailing,
1341
+ children,
1342
+ ...props
1343
+ }, ref) => {
1344
+ return /* @__PURE__ */ jsxs(
1345
+ "button",
1346
+ {
1347
+ ref,
1348
+ type: "button",
1349
+ role: "menuitem",
1350
+ "aria-disabled": disabled || void 0,
1351
+ "data-disabled": disabled,
1352
+ tabIndex: disabled ? -1 : 0,
1353
+ className: cn(dropdownMenuItemVariants({ variant, className })),
1354
+ ...props,
1355
+ children: [
1356
+ icon ? /* @__PURE__ */ jsx("span", { className: "shrink-0", children: icon }) : null,
1357
+ /* @__PURE__ */ jsx("span", { className: "flex-1 truncate text-left", children }),
1358
+ trailing ? /* @__PURE__ */ jsx("span", { className: "ml-auto shrink-0 text-xs text-text-subtle", children: trailing }) : null
1359
+ ]
1360
+ }
1361
+ );
1362
+ }
1363
+ );
1364
+ DropdownMenuItem.displayName = "DropdownMenuItem";
1365
+ var DropdownMenuLabel = React16.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
1366
+ "div",
1367
+ {
1368
+ ref,
1369
+ className: cn(
1370
+ "px-3 py-2 text-xs font-semibold uppercase tracking-wide text-text-subtle",
1371
+ className
1372
+ ),
1373
+ ...props
1374
+ }
1375
+ ));
1376
+ DropdownMenuLabel.displayName = "DropdownMenuLabel";
1377
+ var DropdownMenuSeparator = React16.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
1378
+ "div",
1379
+ {
1380
+ ref,
1381
+ role: "separator",
1382
+ "aria-orientation": "horizontal",
1383
+ className: cn("my-1 h-px bg-border-subtle", className),
1384
+ ...props
1385
+ }
1386
+ ));
1387
+ DropdownMenuSeparator.displayName = "DropdownMenuSeparator";
1388
+ var DropdownMenuEmpty = React16.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsx(
1389
+ "div",
1390
+ {
1391
+ ref,
1392
+ className: cn("px-3 py-6 text-center text-sm text-text-subtle", className),
1393
+ ...props,
1394
+ children: children ?? "No items"
1395
+ }
1396
+ ));
1397
+ DropdownMenuEmpty.displayName = "DropdownMenuEmpty";
1398
+ var selectTriggerVariants = cva(
1399
+ "flex h-10 w-full items-center justify-between gap-2 rounded border bg-surface-default px-3 text-sm text-text-default transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:ring-offset-bg-base disabled:cursor-not-allowed disabled:bg-surface-sunken disabled:text-text-disabled",
1400
+ {
1401
+ variants: {
1402
+ state: {
1403
+ default: "border-border-default hover:border-border-strong focus-visible:ring-brand-border",
1404
+ error: "border-danger-border focus-visible:ring-danger-border"
1405
+ }
1406
+ },
1407
+ defaultVariants: {
1408
+ state: "default"
1409
+ }
1410
+ }
1411
+ );
1412
+ var ChevronIcon = ({ className }) => /* @__PURE__ */ jsx(
1413
+ "svg",
1414
+ {
1415
+ width: 20,
1416
+ height: 20,
1417
+ viewBox: "0 0 20 20",
1418
+ fill: "none",
1419
+ stroke: "currentColor",
1420
+ strokeWidth: 1.5,
1421
+ strokeLinecap: "round",
1422
+ strokeLinejoin: "round",
1423
+ "aria-hidden": "true",
1424
+ className,
1425
+ children: /* @__PURE__ */ jsx("path", { d: "M5 7.5 10 12.5 15 7.5" })
1426
+ }
1427
+ );
1428
+ var CheckIcon3 = ({ className }) => /* @__PURE__ */ jsx(
1429
+ "svg",
1430
+ {
1431
+ width: 20,
1432
+ height: 20,
1433
+ viewBox: "0 0 20 20",
1434
+ fill: "none",
1435
+ stroke: "currentColor",
1436
+ strokeWidth: 1.5,
1437
+ strokeLinecap: "round",
1438
+ strokeLinejoin: "round",
1439
+ "aria-hidden": "true",
1440
+ className,
1441
+ children: /* @__PURE__ */ jsx("path", { d: "m4.5 10.5 3.5 3.5 7.5-8" })
1442
+ }
1443
+ );
1444
+ var Select = React16.forwardRef(
1445
+ ({
1446
+ className,
1447
+ state,
1448
+ options,
1449
+ value,
1450
+ placeholder = "\uC120\uD0DD\uD558\uC138\uC694",
1451
+ onValueChange,
1452
+ disabled = false,
1453
+ defaultOpen = false,
1454
+ errorMessageId,
1455
+ "aria-label": ariaLabel,
1456
+ ...props
1457
+ }, ref) => {
1458
+ const [open, setOpen] = React16.useState(defaultOpen);
1459
+ const selected = React16.useMemo(
1460
+ () => options.find((o) => o.value === value),
1461
+ [options, value]
1462
+ );
1463
+ const isError = state === "error";
1464
+ const handleSelect = (option) => {
1465
+ if (option.disabled) return;
1466
+ onValueChange?.(option.value);
1467
+ setOpen(false);
1468
+ };
1469
+ return /* @__PURE__ */ jsxs("div", { ref, className: cn("relative w-full", className), ...props, children: [
1470
+ /* @__PURE__ */ jsxs(
1471
+ "button",
1472
+ {
1473
+ type: "button",
1474
+ disabled,
1475
+ "aria-haspopup": "listbox",
1476
+ "aria-expanded": open,
1477
+ "aria-invalid": isError || void 0,
1478
+ "aria-describedby": isError ? errorMessageId : void 0,
1479
+ "aria-label": ariaLabel,
1480
+ onClick: () => setOpen((o) => !o),
1481
+ className: cn(selectTriggerVariants({ state })),
1482
+ children: [
1483
+ /* @__PURE__ */ jsx(
1484
+ "span",
1485
+ {
1486
+ className: cn(
1487
+ "truncate",
1488
+ !selected && "text-text-subtle"
1489
+ ),
1490
+ children: selected ? selected.label : placeholder
1491
+ }
1492
+ ),
1493
+ /* @__PURE__ */ jsx(
1494
+ ChevronIcon,
1495
+ {
1496
+ className: cn(
1497
+ "shrink-0 text-text-subtle transition-transform",
1498
+ open && "rotate-180"
1499
+ )
1500
+ }
1501
+ )
1502
+ ]
1503
+ }
1504
+ ),
1505
+ open && !disabled && /* @__PURE__ */ jsxs(
1506
+ "ul",
1507
+ {
1508
+ role: "listbox",
1509
+ "aria-label": ariaLabel,
1510
+ className: "absolute z-10 mt-1 max-h-60 w-full overflow-auto rounded border border-border-subtle bg-surface-raised p-1 shadow-dropdown",
1511
+ children: [
1512
+ options.length === 0 && /* @__PURE__ */ jsx("li", { className: "px-3 py-2 text-sm text-text-subtle", children: "\uD56D\uBAA9\uC774 \uC5C6\uC2B5\uB2C8\uB2E4" }),
1513
+ options.map((option) => {
1514
+ const isSelected = option.value === value;
1515
+ return /* @__PURE__ */ jsxs(
1516
+ "li",
1517
+ {
1518
+ role: "option",
1519
+ "aria-selected": isSelected,
1520
+ "aria-disabled": option.disabled || void 0,
1521
+ onClick: () => handleSelect(option),
1522
+ className: cn(
1523
+ "flex items-center justify-between gap-2 rounded px-3 py-2 text-sm text-text-default",
1524
+ option.disabled ? "cursor-not-allowed text-text-disabled" : "cursor-pointer hover:bg-surface-hover active:bg-surface-pressed",
1525
+ isSelected && "bg-brand-subtle text-brand-text"
1526
+ ),
1527
+ children: [
1528
+ /* @__PURE__ */ jsx("span", { className: "truncate", children: option.label }),
1529
+ isSelected && /* @__PURE__ */ jsx(CheckIcon3, { className: "shrink-0 text-brand-text" })
1530
+ ]
1531
+ },
1532
+ option.value
1533
+ );
1534
+ })
1535
+ ]
1536
+ }
1537
+ )
1538
+ ] });
1539
+ }
1540
+ );
1541
+ Select.displayName = "Select";
1542
+ var DialogOverlay = React16.forwardRef(
1543
+ ({ className, ...props }, ref) => /* @__PURE__ */ jsx(
1544
+ "div",
1545
+ {
1546
+ ref,
1547
+ className: cn(
1548
+ "absolute inset-0 bg-bg-emphasis/60 backdrop-blur-sm",
1549
+ className
1550
+ ),
1551
+ ...props
1552
+ }
1553
+ )
1554
+ );
1555
+ DialogOverlay.displayName = "DialogOverlay";
1556
+ var dialogPanelVariants = cva(
1557
+ "relative flex w-full flex-col gap-4 border border-border-subtle bg-surface-raised p-6 pb-4 text-text-default shadow-modal rounded-lg focus-visible:outline-none",
1558
+ {
1559
+ variants: {
1560
+ size: {
1561
+ sm: "min-w-[280px] max-w-[360px]",
1562
+ md: "min-w-[280px] max-w-[480px]",
1563
+ lg: "min-w-[280px] max-w-[640px]"
1564
+ }
1565
+ },
1566
+ defaultVariants: {
1567
+ size: "md"
1568
+ }
1569
+ }
1570
+ );
1571
+ var Dialog = React16.forwardRef(
1572
+ ({
1573
+ open = true,
1574
+ onOpenChange,
1575
+ size,
1576
+ className,
1577
+ overlayClassName,
1578
+ children,
1579
+ ...props
1580
+ }, ref) => {
1581
+ if (!open) return null;
1582
+ const handleKeyDown = (event) => {
1583
+ if (event.key === "Escape") onOpenChange?.(false);
1584
+ };
1585
+ return /* @__PURE__ */ jsxs(
1586
+ "div",
1587
+ {
1588
+ className: "absolute inset-0 z-50 flex items-center justify-center p-4",
1589
+ onKeyDown: handleKeyDown,
1590
+ children: [
1591
+ /* @__PURE__ */ jsx(
1592
+ DialogOverlay,
1593
+ {
1594
+ className: overlayClassName,
1595
+ onClick: () => onOpenChange?.(false)
1596
+ }
1597
+ ),
1598
+ /* @__PURE__ */ jsx(
1599
+ "div",
1600
+ {
1601
+ ref,
1602
+ role: "dialog",
1603
+ "aria-modal": "true",
1604
+ className: cn(dialogPanelVariants({ size }), className),
1605
+ ...props,
1606
+ children
1607
+ }
1608
+ )
1609
+ ]
1610
+ }
1611
+ );
1612
+ }
1613
+ );
1614
+ Dialog.displayName = "Dialog";
1615
+ var DialogHeader = React16.forwardRef(
1616
+ ({ className, ...props }, ref) => /* @__PURE__ */ jsx(
1617
+ "div",
1618
+ {
1619
+ ref,
1620
+ className: cn("flex flex-col gap-1.5 text-left", className),
1621
+ ...props
1622
+ }
1623
+ )
1624
+ );
1625
+ DialogHeader.displayName = "DialogHeader";
1626
+ var DialogTitle = React16.forwardRef(
1627
+ ({ className, ...props }, ref) => /* @__PURE__ */ jsx(
1628
+ "h2",
1629
+ {
1630
+ ref,
1631
+ className: cn(
1632
+ "text-base font-semibold leading-tight text-text-strong",
1633
+ className
1634
+ ),
1635
+ ...props
1636
+ }
1637
+ )
1638
+ );
1639
+ DialogTitle.displayName = "DialogTitle";
1640
+ var DialogDescription = React16.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
1641
+ "p",
1642
+ {
1643
+ ref,
1644
+ className: cn("text-sm leading-relaxed text-text-subtle", className),
1645
+ ...props
1646
+ }
1647
+ ));
1648
+ DialogDescription.displayName = "DialogDescription";
1649
+ var DialogBody = React16.forwardRef(
1650
+ ({ className, ...props }, ref) => /* @__PURE__ */ jsx(
1651
+ "div",
1652
+ {
1653
+ ref,
1654
+ className: cn("text-sm text-text-default", className),
1655
+ ...props
1656
+ }
1657
+ )
1658
+ );
1659
+ DialogBody.displayName = "DialogBody";
1660
+ var DialogFooter = React16.forwardRef(
1661
+ ({ className, ...props }, ref) => /* @__PURE__ */ jsx(
1662
+ "div",
1663
+ {
1664
+ ref,
1665
+ className: cn(
1666
+ "flex flex-col-reverse gap-2 sm:flex-row sm:justify-end",
1667
+ className
1668
+ ),
1669
+ ...props
1670
+ }
1671
+ )
1672
+ );
1673
+ DialogFooter.displayName = "DialogFooter";
1674
+ var sheetOverlayVariants = cva(
1675
+ "absolute inset-0 bg-[var(--color-overlay-scrim)] transition-opacity duration-200",
1676
+ {
1677
+ variants: {
1678
+ open: {
1679
+ true: "opacity-100",
1680
+ false: "pointer-events-none opacity-0"
1681
+ }
1682
+ },
1683
+ defaultVariants: { open: false }
1684
+ }
1685
+ );
1686
+ var sheetPanelVariants = cva(
1687
+ "absolute flex flex-col gap-4 bg-surface-raised px-5 pb-6 pt-4 text-text-default shadow-modal transition-transform duration-300 ease-out focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-brand-border focus-visible:ring-offset-2 focus-visible:ring-offset-bg-base",
1688
+ {
1689
+ variants: {
1690
+ side: {
1691
+ bottom: "inset-x-0 bottom-0 rounded-b-none rounded-t-[var(--radius-lg)] data-[state=closed]:translate-y-full",
1692
+ top: "inset-x-0 top-0 rounded-b-[var(--radius-lg)] data-[state=closed]:-translate-y-full",
1693
+ left: "inset-y-0 left-0 h-full w-80 max-w-[85%] rounded-r-[var(--radius-lg)] data-[state=closed]:-translate-x-full",
1694
+ right: "inset-y-0 right-0 h-full w-80 max-w-[85%] rounded-l-[var(--radius-lg)] data-[state=closed]:translate-x-full"
1695
+ }
1696
+ },
1697
+ defaultVariants: { side: "bottom" }
1698
+ }
1699
+ );
1700
+ var Sheet = React16.forwardRef(
1701
+ ({
1702
+ className,
1703
+ side = "bottom",
1704
+ open = false,
1705
+ onOpenChange,
1706
+ showHandle = true,
1707
+ children,
1708
+ ...props
1709
+ }, ref) => {
1710
+ const state = open ? "open" : "closed";
1711
+ const handleVisible = showHandle && (side === "bottom" || side === "top");
1712
+ return /* @__PURE__ */ jsxs(
1713
+ "div",
1714
+ {
1715
+ className: cn(
1716
+ "pointer-events-none absolute inset-0 overflow-hidden",
1717
+ open && "pointer-events-auto"
1718
+ ),
1719
+ "data-state": state,
1720
+ children: [
1721
+ /* @__PURE__ */ jsx(
1722
+ "div",
1723
+ {
1724
+ "aria-hidden": "true",
1725
+ className: sheetOverlayVariants({ open }),
1726
+ onClick: () => onOpenChange?.(false)
1727
+ }
1728
+ ),
1729
+ /* @__PURE__ */ jsxs(
1730
+ "div",
1731
+ {
1732
+ ref,
1733
+ role: "dialog",
1734
+ "aria-modal": "true",
1735
+ "aria-hidden": !open,
1736
+ "data-state": state,
1737
+ tabIndex: -1,
1738
+ className: cn(sheetPanelVariants({ side }), className),
1739
+ ...props,
1740
+ children: [
1741
+ handleVisible ? /* @__PURE__ */ jsx(SheetHandle, {}) : null,
1742
+ children
1743
+ ]
1744
+ }
1745
+ )
1746
+ ]
1747
+ }
1748
+ );
1749
+ }
1750
+ );
1751
+ Sheet.displayName = "Sheet";
1752
+ var SheetHandle = React16.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
1753
+ "div",
1754
+ {
1755
+ ref,
1756
+ "aria-hidden": "true",
1757
+ className: cn(
1758
+ "mx-auto mb-1 h-1 w-10 shrink-0 rounded-full bg-bg-emphasis",
1759
+ className
1760
+ ),
1761
+ ...props
1762
+ }
1763
+ ));
1764
+ SheetHandle.displayName = "SheetHandle";
1765
+ var SheetHeader = React16.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
1766
+ "div",
1767
+ {
1768
+ ref,
1769
+ className: cn("flex flex-col gap-0.5", className),
1770
+ ...props
1771
+ }
1772
+ ));
1773
+ SheetHeader.displayName = "SheetHeader";
1774
+ var SheetTitle = React16.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
1775
+ "h2",
1776
+ {
1777
+ ref,
1778
+ className: cn("text-lg font-semibold text-text-strong", className),
1779
+ ...props
1780
+ }
1781
+ ));
1782
+ SheetTitle.displayName = "SheetTitle";
1783
+ var SheetDescription = React16.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
1784
+ "p",
1785
+ {
1786
+ ref,
1787
+ className: cn("text-sm text-text-subtle", className),
1788
+ ...props
1789
+ }
1790
+ ));
1791
+ SheetDescription.displayName = "SheetDescription";
1792
+ var SheetBody = React16.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
1793
+ "div",
1794
+ {
1795
+ ref,
1796
+ className: cn("flex flex-1 flex-col gap-4 overflow-y-auto", className),
1797
+ ...props
1798
+ }
1799
+ ));
1800
+ SheetBody.displayName = "SheetBody";
1801
+ var SheetFooter = React16.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
1802
+ "div",
1803
+ {
1804
+ ref,
1805
+ className: cn(
1806
+ "flex flex-col-reverse gap-2 sm:flex-row sm:justify-end",
1807
+ className
1808
+ ),
1809
+ ...props
1810
+ }
1811
+ ));
1812
+ SheetFooter.displayName = "SheetFooter";
1813
+ var TabsContext = React16.createContext(null);
1814
+ function useTabsContext(component) {
1815
+ const ctx = React16.useContext(TabsContext);
1816
+ if (!ctx) {
1817
+ throw new Error(`<${component}> must be used within <Tabs>`);
1818
+ }
1819
+ return ctx;
1820
+ }
1821
+ var Tabs = React16.forwardRef(
1822
+ ({ className, value, defaultValue, onValueChange, children, ...props }, ref) => {
1823
+ const baseId = React16.useId();
1824
+ const [internalValue, setInternalValue] = React16.useState(
1825
+ defaultValue ?? ""
1826
+ );
1827
+ const isControlled = value !== void 0;
1828
+ const activeValue = isControlled ? value : internalValue;
1829
+ const setValue = React16.useCallback(
1830
+ (next) => {
1831
+ if (!isControlled) setInternalValue(next);
1832
+ onValueChange?.(next);
1833
+ },
1834
+ [isControlled, onValueChange]
1835
+ );
1836
+ const ctx = React16.useMemo(
1837
+ () => ({ value: activeValue, setValue, baseId }),
1838
+ [activeValue, setValue, baseId]
1839
+ );
1840
+ return /* @__PURE__ */ jsx(TabsContext.Provider, { value: ctx, children: /* @__PURE__ */ jsx("div", { ref, className: cn("flex flex-col gap-4", className), ...props, children }) });
1841
+ }
1842
+ );
1843
+ Tabs.displayName = "Tabs";
1844
+ var tabsListVariants = cva(
1845
+ "relative flex items-center border-b border-border-subtle",
1846
+ {
1847
+ variants: {
1848
+ fullWidth: {
1849
+ true: "w-full",
1850
+ false: "w-fit"
1851
+ }
1852
+ },
1853
+ defaultVariants: {
1854
+ fullWidth: false
1855
+ }
1856
+ }
1857
+ );
1858
+ var TabsList = React16.forwardRef(
1859
+ ({ className, fullWidth, ...props }, ref) => /* @__PURE__ */ jsx(
1860
+ "div",
1861
+ {
1862
+ ref,
1863
+ role: "tablist",
1864
+ className: cn(tabsListVariants({ fullWidth }), "gap-1", className),
1865
+ ...props
1866
+ }
1867
+ )
1868
+ );
1869
+ TabsList.displayName = "TabsList";
1870
+ var tabsTriggerVariants = cva(
1871
+ [
1872
+ "relative inline-flex items-center justify-center gap-2 whitespace-nowrap px-3 py-2 text-sm font-medium",
1873
+ "transition-colors focus-visible:outline-none",
1874
+ "focus-visible:ring-2 focus-visible:ring-brand-border focus-visible:ring-offset-2 focus-visible:ring-offset-bg-base",
1875
+ "disabled:pointer-events-none disabled:text-text-disabled",
1876
+ "[&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
1877
+ // bottom indicator element (2px)
1878
+ "after:absolute after:inset-x-0 after:-bottom-px after:h-0.5 after:rounded-full after:transition-colors"
1879
+ ],
1880
+ {
1881
+ variants: {
1882
+ active: {
1883
+ true: "text-text-strong after:bg-brand-solid",
1884
+ false: "text-text-subtle hover:text-text-default after:bg-transparent"
1885
+ }
1886
+ },
1887
+ defaultVariants: {
1888
+ active: false
1889
+ }
1890
+ }
1891
+ );
1892
+ var TabsTrigger = React16.forwardRef(
1893
+ ({ className, value, disabled, ...props }, ref) => {
1894
+ const { value: activeValue, setValue, baseId } = useTabsContext("TabsTrigger");
1895
+ const isActive = activeValue === value;
1896
+ return /* @__PURE__ */ jsx(
1897
+ "button",
1898
+ {
1899
+ ref,
1900
+ type: "button",
1901
+ role: "tab",
1902
+ id: `${baseId}-trigger-${value}`,
1903
+ "aria-selected": isActive,
1904
+ "aria-controls": `${baseId}-content-${value}`,
1905
+ tabIndex: isActive ? 0 : -1,
1906
+ disabled,
1907
+ "data-state": isActive ? "active" : "inactive",
1908
+ onClick: () => setValue(value),
1909
+ className: cn(tabsTriggerVariants({ active: isActive }), className),
1910
+ ...props
1911
+ }
1912
+ );
1913
+ }
1914
+ );
1915
+ TabsTrigger.displayName = "TabsTrigger";
1916
+ var TabsContent = React16.forwardRef(
1917
+ ({ className, value, forceMount, children, ...props }, ref) => {
1918
+ const { value: activeValue, baseId } = useTabsContext("TabsContent");
1919
+ const isActive = activeValue === value;
1920
+ if (!isActive && !forceMount) return null;
1921
+ return /* @__PURE__ */ jsx(
1922
+ "div",
1923
+ {
1924
+ ref,
1925
+ role: "tabpanel",
1926
+ id: `${baseId}-content-${value}`,
1927
+ "aria-labelledby": `${baseId}-trigger-${value}`,
1928
+ hidden: !isActive,
1929
+ tabIndex: 0,
1930
+ "data-state": isActive ? "active" : "inactive",
1931
+ className: cn(
1932
+ "text-sm text-text-default focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-brand-border focus-visible:ring-offset-2 focus-visible:ring-offset-bg-base",
1933
+ className
1934
+ ),
1935
+ ...props,
1936
+ children
1937
+ }
1938
+ );
1939
+ }
1940
+ );
1941
+ TabsContent.displayName = "TabsContent";
1942
+ var tableVariants = cva(
1943
+ "w-full caption-bottom border-separate border-spacing-0 overflow-hidden rounded-lg border border-border-default text-sm text-text-default",
1944
+ {
1945
+ variants: {
1946
+ density: {
1947
+ comfortable: "[&_td]:py-2 [&_th]:py-2",
1948
+ compact: "[&_td]:py-1 [&_th]:py-1"
1949
+ }
1950
+ },
1951
+ defaultVariants: {
1952
+ density: "comfortable"
1953
+ }
1954
+ }
1955
+ );
1956
+ var Table = React16.forwardRef(
1957
+ ({ className, density, containerProps, ...props }, ref) => /* @__PURE__ */ jsx(
1958
+ "div",
1959
+ {
1960
+ ...containerProps,
1961
+ className: cn("relative w-full overflow-x-auto", containerProps?.className),
1962
+ children: /* @__PURE__ */ jsx(
1963
+ "table",
1964
+ {
1965
+ ref,
1966
+ className: cn(tableVariants({ density, className })),
1967
+ ...props
1968
+ }
1969
+ )
1970
+ }
1971
+ )
1972
+ );
1973
+ Table.displayName = "Table";
1974
+ var TableHeader = React16.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
1975
+ "thead",
1976
+ {
1977
+ ref,
1978
+ className: cn("bg-surface-sunken text-text-subtle", className),
1979
+ ...props
1980
+ }
1981
+ ));
1982
+ TableHeader.displayName = "TableHeader";
1983
+ var TableBody = React16.forwardRef(
1984
+ ({ className, ...props }, ref) => /* @__PURE__ */ jsx("tbody", { ref, className: cn("", className), ...props })
1985
+ );
1986
+ TableBody.displayName = "TableBody";
1987
+ var tableRowVariants = cva(
1988
+ "border-b border-border-default transition-colors [&>*]:border-b [&>*]:border-border-default last:[&>*]:border-b-0",
1989
+ {
1990
+ variants: {
1991
+ interactive: {
1992
+ true: "cursor-pointer hover:bg-surface-hover data-[selected=true]:bg-brand-subtle",
1993
+ false: "hover:bg-surface-hover"
1994
+ }
1995
+ },
1996
+ defaultVariants: {
1997
+ interactive: false
1998
+ }
1999
+ }
2000
+ );
2001
+ var TableRow = React16.forwardRef(
2002
+ ({ className, interactive, selected, ...props }, ref) => /* @__PURE__ */ jsx(
2003
+ "tr",
2004
+ {
2005
+ ref,
2006
+ "data-selected": selected ? "true" : void 0,
2007
+ "aria-selected": selected,
2008
+ className: cn(tableRowVariants({ interactive, className })),
2009
+ ...props
2010
+ }
2011
+ )
2012
+ );
2013
+ TableRow.displayName = "TableRow";
2014
+ var TableHead = React16.forwardRef(
2015
+ ({ className, sort, children, ...props }, ref) => /* @__PURE__ */ jsx(
2016
+ "th",
2017
+ {
2018
+ ref,
2019
+ scope: "col",
2020
+ "aria-sort": sort === "asc" ? "ascending" : sort === "desc" ? "descending" : sort === "none" ? "none" : void 0,
2021
+ className: cn(
2022
+ "h-9 px-3 py-2 text-left align-middle text-xs font-medium text-text-subtle",
2023
+ "[&:has([role=checkbox])]:pr-0",
2024
+ className
2025
+ ),
2026
+ ...props,
2027
+ children: sort && sort !== "none" ? /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-1", children: [
2028
+ children,
2029
+ /* @__PURE__ */ jsx("span", { "aria-hidden": "true", className: "text-text-subtle", children: sort === "asc" ? "\u2191" : "\u2193" })
2030
+ ] }) : children
2031
+ }
2032
+ )
2033
+ );
2034
+ TableHead.displayName = "TableHead";
2035
+ var TableCell = React16.forwardRef(
2036
+ ({ className, ...props }, ref) => /* @__PURE__ */ jsx(
2037
+ "td",
2038
+ {
2039
+ ref,
2040
+ className: cn(
2041
+ "px-3 py-2 align-middle text-sm text-text-default [&:has([role=checkbox])]:pr-0",
2042
+ className
2043
+ ),
2044
+ ...props
2045
+ }
2046
+ )
2047
+ );
2048
+ TableCell.displayName = "TableCell";
2049
+ var TableCaption = React16.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
2050
+ "caption",
2051
+ {
2052
+ ref,
2053
+ className: cn("mt-3 text-xs text-text-subtle", className),
2054
+ ...props
2055
+ }
2056
+ ));
2057
+ TableCaption.displayName = "TableCaption";
2058
+ var paginationItemVariants = cva(
2059
+ "inline-flex h-8 min-w-8 items-center justify-center rounded-lg px-2 text-sm font-medium transition-colors select-none focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-border-focus focus-visible:ring-offset-2 focus-visible:ring-offset-bg-base disabled:pointer-events-none disabled:text-text-disabled [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
2060
+ {
2061
+ variants: {
2062
+ active: {
2063
+ true: "bg-brand-solid text-text-on-brand hover:bg-brand-solid-hover",
2064
+ false: "text-text-default hover:bg-surface-hover active:bg-surface-pressed"
2065
+ }
2066
+ },
2067
+ defaultVariants: {
2068
+ active: false
2069
+ }
2070
+ }
2071
+ );
2072
+ var ChevronLeftIcon = (props) => /* @__PURE__ */ jsx(
2073
+ "svg",
2074
+ {
2075
+ viewBox: "0 0 24 24",
2076
+ fill: "none",
2077
+ stroke: "currentColor",
2078
+ strokeWidth: 2,
2079
+ strokeLinecap: "round",
2080
+ strokeLinejoin: "round",
2081
+ "aria-hidden": "true",
2082
+ ...props,
2083
+ children: /* @__PURE__ */ jsx("path", { d: "m15 18-6-6 6-6" })
2084
+ }
2085
+ );
2086
+ var ChevronRightIcon = (props) => /* @__PURE__ */ jsx(
2087
+ "svg",
2088
+ {
2089
+ viewBox: "0 0 24 24",
2090
+ fill: "none",
2091
+ stroke: "currentColor",
2092
+ strokeWidth: 2,
2093
+ strokeLinecap: "round",
2094
+ strokeLinejoin: "round",
2095
+ "aria-hidden": "true",
2096
+ ...props,
2097
+ children: /* @__PURE__ */ jsx("path", { d: "m9 18 6-6-6-6" })
2098
+ }
2099
+ );
2100
+ var EllipsisIcon = (props) => /* @__PURE__ */ jsxs(
2101
+ "svg",
2102
+ {
2103
+ viewBox: "0 0 24 24",
2104
+ fill: "currentColor",
2105
+ stroke: "none",
2106
+ "aria-hidden": "true",
2107
+ ...props,
2108
+ children: [
2109
+ /* @__PURE__ */ jsx("circle", { cx: "5", cy: "12", r: "1.5" }),
2110
+ /* @__PURE__ */ jsx("circle", { cx: "12", cy: "12", r: "1.5" }),
2111
+ /* @__PURE__ */ jsx("circle", { cx: "19", cy: "12", r: "1.5" })
2112
+ ]
2113
+ }
2114
+ );
2115
+ var PaginationItem = React16.forwardRef(
2116
+ ({ className, active, page, ...props }, ref) => {
2117
+ return /* @__PURE__ */ jsx(
2118
+ "button",
2119
+ {
2120
+ ref,
2121
+ type: "button",
2122
+ "aria-current": active ? "page" : void 0,
2123
+ "aria-label": `Go to page ${page}`,
2124
+ className: cn(paginationItemVariants({ active, className })),
2125
+ ...props,
2126
+ children: page
2127
+ }
2128
+ );
2129
+ }
2130
+ );
2131
+ PaginationItem.displayName = "PaginationItem";
2132
+ var PaginationEllipsis = React16.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxs(
2133
+ "span",
2134
+ {
2135
+ ref,
2136
+ "aria-hidden": "true",
2137
+ className: cn(
2138
+ "inline-flex h-8 min-w-8 items-center justify-center px-1 text-sm text-text-subtle",
2139
+ className
2140
+ ),
2141
+ ...props,
2142
+ children: [
2143
+ /* @__PURE__ */ jsx(EllipsisIcon, {}),
2144
+ /* @__PURE__ */ jsx("span", { className: "sr-only", children: "More pages" })
2145
+ ]
2146
+ }
2147
+ ));
2148
+ PaginationEllipsis.displayName = "PaginationEllipsis";
2149
+ var PaginationPrevious = React16.forwardRef(({ className, disabled, ...props }, ref) => /* @__PURE__ */ jsx(
2150
+ "button",
2151
+ {
2152
+ ref,
2153
+ type: "button",
2154
+ disabled,
2155
+ "aria-label": "Go to previous page",
2156
+ className: cn(paginationItemVariants({ active: false, className })),
2157
+ ...props,
2158
+ children: /* @__PURE__ */ jsx(ChevronLeftIcon, {})
2159
+ }
2160
+ ));
2161
+ PaginationPrevious.displayName = "PaginationPrevious";
2162
+ var PaginationNext = React16.forwardRef(
2163
+ ({ className, disabled, ...props }, ref) => /* @__PURE__ */ jsx(
2164
+ "button",
2165
+ {
2166
+ ref,
2167
+ type: "button",
2168
+ disabled,
2169
+ "aria-label": "Go to next page",
2170
+ className: cn(paginationItemVariants({ active: false, className })),
2171
+ ...props,
2172
+ children: /* @__PURE__ */ jsx(ChevronRightIcon, {})
2173
+ }
2174
+ )
2175
+ );
2176
+ PaginationNext.displayName = "PaginationNext";
2177
+ function buildPageRange(current, total, siblingCount = 1) {
2178
+ const totalSlots = siblingCount * 2 + 5;
2179
+ if (total <= totalSlots) {
2180
+ return Array.from({ length: total }, (_, i) => i + 1);
2181
+ }
2182
+ const leftSibling = Math.max(current - siblingCount, 1);
2183
+ const rightSibling = Math.min(current + siblingCount, total);
2184
+ const showLeftEllipsis = leftSibling > 2;
2185
+ const showRightEllipsis = rightSibling < total - 1;
2186
+ const range = [];
2187
+ if (!showLeftEllipsis && showRightEllipsis) {
2188
+ const leftItemCount = siblingCount * 2 + 3;
2189
+ for (let i = 1; i <= leftItemCount; i++) range.push(i);
2190
+ range.push("ellipsis", total);
2191
+ } else if (showLeftEllipsis && !showRightEllipsis) {
2192
+ const rightItemCount = siblingCount * 2 + 3;
2193
+ range.push(1, "ellipsis");
2194
+ for (let i = total - rightItemCount + 1; i <= total; i++) range.push(i);
2195
+ } else {
2196
+ range.push(1, "ellipsis");
2197
+ for (let i = leftSibling; i <= rightSibling; i++) range.push(i);
2198
+ range.push("ellipsis", total);
2199
+ }
2200
+ return range;
2201
+ }
2202
+ var Pagination = React16.forwardRef(
2203
+ ({
2204
+ className,
2205
+ totalPages,
2206
+ page,
2207
+ onPageChange,
2208
+ siblingCount = 1,
2209
+ label = "Pagination",
2210
+ ...props
2211
+ }, ref) => {
2212
+ const safeTotal = Math.max(totalPages, 1);
2213
+ const current = Math.min(Math.max(page, 1), safeTotal);
2214
+ const items = buildPageRange(current, safeTotal, siblingCount);
2215
+ const goTo = (target) => {
2216
+ if (target < 1 || target > safeTotal || target === current) return;
2217
+ onPageChange?.(target);
2218
+ };
2219
+ return /* @__PURE__ */ jsxs(
2220
+ "nav",
2221
+ {
2222
+ ref,
2223
+ role: "navigation",
2224
+ "aria-label": label,
2225
+ className: cn("flex items-center gap-1", className),
2226
+ ...props,
2227
+ children: [
2228
+ /* @__PURE__ */ jsx(
2229
+ PaginationPrevious,
2230
+ {
2231
+ disabled: current <= 1,
2232
+ onClick: () => goTo(current - 1)
2233
+ }
2234
+ ),
2235
+ items.map(
2236
+ (item, index) => item === "ellipsis" ? /* @__PURE__ */ jsx(PaginationEllipsis, {}, `ellipsis-${index}`) : /* @__PURE__ */ jsx(
2237
+ PaginationItem,
2238
+ {
2239
+ page: item,
2240
+ active: item === current,
2241
+ onClick: () => goTo(item)
2242
+ },
2243
+ item
2244
+ )
2245
+ ),
2246
+ /* @__PURE__ */ jsx(
2247
+ PaginationNext,
2248
+ {
2249
+ disabled: current >= safeTotal,
2250
+ onClick: () => goTo(current + 1)
2251
+ }
2252
+ )
2253
+ ]
2254
+ }
2255
+ );
2256
+ }
2257
+ );
2258
+ Pagination.displayName = "Pagination";
2259
+ var segmentContainerVariants = cva(
2260
+ "relative inline-grid h-8 auto-cols-fr grid-flow-col items-center rounded-lg bg-surface-sunken p-0.5 text-sm",
2261
+ {
2262
+ variants: {
2263
+ fullWidth: {
2264
+ true: "flex w-full",
2265
+ false: "inline-grid w-auto"
2266
+ }
2267
+ },
2268
+ defaultVariants: {
2269
+ fullWidth: false
2270
+ }
2271
+ }
2272
+ );
2273
+ var segmentItemVariants = cva(
2274
+ "relative z-10 inline-flex h-full w-full items-center justify-center gap-1.5 whitespace-nowrap rounded-lg px-3 text-sm transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-border-focus focus-visible:ring-offset-2 focus-visible:ring-offset-bg-base disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
2275
+ {
2276
+ variants: {
2277
+ // 활성=label1 굵기(semibold/600) · 비활성=body-sm 굵기(regular/400), 둘 다 14px.
2278
+ // NOTE: text-label1/body-sm 클래스는 twMerge가 text-* 색상과 같은 충돌군으로
2279
+ // 보고 제거하므로, 색상과 충돌 안 하는 font-weight 유틸로 굵기를 지정한다.
2280
+ active: {
2281
+ true: "font-semibold text-text-strong",
2282
+ false: "font-normal text-text-subtle hover:text-text-default"
2283
+ }
2284
+ },
2285
+ defaultVariants: {
2286
+ active: false
2287
+ }
2288
+ }
2289
+ );
2290
+ var SegmentedControl = React16.forwardRef(
2291
+ ({
2292
+ className,
2293
+ items,
2294
+ value,
2295
+ defaultValue,
2296
+ onValueChange,
2297
+ disabled = false,
2298
+ fullWidth,
2299
+ "aria-label": ariaLabel,
2300
+ ...props
2301
+ }, ref) => {
2302
+ const isControlled = value !== void 0;
2303
+ const [internalValue, setInternalValue] = React16.useState(
2304
+ defaultValue ?? items[0]?.value ?? ""
2305
+ );
2306
+ const currentValue = isControlled ? value : internalValue;
2307
+ const activeIndex = Math.max(
2308
+ 0,
2309
+ items.findIndex((item) => item.value === currentValue)
2310
+ );
2311
+ const count = items.length || 1;
2312
+ const handleSelect = (next) => {
2313
+ if (!isControlled) setInternalValue(next);
2314
+ onValueChange?.(next);
2315
+ };
2316
+ return /* @__PURE__ */ jsxs(
2317
+ "div",
2318
+ {
2319
+ ref,
2320
+ role: "tablist",
2321
+ "aria-label": ariaLabel,
2322
+ "aria-disabled": disabled || void 0,
2323
+ className: cn(segmentContainerVariants({ fullWidth }), className),
2324
+ ...props,
2325
+ children: [
2326
+ items.length > 0 && /* @__PURE__ */ jsx(
2327
+ "span",
2328
+ {
2329
+ "aria-hidden": "true",
2330
+ className: "pointer-events-none absolute bottom-0.5 left-0.5 top-0.5 z-0 rounded-lg bg-surface-default shadow-sm transition-transform duration-200 ease-out",
2331
+ style: {
2332
+ width: `calc((100% - 0.25rem) / ${count})`,
2333
+ transform: `translateX(calc(${activeIndex} * 100%))`
2334
+ }
2335
+ }
2336
+ ),
2337
+ items.map((item) => {
2338
+ const isActive = item.value === currentValue;
2339
+ const isDisabled = disabled || item.disabled;
2340
+ return /* @__PURE__ */ jsxs(
2341
+ "button",
2342
+ {
2343
+ type: "button",
2344
+ role: "tab",
2345
+ "aria-selected": isActive,
2346
+ disabled: isDisabled,
2347
+ tabIndex: isActive ? 0 : -1,
2348
+ onClick: () => handleSelect(item.value),
2349
+ className: cn(segmentItemVariants({ active: isActive })),
2350
+ children: [
2351
+ item.icon,
2352
+ item.label
2353
+ ]
2354
+ },
2355
+ item.value
2356
+ );
2357
+ })
2358
+ ]
2359
+ }
2360
+ );
2361
+ }
2362
+ );
2363
+ SegmentedControl.displayName = "SegmentedControl";
2364
+ var toastVariants = cva(
2365
+ "pointer-events-auto flex w-full max-w-sm gap-3 rounded-lg border px-4 py-4 shadow-toast focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:ring-offset-bg-base",
2366
+ {
2367
+ variants: {
2368
+ variant: {
2369
+ default: "border-border-default bg-surface-raised text-text-default focus-visible:ring-border-focus",
2370
+ danger: "border-danger-border bg-danger-subtle text-danger-text focus-visible:ring-danger-border"
2371
+ }
2372
+ },
2373
+ defaultVariants: {
2374
+ variant: "default"
2375
+ }
2376
+ }
2377
+ );
2378
+ function InfoIcon(props) {
2379
+ return /* @__PURE__ */ jsxs(
2380
+ "svg",
2381
+ {
2382
+ viewBox: "0 0 20 20",
2383
+ fill: "none",
2384
+ stroke: "currentColor",
2385
+ strokeWidth: 1.6,
2386
+ strokeLinecap: "round",
2387
+ strokeLinejoin: "round",
2388
+ "aria-hidden": "true",
2389
+ ...props,
2390
+ children: [
2391
+ /* @__PURE__ */ jsx("circle", { cx: "10", cy: "10", r: "7.5" }),
2392
+ /* @__PURE__ */ jsx("path", { d: "M10 9v4.5" }),
2393
+ /* @__PURE__ */ jsx("path", { d: "M10 6.5h.01" })
2394
+ ]
2395
+ }
2396
+ );
2397
+ }
2398
+ function AlertIcon(props) {
2399
+ return /* @__PURE__ */ jsxs(
2400
+ "svg",
2401
+ {
2402
+ viewBox: "0 0 20 20",
2403
+ fill: "none",
2404
+ stroke: "currentColor",
2405
+ strokeWidth: 1.6,
2406
+ strokeLinecap: "round",
2407
+ strokeLinejoin: "round",
2408
+ "aria-hidden": "true",
2409
+ ...props,
2410
+ children: [
2411
+ /* @__PURE__ */ jsx("path", { d: "M10 2.5 18 16.5H2z" }),
2412
+ /* @__PURE__ */ jsx("path", { d: "M10 8v3.5" }),
2413
+ /* @__PURE__ */ jsx("path", { d: "M10 14h.01" })
2414
+ ]
2415
+ }
2416
+ );
2417
+ }
2418
+ function CloseIcon2(props) {
2419
+ return /* @__PURE__ */ jsx(
2420
+ "svg",
2421
+ {
2422
+ viewBox: "0 0 20 20",
2423
+ fill: "none",
2424
+ stroke: "currentColor",
2425
+ strokeWidth: 1.6,
2426
+ strokeLinecap: "round",
2427
+ strokeLinejoin: "round",
2428
+ "aria-hidden": "true",
2429
+ ...props,
2430
+ children: /* @__PURE__ */ jsx("path", { d: "M5 5l10 10M15 5L5 15" })
2431
+ }
2432
+ );
2433
+ }
2434
+ var Toast = React16.forwardRef(
2435
+ ({
2436
+ className,
2437
+ variant,
2438
+ message,
2439
+ title,
2440
+ icon = true,
2441
+ dismissible = true,
2442
+ onClose,
2443
+ closeLabel = "Close",
2444
+ ...props
2445
+ }, ref) => {
2446
+ const StatusIcon = variant === "danger" ? AlertIcon : InfoIcon;
2447
+ return /* @__PURE__ */ jsxs(
2448
+ "div",
2449
+ {
2450
+ ref,
2451
+ role: variant === "danger" ? "alert" : "status",
2452
+ "aria-live": variant === "danger" ? "assertive" : "polite",
2453
+ className: cn(
2454
+ toastVariants({ variant, className }),
2455
+ title ? "items-start" : "items-center"
2456
+ ),
2457
+ ...props,
2458
+ children: [
2459
+ /* @__PURE__ */ jsxs(
2460
+ "div",
2461
+ {
2462
+ className: cn(
2463
+ "flex min-w-0 flex-1 gap-2",
2464
+ title ? "items-start" : "items-center"
2465
+ ),
2466
+ children: [
2467
+ icon ? /* @__PURE__ */ jsx(
2468
+ StatusIcon,
2469
+ {
2470
+ className: cn(
2471
+ "size-5 shrink-0",
2472
+ variant === "danger" ? "text-danger-text" : "text-text-subtle"
2473
+ )
2474
+ }
2475
+ ) : null,
2476
+ /* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
2477
+ title ? /* @__PURE__ */ jsx(
2478
+ "p",
2479
+ {
2480
+ className: cn(
2481
+ "text-label1",
2482
+ variant === "danger" ? "text-danger-text" : "text-text-strong"
2483
+ ),
2484
+ children: title
2485
+ }
2486
+ ) : null,
2487
+ /* @__PURE__ */ jsx("p", { className: cn("text-body-sm break-words", title && "mt-0.5"), children: message })
2488
+ ] })
2489
+ ]
2490
+ }
2491
+ ),
2492
+ dismissible ? /* @__PURE__ */ jsx(
2493
+ "button",
2494
+ {
2495
+ type: "button",
2496
+ onClick: onClose,
2497
+ "aria-label": closeLabel,
2498
+ className: cn(
2499
+ "-mr-1 inline-flex size-6 shrink-0 items-center justify-center rounded text-text-subtle transition-colors hover:bg-surface-hover hover:text-text-default active:bg-surface-pressed focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-border-focus focus-visible:ring-offset-2 focus-visible:ring-offset-bg-base",
2500
+ title && "-mt-0.5"
2501
+ ),
2502
+ children: /* @__PURE__ */ jsx(CloseIcon2, { className: "size-4" })
2503
+ }
2504
+ ) : null
2505
+ ]
2506
+ }
2507
+ );
2508
+ }
2509
+ );
2510
+ Toast.displayName = "Toast";
2511
+ var SIZES = {
2512
+ lg: {
2513
+ plate: 180,
2514
+ art: 120,
2515
+ pad: "px-6 py-12",
2516
+ plateMb: "mb-4",
2517
+ title: "text-xl",
2518
+ desc: "text-base max-w-[360px]",
2519
+ gap: "mt-1",
2520
+ act: "mt-5"
2521
+ },
2522
+ md: {
2523
+ plate: 144,
2524
+ art: 96,
2525
+ pad: "px-5 py-10",
2526
+ plateMb: "mb-3",
2527
+ title: "text-lg",
2528
+ desc: "text-base max-w-[300px]",
2529
+ gap: "mt-0.5",
2530
+ act: "mt-4"
2531
+ },
2532
+ sm: {
2533
+ plate: 108,
2534
+ art: 72,
2535
+ pad: "px-4 py-8",
2536
+ plateMb: "mb-3",
2537
+ title: "text-lg",
2538
+ desc: "text-sm max-w-[240px]",
2539
+ gap: "mt-0.5",
2540
+ act: "mt-4"
2541
+ }
2542
+ };
2543
+ function EmptyState({
2544
+ size = "lg",
2545
+ illustration,
2546
+ title,
2547
+ description,
2548
+ action,
2549
+ className,
2550
+ ...props
2551
+ }) {
2552
+ const s = SIZES[size];
2553
+ return /* @__PURE__ */ jsxs(
2554
+ "div",
2555
+ {
2556
+ className: cn("flex flex-col items-center text-center", s.pad, className),
2557
+ ...props,
2558
+ children: [
2559
+ illustration && // 플레이트: border-subtle = 라이트 gray-20(sunken) · 다크 gray-60(카드 gray-80보다 밝아 떠 보임)
2560
+ /* @__PURE__ */ jsx(
2561
+ "div",
2562
+ {
2563
+ className: cn(
2564
+ "flex items-center justify-center rounded-full bg-border-subtle",
2565
+ s.plateMb
2566
+ ),
2567
+ style: { width: s.plate, height: s.plate },
2568
+ children: /* @__PURE__ */ jsx(
2569
+ "span",
2570
+ {
2571
+ className: "inline-flex [&>img]:h-full [&>img]:w-full [&>img]:object-contain [&>svg]:h-full [&>svg]:w-full",
2572
+ style: { width: s.art, height: s.art },
2573
+ children: illustration
2574
+ }
2575
+ )
2576
+ }
2577
+ ),
2578
+ /* @__PURE__ */ jsx("h3", { className: cn("font-bold leading-snug text-text-strong", s.title), children: title }),
2579
+ description && /* @__PURE__ */ jsx("p", { className: cn("leading-relaxed text-text-subtle", s.desc, s.gap), children: description }),
2580
+ action && /* @__PURE__ */ jsx("div", { className: cn("flex gap-2", s.act), children: action })
2581
+ ]
2582
+ }
2583
+ );
2584
+ }
2585
+ function IconBase({
2586
+ size = 20,
2587
+ children,
2588
+ ...props
2589
+ }) {
2590
+ return /* @__PURE__ */ jsx(
2591
+ "svg",
2592
+ {
2593
+ xmlns: "http://www.w3.org/2000/svg",
2594
+ width: size,
2595
+ height: size,
2596
+ viewBox: "0 0 24 24",
2597
+ fill: "none",
2598
+ stroke: "currentColor",
2599
+ strokeWidth: 1.5,
2600
+ strokeLinecap: "round",
2601
+ strokeLinejoin: "round",
2602
+ "aria-hidden": "true",
2603
+ ...props,
2604
+ children
2605
+ }
2606
+ );
2607
+ }
2608
+ var CheckIcon4 = (props) => /* @__PURE__ */ jsx(IconBase, { ...props, children: /* @__PURE__ */ jsx("path", { d: "M20 6 9 17l-5-5" }) });
2609
+ var ChevronDownIcon = (props) => /* @__PURE__ */ jsx(IconBase, { ...props, children: /* @__PURE__ */ jsx("path", { d: "m6 9 6 6 6-6" }) });
2610
+ var ChevronLeftIcon2 = (props) => /* @__PURE__ */ jsx(IconBase, { ...props, children: /* @__PURE__ */ jsx("path", { d: "m15 6-6 6 6 6" }) });
2611
+ var ChevronRightIcon2 = (props) => /* @__PURE__ */ jsx(IconBase, { ...props, children: /* @__PURE__ */ jsx("path", { d: "m9 6 6 6-6 6" }) });
2612
+ var HashIcon = (props) => /* @__PURE__ */ jsx(IconBase, { ...props, children: /* @__PURE__ */ jsx("path", { d: "M4 9h16M4 15h16M10 3 8 21M16 3 14 21" }) });
2613
+ var LockIcon = (props) => /* @__PURE__ */ jsxs(IconBase, { ...props, children: [
2614
+ /* @__PURE__ */ jsx("rect", { width: "18", height: "11", x: "3", y: "11", rx: "3", ry: "3" }),
2615
+ /* @__PURE__ */ jsx("path", { d: "M7 11V7a5 5 0 0 1 10 0v4" })
2616
+ ] });
2617
+ var PinIcon = (props) => /* @__PURE__ */ jsxs(IconBase, { ...props, children: [
2618
+ /* @__PURE__ */ jsx("path", { d: "M12 17v5" }),
2619
+ /* @__PURE__ */ jsx("path", { d: "M9 10.76a2 2 0 0 1-1.11 1.79l-1.78.9A2 2 0 0 0 5 15.24V16a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1v-.76a2 2 0 0 0-1.11-1.79l-1.78-.9A2 2 0 0 1 15 10.76V7a1 1 0 0 1 1-1 2 2 0 0 0 0-4H8a2 2 0 0 0 0 4 1 1 0 0 1 1 1z" })
2620
+ ] });
2621
+ var PlusIcon = (props) => /* @__PURE__ */ jsx(IconBase, { ...props, children: /* @__PURE__ */ jsx("path", { d: "M12 5v14M5 12h14" }) });
2622
+ var SearchIcon = (props) => /* @__PURE__ */ jsxs(IconBase, { ...props, children: [
2623
+ /* @__PURE__ */ jsx("circle", { cx: "11", cy: "11", r: "7" }),
2624
+ /* @__PURE__ */ jsx("path", { d: "m20 20-3.4-3.4" })
2625
+ ] });
2626
+ var XIcon = (props) => /* @__PURE__ */ jsx(IconBase, { ...props, children: /* @__PURE__ */ jsx("path", { d: "M18 6 6 18M6 6l12 12" }) });
2627
+ var AccordionItem = React16.forwardRef(
2628
+ ({ className, title, children, ...props }, ref) => /* @__PURE__ */ jsxs(
2629
+ "details",
2630
+ {
2631
+ ref,
2632
+ className: cn(
2633
+ "group border-t border-border-subtle first:border-t-0",
2634
+ className
2635
+ ),
2636
+ ...props,
2637
+ children: [
2638
+ /* @__PURE__ */ jsxs("summary", { className: "flex cursor-pointer select-none list-none items-center justify-between gap-2.5 px-4 py-3.5 text-sm font-semibold text-text-default group-open:text-brand-text [&::-webkit-details-marker]:hidden", children: [
2639
+ title,
2640
+ /* @__PURE__ */ jsx(
2641
+ ChevronDownIcon,
2642
+ {
2643
+ size: 18,
2644
+ className: "shrink-0 text-text-subtle transition-transform duration-200 group-open:rotate-180"
2645
+ }
2646
+ )
2647
+ ] }),
2648
+ /* @__PURE__ */ jsx("div", { className: "px-4 pb-3.5 text-sm leading-relaxed text-text-subtle", children })
2649
+ ]
2650
+ }
2651
+ )
2652
+ );
2653
+ AccordionItem.displayName = "AccordionItem";
2654
+ var Accordion = React16.forwardRef(
2655
+ ({ className, items, children, ...props }, ref) => /* @__PURE__ */ jsx(
2656
+ "div",
2657
+ {
2658
+ ref,
2659
+ className: cn(
2660
+ "overflow-hidden rounded-lg border border-border-subtle bg-surface-default",
2661
+ className
2662
+ ),
2663
+ ...props,
2664
+ children: items ? items.map((it) => /* @__PURE__ */ jsx(
2665
+ AccordionItem,
2666
+ {
2667
+ title: it.title,
2668
+ open: it.defaultOpen,
2669
+ children: it.content
2670
+ },
2671
+ it.id ?? String(it.title)
2672
+ )) : children
2673
+ }
2674
+ )
2675
+ );
2676
+ Accordion.displayName = "Accordion";
2677
+ var KpiRow = React16.forwardRef(
2678
+ ({ items, className, ...props }, ref) => /* @__PURE__ */ jsx("div", { ref, className: cn("flex w-[360px] gap-2", className), ...props, children: items.map((k) => /* @__PURE__ */ jsxs(
2679
+ "div",
2680
+ {
2681
+ className: "flex-1 rounded-lg border border-border-subtle bg-surface-default p-3",
2682
+ children: [
2683
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5", children: [
2684
+ k.dot != null && /* @__PURE__ */ jsx(
2685
+ "span",
2686
+ {
2687
+ className: cn("h-2 w-2 shrink-0 rounded-full", k.dot),
2688
+ "aria-hidden": true
2689
+ }
2690
+ ),
2691
+ /* @__PURE__ */ jsx("span", { className: "text-xs text-text-subtle", children: k.label })
2692
+ ] }),
2693
+ /* @__PURE__ */ jsx("div", { className: "mt-1 text-2xl font-bold text-text-strong", children: k.value })
2694
+ ]
2695
+ },
2696
+ k.label
2697
+ )) })
2698
+ );
2699
+ KpiRow.displayName = "KpiRow";
2700
+ var ProgressList = React16.forwardRef(
2701
+ ({ items, className, ...props }, ref) => /* @__PURE__ */ jsx(
2702
+ "div",
2703
+ {
2704
+ ref,
2705
+ className: cn("flex w-[320px] flex-col gap-3", className),
2706
+ ...props,
2707
+ children: items.map((r) => /* @__PURE__ */ jsxs("div", { children: [
2708
+ /* @__PURE__ */ jsxs("div", { className: "mb-1 flex justify-between text-sm", children: [
2709
+ /* @__PURE__ */ jsx("span", { className: "text-text-default", children: r.label }),
2710
+ /* @__PURE__ */ jsxs("span", { className: r.danger ? "text-danger-text" : "text-brand-text", children: [
2711
+ r.pct,
2712
+ "%"
2713
+ ] })
2714
+ ] }),
2715
+ /* @__PURE__ */ jsx("div", { className: "h-2 overflow-hidden rounded-full bg-surface-sunken", children: /* @__PURE__ */ jsx(
2716
+ "div",
2717
+ {
2718
+ className: cn(
2719
+ "h-full rounded-full",
2720
+ r.danger ? "bg-danger-solid" : "bg-brand-solid"
2721
+ ),
2722
+ style: { width: `${r.pct}%` }
2723
+ }
2724
+ ) })
2725
+ ] }, r.label))
2726
+ }
2727
+ )
2728
+ );
2729
+ ProgressList.displayName = "ProgressList";
2730
+ var Ring = React16.forwardRef(
2731
+ ({ pct, size = 84, label, className, style, ...props }, ref) => /* @__PURE__ */ jsxs(
2732
+ "div",
2733
+ {
2734
+ ref,
2735
+ className: cn("relative grid place-items-center", className),
2736
+ style: { height: size, width: size, ...style },
2737
+ ...props,
2738
+ children: [
2739
+ /* @__PURE__ */ jsxs("svg", { viewBox: "0 0 36 36", className: "h-full w-full -rotate-90", children: [
2740
+ /* @__PURE__ */ jsx(
2741
+ "circle",
2742
+ {
2743
+ cx: "18",
2744
+ cy: "18",
2745
+ r: "15.5",
2746
+ fill: "none",
2747
+ strokeWidth: "3",
2748
+ className: "stroke-surface-sunken"
2749
+ }
2750
+ ),
2751
+ /* @__PURE__ */ jsx(
2752
+ "circle",
2753
+ {
2754
+ cx: "18",
2755
+ cy: "18",
2756
+ r: "15.5",
2757
+ fill: "none",
2758
+ strokeWidth: "3",
2759
+ strokeLinecap: "round",
2760
+ pathLength: 100,
2761
+ strokeDasharray: `${pct} 100`,
2762
+ className: "stroke-brand-solid"
2763
+ }
2764
+ )
2765
+ ] }),
2766
+ /* @__PURE__ */ jsx("b", { className: "absolute text-sm font-bold text-text-strong", children: label ?? `${pct}%` })
2767
+ ]
2768
+ }
2769
+ )
2770
+ );
2771
+ Ring.displayName = "Ring";
2772
+ var StackBar = React16.forwardRef(
2773
+ ({ segments, showLegend = true, className, ...props }, ref) => /* @__PURE__ */ jsxs("div", { ref, className: cn("w-[360px]", className), ...props, children: [
2774
+ /* @__PURE__ */ jsx("div", { className: "flex h-6 gap-0.5", children: segments.map((s, i) => /* @__PURE__ */ jsx(
2775
+ "div",
2776
+ {
2777
+ className: cn(
2778
+ s.className ?? "bg-brand-solid",
2779
+ i === 0 && "rounded-l-full",
2780
+ i === segments.length - 1 && "rounded-r-full"
2781
+ ),
2782
+ style: { width: `${s.pct}%` }
2783
+ },
2784
+ s.label
2785
+ )) }),
2786
+ showLegend && /* @__PURE__ */ jsx("div", { className: "mt-3 flex flex-wrap gap-x-4 gap-y-1", children: segments.map((s) => /* @__PURE__ */ jsxs(
2787
+ "div",
2788
+ {
2789
+ className: "flex items-center gap-1.5 text-xs text-text-subtle",
2790
+ children: [
2791
+ /* @__PURE__ */ jsx(
2792
+ "span",
2793
+ {
2794
+ className: cn(
2795
+ "h-2.5 w-2.5 rounded-full",
2796
+ s.className ?? "bg-brand-solid"
2797
+ )
2798
+ }
2799
+ ),
2800
+ s.label,
2801
+ " ",
2802
+ /* @__PURE__ */ jsxs("span", { className: "text-text-default", children: [
2803
+ s.pct,
2804
+ "%"
2805
+ ] })
2806
+ ]
2807
+ },
2808
+ s.label
2809
+ )) })
2810
+ ] })
2811
+ );
2812
+ StackBar.displayName = "StackBar";
2813
+ var BAR_BY_STATE = {
2814
+ reached: "bg-brand-solid",
2815
+ unreached: "bg-brand-subtle",
2816
+ pending: "bg-surface-sunken"
2817
+ };
2818
+ var WorkHoursBar = React16.forwardRef(
2819
+ ({ data, max = 12, height = 120, caption, className, ...props }, ref) => /* @__PURE__ */ jsxs("div", { ref, className, ...props, children: [
2820
+ /* @__PURE__ */ jsx("div", { className: "flex items-end gap-3", children: data.map((d) => {
2821
+ const barH = d.state === "pending" ? 4 : d.hours / max * height;
2822
+ const labelCls = d.today ? "text-brand-text font-semibold" : d.future ? "text-text-disabled" : "text-text-subtle";
2823
+ return /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center gap-2", children: [
2824
+ /* @__PURE__ */ jsx("div", { className: "flex w-8 items-end", style: { height }, children: /* @__PURE__ */ jsx(
2825
+ "div",
2826
+ {
2827
+ className: cn("w-full rounded-t", BAR_BY_STATE[d.state]),
2828
+ style: { height: barH }
2829
+ }
2830
+ ) }),
2831
+ /* @__PURE__ */ jsx("div", { className: cn("text-xs", labelCls), children: d.day })
2832
+ ] }, d.day);
2833
+ }) }),
2834
+ caption != null && /* @__PURE__ */ jsx("div", { className: "mt-2 text-xs text-text-subtle", children: caption })
2835
+ ] })
2836
+ );
2837
+ WorkHoursBar.displayName = "WorkHoursBar";
2838
+ var Kbd = React16.forwardRef(
2839
+ ({ active, className, children, ...props }, ref) => /* @__PURE__ */ jsx(
2840
+ "kbd",
2841
+ {
2842
+ ref,
2843
+ className: cn(
2844
+ "rounded border px-1.5 py-px font-mono text-xs",
2845
+ active ? "border-current bg-transparent text-brand-text" : "border-border-subtle bg-bg-base text-text-subtle",
2846
+ className
2847
+ ),
2848
+ ...props,
2849
+ children
2850
+ }
2851
+ )
2852
+ );
2853
+ Kbd.displayName = "Kbd";
2854
+ var CommandPalette = React16.forwardRef(
2855
+ ({
2856
+ groups,
2857
+ onSelect,
2858
+ activeId,
2859
+ placeholder = "\uBA85\uB839 \uB610\uB294 \uAC80\uC0C9\u2026",
2860
+ searchKbd = "\u2318K",
2861
+ open = true,
2862
+ className,
2863
+ ...props
2864
+ }, ref) => {
2865
+ if (!open) return null;
2866
+ return /* @__PURE__ */ jsxs(
2867
+ "div",
2868
+ {
2869
+ ref,
2870
+ role: "dialog",
2871
+ "aria-label": "\uCEE4\uB9E8\uB4DC \uD314\uB808\uD2B8",
2872
+ className: cn(
2873
+ "w-[420px] max-w-full overflow-hidden rounded-lg border border-border-subtle bg-surface-default shadow-lg",
2874
+ className
2875
+ ),
2876
+ ...props,
2877
+ children: [
2878
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2.5 border-b border-border-subtle px-4 py-3.5", children: [
2879
+ /* @__PURE__ */ jsx(SearchIcon, { size: 18, className: "text-text-subtle" }),
2880
+ /* @__PURE__ */ jsx("span", { className: "flex-1 text-sm text-text-subtle", children: placeholder }),
2881
+ searchKbd ? /* @__PURE__ */ jsx(Kbd, { children: searchKbd }) : null
2882
+ ] }),
2883
+ /* @__PURE__ */ jsx("div", { className: "p-1.5", children: groups.map((group, gi) => /* @__PURE__ */ jsxs("div", { children: [
2884
+ group.heading ? /* @__PURE__ */ jsx("div", { className: "px-2.5 pb-1 pt-2 text-xs font-bold uppercase tracking-wide text-text-subtle", children: group.heading }) : null,
2885
+ group.items.map((item) => {
2886
+ const active = item.id === activeId;
2887
+ return /* @__PURE__ */ jsxs(
2888
+ "button",
2889
+ {
2890
+ type: "button",
2891
+ onClick: () => onSelect?.(item),
2892
+ className: cn(
2893
+ "flex w-full items-center gap-2.5 rounded px-2.5 py-2 text-left text-sm [&>svg]:h-[17px] [&>svg]:w-[17px]",
2894
+ active ? "bg-brand-subtle text-brand-text" : "text-text-default hover:bg-surface-hover"
2895
+ ),
2896
+ children: [
2897
+ item.icon,
2898
+ /* @__PURE__ */ jsx("span", { className: "flex-1", children: item.label }),
2899
+ item.kbd ? /* @__PURE__ */ jsx(Kbd, { active, children: item.kbd }) : null
2900
+ ]
2901
+ },
2902
+ item.id
2903
+ );
2904
+ })
2905
+ ] }, group.heading ?? gi)) })
2906
+ ]
2907
+ }
2908
+ );
2909
+ }
2910
+ );
2911
+ CommandPalette.displayName = "CommandPalette";
2912
+ var AvatarWithPresence = React16.forwardRef(
2913
+ ({
2914
+ name,
2915
+ status = "online",
2916
+ color,
2917
+ src,
2918
+ size,
2919
+ ringClassName = "ring-surface-default",
2920
+ className
2921
+ }, ref) => /* @__PURE__ */ jsxs("span", { ref, className: cn("relative inline-flex shrink-0", className), children: [
2922
+ /* @__PURE__ */ jsx(
2923
+ Avatar,
2924
+ {
2925
+ size,
2926
+ color,
2927
+ src,
2928
+ alt: name,
2929
+ className: cn("ring-0", status === "offline" && "opacity-[.55]")
2930
+ }
2931
+ ),
2932
+ /* @__PURE__ */ jsx(
2933
+ Presence,
2934
+ {
2935
+ status,
2936
+ size: "sm",
2937
+ className: cn("absolute -bottom-0.5 -right-0.5 ring-2", ringClassName)
2938
+ }
2939
+ )
2940
+ ] })
2941
+ );
2942
+ AvatarWithPresence.displayName = "AvatarWithPresence";
2943
+ var SidebarSection = React16.forwardRef(({ title, addable, onAdd, addLabel, className, children, ...props }, ref) => /* @__PURE__ */ jsxs("div", { ref, className, ...props, children: [
2944
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5 px-2 pb-1.5 pt-2.5 text-xs font-semibold text-text-subtle", children: [
2945
+ title,
2946
+ addable && /* @__PURE__ */ jsx(
2947
+ Button,
2948
+ {
2949
+ type: "button",
2950
+ variant: "ghost",
2951
+ size: "sm",
2952
+ onClick: onAdd,
2953
+ "aria-label": addLabel ?? `${title} \uCD94\uAC00`,
2954
+ className: "ml-auto h-6 w-6 rounded p-0 text-brand-solid",
2955
+ children: /* @__PURE__ */ jsx(PlusIcon, { size: 16 })
2956
+ }
2957
+ )
2958
+ ] }),
2959
+ children
2960
+ ] }));
2961
+ SidebarSection.displayName = "SidebarSection";
2962
+ var SidebarChannel = React16.forwardRef(({ name, icon, locked, active, badge, className, ...props }, ref) => {
2963
+ const resolvedIcon = icon ?? (locked ? /* @__PURE__ */ jsx(LockIcon, { size: 17 }) : /* @__PURE__ */ jsx(HashIcon, { size: 17 }));
2964
+ return /* @__PURE__ */ jsxs(
2965
+ "button",
2966
+ {
2967
+ ref,
2968
+ type: "button",
2969
+ "aria-current": active ? "page" : void 0,
2970
+ className: cn(
2971
+ "flex h-9 w-full cursor-pointer items-center gap-2.5 rounded-lg px-2.5 text-left text-sm hover:bg-sidebar-accent",
2972
+ active ? "bg-sidebar-accent font-semibold text-sidebar-accent-fg" : "text-sidebar-fg",
2973
+ className
2974
+ ),
2975
+ ...props,
2976
+ children: [
2977
+ /* @__PURE__ */ jsx(
2978
+ "span",
2979
+ {
2980
+ className: cn(
2981
+ "inline-flex shrink-0",
2982
+ active ? "text-sidebar-accent-fg" : "text-sidebar-fg"
2983
+ ),
2984
+ children: resolvedIcon
2985
+ }
2986
+ ),
2987
+ /* @__PURE__ */ jsx("span", { className: "min-w-0 flex-1 truncate", children: name }),
2988
+ badge != null && badge !== "" && /* @__PURE__ */ jsx("span", { className: "ml-auto inline-flex h-[18px] min-w-[18px] shrink-0 items-center justify-center rounded-full bg-brand-solid px-1.5 text-[11px] font-semibold leading-none text-text-on-brand", children: badge })
2989
+ ]
2990
+ }
2991
+ );
2992
+ });
2993
+ SidebarChannel.displayName = "SidebarChannel";
2994
+ var SidebarDm = React16.forwardRef(
2995
+ ({ name, status = "online", color, avatarSrc, pinned, className, ...props }, ref) => /* @__PURE__ */ jsxs(
2996
+ "button",
2997
+ {
2998
+ ref,
2999
+ type: "button",
3000
+ className: cn(
3001
+ "flex h-9 w-full cursor-pointer items-center gap-2.5 rounded-lg px-2.5 text-left text-sm text-sidebar-fg hover:bg-sidebar-accent",
3002
+ className
3003
+ ),
3004
+ ...props,
3005
+ children: [
3006
+ /* @__PURE__ */ jsx(
3007
+ AvatarWithPresence,
3008
+ {
3009
+ name,
3010
+ status,
3011
+ color,
3012
+ src: avatarSrc,
3013
+ size: "xs",
3014
+ ringClassName: "ring-sidebar-bg"
3015
+ }
3016
+ ),
3017
+ /* @__PURE__ */ jsx("span", { className: "min-w-0 flex-1 truncate", children: name }),
3018
+ pinned && /* @__PURE__ */ jsx(
3019
+ PinIcon,
3020
+ {
3021
+ size: 14,
3022
+ className: "ml-auto shrink-0 text-text-subtle",
3023
+ "aria-label": "\uACE0\uC815\uB428"
3024
+ }
3025
+ )
3026
+ ]
3027
+ }
3028
+ )
3029
+ );
3030
+ SidebarDm.displayName = "SidebarDm";
3031
+ var sidebarContainerVariants = cva(
3032
+ "border-border-subtle bg-sidebar-bg p-2",
3033
+ {
3034
+ variants: {
3035
+ variant: {
3036
+ panel: "w-[260px] max-w-full rounded-lg border",
3037
+ shell: "h-full border-r"
3038
+ }
3039
+ },
3040
+ defaultVariants: { variant: "panel" }
3041
+ }
3042
+ );
3043
+ var SidebarPanel = React16.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
3044
+ "div",
3045
+ {
3046
+ ref,
3047
+ className: cn(sidebarContainerVariants({ variant: "panel" }), className),
3048
+ ...props
3049
+ }
3050
+ ));
3051
+ SidebarPanel.displayName = "SidebarPanel";
3052
+ var Sidebar = React16.forwardRef(
3053
+ ({ sections, variant = "panel", className, ...props }, ref) => /* @__PURE__ */ jsx(
3054
+ "nav",
3055
+ {
3056
+ ref,
3057
+ className: cn(sidebarContainerVariants({ variant }), className),
3058
+ ...props,
3059
+ children: sections.map((section, si) => /* @__PURE__ */ jsx(
3060
+ SidebarSection,
3061
+ {
3062
+ title: section.title,
3063
+ addable: section.addable,
3064
+ onAdd: section.onAdd,
3065
+ children: section.items.map((item, ii) => {
3066
+ const key = item.id ?? item.name + ii;
3067
+ if (item.kind === "dm") {
3068
+ const { kind: kind2, id: id2, ...rest2 } = item;
3069
+ return /* @__PURE__ */ jsx(SidebarDm, { ...rest2 }, key);
3070
+ }
3071
+ const { kind, id, ...rest } = item;
3072
+ return /* @__PURE__ */ jsx(SidebarChannel, { ...rest }, key);
3073
+ })
3074
+ },
3075
+ section.title + si
3076
+ ))
3077
+ }
3078
+ )
3079
+ );
3080
+ Sidebar.displayName = "Sidebar";
3081
+ var List = React16.forwardRef(
3082
+ ({ className, bordered = true, divided = true, ...props }, ref) => {
3083
+ return /* @__PURE__ */ jsx(
3084
+ "ul",
3085
+ {
3086
+ ref,
3087
+ className: cn(
3088
+ "bg-surface-default px-4",
3089
+ bordered && "rounded-lg border border-border-subtle",
3090
+ divided && "[&>li+li]:border-t [&>li+li]:border-border-subtle",
3091
+ className
3092
+ ),
3093
+ ...props
3094
+ }
3095
+ );
3096
+ }
3097
+ );
3098
+ List.displayName = "List";
3099
+ var ListItem = React16.forwardRef(
3100
+ ({ className, leading, title, description, trailing, ...props }, ref) => {
3101
+ return /* @__PURE__ */ jsxs(
3102
+ "li",
3103
+ {
3104
+ ref,
3105
+ className: cn("flex items-center gap-3 py-2.5", className),
3106
+ ...props,
3107
+ children: [
3108
+ leading != null ? /* @__PURE__ */ jsx("div", { className: "relative shrink-0", children: leading }) : null,
3109
+ /* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
3110
+ /* @__PURE__ */ jsx("div", { className: "truncate text-sm font-semibold text-text-default", children: title }),
3111
+ description != null ? /* @__PURE__ */ jsx("div", { className: "truncate text-xs text-text-subtle", children: description }) : null
3112
+ ] }),
3113
+ trailing != null ? /* @__PURE__ */ jsx("div", { className: "flex shrink-0 items-center gap-2", children: trailing }) : null
3114
+ ]
3115
+ }
3116
+ );
3117
+ }
3118
+ );
3119
+ ListItem.displayName = "ListItem";
3120
+ var SuccessCheck = React16.forwardRef(
3121
+ ({ size = 22, className, ...props }, ref) => /* @__PURE__ */ jsx(
3122
+ "span",
3123
+ {
3124
+ ref,
3125
+ className: cn(
3126
+ "inline-flex shrink-0 items-center justify-center rounded-full bg-brand-solid text-text-on-brand",
3127
+ className
3128
+ ),
3129
+ style: { width: size, height: size },
3130
+ ...props,
3131
+ children: /* @__PURE__ */ jsx(CheckIcon4, { size: Math.round(size * 0.6), strokeWidth: 3 })
3132
+ }
3133
+ )
3134
+ );
3135
+ SuccessCheck.displayName = "SuccessCheck";
3136
+ var separatorVariants = cva("shrink-0 border-border-default", {
3137
+ variants: {
3138
+ orientation: {
3139
+ horizontal: "w-full border-t",
3140
+ vertical: "h-full self-stretch border-l"
3141
+ }
3142
+ },
3143
+ defaultVariants: {
3144
+ orientation: "horizontal"
3145
+ }
3146
+ });
3147
+ var Separator = React16.forwardRef(
3148
+ ({ className, orientation = "horizontal", label, decorative = false, ...props }, ref) => {
3149
+ const resolvedOrientation = orientation === "vertical" ? "vertical" : "horizontal";
3150
+ const ariaOrientation = label != null ? "horizontal" : resolvedOrientation;
3151
+ const a11yProps = decorative ? { role: "none" } : { role: "separator", "aria-orientation": ariaOrientation };
3152
+ if (label != null) {
3153
+ return /* @__PURE__ */ jsxs(
3154
+ "div",
3155
+ {
3156
+ ref,
3157
+ ...a11yProps,
3158
+ className: cn(
3159
+ "flex w-full items-center gap-3 text-sm text-text-subtle",
3160
+ className
3161
+ ),
3162
+ ...props,
3163
+ children: [
3164
+ /* @__PURE__ */ jsx(
3165
+ "span",
3166
+ {
3167
+ "aria-hidden": "true",
3168
+ className: "flex-1 border-t border-border-default"
3169
+ }
3170
+ ),
3171
+ /* @__PURE__ */ jsx("span", { className: "shrink-0", children: label }),
3172
+ /* @__PURE__ */ jsx(
3173
+ "span",
3174
+ {
3175
+ "aria-hidden": "true",
3176
+ className: "flex-1 border-t border-border-default"
3177
+ }
3178
+ )
3179
+ ]
3180
+ }
3181
+ );
3182
+ }
3183
+ return /* @__PURE__ */ jsx(
3184
+ "div",
3185
+ {
3186
+ ref,
3187
+ ...a11yProps,
3188
+ className: cn(
3189
+ separatorVariants({ orientation: resolvedOrientation }),
3190
+ className
3191
+ ),
3192
+ ...props
3193
+ }
3194
+ );
3195
+ }
3196
+ );
3197
+ Separator.displayName = "Separator";
3198
+ var popoverContentVariants = cva(
3199
+ // content shell: raised surface · border · radius lg(8) · shadow-popover · padding
3200
+ "absolute z-50 w-max min-w-[220px] max-w-xs rounded-lg border border-border-default bg-surface-default p-4 text-sm text-text-default shadow-popover outline-none",
3201
+ {
3202
+ variants: {
3203
+ placement: {
3204
+ top: "bottom-full mb-2",
3205
+ bottom: "top-full mt-2",
3206
+ left: "right-full mr-2",
3207
+ right: "left-full ml-2"
3208
+ },
3209
+ align: {
3210
+ start: "",
3211
+ center: "",
3212
+ end: ""
3213
+ }
3214
+ },
3215
+ compoundVariants: [
3216
+ // vertical placements (top/bottom) → align along the X axis
3217
+ { placement: ["top", "bottom"], align: "start", class: "left-0" },
3218
+ {
3219
+ placement: ["top", "bottom"],
3220
+ align: "center",
3221
+ class: "left-1/2 -translate-x-1/2"
3222
+ },
3223
+ { placement: ["top", "bottom"], align: "end", class: "right-0" },
3224
+ // horizontal placements (left/right) → align along the Y axis
3225
+ { placement: ["left", "right"], align: "start", class: "top-0" },
3226
+ {
3227
+ placement: ["left", "right"],
3228
+ align: "center",
3229
+ class: "top-1/2 -translate-y-1/2"
3230
+ },
3231
+ { placement: ["left", "right"], align: "end", class: "bottom-0" }
3232
+ ],
3233
+ defaultVariants: {
3234
+ placement: "bottom",
3235
+ align: "center"
3236
+ }
3237
+ }
3238
+ );
3239
+ var Popover = React16.forwardRef(
3240
+ ({
3241
+ className,
3242
+ content,
3243
+ children,
3244
+ placement = "bottom",
3245
+ align = "center",
3246
+ open: openProp,
3247
+ defaultOpen = false,
3248
+ onOpenChange,
3249
+ ...props
3250
+ }, ref) => {
3251
+ const [uncontrolledOpen, setUncontrolledOpen] = React16.useState(defaultOpen);
3252
+ const isControlled = openProp !== void 0;
3253
+ const open = isControlled ? openProp : uncontrolledOpen;
3254
+ const innerRef = React16.useRef(null);
3255
+ React16.useImperativeHandle(ref, () => innerRef.current, []);
3256
+ const contentId = React16.useId();
3257
+ const setOpen = React16.useCallback(
3258
+ (next) => {
3259
+ if (!isControlled) setUncontrolledOpen(next);
3260
+ onOpenChange?.(next);
3261
+ },
3262
+ [isControlled, onOpenChange]
3263
+ );
3264
+ React16.useEffect(() => {
3265
+ if (!open) return;
3266
+ const onPointerDown = (event) => {
3267
+ const node = innerRef.current;
3268
+ if (node && !node.contains(event.target)) setOpen(false);
3269
+ };
3270
+ const onKeyDown = (event) => {
3271
+ if (event.key === "Escape") setOpen(false);
3272
+ };
3273
+ document.addEventListener("pointerdown", onPointerDown);
3274
+ document.addEventListener("keydown", onKeyDown);
3275
+ return () => {
3276
+ document.removeEventListener("pointerdown", onPointerDown);
3277
+ document.removeEventListener("keydown", onKeyDown);
3278
+ };
3279
+ }, [open, setOpen]);
3280
+ return /* @__PURE__ */ jsxs(
3281
+ "div",
3282
+ {
3283
+ ref: innerRef,
3284
+ className: cn("relative inline-flex", className),
3285
+ ...props,
3286
+ children: [
3287
+ /* @__PURE__ */ jsx(
3288
+ "span",
3289
+ {
3290
+ className: "inline-flex",
3291
+ "aria-haspopup": "dialog",
3292
+ "aria-expanded": open,
3293
+ "aria-controls": open ? contentId : void 0,
3294
+ onClick: (event) => {
3295
+ if (!event.defaultPrevented) setOpen(!open);
3296
+ },
3297
+ children
3298
+ }
3299
+ ),
3300
+ open ? /* @__PURE__ */ jsx(
3301
+ "div",
3302
+ {
3303
+ id: contentId,
3304
+ role: "dialog",
3305
+ "aria-modal": "false",
3306
+ className: cn(popoverContentVariants({ placement, align })),
3307
+ children: content
3308
+ }
3309
+ ) : null
3310
+ ]
3311
+ }
3312
+ );
3313
+ }
3314
+ );
3315
+ Popover.displayName = "Popover";
3316
+ var fieldVariants = cva("flex gap-1.5", {
3317
+ variants: {
3318
+ orientation: {
3319
+ vertical: "flex-col",
3320
+ horizontal: "flex-row items-start gap-4"
3321
+ }
3322
+ },
3323
+ defaultVariants: {
3324
+ orientation: "vertical"
3325
+ }
3326
+ });
3327
+ var Field = React16.forwardRef(
3328
+ ({
3329
+ className,
3330
+ orientation = "vertical",
3331
+ label,
3332
+ htmlFor,
3333
+ required = false,
3334
+ optional = false,
3335
+ hint,
3336
+ error,
3337
+ children,
3338
+ ...props
3339
+ }, ref) => {
3340
+ const reactId = React16.useId();
3341
+ const controlId = htmlFor ?? reactId;
3342
+ const isHorizontal = orientation === "horizontal";
3343
+ const messageId = error ? `${controlId}-error` : hint ? `${controlId}-hint` : void 0;
3344
+ const control = React16.isValidElement(children) ? React16.cloneElement(children, {
3345
+ id: children.props.id ?? controlId,
3346
+ "aria-invalid": error ? true : children.props["aria-invalid"],
3347
+ "aria-describedby": [children.props["aria-describedby"], messageId].filter(Boolean).join(" ") || void 0
3348
+ }) : children;
3349
+ return /* @__PURE__ */ jsxs(
3350
+ "div",
3351
+ {
3352
+ ref,
3353
+ className: cn(fieldVariants({ orientation }), className),
3354
+ ...props,
3355
+ children: [
3356
+ label ? /* @__PURE__ */ jsx("div", { className: cn(isHorizontal && "w-40 shrink-0 pt-2"), children: /* @__PURE__ */ jsxs(
3357
+ "label",
3358
+ {
3359
+ htmlFor: controlId,
3360
+ className: "inline-flex items-center gap-1.5 text-sm font-medium text-text-default",
3361
+ children: [
3362
+ /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-1", children: [
3363
+ label,
3364
+ required ? /* @__PURE__ */ jsx("span", { "aria-hidden": "true", className: "text-danger-text", children: "*" }) : null
3365
+ ] }),
3366
+ optional && !required ? /* @__PURE__ */ jsx(Label, { variant: "neutral", children: "\uC120\uD0DD" }) : null
3367
+ ]
3368
+ }
3369
+ ) }) : null,
3370
+ /* @__PURE__ */ jsxs(
3371
+ "div",
3372
+ {
3373
+ className: cn(
3374
+ "flex min-w-0 flex-col gap-1.5",
3375
+ isHorizontal && "flex-1"
3376
+ ),
3377
+ children: [
3378
+ control,
3379
+ error ? /* @__PURE__ */ jsx(
3380
+ "p",
3381
+ {
3382
+ id: `${controlId}-error`,
3383
+ className: "flex items-center gap-1 text-sm text-danger-text",
3384
+ children: error
3385
+ }
3386
+ ) : hint ? /* @__PURE__ */ jsx(
3387
+ "p",
3388
+ {
3389
+ id: `${controlId}-hint`,
3390
+ className: "flex items-center gap-1 text-sm text-text-subtle",
3391
+ children: hint
3392
+ }
3393
+ ) : null
3394
+ ]
3395
+ }
3396
+ )
3397
+ ]
3398
+ }
3399
+ );
3400
+ }
3401
+ );
3402
+ Field.displayName = "Field";
3403
+ var accessoryButtonClass = "inline-flex size-6 shrink-0 items-center justify-center rounded text-text-subtle transition-colors hover:bg-surface-hover hover:text-text-default active:bg-surface-pressed focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-brand-border disabled:pointer-events-none disabled:text-text-disabled";
3404
+ var SearchField = React16.forwardRef(
3405
+ ({
3406
+ className,
3407
+ value,
3408
+ defaultValue,
3409
+ onValueChange,
3410
+ placeholder = "\uAC80\uC0C9",
3411
+ resultCount,
3412
+ onPrev,
3413
+ onNext,
3414
+ onClear,
3415
+ size,
3416
+ disabled,
3417
+ ...props
3418
+ }, ref) => {
3419
+ const isControlled = value !== void 0;
3420
+ const [internalValue, setInternalValue] = React16.useState(
3421
+ defaultValue ?? ""
3422
+ );
3423
+ const currentValue = isControlled ? value : internalValue;
3424
+ const innerRef = React16.useRef(null);
3425
+ React16.useImperativeHandle(ref, () => innerRef.current);
3426
+ const commit = (next) => {
3427
+ if (!isControlled) setInternalValue(next);
3428
+ onValueChange?.(next);
3429
+ };
3430
+ const handleClear = () => {
3431
+ commit("");
3432
+ onClear?.();
3433
+ innerRef.current?.focus();
3434
+ };
3435
+ const hasCount = typeof resultCount === "number";
3436
+ const hasNav = Boolean(onPrev || onNext);
3437
+ const showClear = Boolean(onClear) && currentValue.length > 0;
3438
+ const showAccessories = hasCount || hasNav || showClear;
3439
+ const showDivider = hasCount && (hasNav || showClear);
3440
+ return /* @__PURE__ */ jsxs(
3441
+ "div",
3442
+ {
3443
+ className: cn(
3444
+ inputVariants({ inputSize: size }),
3445
+ "cursor-text focus-within:border-border-focus focus-within:ring-2 focus-within:ring-brand-border focus-within:ring-offset-2 focus-within:ring-offset-bg-base",
3446
+ disabled && "cursor-not-allowed bg-surface-sunken text-text-disabled",
3447
+ className
3448
+ ),
3449
+ children: [
3450
+ /* @__PURE__ */ jsx(
3451
+ "span",
3452
+ {
3453
+ "aria-hidden": "true",
3454
+ className: "flex size-5 shrink-0 items-center justify-center text-text-subtle [&_svg]:size-5",
3455
+ children: /* @__PURE__ */ jsx(SearchIcon, {})
3456
+ }
3457
+ ),
3458
+ /* @__PURE__ */ jsx(
3459
+ "input",
3460
+ {
3461
+ ref: innerRef,
3462
+ type: "search",
3463
+ role: "searchbox",
3464
+ value: currentValue,
3465
+ placeholder,
3466
+ disabled,
3467
+ onChange: (event) => commit(event.target.value),
3468
+ className: "h-full w-full min-w-0 bg-transparent text-text-default outline-none placeholder:text-text-subtle disabled:cursor-not-allowed disabled:text-text-disabled [&::-webkit-search-cancel-button]:hidden",
3469
+ ...props
3470
+ }
3471
+ ),
3472
+ showAccessories ? /* @__PURE__ */ jsxs("div", { className: "flex shrink-0 items-center gap-1", children: [
3473
+ hasCount ? /* @__PURE__ */ jsxs("span", { className: "shrink-0 whitespace-nowrap text-sm tabular-nums text-text-subtle", children: [
3474
+ resultCount,
3475
+ "\uAC74"
3476
+ ] }) : null,
3477
+ showDivider ? /* @__PURE__ */ jsx("span", { "aria-hidden": "true", className: "h-4 w-px bg-border-subtle" }) : null,
3478
+ onPrev ? /* @__PURE__ */ jsx(
3479
+ "button",
3480
+ {
3481
+ type: "button",
3482
+ "aria-label": "\uC774\uC804 \uACB0\uACFC",
3483
+ disabled,
3484
+ onClick: onPrev,
3485
+ className: accessoryButtonClass,
3486
+ children: /* @__PURE__ */ jsx(ChevronLeftIcon2, { size: 16 })
3487
+ }
3488
+ ) : null,
3489
+ onNext ? /* @__PURE__ */ jsx(
3490
+ "button",
3491
+ {
3492
+ type: "button",
3493
+ "aria-label": "\uB2E4\uC74C \uACB0\uACFC",
3494
+ disabled,
3495
+ onClick: onNext,
3496
+ className: accessoryButtonClass,
3497
+ children: /* @__PURE__ */ jsx(ChevronRightIcon2, { size: 16 })
3498
+ }
3499
+ ) : null,
3500
+ showClear ? /* @__PURE__ */ jsx(
3501
+ "button",
3502
+ {
3503
+ type: "button",
3504
+ "aria-label": "\uAC80\uC0C9\uC5B4 \uC9C0\uC6B0\uAE30",
3505
+ disabled,
3506
+ onClick: handleClear,
3507
+ className: accessoryButtonClass,
3508
+ children: /* @__PURE__ */ jsx(XIcon, { size: 16 })
3509
+ }
3510
+ ) : null
3511
+ ] }) : null
3512
+ ]
3513
+ }
3514
+ );
3515
+ }
3516
+ );
3517
+ SearchField.displayName = "SearchField";
3518
+ var bannerVariants = cva(
3519
+ "flex w-full items-start gap-3 rounded-lg border p-4 text-sm",
3520
+ {
3521
+ variants: {
3522
+ variant: {
3523
+ neutral: "border-border-default bg-surface-sunken text-text-default",
3524
+ brand: "border-brand-border bg-brand-subtle text-brand-text",
3525
+ danger: "border-danger-border bg-danger-subtle text-danger-text"
3526
+ }
3527
+ },
3528
+ defaultVariants: {
3529
+ variant: "neutral"
3530
+ }
3531
+ }
3532
+ );
3533
+ var Banner = React16.forwardRef(
3534
+ ({
3535
+ className,
3536
+ variant,
3537
+ icon,
3538
+ title,
3539
+ dismissible = false,
3540
+ onDismiss,
3541
+ children,
3542
+ role = "status",
3543
+ ...props
3544
+ }, ref) => {
3545
+ return /* @__PURE__ */ jsxs(
3546
+ "div",
3547
+ {
3548
+ ref,
3549
+ role,
3550
+ className: cn(bannerVariants({ variant }), className),
3551
+ ...props,
3552
+ children: [
3553
+ icon ? /* @__PURE__ */ jsx(
3554
+ "span",
3555
+ {
3556
+ "aria-hidden": "true",
3557
+ className: "mt-0.5 flex size-5 shrink-0 items-center justify-center [&_svg]:size-5",
3558
+ children: icon
3559
+ }
3560
+ ) : null,
3561
+ /* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
3562
+ title ? /* @__PURE__ */ jsx("p", { className: "font-semibold leading-5", children: title }) : null,
3563
+ children ? /* @__PURE__ */ jsx("div", { className: cn("leading-5", title && "mt-1"), children }) : null
3564
+ ] }),
3565
+ dismissible ? /* @__PURE__ */ jsx(
3566
+ "button",
3567
+ {
3568
+ type: "button",
3569
+ "aria-label": "\uB2EB\uAE30",
3570
+ onClick: onDismiss,
3571
+ className: "-m-1 inline-flex size-6 shrink-0 items-center justify-center rounded text-current opacity-70 transition-opacity hover:opacity-100 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-brand-border focus-visible:ring-offset-2 focus-visible:ring-offset-bg-base",
3572
+ children: /* @__PURE__ */ jsx(XIcon, { className: "size-4" })
3573
+ }
3574
+ ) : null
3575
+ ]
3576
+ }
3577
+ );
3578
+ }
3579
+ );
3580
+ Banner.displayName = "Banner";
3581
+
3582
+ export { AVATAR_COLORS, Accordion, AccordionItem, Alert, AlertDescription, AlertTitle, Avatar, AvatarFallback, AvatarGroup, AvatarImage, AvatarWithPresence, Banner, Button, Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, Checkbox, Chip, CommandPalette, Dialog, Button as DialogAction, DialogBody, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogTitle, DropdownMenu, DropdownMenuContent, DropdownMenuEmpty, DropdownMenuItem, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuTrigger, EmptyState, Field, Input, Kbd, KpiRow, Label, List, ListItem, PRESENCE_LABEL, Pagination, PaginationEllipsis, PaginationItem, PaginationNext, PaginationPrevious, Popover, Presence, Progress, ProgressList, Radio, RadioGroup, Ring, ScrollArea, ScrollBar, SearchField, SegmentedControl, Select, Separator, Sheet, SheetBody, SheetDescription, SheetFooter, SheetHandle, SheetHeader, SheetTitle, Sidebar, SidebarChannel, SidebarDm, SidebarPanel, SidebarSection, Skeleton, StackBar, SuccessCheck, Table, TableBody, TableCaption, TableCell, TableHead, TableHeader, TableRow, Tabs, TabsContent, TabsList, TabsTrigger, Textarea, Toast, Toggle, Tooltip, WorkHoursBar, alertVariants, avatarVariants, bannerVariants, buildPageRange, buttonVariants, cardVariants, checkboxVariants, chipVariants, cn, dialogPanelVariants, dropdownMenuContentVariants, dropdownMenuItemVariants, fieldVariants, inputVariants, labelVariants, paginationItemVariants, popoverContentVariants, progressFillVariants, progressTrackVariants, radioVariants, segmentContainerVariants, segmentItemVariants, selectTriggerVariants, separatorVariants, sheetOverlayVariants, sheetPanelVariants, sidebarContainerVariants, skeletonVariants, tableRowVariants, tableVariants, tabsListVariants, tabsTriggerVariants, textareaVariants, toastVariants, toggleThumbVariants, toggleTrackVariants, tooltipArrowVariants, tooltipContentVariants };
3583
+ //# sourceMappingURL=index.mjs.map
3584
+ //# sourceMappingURL=index.mjs.map