sh-ui-cli 0.52.0 → 0.52.2
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/data/changelog/versions.json +25 -0
- package/data/registry/react/components/_smoke/vanilla-extract.test.ts +33 -0
- package/data/registry/react/components/input/styles.css.ts +6 -6
- package/data/registry/react/registry.json +35 -852
- package/package.json +2 -2
- package/src/api.d.ts +3 -4
- package/src/constants.js +9 -5
- package/src/create/plugins/pluginSchema.js +5 -3
- package/src/mcp.mjs +4 -3
- package/data/registry/react/components/accordion/index.vanilla-extract.tsx +0 -97
- package/data/registry/react/components/accordion/styles.css.ts +0 -131
- package/data/registry/react/components/avatar/index.vanilla-extract.tsx +0 -73
- package/data/registry/react/components/avatar/styles.css.ts +0 -68
- package/data/registry/react/components/badge/index.vanilla-extract.tsx +0 -40
- package/data/registry/react/components/badge/styles.css.ts +0 -71
- package/data/registry/react/components/breadcrumb/index.vanilla-extract.tsx +0 -152
- package/data/registry/react/components/breadcrumb/styles.css.ts +0 -95
- package/data/registry/react/components/calendar/index.vanilla-extract.tsx +0 -806
- package/data/registry/react/components/calendar/styles.css.ts +0 -250
- package/data/registry/react/components/carousel/index.vanilla-extract.tsx +0 -430
- package/data/registry/react/components/carousel/styles.css.ts +0 -169
- package/data/registry/react/components/checkbox/index.vanilla-extract.tsx +0 -96
- package/data/registry/react/components/checkbox/styles.css.ts +0 -74
- package/data/registry/react/components/code-editor/index.vanilla-extract.tsx +0 -230
- package/data/registry/react/components/code-editor/styles.css.ts +0 -97
- package/data/registry/react/components/code-panel/index.vanilla-extract.tsx +0 -191
- package/data/registry/react/components/code-panel/styles.css.ts +0 -151
- package/data/registry/react/components/color-picker/index.vanilla-extract.tsx +0 -467
- package/data/registry/react/components/color-picker/styles.css.ts +0 -169
- package/data/registry/react/components/combobox/index.vanilla-extract.tsx +0 -165
- package/data/registry/react/components/combobox/styles.css.ts +0 -174
- package/data/registry/react/components/context-menu/index.vanilla-extract.tsx +0 -251
- package/data/registry/react/components/context-menu/styles.css.ts +0 -167
- package/data/registry/react/components/date-picker/index.vanilla-extract.tsx +0 -520
- package/data/registry/react/components/date-picker/styles.css.ts +0 -111
- package/data/registry/react/components/dialog/index.vanilla-extract.tsx +0 -95
- package/data/registry/react/components/dialog/styles.css.ts +0 -140
- package/data/registry/react/components/dropdown-menu/index.vanilla-extract.tsx +0 -255
- package/data/registry/react/components/dropdown-menu/styles.css.ts +0 -175
- package/data/registry/react/components/file-upload/index.vanilla-extract.tsx +0 -487
- package/data/registry/react/components/file-upload/styles.css.ts +0 -193
- package/data/registry/react/components/form/index.vanilla-extract.tsx +0 -61
- package/data/registry/react/components/form/styles.css.ts +0 -56
- package/data/registry/react/components/header/index.vanilla-extract.tsx +0 -805
- package/data/registry/react/components/header/styles.css.ts +0 -413
- package/data/registry/react/components/label/index.vanilla-extract.tsx +0 -52
- package/data/registry/react/components/label/styles.css.ts +0 -141
- package/data/registry/react/components/markdown-editor/index.vanilla-extract.tsx +0 -119
- package/data/registry/react/components/markdown-editor/styles.css.ts +0 -231
- package/data/registry/react/components/menubar/index.vanilla-extract.tsx +0 -32
- package/data/registry/react/components/menubar/styles.css.ts +0 -53
- package/data/registry/react/components/numeric-input/index.vanilla-extract.tsx +0 -148
- package/data/registry/react/components/numeric-input/styles.css.ts +0 -65
- package/data/registry/react/components/page-toc/index.vanilla-extract.tsx +0 -174
- package/data/registry/react/components/page-toc/styles.css.ts +0 -97
- package/data/registry/react/components/pagination/index.vanilla-extract.tsx +0 -269
- package/data/registry/react/components/pagination/styles.css.ts +0 -113
- package/data/registry/react/components/popover/index.vanilla-extract.tsx +0 -113
- package/data/registry/react/components/popover/styles.css.ts +0 -78
- package/data/registry/react/components/progress/index.vanilla-extract.tsx +0 -54
- package/data/registry/react/components/progress/styles.css.ts +0 -53
- package/data/registry/react/components/radio/index.vanilla-extract.tsx +0 -65
- package/data/registry/react/components/radio/styles.css.ts +0 -79
- package/data/registry/react/components/rich-text-editor/index.vanilla-extract.tsx +0 -348
- package/data/registry/react/components/rich-text-editor/styles.css.ts +0 -243
- package/data/registry/react/components/select/index.vanilla-extract.tsx +0 -234
- package/data/registry/react/components/select/styles.css.ts +0 -225
- package/data/registry/react/components/separator/index.vanilla-extract.tsx +0 -46
- package/data/registry/react/components/separator/styles.css.ts +0 -24
- package/data/registry/react/components/sidebar/index.vanilla-extract.tsx +0 -1067
- package/data/registry/react/components/sidebar/styles.css.ts +0 -578
- package/data/registry/react/components/skeleton/index.vanilla-extract.tsx +0 -22
- package/data/registry/react/components/skeleton/styles.css.ts +0 -30
- package/data/registry/react/components/slider/index.vanilla-extract.tsx +0 -298
- package/data/registry/react/components/slider/styles.css.ts +0 -75
- package/data/registry/react/components/spinner/index.vanilla-extract.tsx +0 -38
- package/data/registry/react/components/spinner/styles.css.ts +0 -60
- package/data/registry/react/components/switch/index.vanilla-extract.tsx +0 -39
- package/data/registry/react/components/switch/styles.css.ts +0 -87
- package/data/registry/react/components/tabs/index.vanilla-extract.tsx +0 -91
- package/data/registry/react/components/tabs/styles.css.ts +0 -145
- package/data/registry/react/components/textarea/index.vanilla-extract.tsx +0 -23
- package/data/registry/react/components/textarea/styles.css.ts +0 -55
- package/data/registry/react/components/toast/index.vanilla-extract.tsx +0 -258
- package/data/registry/react/components/toast/styles.css.ts +0 -307
- package/data/registry/react/components/toggle/index.vanilla-extract.tsx +0 -131
- package/data/registry/react/components/toggle/styles.css.ts +0 -109
- package/data/registry/react/components/tooltip/index.vanilla-extract.tsx +0 -83
- package/data/registry/react/components/tooltip/styles.css.ts +0 -59
|
@@ -1,113 +0,0 @@
|
|
|
1
|
-
import * as React from "react";
|
|
2
|
-
import { Popover as BasePopover } from "@base-ui/react/popover";
|
|
3
|
-
import { byKey, popover__positioner, popover__content, popover__arrow, popover__title, popover__description } from "./styles.css";
|
|
4
|
-
|
|
5
|
-
import { cn } from "@SH_UI_UTILS@";
|
|
6
|
-
type WithStringClassName<T> = Omit<T, "className"> & { className?: string };
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* 트리거 요소에 떠다니는 가벼운 패널을 띄우는 비모달 컨테이너. 포커스를 가두지 않으므로
|
|
11
|
-
* 짧은 폼·정보 표시에 적합하고, 강제 응답이 필요하면 Dialog를 사용할 것.
|
|
12
|
-
*/
|
|
13
|
-
export const Popover = BasePopover.Root;
|
|
14
|
-
|
|
15
|
-
/** Popover를 여는 트리거. */
|
|
16
|
-
export const PopoverTrigger = BasePopover.Trigger;
|
|
17
|
-
|
|
18
|
-
/** Popover를 닫는 요소. */
|
|
19
|
-
export const PopoverClose = BasePopover.Close;
|
|
20
|
-
|
|
21
|
-
export interface PopoverContentProps
|
|
22
|
-
extends WithStringClassName<React.ComponentPropsWithoutRef<typeof BasePopover.Popup>> {
|
|
23
|
-
/**
|
|
24
|
-
* Trigger 기준 배치 방향. 공간 부족 시 자동으로 반대편으로 뒤집힌다.
|
|
25
|
-
* @default "bottom"
|
|
26
|
-
*/
|
|
27
|
-
side?: "top" | "right" | "bottom" | "left";
|
|
28
|
-
/**
|
|
29
|
-
* 트리거 축에서의 정렬.
|
|
30
|
-
* - `start` — 트리거 시작 가장자리 정렬
|
|
31
|
-
* - `center` — 가운데 (기본)
|
|
32
|
-
* - `end` — 트리거 끝 가장자리 정렬
|
|
33
|
-
*
|
|
34
|
-
* @default "center"
|
|
35
|
-
*/
|
|
36
|
-
align?: "start" | "center" | "end";
|
|
37
|
-
/**
|
|
38
|
-
* Trigger와 Popup 사이 간격(px).
|
|
39
|
-
* @default 8
|
|
40
|
-
*/
|
|
41
|
-
sideOffset?: number;
|
|
42
|
-
/**
|
|
43
|
-
* Portal이 마운트될 DOM 노드.
|
|
44
|
-
* @default document.body
|
|
45
|
-
*/
|
|
46
|
-
container?: React.ComponentPropsWithoutRef<typeof BasePopover.Portal>["container"];
|
|
47
|
-
/**
|
|
48
|
-
* Trigger를 가리키는 화살표 표시 여부.
|
|
49
|
-
* @default false
|
|
50
|
-
*/
|
|
51
|
-
showArrow?: boolean;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
/**
|
|
55
|
-
* Popover의 콘텐츠. 트리거에 자동 위치 조정되어 portal로 마운트된다.
|
|
56
|
-
* `side`/`align`/`sideOffset`로 배치를 미세조정하고, `showArrow`로 화살표를 노출한다.
|
|
57
|
-
*/
|
|
58
|
-
export const PopoverContent = React.forwardRef<HTMLDivElement, PopoverContentProps>(
|
|
59
|
-
function PopoverContent(
|
|
60
|
-
{ className, children, side, align, sideOffset = 8, container, showArrow, ...props },
|
|
61
|
-
ref,
|
|
62
|
-
) {
|
|
63
|
-
return (
|
|
64
|
-
<BasePopover.Portal container={container}>
|
|
65
|
-
<BasePopover.Positioner
|
|
66
|
-
className={popover__positioner}
|
|
67
|
-
side={side}
|
|
68
|
-
align={align}
|
|
69
|
-
sideOffset={sideOffset}
|
|
70
|
-
>
|
|
71
|
-
<BasePopover.Popup
|
|
72
|
-
ref={ref}
|
|
73
|
-
className={cn(popover__content, className)}
|
|
74
|
-
{...props}
|
|
75
|
-
>
|
|
76
|
-
{showArrow && (
|
|
77
|
-
<BasePopover.Arrow className={popover__arrow} />
|
|
78
|
-
)}
|
|
79
|
-
{children}
|
|
80
|
-
</BasePopover.Popup>
|
|
81
|
-
</BasePopover.Positioner>
|
|
82
|
-
</BasePopover.Portal>
|
|
83
|
-
);
|
|
84
|
-
},
|
|
85
|
-
);
|
|
86
|
-
|
|
87
|
-
/** Popover 콘텐츠의 제목. 접근성을 위해 짧은 제목이라도 함께 두는 것을 권장. */
|
|
88
|
-
export const PopoverTitle = React.forwardRef<
|
|
89
|
-
HTMLHeadingElement,
|
|
90
|
-
WithStringClassName<React.ComponentPropsWithoutRef<typeof BasePopover.Title>>
|
|
91
|
-
>(function PopoverTitle({ className, ...props }, ref) {
|
|
92
|
-
return (
|
|
93
|
-
<BasePopover.Title
|
|
94
|
-
ref={ref}
|
|
95
|
-
className={cn(popover__title, className)}
|
|
96
|
-
{...props}
|
|
97
|
-
/>
|
|
98
|
-
);
|
|
99
|
-
});
|
|
100
|
-
|
|
101
|
-
/** Popover 콘텐츠의 보조 설명. */
|
|
102
|
-
export const PopoverDescription = React.forwardRef<
|
|
103
|
-
HTMLParagraphElement,
|
|
104
|
-
WithStringClassName<React.ComponentPropsWithoutRef<typeof BasePopover.Description>>
|
|
105
|
-
>(function PopoverDescription({ className, ...props }, ref) {
|
|
106
|
-
return (
|
|
107
|
-
<BasePopover.Description
|
|
108
|
-
ref={ref}
|
|
109
|
-
className={cn(popover__description, className)}
|
|
110
|
-
{...props}
|
|
111
|
-
/>
|
|
112
|
-
);
|
|
113
|
-
});
|
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
import { style } from "@vanilla-extract/css";
|
|
2
|
-
|
|
3
|
-
export const popover__positioner = style({
|
|
4
|
-
zIndex: "var(--z-popover)",
|
|
5
|
-
outline: "none",
|
|
6
|
-
});
|
|
7
|
-
|
|
8
|
-
export const popover__content = style({
|
|
9
|
-
minWidth: "12rem",
|
|
10
|
-
padding: "var(--space-2)",
|
|
11
|
-
background: "var(--background)",
|
|
12
|
-
color: "var(--foreground)",
|
|
13
|
-
border: "1px solid var(--border)",
|
|
14
|
-
borderRadius: "var(--radius)",
|
|
15
|
-
boxShadow: "0 8px 24px rgba(0, 0, 0, 0.12)",
|
|
16
|
-
outline: "none",
|
|
17
|
-
fontSize: "var(--text-sm)",
|
|
18
|
-
lineHeight: 1.4,
|
|
19
|
-
transformOrigin: "var(--transform-origin)",
|
|
20
|
-
transition: "opacity 140ms ease,\n transform 140ms ease",
|
|
21
|
-
selectors: {
|
|
22
|
-
"&[data-starting-style]": {
|
|
23
|
-
opacity: 0,
|
|
24
|
-
transform: "scale(0.96)",
|
|
25
|
-
},
|
|
26
|
-
"&[data-ending-style]": {
|
|
27
|
-
opacity: 0,
|
|
28
|
-
transform: "scale(0.96)",
|
|
29
|
-
},
|
|
30
|
-
"&:focus-visible": {
|
|
31
|
-
outline: "var(--border-width-strong) solid var(--foreground)",
|
|
32
|
-
outlineOffset: "2px",
|
|
33
|
-
},
|
|
34
|
-
},
|
|
35
|
-
"@media": {
|
|
36
|
-
"(prefers-reduced-motion: reduce)": {
|
|
37
|
-
transition: "none",
|
|
38
|
-
selectors: {
|
|
39
|
-
"&[data-starting-style]": {
|
|
40
|
-
transform: "none",
|
|
41
|
-
},
|
|
42
|
-
"&[data-ending-style]": {
|
|
43
|
-
transform: "none",
|
|
44
|
-
},
|
|
45
|
-
},
|
|
46
|
-
},
|
|
47
|
-
},
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
export const popover__arrow = style({
|
|
51
|
-
color: "var(--background)",
|
|
52
|
-
selectors: {
|
|
53
|
-
"& svg": {
|
|
54
|
-
display: "block",
|
|
55
|
-
},
|
|
56
|
-
},
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
export const popover__title = style({
|
|
60
|
-
margin: "0 0 var(--space-1)",
|
|
61
|
-
fontWeight: "var(--weight-semibold)",
|
|
62
|
-
fontSize: "0.9375rem",
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
export const popover__description = style({
|
|
66
|
-
margin: 0,
|
|
67
|
-
color: "var(--foreground-muted)",
|
|
68
|
-
fontSize: "0.8125rem",
|
|
69
|
-
});
|
|
70
|
-
|
|
71
|
-
/** 동적 키로 클래스 참조용 — `byKey[\`badge--${variant}\`]` 같은 패턴 지원. */
|
|
72
|
-
export const byKey: Record<string, string> = {
|
|
73
|
-
"popover__positioner": popover__positioner,
|
|
74
|
-
"popover__content": popover__content,
|
|
75
|
-
"popover__arrow": popover__arrow,
|
|
76
|
-
"popover__title": popover__title,
|
|
77
|
-
"popover__description": popover__description,
|
|
78
|
-
};
|
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
import * as React from "react";
|
|
2
|
-
import { byKey, progress, progress__indicator } from "./styles.css";
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
import { cn } from "@SH_UI_UTILS@";
|
|
6
|
-
function clamp(n: number, min: number, max: number) {
|
|
7
|
-
return Math.min(max, Math.max(min, n));
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
export interface ProgressProps
|
|
11
|
-
extends Omit<React.HTMLAttributes<HTMLDivElement>, "role"> {
|
|
12
|
-
/** 0~100 사이의 현재 값. 생략 시 indeterminate 모드. */
|
|
13
|
-
value?: number;
|
|
14
|
-
/** 최댓값. 기본 100. */
|
|
15
|
-
max?: number;
|
|
16
|
-
/** 접근성: aria-label (시각적 라벨이 없으면 권장). */
|
|
17
|
-
"aria-label"?: string;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* 작업 진행도를 가로 바로 표시. value가 없으면 무한 루프 indeterminate.
|
|
22
|
-
*
|
|
23
|
-
* - determinate: `<Progress value={40} />`
|
|
24
|
-
* - indeterminate: `<Progress aria-label="로딩 중" />`
|
|
25
|
-
*/
|
|
26
|
-
export const Progress = React.forwardRef<HTMLDivElement, ProgressProps>(
|
|
27
|
-
function Progress(
|
|
28
|
-
{ value, max = 100, className, "aria-label": ariaLabel, ...props },
|
|
29
|
-
ref,
|
|
30
|
-
) {
|
|
31
|
-
const isDeterminate = value !== undefined;
|
|
32
|
-
const normalized = isDeterminate ? clamp((value / max) * 100, 0, 100) : 0;
|
|
33
|
-
|
|
34
|
-
return (
|
|
35
|
-
<div
|
|
36
|
-
ref={ref}
|
|
37
|
-
role="progressbar"
|
|
38
|
-
aria-label={ariaLabel}
|
|
39
|
-
aria-valuemin={isDeterminate ? 0 : undefined}
|
|
40
|
-
aria-valuemax={isDeterminate ? max : undefined}
|
|
41
|
-
aria-valuenow={isDeterminate ? value : undefined}
|
|
42
|
-
data-state={isDeterminate ? "determinate" : "indeterminate"}
|
|
43
|
-
className={cn(progress, className)}
|
|
44
|
-
{...props}
|
|
45
|
-
>
|
|
46
|
-
<div
|
|
47
|
-
className={progress__indicator}
|
|
48
|
-
style={isDeterminate ? { width: `${normalized}%` } : undefined}
|
|
49
|
-
/>
|
|
50
|
-
</div>
|
|
51
|
-
);
|
|
52
|
-
},
|
|
53
|
-
);
|
|
54
|
-
Progress.displayName = "Progress";
|
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
import { style, keyframes } from "@vanilla-extract/css";
|
|
2
|
-
|
|
3
|
-
export const shUiProgressSlide = keyframes({
|
|
4
|
-
"0%": {
|
|
5
|
-
transform: "translateX(-100%)",
|
|
6
|
-
},
|
|
7
|
-
"100%": {
|
|
8
|
-
transform: "translateX(250%)",
|
|
9
|
-
},
|
|
10
|
-
});
|
|
11
|
-
|
|
12
|
-
export const progress = style({
|
|
13
|
-
position: "relative",
|
|
14
|
-
width: "100%",
|
|
15
|
-
height: "0.5rem",
|
|
16
|
-
overflow: "hidden",
|
|
17
|
-
background: "var(--background-muted)",
|
|
18
|
-
borderRadius: "999px",
|
|
19
|
-
selectors: {
|
|
20
|
-
[`&[data-state="indeterminate"] ${progress__indicator}`]: {
|
|
21
|
-
width: "40%",
|
|
22
|
-
animation: "sh-ui-progress-slide 1.2s ease-in-out infinite",
|
|
23
|
-
},
|
|
24
|
-
},
|
|
25
|
-
"@media": {
|
|
26
|
-
"(prefers-reduced-motion: reduce)": {
|
|
27
|
-
selectors: {
|
|
28
|
-
[`&[data-state="indeterminate"] ${progress__indicator}`]: {
|
|
29
|
-
animation: "none",
|
|
30
|
-
transform: "translateX(75%)",
|
|
31
|
-
},
|
|
32
|
-
},
|
|
33
|
-
},
|
|
34
|
-
},
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
export const progress__indicator = style({
|
|
38
|
-
height: "100%",
|
|
39
|
-
background: "var(--primary)",
|
|
40
|
-
borderRadius: "999px",
|
|
41
|
-
transition: "width var(--duration-base) ease",
|
|
42
|
-
"@media": {
|
|
43
|
-
"(prefers-reduced-motion: reduce)": {
|
|
44
|
-
transition: "none",
|
|
45
|
-
},
|
|
46
|
-
},
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
/** 동적 키로 클래스 참조용 — `byKey[\`badge--${variant}\`]` 같은 패턴 지원. */
|
|
50
|
-
export const byKey: Record<string, string> = {
|
|
51
|
-
"progress": progress,
|
|
52
|
-
"progress__indicator": progress__indicator,
|
|
53
|
-
};
|
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
import * as React from "react";
|
|
2
|
-
import { Radio as BaseRadio } from "@base-ui/react/radio";
|
|
3
|
-
import { RadioGroup as BaseRadioGroup } from "@base-ui/react/radio-group";
|
|
4
|
-
import { byKey, radio, radio__indicator, radioGroup } from "./styles.css";
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
import { cn } from "@SH_UI_UTILS@";
|
|
8
|
-
/* ───────────── Radio ───────────── */
|
|
9
|
-
|
|
10
|
-
export type RadioProps = Omit<
|
|
11
|
-
React.ComponentPropsWithoutRef<typeof BaseRadio.Root>,
|
|
12
|
-
"className"
|
|
13
|
-
> & {
|
|
14
|
-
className?: string;
|
|
15
|
-
};
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* 단일 선택지. 단독으로 쓰지 않고 반드시 `RadioGroup` 안에 두 개 이상을 묶어 사용한다.
|
|
19
|
-
* 단일 선택이지만 즉시 적용되는 설정에는 Switch를, 다중 선택에는 Checkbox를 권장.
|
|
20
|
-
*/
|
|
21
|
-
export const Radio = React.forwardRef<HTMLElement, RadioProps>(
|
|
22
|
-
({ className, ...props }, ref) => (
|
|
23
|
-
<BaseRadio.Root
|
|
24
|
-
ref={ref}
|
|
25
|
-
className={cn(radio, className)}
|
|
26
|
-
{...props}
|
|
27
|
-
>
|
|
28
|
-
<BaseRadio.Indicator className={radio__indicator} />
|
|
29
|
-
</BaseRadio.Root>
|
|
30
|
-
),
|
|
31
|
-
);
|
|
32
|
-
Radio.displayName = "Radio";
|
|
33
|
-
|
|
34
|
-
/* ───────────── RadioGroup ───────────── */
|
|
35
|
-
|
|
36
|
-
export type RadioGroupProps = Omit<
|
|
37
|
-
React.ComponentPropsWithoutRef<typeof BaseRadioGroup>,
|
|
38
|
-
"className"
|
|
39
|
-
> & {
|
|
40
|
-
className?: string;
|
|
41
|
-
/**
|
|
42
|
-
* 그룹 내 항목 배치 방향.
|
|
43
|
-
* - `vertical` — 세로 나열 (기본)
|
|
44
|
-
* - `horizontal` — 가로 나열. 짧은 라벨 2~3개에만 권장
|
|
45
|
-
*
|
|
46
|
-
* @default "vertical"
|
|
47
|
-
*/
|
|
48
|
-
orientation?: "horizontal" | "vertical";
|
|
49
|
-
};
|
|
50
|
-
|
|
51
|
-
/**
|
|
52
|
-
* 여러 Radio를 묶는 컨테이너. 같은 `name` 아래 단일 선택을 보장하고,
|
|
53
|
-
* 키보드 화살표로 항목 간 이동이 가능하다. 그룹 라벨은 외부 `<Label>`로 제공할 것.
|
|
54
|
-
*/
|
|
55
|
-
export const RadioGroup = React.forwardRef<HTMLDivElement, RadioGroupProps>(
|
|
56
|
-
({ className, orientation = "vertical", ...props }, ref) => (
|
|
57
|
-
<BaseRadioGroup
|
|
58
|
-
ref={ref}
|
|
59
|
-
className={cn(radioGroup, className)}
|
|
60
|
-
data-orientation={orientation}
|
|
61
|
-
{...props}
|
|
62
|
-
/>
|
|
63
|
-
),
|
|
64
|
-
);
|
|
65
|
-
RadioGroup.displayName = "RadioGroup";
|
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
import { style } from "@vanilla-extract/css";
|
|
2
|
-
|
|
3
|
-
export const radio = style({
|
|
4
|
-
display: "inline-flex",
|
|
5
|
-
alignItems: "center",
|
|
6
|
-
justifyContent: "center",
|
|
7
|
-
width: "1.125rem",
|
|
8
|
-
height: "1.125rem",
|
|
9
|
-
border: "1px solid var(--border-strong)",
|
|
10
|
-
borderRadius: "999px",
|
|
11
|
-
background: "var(--background)",
|
|
12
|
-
cursor: "pointer",
|
|
13
|
-
flexShrink: 0,
|
|
14
|
-
transition: "border-color var(--duration-fast)",
|
|
15
|
-
WebkitTapHighlightColor: "transparent",
|
|
16
|
-
selectors: {
|
|
17
|
-
"&:hover:not([data-disabled])": {
|
|
18
|
-
borderColor: "var(--foreground)",
|
|
19
|
-
},
|
|
20
|
-
"&:focus-visible": {
|
|
21
|
-
outline: "var(--border-width-strong) solid var(--foreground)",
|
|
22
|
-
outlineOffset: "2px",
|
|
23
|
-
},
|
|
24
|
-
"&[data-checked]": {
|
|
25
|
-
borderColor: "var(--primary)",
|
|
26
|
-
},
|
|
27
|
-
"&[data-disabled]": {
|
|
28
|
-
opacity: "var(--opacity-disabled)",
|
|
29
|
-
cursor: "not-allowed",
|
|
30
|
-
},
|
|
31
|
-
[`&[data-checked] ${radio__indicator}`]: {
|
|
32
|
-
transform: "scale(1)",
|
|
33
|
-
},
|
|
34
|
-
},
|
|
35
|
-
"@media": {
|
|
36
|
-
"(hover: none) and (pointer: coarse)": {
|
|
37
|
-
width: "1.25rem",
|
|
38
|
-
height: "1.25rem",
|
|
39
|
-
},
|
|
40
|
-
"(prefers-reduced-motion: reduce)": {
|
|
41
|
-
transition: "none",
|
|
42
|
-
},
|
|
43
|
-
},
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
export const radio__indicator = style({
|
|
47
|
-
width: "0.5rem",
|
|
48
|
-
height: "0.5rem",
|
|
49
|
-
borderRadius: "999px",
|
|
50
|
-
background: "var(--primary)",
|
|
51
|
-
transform: "scale(0)",
|
|
52
|
-
transition: "transform var(--duration-fast) ease-out",
|
|
53
|
-
"@media": {
|
|
54
|
-
"(prefers-reduced-motion: reduce)": {
|
|
55
|
-
transition: "none",
|
|
56
|
-
},
|
|
57
|
-
},
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
export const radioGroup = style({
|
|
61
|
-
display: "flex",
|
|
62
|
-
gap: "0.625rem",
|
|
63
|
-
selectors: {
|
|
64
|
-
"&[data-orientation="vertical"]": {
|
|
65
|
-
flexDirection: "column",
|
|
66
|
-
},
|
|
67
|
-
"&[data-orientation="horizontal"]": {
|
|
68
|
-
flexDirection: "row",
|
|
69
|
-
flexWrap: "wrap",
|
|
70
|
-
},
|
|
71
|
-
},
|
|
72
|
-
});
|
|
73
|
-
|
|
74
|
-
/** 동적 키로 클래스 참조용 — `byKey[\`badge--${variant}\`]` 같은 패턴 지원. */
|
|
75
|
-
export const byKey: Record<string, string> = {
|
|
76
|
-
"radio": radio,
|
|
77
|
-
"radio__indicator": radio__indicator,
|
|
78
|
-
"radio-group": radioGroup,
|
|
79
|
-
};
|