sh-ui-cli 0.52.1 → 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 +14 -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 +1 -1
- package/src/api.d.ts +3 -4
- package/src/constants.js +9 -5
- package/src/mcp.mjs +0 -1
- 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,165 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
|
|
3
|
-
import * as React from "react";
|
|
4
|
-
import { Combobox as BaseCombobox } from "@base-ui/react/combobox";
|
|
5
|
-
import { byKey, combobox__input, combobox__positioner, combobox__content, combobox__item, comboboxItemIndicator, comboboxItemText, combobox__empty, comboboxGroupLabel, combobox__chip, comboboxChipRemove } from "./styles.css";
|
|
6
|
-
|
|
7
|
-
import { cn } from "@SH_UI_UTILS@";
|
|
8
|
-
type WithStringClassName<T> = Omit<T, "className"> & { className?: string };
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* Select + Input의 결합 — 타이핑으로 목록이 자동 필터링된다.
|
|
13
|
-
* Base UI Combobox를 래핑해 `items` 배열을 받으면 기본 필터가 `input` 값 기준으로 동작.
|
|
14
|
-
*
|
|
15
|
-
* <Combobox items={fruits}>
|
|
16
|
-
* <ComboboxInput placeholder="과일 검색" />
|
|
17
|
-
* <ComboboxContent>
|
|
18
|
-
* <ComboboxList>
|
|
19
|
-
* {(item) => <ComboboxItem key={item} value={item}>{item}</ComboboxItem>}
|
|
20
|
-
* </ComboboxList>
|
|
21
|
-
* <ComboboxEmpty>일치하는 항목 없음</ComboboxEmpty>
|
|
22
|
-
* </ComboboxContent>
|
|
23
|
-
* </Combobox>
|
|
24
|
-
*/
|
|
25
|
-
export const Combobox = BaseCombobox.Root;
|
|
26
|
-
|
|
27
|
-
export const ComboboxIcon = BaseCombobox.Icon;
|
|
28
|
-
export const ComboboxTrigger = BaseCombobox.Trigger;
|
|
29
|
-
export const ComboboxClear = BaseCombobox.Clear;
|
|
30
|
-
export const ComboboxValue = BaseCombobox.Value;
|
|
31
|
-
export const ComboboxGroup = BaseCombobox.Group;
|
|
32
|
-
export const ComboboxChips = BaseCombobox.Chips;
|
|
33
|
-
|
|
34
|
-
export const ComboboxInput = React.forwardRef<
|
|
35
|
-
HTMLInputElement,
|
|
36
|
-
WithStringClassName<React.ComponentPropsWithoutRef<typeof BaseCombobox.Input>>
|
|
37
|
-
>(function ComboboxInput({ className, ...props }, ref) {
|
|
38
|
-
return (
|
|
39
|
-
<BaseCombobox.Input
|
|
40
|
-
ref={ref}
|
|
41
|
-
className={cn(combobox__input, className)}
|
|
42
|
-
{...props}
|
|
43
|
-
/>
|
|
44
|
-
);
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
/** Portal + Positioner + Popup 래퍼. */
|
|
48
|
-
export const ComboboxContent = React.forwardRef<
|
|
49
|
-
HTMLDivElement,
|
|
50
|
-
WithStringClassName<React.ComponentPropsWithoutRef<typeof BaseCombobox.Popup>> & {
|
|
51
|
-
container?: React.ComponentPropsWithoutRef<typeof BaseCombobox.Portal>["container"];
|
|
52
|
-
sideOffset?: number;
|
|
53
|
-
}
|
|
54
|
-
>(function ComboboxContent(
|
|
55
|
-
{ className, children, container, sideOffset = 4, ...props },
|
|
56
|
-
ref,
|
|
57
|
-
) {
|
|
58
|
-
return (
|
|
59
|
-
<BaseCombobox.Portal container={container}>
|
|
60
|
-
<BaseCombobox.Positioner
|
|
61
|
-
className={combobox__positioner}
|
|
62
|
-
sideOffset={sideOffset}
|
|
63
|
-
align="start"
|
|
64
|
-
>
|
|
65
|
-
<BaseCombobox.Popup
|
|
66
|
-
ref={ref}
|
|
67
|
-
className={cn(combobox__content, className)}
|
|
68
|
-
{...props}
|
|
69
|
-
>
|
|
70
|
-
{children}
|
|
71
|
-
</BaseCombobox.Popup>
|
|
72
|
-
</BaseCombobox.Positioner>
|
|
73
|
-
</BaseCombobox.Portal>
|
|
74
|
-
);
|
|
75
|
-
});
|
|
76
|
-
|
|
77
|
-
export const ComboboxList = BaseCombobox.List;
|
|
78
|
-
|
|
79
|
-
export const ComboboxItem = React.forwardRef<
|
|
80
|
-
HTMLDivElement,
|
|
81
|
-
WithStringClassName<React.ComponentPropsWithoutRef<typeof BaseCombobox.Item>>
|
|
82
|
-
>(function ComboboxItem({ className, children, ...props }, ref) {
|
|
83
|
-
return (
|
|
84
|
-
<BaseCombobox.Item
|
|
85
|
-
ref={ref}
|
|
86
|
-
className={cn(combobox__item, className)}
|
|
87
|
-
{...props}
|
|
88
|
-
>
|
|
89
|
-
<BaseCombobox.ItemIndicator className={comboboxItemIndicator}>
|
|
90
|
-
<CheckIcon />
|
|
91
|
-
</BaseCombobox.ItemIndicator>
|
|
92
|
-
<span className={comboboxItemText}>{children}</span>
|
|
93
|
-
</BaseCombobox.Item>
|
|
94
|
-
);
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
export const ComboboxEmpty = React.forwardRef<
|
|
98
|
-
HTMLDivElement,
|
|
99
|
-
WithStringClassName<React.ComponentPropsWithoutRef<typeof BaseCombobox.Empty>>
|
|
100
|
-
>(function ComboboxEmpty({ className, ...props }, ref) {
|
|
101
|
-
return (
|
|
102
|
-
<BaseCombobox.Empty
|
|
103
|
-
ref={ref}
|
|
104
|
-
className={cn(combobox__empty, className)}
|
|
105
|
-
{...props}
|
|
106
|
-
/>
|
|
107
|
-
);
|
|
108
|
-
});
|
|
109
|
-
|
|
110
|
-
export const ComboboxGroupLabel = React.forwardRef<
|
|
111
|
-
HTMLDivElement,
|
|
112
|
-
WithStringClassName<React.ComponentPropsWithoutRef<typeof BaseCombobox.GroupLabel>>
|
|
113
|
-
>(function ComboboxGroupLabel({ className, ...props }, ref) {
|
|
114
|
-
return (
|
|
115
|
-
<BaseCombobox.GroupLabel
|
|
116
|
-
ref={ref}
|
|
117
|
-
className={cn(comboboxGroupLabel, className)}
|
|
118
|
-
{...props}
|
|
119
|
-
/>
|
|
120
|
-
);
|
|
121
|
-
});
|
|
122
|
-
|
|
123
|
-
/** 다중 선택 칩. */
|
|
124
|
-
export const ComboboxChip = React.forwardRef<
|
|
125
|
-
HTMLDivElement,
|
|
126
|
-
WithStringClassName<React.ComponentPropsWithoutRef<typeof BaseCombobox.Chip>>
|
|
127
|
-
>(function ComboboxChip({ className, ...props }, ref) {
|
|
128
|
-
return (
|
|
129
|
-
<BaseCombobox.Chip
|
|
130
|
-
ref={ref}
|
|
131
|
-
className={cn(combobox__chip, className)}
|
|
132
|
-
{...props}
|
|
133
|
-
/>
|
|
134
|
-
);
|
|
135
|
-
});
|
|
136
|
-
|
|
137
|
-
/** 칩 × 제거 버튼. */
|
|
138
|
-
export const ComboboxChipRemove = React.forwardRef<
|
|
139
|
-
HTMLButtonElement,
|
|
140
|
-
WithStringClassName<React.ComponentPropsWithoutRef<typeof BaseCombobox.ChipRemove>>
|
|
141
|
-
>(function ComboboxChipRemove({ className, children, ...props }, ref) {
|
|
142
|
-
return (
|
|
143
|
-
<BaseCombobox.ChipRemove
|
|
144
|
-
ref={ref}
|
|
145
|
-
className={cn(comboboxChipRemove, className)}
|
|
146
|
-
{...props}
|
|
147
|
-
>
|
|
148
|
-
{children ?? "×"}
|
|
149
|
-
</BaseCombobox.ChipRemove>
|
|
150
|
-
);
|
|
151
|
-
});
|
|
152
|
-
|
|
153
|
-
function CheckIcon() {
|
|
154
|
-
return (
|
|
155
|
-
<svg viewBox="0 0 16 16" width="14" height="14" fill="none" aria-hidden>
|
|
156
|
-
<path
|
|
157
|
-
d="M3 8.5l3 3 7-7"
|
|
158
|
-
stroke="currentColor"
|
|
159
|
-
strokeWidth="2"
|
|
160
|
-
strokeLinecap="round"
|
|
161
|
-
strokeLinejoin="round"
|
|
162
|
-
/>
|
|
163
|
-
</svg>
|
|
164
|
-
);
|
|
165
|
-
}
|
|
@@ -1,174 +0,0 @@
|
|
|
1
|
-
import { style } from "@vanilla-extract/css";
|
|
2
|
-
|
|
3
|
-
export const combobox__input = style({
|
|
4
|
-
display: "inline-flex",
|
|
5
|
-
width: "100%",
|
|
6
|
-
minWidth: "10rem",
|
|
7
|
-
height: "var(--control-md)",
|
|
8
|
-
padding: "0 var(--space-3)",
|
|
9
|
-
background: "var(--background)",
|
|
10
|
-
color: "var(--foreground)",
|
|
11
|
-
border: "1px solid var(--border)",
|
|
12
|
-
borderRadius: "var(--radius)",
|
|
13
|
-
fontSize: "var(--text-sm)",
|
|
14
|
-
lineHeight: 1,
|
|
15
|
-
outline: "none",
|
|
16
|
-
transition: "border-color var(--duration-fast)",
|
|
17
|
-
selectors: {
|
|
18
|
-
"&::placeholder": {
|
|
19
|
-
color: "var(--foreground-subtle)",
|
|
20
|
-
},
|
|
21
|
-
"&:hover:not(:disabled)": {
|
|
22
|
-
borderColor: "var(--border-strong)",
|
|
23
|
-
},
|
|
24
|
-
"&:focus-visible": {
|
|
25
|
-
outline: "var(--border-width-strong) solid var(--foreground)",
|
|
26
|
-
outlineOffset: "2px",
|
|
27
|
-
},
|
|
28
|
-
"&:disabled": {
|
|
29
|
-
opacity: "var(--opacity-disabled)",
|
|
30
|
-
pointerEvents: "none",
|
|
31
|
-
},
|
|
32
|
-
},
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
export const combobox__positioner = style({
|
|
36
|
-
zIndex: "var(--z-dropdown)",
|
|
37
|
-
outline: "none",
|
|
38
|
-
width: "var(--anchor-width)",
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
export const combobox__content = style({
|
|
42
|
-
maxHeight: "min(20rem, var(--available-height))",
|
|
43
|
-
overflowY: "auto",
|
|
44
|
-
padding: "var(--space-1)",
|
|
45
|
-
background: "var(--background)",
|
|
46
|
-
color: "var(--foreground)",
|
|
47
|
-
border: "1px solid var(--border)",
|
|
48
|
-
borderRadius: "var(--radius)",
|
|
49
|
-
boxShadow: "0 8px 24px rgba(0, 0, 0, 0.08)",
|
|
50
|
-
outline: "none",
|
|
51
|
-
transformOrigin: "var(--transform-origin)",
|
|
52
|
-
transition: "opacity 140ms ease, transform 140ms ease",
|
|
53
|
-
selectors: {
|
|
54
|
-
"&[data-starting-style]": {
|
|
55
|
-
opacity: 0,
|
|
56
|
-
transform: "scale(0.97)",
|
|
57
|
-
},
|
|
58
|
-
"&[data-ending-style]": {
|
|
59
|
-
opacity: 0,
|
|
60
|
-
transform: "scale(0.97)",
|
|
61
|
-
},
|
|
62
|
-
},
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
export const combobox__item = style({
|
|
66
|
-
display: "flex",
|
|
67
|
-
alignItems: "center",
|
|
68
|
-
gap: "var(--space-2)",
|
|
69
|
-
padding: "0.375rem 0.75rem",
|
|
70
|
-
fontSize: "var(--text-sm)",
|
|
71
|
-
lineHeight: 1.4,
|
|
72
|
-
borderRadius: "calc(var(--radius) - 2px)",
|
|
73
|
-
cursor: "pointer",
|
|
74
|
-
userSelect: "none",
|
|
75
|
-
outline: "none",
|
|
76
|
-
selectors: {
|
|
77
|
-
"&[data-highlighted]": {
|
|
78
|
-
background: "var(--background-muted)",
|
|
79
|
-
},
|
|
80
|
-
"&:hover": {
|
|
81
|
-
background: "var(--background-muted)",
|
|
82
|
-
},
|
|
83
|
-
"&[data-selected]": {
|
|
84
|
-
color: "var(--foreground)",
|
|
85
|
-
fontWeight: "var(--weight-medium)",
|
|
86
|
-
},
|
|
87
|
-
"&[data-disabled]": {
|
|
88
|
-
opacity: "var(--opacity-disabled)",
|
|
89
|
-
pointerEvents: "none",
|
|
90
|
-
},
|
|
91
|
-
},
|
|
92
|
-
});
|
|
93
|
-
|
|
94
|
-
export const comboboxItemIndicator = style({
|
|
95
|
-
order: 1,
|
|
96
|
-
marginLeft: "auto",
|
|
97
|
-
display: "inline-flex",
|
|
98
|
-
alignItems: "center",
|
|
99
|
-
justifyContent: "center",
|
|
100
|
-
color: "var(--foreground)",
|
|
101
|
-
});
|
|
102
|
-
|
|
103
|
-
export const comboboxItemText = style({
|
|
104
|
-
flex: 1,
|
|
105
|
-
overflow: "hidden",
|
|
106
|
-
textOverflow: "ellipsis",
|
|
107
|
-
whiteSpace: "nowrap",
|
|
108
|
-
});
|
|
109
|
-
|
|
110
|
-
export const combobox__empty = style({
|
|
111
|
-
padding: "var(--space-3) var(--space-2)",
|
|
112
|
-
textAlign: "center",
|
|
113
|
-
fontSize: "0.8125rem",
|
|
114
|
-
color: "var(--foreground-muted)",
|
|
115
|
-
});
|
|
116
|
-
|
|
117
|
-
export const comboboxGroupLabel = style({
|
|
118
|
-
padding: "0.375rem var(--space-2) var(--space-1)",
|
|
119
|
-
fontSize: "var(--text-xs)",
|
|
120
|
-
fontWeight: "var(--weight-semibold)",
|
|
121
|
-
color: "var(--foreground-muted)",
|
|
122
|
-
textTransform: "uppercase",
|
|
123
|
-
letterSpacing: "0.04em",
|
|
124
|
-
});
|
|
125
|
-
|
|
126
|
-
export const combobox__chip = style({
|
|
127
|
-
display: "inline-flex",
|
|
128
|
-
alignItems: "center",
|
|
129
|
-
gap: "var(--space-1)",
|
|
130
|
-
padding: "0.125rem 0.375rem 0.125rem var(--space-2)",
|
|
131
|
-
marginRight: "var(--space-1)",
|
|
132
|
-
fontSize: "var(--text-xs)",
|
|
133
|
-
lineHeight: "1.25rem",
|
|
134
|
-
background: "var(--background-muted)",
|
|
135
|
-
borderRadius: "calc(var(--radius) - 2px)",
|
|
136
|
-
whiteSpace: "nowrap",
|
|
137
|
-
});
|
|
138
|
-
|
|
139
|
-
export const comboboxChipRemove = style({
|
|
140
|
-
display: "inline-flex",
|
|
141
|
-
alignItems: "center",
|
|
142
|
-
justifyContent: "center",
|
|
143
|
-
width: "1rem",
|
|
144
|
-
height: "1rem",
|
|
145
|
-
padding: 0,
|
|
146
|
-
border: 0,
|
|
147
|
-
borderRadius: "999px",
|
|
148
|
-
background: "transparent",
|
|
149
|
-
color: "var(--foreground-muted)",
|
|
150
|
-
fontSize: "var(--text-sm)",
|
|
151
|
-
lineHeight: 1,
|
|
152
|
-
cursor: "pointer",
|
|
153
|
-
transition: "background-color var(--duration-fast), color var(--duration-fast)",
|
|
154
|
-
selectors: {
|
|
155
|
-
"&:hover": {
|
|
156
|
-
background: "var(--background)",
|
|
157
|
-
color: "var(--foreground)",
|
|
158
|
-
},
|
|
159
|
-
},
|
|
160
|
-
});
|
|
161
|
-
|
|
162
|
-
/** 동적 키로 클래스 참조용 — `byKey[\`badge--${variant}\`]` 같은 패턴 지원. */
|
|
163
|
-
export const byKey: Record<string, string> = {
|
|
164
|
-
"combobox__input": combobox__input,
|
|
165
|
-
"combobox__positioner": combobox__positioner,
|
|
166
|
-
"combobox__content": combobox__content,
|
|
167
|
-
"combobox__item": combobox__item,
|
|
168
|
-
"combobox__item-indicator": comboboxItemIndicator,
|
|
169
|
-
"combobox__item-text": comboboxItemText,
|
|
170
|
-
"combobox__empty": combobox__empty,
|
|
171
|
-
"combobox__group-label": comboboxGroupLabel,
|
|
172
|
-
"combobox__chip": combobox__chip,
|
|
173
|
-
"combobox__chip-remove": comboboxChipRemove,
|
|
174
|
-
};
|
|
@@ -1,251 +0,0 @@
|
|
|
1
|
-
import * as React from "react";
|
|
2
|
-
import { ContextMenu as BaseContextMenu } from "@base-ui/react/context-menu";
|
|
3
|
-
import { byKey, cm__trigger, cm__positioner, cm__content, cm__item, cmItemText, cmItemCheck, cmItemIndicator, cm__group, cm__label, cm__separator, cmSubArrow, cmSubTrigger } from "./styles.css";
|
|
4
|
-
|
|
5
|
-
import { cn } from "@SH_UI_UTILS@";
|
|
6
|
-
type WithStringClassName<T> = Omit<T, "className"> & { className?: string };
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
/* ───────── Root ───────── */
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* 우클릭(또는 long-press)으로 열리는 컨텍스트 메뉴 루트. 자식으로 Trigger와 Content를 둔다.
|
|
13
|
-
* 사용 가능 액션이 명시적으로 보이는 게 유리한 경우엔 일반 버튼 + DropdownMenu를 권장.
|
|
14
|
-
*/
|
|
15
|
-
export const ContextMenu = BaseContextMenu.Root;
|
|
16
|
-
|
|
17
|
-
/* ───────── Trigger ─────────
|
|
18
|
-
* 우클릭(또는 long-press)을 감지하는 wrapper. 기본은 투명, 사용자는
|
|
19
|
-
* 자신의 영역(Card, 이미지 등)에 적용하여 감싼다.
|
|
20
|
-
*/
|
|
21
|
-
|
|
22
|
-
/** 우클릭/long-press를 감지할 영역. Card나 이미지 등 임의 영역을 자식으로 감싼다. */
|
|
23
|
-
export const ContextMenuTrigger = React.forwardRef<
|
|
24
|
-
HTMLDivElement,
|
|
25
|
-
WithStringClassName<React.ComponentPropsWithoutRef<typeof BaseContextMenu.Trigger>>
|
|
26
|
-
>(function ContextMenuTrigger({ className, ...props }, ref) {
|
|
27
|
-
return (
|
|
28
|
-
<BaseContextMenu.Trigger
|
|
29
|
-
ref={ref}
|
|
30
|
-
className={cn(cm__trigger, className)}
|
|
31
|
-
{...props}
|
|
32
|
-
/>
|
|
33
|
-
);
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
/* ───────── Content ───────── */
|
|
37
|
-
|
|
38
|
-
export interface ContextMenuContentProps
|
|
39
|
-
extends WithStringClassName<
|
|
40
|
-
React.ComponentPropsWithoutRef<typeof BaseContextMenu.Popup>
|
|
41
|
-
> {
|
|
42
|
-
/**
|
|
43
|
-
* Portal이 마운트될 DOM 노드.
|
|
44
|
-
* @default document.body
|
|
45
|
-
*/
|
|
46
|
-
container?: React.ComponentPropsWithoutRef<
|
|
47
|
-
typeof BaseContextMenu.Portal
|
|
48
|
-
>["container"];
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
/** 메뉴의 실제 콘텐츠. portal로 마운트되며 클릭 위치 기준으로 자동 배치된다. */
|
|
52
|
-
export const ContextMenuContent = React.forwardRef<
|
|
53
|
-
HTMLDivElement,
|
|
54
|
-
ContextMenuContentProps
|
|
55
|
-
>(function ContextMenuContent({ className, children, container, ...props }, ref) {
|
|
56
|
-
return (
|
|
57
|
-
<BaseContextMenu.Portal container={container}>
|
|
58
|
-
<BaseContextMenu.Positioner className={cm__positioner}>
|
|
59
|
-
<BaseContextMenu.Popup
|
|
60
|
-
ref={ref}
|
|
61
|
-
className={cn(cm__content, className)}
|
|
62
|
-
{...props}
|
|
63
|
-
>
|
|
64
|
-
{children}
|
|
65
|
-
</BaseContextMenu.Popup>
|
|
66
|
-
</BaseContextMenu.Positioner>
|
|
67
|
-
</BaseContextMenu.Portal>
|
|
68
|
-
);
|
|
69
|
-
});
|
|
70
|
-
|
|
71
|
-
/* ───────── Item ───────── */
|
|
72
|
-
|
|
73
|
-
/** 일반 메뉴 항목. 클릭 시 메뉴가 닫히고 onClick이 발생한다. */
|
|
74
|
-
export const ContextMenuItem = React.forwardRef<
|
|
75
|
-
HTMLDivElement,
|
|
76
|
-
WithStringClassName<React.ComponentPropsWithoutRef<typeof BaseContextMenu.Item>>
|
|
77
|
-
>(function ContextMenuItem({ className, ...props }, ref) {
|
|
78
|
-
return (
|
|
79
|
-
<BaseContextMenu.Item
|
|
80
|
-
ref={ref}
|
|
81
|
-
className={cn(cm__item, className)}
|
|
82
|
-
{...props}
|
|
83
|
-
/>
|
|
84
|
-
);
|
|
85
|
-
});
|
|
86
|
-
|
|
87
|
-
/* ───────── CheckboxItem / RadioItem ───────── */
|
|
88
|
-
|
|
89
|
-
/** 체크 표시를 토글하는 메뉴 항목. 메뉴는 닫지 않고 상태만 바꾸는 옵션 ON/OFF 용도. */
|
|
90
|
-
export const ContextMenuCheckboxItem = React.forwardRef<
|
|
91
|
-
HTMLDivElement,
|
|
92
|
-
WithStringClassName<
|
|
93
|
-
React.ComponentPropsWithoutRef<typeof BaseContextMenu.CheckboxItem>
|
|
94
|
-
>
|
|
95
|
-
>(function ContextMenuCheckboxItem({ className, children, ...props }, ref) {
|
|
96
|
-
return (
|
|
97
|
-
<BaseContextMenu.CheckboxItem
|
|
98
|
-
ref={ref}
|
|
99
|
-
className={cn(cm__item, cmItemCheck, className)}
|
|
100
|
-
{...props}
|
|
101
|
-
>
|
|
102
|
-
<span className={cmItemIndicator} aria-hidden>
|
|
103
|
-
<BaseContextMenu.CheckboxItemIndicator>
|
|
104
|
-
<CheckIcon />
|
|
105
|
-
</BaseContextMenu.CheckboxItemIndicator>
|
|
106
|
-
</span>
|
|
107
|
-
<span className={cmItemText}>{children}</span>
|
|
108
|
-
</BaseContextMenu.CheckboxItem>
|
|
109
|
-
);
|
|
110
|
-
});
|
|
111
|
-
|
|
112
|
-
/** RadioItem들을 단일 선택 그룹으로 묶는 컨테이너. `value`로 선택값을 제어. */
|
|
113
|
-
export const ContextMenuRadioGroup = BaseContextMenu.RadioGroup;
|
|
114
|
-
|
|
115
|
-
/** ContextMenuRadioGroup 내부의 단일 선택 항목. */
|
|
116
|
-
export const ContextMenuRadioItem = React.forwardRef<
|
|
117
|
-
HTMLDivElement,
|
|
118
|
-
WithStringClassName<
|
|
119
|
-
React.ComponentPropsWithoutRef<typeof BaseContextMenu.RadioItem>
|
|
120
|
-
>
|
|
121
|
-
>(function ContextMenuRadioItem({ className, children, ...props }, ref) {
|
|
122
|
-
return (
|
|
123
|
-
<BaseContextMenu.RadioItem
|
|
124
|
-
ref={ref}
|
|
125
|
-
className={cn(cm__item, cmItemCheck, className)}
|
|
126
|
-
{...props}
|
|
127
|
-
>
|
|
128
|
-
<span className={cmItemIndicator} aria-hidden>
|
|
129
|
-
<BaseContextMenu.RadioItemIndicator>
|
|
130
|
-
<DotIcon />
|
|
131
|
-
</BaseContextMenu.RadioItemIndicator>
|
|
132
|
-
</span>
|
|
133
|
-
<span className={cmItemText}>{children}</span>
|
|
134
|
-
</BaseContextMenu.RadioItem>
|
|
135
|
-
);
|
|
136
|
-
});
|
|
137
|
-
|
|
138
|
-
/* ───────── Group / Label / Separator ───────── */
|
|
139
|
-
|
|
140
|
-
/** 의미적으로 묶이는 항목 그룹. ContextMenuLabel과 함께 사용해 카테고리 헤더를 붙인다. */
|
|
141
|
-
export const ContextMenuGroup = React.forwardRef<
|
|
142
|
-
HTMLDivElement,
|
|
143
|
-
WithStringClassName<React.ComponentPropsWithoutRef<typeof BaseContextMenu.Group>>
|
|
144
|
-
>(function ContextMenuGroup({ className, ...props }, ref) {
|
|
145
|
-
return (
|
|
146
|
-
<BaseContextMenu.Group
|
|
147
|
-
ref={ref}
|
|
148
|
-
className={cn(cm__group, className)}
|
|
149
|
-
{...props}
|
|
150
|
-
/>
|
|
151
|
-
);
|
|
152
|
-
});
|
|
153
|
-
|
|
154
|
-
/** 그룹의 카테고리 헤더 라벨. 클릭 불가, 시각·접근성 컨텍스트 제공용. */
|
|
155
|
-
export const ContextMenuLabel = React.forwardRef<
|
|
156
|
-
HTMLDivElement,
|
|
157
|
-
WithStringClassName<
|
|
158
|
-
React.ComponentPropsWithoutRef<typeof BaseContextMenu.GroupLabel>
|
|
159
|
-
>
|
|
160
|
-
>(function ContextMenuLabel({ className, ...props }, ref) {
|
|
161
|
-
return (
|
|
162
|
-
<BaseContextMenu.GroupLabel
|
|
163
|
-
ref={ref}
|
|
164
|
-
className={cn(cm__label, className)}
|
|
165
|
-
{...props}
|
|
166
|
-
/>
|
|
167
|
-
);
|
|
168
|
-
});
|
|
169
|
-
|
|
170
|
-
/** 항목 사이의 시각적 구분선. */
|
|
171
|
-
export const ContextMenuSeparator = React.forwardRef<
|
|
172
|
-
HTMLDivElement,
|
|
173
|
-
React.HTMLAttributes<HTMLDivElement>
|
|
174
|
-
>(function ContextMenuSeparator({ className, ...props }, ref) {
|
|
175
|
-
return (
|
|
176
|
-
<div
|
|
177
|
-
ref={ref}
|
|
178
|
-
role="separator"
|
|
179
|
-
aria-orientation="horizontal"
|
|
180
|
-
className={cn(cm__separator, className)}
|
|
181
|
-
{...props}
|
|
182
|
-
/>
|
|
183
|
-
);
|
|
184
|
-
});
|
|
185
|
-
|
|
186
|
-
/* ───────── Submenu ───────── */
|
|
187
|
-
|
|
188
|
-
/** 서브메뉴 루트. 호버/포커스 시 우측으로 자식 메뉴를 펼친다. */
|
|
189
|
-
export const ContextMenuSub = BaseContextMenu.SubmenuRoot;
|
|
190
|
-
|
|
191
|
-
/** 서브메뉴를 여는 항목. 우측 화살표 아이콘이 자동 부착된다. */
|
|
192
|
-
export const ContextMenuSubTrigger = React.forwardRef<
|
|
193
|
-
HTMLDivElement,
|
|
194
|
-
WithStringClassName<
|
|
195
|
-
React.ComponentPropsWithoutRef<typeof BaseContextMenu.SubmenuTrigger>
|
|
196
|
-
>
|
|
197
|
-
>(function ContextMenuSubTrigger({ className, children, ...props }, ref) {
|
|
198
|
-
return (
|
|
199
|
-
<BaseContextMenu.SubmenuTrigger
|
|
200
|
-
ref={ref}
|
|
201
|
-
className={cn(cm__item, cmSubTrigger, className)}
|
|
202
|
-
{...props}
|
|
203
|
-
>
|
|
204
|
-
<span className={cmItemText}>{children}</span>
|
|
205
|
-
<span className={cmSubArrow} aria-hidden>
|
|
206
|
-
<ChevronRightIcon />
|
|
207
|
-
</span>
|
|
208
|
-
</BaseContextMenu.SubmenuTrigger>
|
|
209
|
-
);
|
|
210
|
-
});
|
|
211
|
-
|
|
212
|
-
/** 서브메뉴의 콘텐츠. ContextMenuContent의 별칭이다. */
|
|
213
|
-
export const ContextMenuSubContent = ContextMenuContent;
|
|
214
|
-
|
|
215
|
-
/* ───────── 기본 아이콘 ───────── */
|
|
216
|
-
|
|
217
|
-
function CheckIcon() {
|
|
218
|
-
return (
|
|
219
|
-
<svg width="12" height="12" viewBox="0 0 16 16" fill="none" aria-hidden>
|
|
220
|
-
<path
|
|
221
|
-
d="M3.5 8.5l3 3 6-7"
|
|
222
|
-
stroke="currentColor"
|
|
223
|
-
strokeWidth="1.75"
|
|
224
|
-
strokeLinecap="round"
|
|
225
|
-
strokeLinejoin="round"
|
|
226
|
-
/>
|
|
227
|
-
</svg>
|
|
228
|
-
);
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
function DotIcon() {
|
|
232
|
-
return (
|
|
233
|
-
<svg width="8" height="8" viewBox="0 0 8 8" fill="currentColor" aria-hidden>
|
|
234
|
-
<circle cx="4" cy="4" r="3" />
|
|
235
|
-
</svg>
|
|
236
|
-
);
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
function ChevronRightIcon() {
|
|
240
|
-
return (
|
|
241
|
-
<svg width="12" height="12" viewBox="0 0 16 16" fill="none" aria-hidden>
|
|
242
|
-
<path
|
|
243
|
-
d="M6 4l4 4-4 4"
|
|
244
|
-
stroke="currentColor"
|
|
245
|
-
strokeWidth="1.5"
|
|
246
|
-
strokeLinecap="round"
|
|
247
|
-
strokeLinejoin="round"
|
|
248
|
-
/>
|
|
249
|
-
</svg>
|
|
250
|
-
);
|
|
251
|
-
}
|