@wishket/design-system 2.2.1 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +58 -0
- package/dist/Components/Base/Layouts/Box/Box.d.ts +5 -5
- package/dist/Components/Base/Layouts/Box/Box.js +3 -2
- package/dist/Components/Base/Typography/Typography.d.ts +2 -1
- package/dist/Components/Base/Typography/Typography.js +1 -1
- package/dist/Components/Base/Typography/Typography.types.d.ts +4 -5
- package/dist/Components/Navigations/GNBList/GNBList.d.ts +1 -1
- package/dist/Components/Navigations/GNBList/GNBList.parts.d.ts +3 -2
- package/dist/Components/Navigations/GNBList/GNBList.parts.js +10 -10
- package/dist/Components/Navigations/GNBList/GNBList.types.d.ts +8 -3
- package/dist/Components/Navigations/Menu/Menu.types.d.ts +53 -0
- package/dist/Components/Navigations/Menu/MenuBase.d.ts +21 -0
- package/dist/Components/Navigations/Menu/MenuBase.js +3 -0
- package/dist/Components/Navigations/Menu/MenuButton.d.ts +40 -0
- package/dist/Components/Navigations/Menu/MenuButton.js +39 -0
- package/dist/Components/Navigations/Menu/MenuLink.d.ts +43 -0
- package/dist/Components/Navigations/Menu/MenuLink.js +42 -0
- package/dist/Components/Navigations/Menu/index.d.ts +3 -1
- package/dist/Components/Navigations/TextLink/TextLink.d.ts +13 -35
- package/dist/Components/Navigations/TextLink/TextLink.js +11 -34
- package/dist/cjs/Components/Base/Layouts/Box/Box.js +3 -2
- package/dist/cjs/Components/Base/Typography/Typography.js +2 -2
- package/dist/cjs/Components/Navigations/GNBList/GNBList.parts.js +11 -11
- package/dist/cjs/Components/Navigations/Menu/MenuBase.js +3 -0
- package/dist/cjs/Components/Navigations/Menu/MenuButton.js +39 -0
- package/dist/cjs/Components/Navigations/Menu/MenuLink.js +42 -0
- package/dist/cjs/Components/Navigations/TextLink/TextLink.js +11 -34
- package/dist/cjs/index.js +1 -1
- package/dist/index.js +1 -1
- package/package.json +7 -5
- package/scripts/codemods/README.md +178 -0
- package/scripts/codemods/__tests__/__fixtures__/add-as-link/already-has-as.input.tsx +6 -0
- package/scripts/codemods/__tests__/__fixtures__/add-as-link/already-has-as.output.tsx +6 -0
- package/scripts/codemods/__tests__/__fixtures__/add-as-link/basic.input.tsx +9 -0
- package/scripts/codemods/__tests__/__fixtures__/add-as-link/basic.output.tsx +10 -0
- package/scripts/codemods/__tests__/__fixtures__/add-as-link/existing-next-link-import.input.tsx +9 -0
- package/scripts/codemods/__tests__/__fixtures__/add-as-link/existing-next-link-import.output.tsx +9 -0
- package/scripts/codemods/__tests__/__fixtures__/add-as-link/menu-button-fallback.input.tsx +5 -0
- package/scripts/codemods/__tests__/__fixtures__/add-as-link/menu-button-fallback.output.tsx +5 -0
- package/scripts/codemods/__tests__/__fixtures__/add-as-link/unrelated-import.input.tsx +3 -0
- package/scripts/codemods/__tests__/__fixtures__/add-as-link/unrelated-import.output.tsx +3 -0
- package/scripts/codemods/__tests__/__fixtures__/menu-split/basic.input.tsx +8 -0
- package/scripts/codemods/__tests__/__fixtures__/menu-split/basic.output.tsx +8 -0
- package/scripts/codemods/__tests__/__fixtures__/menu-split/conflict.input.tsx +5 -0
- package/scripts/codemods/__tests__/__fixtures__/menu-split/conflict.output.tsx +6 -0
- package/scripts/codemods/__tests__/__fixtures__/menu-split/href-only.input.tsx +3 -0
- package/scripts/codemods/__tests__/__fixtures__/menu-split/href-only.output.tsx +3 -0
- package/scripts/codemods/__tests__/__fixtures__/menu-split/onclick-only.input.tsx +3 -0
- package/scripts/codemods/__tests__/__fixtures__/menu-split/onclick-only.output.tsx +3 -0
- package/scripts/codemods/__tests__/__fixtures__/menu-split/spread-props.input.tsx +3 -0
- package/scripts/codemods/__tests__/__fixtures__/menu-split/spread-props.output.tsx +4 -0
- package/scripts/codemods/__tests__/run-fixtures.cjs +100 -0
- package/scripts/codemods/add-as-link.ts +110 -0
- package/scripts/codemods/menu-split.ts +252 -0
- package/dist/Components/Navigations/Menu/Menu.d.ts +0 -81
- package/dist/Components/Navigations/Menu/Menu.js +0 -62
- package/dist/cjs/Components/Navigations/Menu/Menu.js +0 -2
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
API,
|
|
3
|
+
ASTPath,
|
|
4
|
+
Collection,
|
|
5
|
+
FileInfo,
|
|
6
|
+
ImportDeclaration,
|
|
7
|
+
ImportSpecifier,
|
|
8
|
+
JSCodeshift,
|
|
9
|
+
JSXAttribute,
|
|
10
|
+
JSXIdentifier,
|
|
11
|
+
JSXOpeningElement,
|
|
12
|
+
} from 'jscodeshift';
|
|
13
|
+
|
|
14
|
+
const DESIGN_SYSTEM_PACKAGE = '@wishket/design-system';
|
|
15
|
+
const SOURCE_NAME = 'Menu';
|
|
16
|
+
const LINK_NAME = 'MenuLink';
|
|
17
|
+
const BUTTON_NAME = 'MenuButton';
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* v3 — `Menu` 컴포넌트를 `MenuLink` / `MenuButton`으로 분리하는 codemod.
|
|
21
|
+
*
|
|
22
|
+
* 변환 규칙:
|
|
23
|
+
*
|
|
24
|
+
* 1. `@wishket/design-system`에서 import된 `Menu`만 처리 (alias 미지원).
|
|
25
|
+
* 2. JSX 호출 분석:
|
|
26
|
+
* - `href` prop 있음 → `<MenuLink ...>`로 rename + import 정리
|
|
27
|
+
* - `href` 없고 `onClick` 있음 → `<MenuButton ...>`로 rename + import 정리
|
|
28
|
+
* - 둘 다 있음 (모순) → 변환 안 함 + TODO 코멘트
|
|
29
|
+
* - 둘 다 없음 → 변환 안 함 + TODO 코멘트
|
|
30
|
+
* - spread props (`<Menu {...props} />`) → 변환 안 함 + TODO 코멘트
|
|
31
|
+
* 3. import 정리:
|
|
32
|
+
* - `Menu`를 사용된 변형(`MenuLink` / `MenuButton` / 둘 다)으로 교체
|
|
33
|
+
* - 모든 호출을 변환하지 못해 `Menu`가 여전히 필요한 경우 `Menu`도 유지
|
|
34
|
+
*/
|
|
35
|
+
export default function transformer(file: FileInfo, api: API): string {
|
|
36
|
+
const j: JSCodeshift = api.jscodeshift;
|
|
37
|
+
const root = j(file.source);
|
|
38
|
+
|
|
39
|
+
const dsImports: Collection<ImportDeclaration> = root.find(
|
|
40
|
+
j.ImportDeclaration,
|
|
41
|
+
{
|
|
42
|
+
source: { value: DESIGN_SYSTEM_PACKAGE },
|
|
43
|
+
},
|
|
44
|
+
);
|
|
45
|
+
if (dsImports.size() === 0) return file.source;
|
|
46
|
+
|
|
47
|
+
// `Menu`가 alias 없이 default-named로 import 되었는지 확인.
|
|
48
|
+
let hasMenuImport = false;
|
|
49
|
+
let hasAliasedMenuImport = false;
|
|
50
|
+
|
|
51
|
+
dsImports.forEach(path => {
|
|
52
|
+
const specifiers = path.node.specifiers ?? [];
|
|
53
|
+
specifiers.forEach(spec => {
|
|
54
|
+
if (spec.type !== 'ImportSpecifier') return;
|
|
55
|
+
const importedName = spec.imported.name;
|
|
56
|
+
if (importedName !== SOURCE_NAME) return;
|
|
57
|
+
const localName = spec.local?.name ?? importedName;
|
|
58
|
+
if (localName !== SOURCE_NAME) {
|
|
59
|
+
hasAliasedMenuImport = true;
|
|
60
|
+
} else {
|
|
61
|
+
hasMenuImport = true;
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
if (!hasMenuImport && !hasAliasedMenuImport) {
|
|
67
|
+
return file.source;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
let didChange = false;
|
|
71
|
+
let usedMenuLink = false;
|
|
72
|
+
let usedMenuButton = false;
|
|
73
|
+
let hasUnconvertedMenu = false;
|
|
74
|
+
|
|
75
|
+
if (hasAliasedMenuImport) {
|
|
76
|
+
// alias가 있는 경우 안전하게 변환하기 어려움 — 파일 상단에 안내 코멘트 추가.
|
|
77
|
+
addLeadingComment(
|
|
78
|
+
j,
|
|
79
|
+
root,
|
|
80
|
+
` TODO: codemod could not safely resolve aliased Menu import — manually split into MenuLink / MenuButton.`,
|
|
81
|
+
);
|
|
82
|
+
return root.toSource({ quote: 'single' });
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
root
|
|
86
|
+
.find(j.JSXOpeningElement)
|
|
87
|
+
.forEach((path: ASTPath<JSXOpeningElement>) => {
|
|
88
|
+
const name = getJSXName(path.node);
|
|
89
|
+
if (name !== SOURCE_NAME) return;
|
|
90
|
+
|
|
91
|
+
const attrs = path.node.attributes ?? [];
|
|
92
|
+
|
|
93
|
+
const hasSpread = attrs.some(attr => attr.type === 'JSXSpreadAttribute');
|
|
94
|
+
if (hasSpread) {
|
|
95
|
+
addCommentBefore(
|
|
96
|
+
j,
|
|
97
|
+
path,
|
|
98
|
+
` TODO: codemod could not safely resolve spread props — manually choose MenuLink or MenuButton.`,
|
|
99
|
+
);
|
|
100
|
+
hasUnconvertedMenu = true;
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const hasHref = attrs.some(
|
|
105
|
+
(attr): attr is JSXAttribute =>
|
|
106
|
+
attr.type === 'JSXAttribute' &&
|
|
107
|
+
attr.name.type === 'JSXIdentifier' &&
|
|
108
|
+
attr.name.name === 'href',
|
|
109
|
+
);
|
|
110
|
+
const hasOnClick = attrs.some(
|
|
111
|
+
(attr): attr is JSXAttribute =>
|
|
112
|
+
attr.type === 'JSXAttribute' &&
|
|
113
|
+
attr.name.type === 'JSXIdentifier' &&
|
|
114
|
+
attr.name.name === 'onClick',
|
|
115
|
+
);
|
|
116
|
+
|
|
117
|
+
if (hasHref && hasOnClick) {
|
|
118
|
+
addCommentBefore(
|
|
119
|
+
j,
|
|
120
|
+
path,
|
|
121
|
+
` TODO: codemod could not safely resolve — manually choose MenuLink or MenuButton.`,
|
|
122
|
+
);
|
|
123
|
+
hasUnconvertedMenu = true;
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
if (!hasHref && !hasOnClick) {
|
|
128
|
+
addCommentBefore(
|
|
129
|
+
j,
|
|
130
|
+
path,
|
|
131
|
+
` TODO: codemod could not safely resolve — Menu without href or onClick. Choose MenuLink (add href) or MenuButton (add onClick).`,
|
|
132
|
+
);
|
|
133
|
+
hasUnconvertedMenu = true;
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
const target = hasHref ? LINK_NAME : BUTTON_NAME;
|
|
138
|
+
renameJSXElement(j, path, target);
|
|
139
|
+
|
|
140
|
+
if (target === LINK_NAME) usedMenuLink = true;
|
|
141
|
+
else usedMenuButton = true;
|
|
142
|
+
|
|
143
|
+
didChange = true;
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
// Import 정리.
|
|
147
|
+
dsImports.forEach(path => {
|
|
148
|
+
const specifiers = path.node.specifiers ?? [];
|
|
149
|
+
const next: ImportSpecifier[] = [];
|
|
150
|
+
let touched = false;
|
|
151
|
+
|
|
152
|
+
specifiers.forEach(spec => {
|
|
153
|
+
if (spec.type !== 'ImportSpecifier') {
|
|
154
|
+
next.push(spec as ImportSpecifier);
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
if (spec.imported.name !== SOURCE_NAME) {
|
|
158
|
+
next.push(spec);
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
// `Menu`를 제거하고 사용된 변형으로 교체.
|
|
162
|
+
touched = true;
|
|
163
|
+
if (hasUnconvertedMenu) {
|
|
164
|
+
// 일부 변환 못한 호출이 남아있어 Menu도 유지가 필요할 수 있음.
|
|
165
|
+
next.push(spec);
|
|
166
|
+
}
|
|
167
|
+
if (usedMenuLink) {
|
|
168
|
+
next.push(
|
|
169
|
+
j.importSpecifier(j.identifier(LINK_NAME)) as ImportSpecifier,
|
|
170
|
+
);
|
|
171
|
+
}
|
|
172
|
+
if (usedMenuButton) {
|
|
173
|
+
next.push(
|
|
174
|
+
j.importSpecifier(j.identifier(BUTTON_NAME)) as ImportSpecifier,
|
|
175
|
+
);
|
|
176
|
+
}
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
if (touched) {
|
|
180
|
+
// 중복 제거 (같은 이름이 이미 import된 경우 대비).
|
|
181
|
+
const seen = new Set<string>();
|
|
182
|
+
path.node.specifiers = next.filter(spec => {
|
|
183
|
+
if (spec.type !== 'ImportSpecifier') return true;
|
|
184
|
+
const importedName = spec.imported.name;
|
|
185
|
+
const localName = spec.local?.name ?? importedName;
|
|
186
|
+
const key = `${importedName}::${localName}`;
|
|
187
|
+
if (seen.has(key)) return false;
|
|
188
|
+
seen.add(key);
|
|
189
|
+
return true;
|
|
190
|
+
});
|
|
191
|
+
didChange = true;
|
|
192
|
+
}
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
if (!didChange) return file.source;
|
|
196
|
+
|
|
197
|
+
return root.toSource({ quote: 'single' });
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
function getJSXName(node: JSXOpeningElement): string | null {
|
|
201
|
+
const { name } = node;
|
|
202
|
+
if (name.type === 'JSXIdentifier') return name.name;
|
|
203
|
+
return null;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
function renameJSXElement(
|
|
207
|
+
j: JSCodeshift,
|
|
208
|
+
path: ASTPath<JSXOpeningElement>,
|
|
209
|
+
newName: string,
|
|
210
|
+
) {
|
|
211
|
+
const opening = path.node;
|
|
212
|
+
(opening.name as JSXIdentifier).name = newName;
|
|
213
|
+
|
|
214
|
+
// self-closing이 아니면 닫는 태그도 함께 갱신.
|
|
215
|
+
const parent = path.parent.node;
|
|
216
|
+
if (parent && parent.type === 'JSXElement' && parent.closingElement) {
|
|
217
|
+
const closingName = parent.closingElement.name;
|
|
218
|
+
if (closingName.type === 'JSXIdentifier') {
|
|
219
|
+
(closingName as JSXIdentifier).name = newName;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
function addCommentBefore(
|
|
225
|
+
j: JSCodeshift,
|
|
226
|
+
path: ASTPath<JSXOpeningElement>,
|
|
227
|
+
message: string,
|
|
228
|
+
) {
|
|
229
|
+
// JSX element 자체의 부모(보통 JSXElement)를 찾아 그 앞에 코멘트를 삽입.
|
|
230
|
+
let current: ASTPath | null = path;
|
|
231
|
+
while (current && current.node && current.node.type !== 'JSXElement') {
|
|
232
|
+
current = current.parent;
|
|
233
|
+
}
|
|
234
|
+
if (!current) return;
|
|
235
|
+
|
|
236
|
+
const node = current.node;
|
|
237
|
+
node.comments = node.comments ?? [];
|
|
238
|
+
node.comments.push(j.commentLine(message, true, false));
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
function addLeadingComment(
|
|
242
|
+
j: JSCodeshift,
|
|
243
|
+
root: Collection,
|
|
244
|
+
message: string,
|
|
245
|
+
) {
|
|
246
|
+
const program = root.get().node.program;
|
|
247
|
+
const body = program.body;
|
|
248
|
+
if (!body || body.length === 0) return;
|
|
249
|
+
const first = body[0];
|
|
250
|
+
first.comments = first.comments ?? [];
|
|
251
|
+
first.comments.push(j.commentLine(message, true, false));
|
|
252
|
+
}
|
|
@@ -1,81 +0,0 @@
|
|
|
1
|
-
import { ReactNode } from 'react';
|
|
2
|
-
import { MediumSystemIconName, SmallSystemIconName } from '../../DataDisplays/SystemIcon';
|
|
3
|
-
import { IconButtonDropdownItem } from '../../Inputs';
|
|
4
|
-
export interface MenuProps {
|
|
5
|
-
type?: 'main' | 'sub';
|
|
6
|
-
variant?: 'white' | 'gray';
|
|
7
|
-
name: string;
|
|
8
|
-
badgeCount?: string;
|
|
9
|
-
isSelected?: boolean;
|
|
10
|
-
leadingIcon?: MediumSystemIconName;
|
|
11
|
-
iconButtonName?: SmallSystemIconName;
|
|
12
|
-
onClick?: () => void;
|
|
13
|
-
onOptionClick?: () => void;
|
|
14
|
-
href?: string;
|
|
15
|
-
children?: ReactNode;
|
|
16
|
-
items?: IconButtonDropdownItem[];
|
|
17
|
-
selectedItem?: IconButtonDropdownItem;
|
|
18
|
-
onItemClick?: (item: IconButtonDropdownItem) => void;
|
|
19
|
-
}
|
|
20
|
-
/**
|
|
21
|
-
* 메뉴 컴포넌트는 네비게이션에서 사용되는 클릭 가능한 항목을 표시합니다.
|
|
22
|
-
*
|
|
23
|
-
* @param {Object} props
|
|
24
|
-
* @param {'main' | 'sub'} [props.type='main'] - 메뉴 타입 ('main', 'sub')
|
|
25
|
-
* @param {'white' | 'gray'} [props.variant='white'] - 메뉴 스타일 변형 ('white', 'gray')
|
|
26
|
-
* @param {string} props.name - 메뉴에 표시될 텍스트
|
|
27
|
-
* @param {string} [props.badgeCount] - 메뉴 항목에 표시될 뱃지 숫자
|
|
28
|
-
* @param {boolean} [props.isSelected=false] - 메뉴 선택 상태
|
|
29
|
-
* @param {SystemIconName} [props.leadingIcon] - 메뉴 앞쪽에 표시될 아이콘(medium 사이즈)
|
|
30
|
-
* @param {SystemIconName} [props.iconButtonName] - IconButtonDropdown 아이콘(small 사이즈)
|
|
31
|
-
* @param {() => void} props.onClick - 메뉴 클릭 시 실행될 함수
|
|
32
|
-
* @param {() => void} [props.onOptionClick] - 후행 아이콘 클릭 시 실행될 함수 (IconButtonDropdown이 아닌 IconButton으로만 사용 시 필요)
|
|
33
|
-
* @param {string} [props.href] - 메뉴 링크 주소
|
|
34
|
-
* @param {ReactNode} [props.children] - 메뉴 내부에 표시될 자식 요소 (IconButtonDropdown 컴포넌트 사용 시 필요, trailingIcon과 함께 사용하지 않도록 주의 필요)
|
|
35
|
-
* @param {Item[]} [props.items] - IconButtonDropdown 컴포넌트 사용 시 필요 {key: number, value: string}[]
|
|
36
|
-
* @param {Item} [props.selectedItem] - IconButtonDropdown 컴포넌트 사용 시 필요 {key: number, value: string}
|
|
37
|
-
* @param {(item: Item) => void} [props.onItemClick] - IconButtonDropdown 컴포넌트 사용 시 필요 (item: Item) => {}
|
|
38
|
-
*
|
|
39
|
-
* @example
|
|
40
|
-
* // 기본 메뉴
|
|
41
|
-
* <Menu
|
|
42
|
-
* name="메뉴 항목"
|
|
43
|
-
* onClick={() => console.log('메뉴 클릭')}
|
|
44
|
-
* />
|
|
45
|
-
*
|
|
46
|
-
* // 아이콘과 뱃지가 있는 서브 메뉴
|
|
47
|
-
* <Menu
|
|
48
|
-
* type="sub"
|
|
49
|
-
* name="서브 메뉴"
|
|
50
|
-
* leadingIcon="home"
|
|
51
|
-
* badgeCount="5"
|
|
52
|
-
* onClick={() => console.log('서브 메뉴 클릭')}
|
|
53
|
-
* />
|
|
54
|
-
*
|
|
55
|
-
* // IconButtonDropdown과 함께 사용시
|
|
56
|
-
* <Menu
|
|
57
|
-
* name="메뉴"
|
|
58
|
-
* onClick={() => console.log('메뉴 클릭')}
|
|
59
|
-
* iconButtonName="small_more_options"
|
|
60
|
-
* items={items}
|
|
61
|
-
* selectedItem={selectedItem}
|
|
62
|
-
* onItemClick={onItemClick}
|
|
63
|
-
* />
|
|
64
|
-
*
|
|
65
|
-
* // 드롭다운이 필요하지 않은 옵션 메뉴(ex. 버튼 클릭 시 모달 오픈))
|
|
66
|
-
* <Menu
|
|
67
|
-
* variant="gray"
|
|
68
|
-
* name="옵션 메뉴"
|
|
69
|
-
* isSelected={true}
|
|
70
|
-
* iconButtonName="small_more_options"
|
|
71
|
-
* onClick={() => console.log('메뉴 클릭')}
|
|
72
|
-
* onOptionClick={() => console.log('옵션 클릭', '모달 오픈')}
|
|
73
|
-
* />
|
|
74
|
-
* // 링크로 사용해야하는 메뉴
|
|
75
|
-
* <Menu
|
|
76
|
-
* href="/test"
|
|
77
|
-
* name="테스트 메뉴"
|
|
78
|
-
* />
|
|
79
|
-
*/
|
|
80
|
-
declare const Menu: ({ type, variant, name, isSelected, badgeCount, leadingIcon, iconButtonName, onClick, onOptionClick, href, items, selectedItem, onItemClick, }: MenuProps) => import("react/jsx-runtime").JSX.Element;
|
|
81
|
-
export { Menu };
|
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
import{jsxs as t,Fragment as e,jsx as o}from"react/jsx-runtime";import{twJoin as s,twMerge as i}from"tailwind-merge";import r from"next/link";import"../../DataDisplays/Avatar/Avatar.js";import{CountBadge as n}from"../../DataDisplays/CountBadge/CountBadge.js";import{Typography as a}from"../../Base/Typography/Typography.js";import"../../Base/Layouts/Box/Box.js";import"../../Base/Layouts/FullBleed/FullBleed.js";import"react";import{SystemIcon as p}from"../../DataDisplays/SystemIcon/SystemIcon.js";import"../../DataDisplays/SystemIcon/SystemIcon.constants.js";import"../../DataDisplays/Accordion/Accordion.js";import"@wishket/yogokit";import"../../Inputs/AutoCompleteList/AutoCompleteList.parts.js";import"../../Inputs/Input/Input.js";import"../../Inputs/Input/PasswordInput.js";import"../../Inputs/Input/LabelInput.js";import"../../Inputs/Input/InputTypeSelector.js";import"../../Inputs/Button/Button.js";import"../../Inputs/Calendar/Calendar.utils.js";import"../../Inputs/Checkbox/Checkbox.js";import"../../Inputs/ChoiceChip/ChoiceChip.js";import{IconButton as m}from"../../Inputs/IconButton/IconButton.js";import"../../Inputs/Radio/Radio.js";import"../../Inputs/TextField/TextField.js";import"../../Inputs/CommentArea/CommentArea.js";import{IconButtonDropdown as l}from"../../Inputs/IconButtonDropdown/IconButtonDropdown.js";import"../../Inputs/FilterChip/FilterChip.js";
|
|
2
|
-
/**
|
|
3
|
-
* 메뉴 컴포넌트는 네비게이션에서 사용되는 클릭 가능한 항목을 표시합니다.
|
|
4
|
-
*
|
|
5
|
-
* @param {Object} props
|
|
6
|
-
* @param {'main' | 'sub'} [props.type='main'] - 메뉴 타입 ('main', 'sub')
|
|
7
|
-
* @param {'white' | 'gray'} [props.variant='white'] - 메뉴 스타일 변형 ('white', 'gray')
|
|
8
|
-
* @param {string} props.name - 메뉴에 표시될 텍스트
|
|
9
|
-
* @param {string} [props.badgeCount] - 메뉴 항목에 표시될 뱃지 숫자
|
|
10
|
-
* @param {boolean} [props.isSelected=false] - 메뉴 선택 상태
|
|
11
|
-
* @param {SystemIconName} [props.leadingIcon] - 메뉴 앞쪽에 표시될 아이콘(medium 사이즈)
|
|
12
|
-
* @param {SystemIconName} [props.iconButtonName] - IconButtonDropdown 아이콘(small 사이즈)
|
|
13
|
-
* @param {() => void} props.onClick - 메뉴 클릭 시 실행될 함수
|
|
14
|
-
* @param {() => void} [props.onOptionClick] - 후행 아이콘 클릭 시 실행될 함수 (IconButtonDropdown이 아닌 IconButton으로만 사용 시 필요)
|
|
15
|
-
* @param {string} [props.href] - 메뉴 링크 주소
|
|
16
|
-
* @param {ReactNode} [props.children] - 메뉴 내부에 표시될 자식 요소 (IconButtonDropdown 컴포넌트 사용 시 필요, trailingIcon과 함께 사용하지 않도록 주의 필요)
|
|
17
|
-
* @param {Item[]} [props.items] - IconButtonDropdown 컴포넌트 사용 시 필요 {key: number, value: string}[]
|
|
18
|
-
* @param {Item} [props.selectedItem] - IconButtonDropdown 컴포넌트 사용 시 필요 {key: number, value: string}
|
|
19
|
-
* @param {(item: Item) => void} [props.onItemClick] - IconButtonDropdown 컴포넌트 사용 시 필요 (item: Item) => {}
|
|
20
|
-
*
|
|
21
|
-
* @example
|
|
22
|
-
* // 기본 메뉴
|
|
23
|
-
* <Menu
|
|
24
|
-
* name="메뉴 항목"
|
|
25
|
-
* onClick={() => console.log('메뉴 클릭')}
|
|
26
|
-
* />
|
|
27
|
-
*
|
|
28
|
-
* // 아이콘과 뱃지가 있는 서브 메뉴
|
|
29
|
-
* <Menu
|
|
30
|
-
* type="sub"
|
|
31
|
-
* name="서브 메뉴"
|
|
32
|
-
* leadingIcon="home"
|
|
33
|
-
* badgeCount="5"
|
|
34
|
-
* onClick={() => console.log('서브 메뉴 클릭')}
|
|
35
|
-
* />
|
|
36
|
-
*
|
|
37
|
-
* // IconButtonDropdown과 함께 사용시
|
|
38
|
-
* <Menu
|
|
39
|
-
* name="메뉴"
|
|
40
|
-
* onClick={() => console.log('메뉴 클릭')}
|
|
41
|
-
* iconButtonName="small_more_options"
|
|
42
|
-
* items={items}
|
|
43
|
-
* selectedItem={selectedItem}
|
|
44
|
-
* onItemClick={onItemClick}
|
|
45
|
-
* />
|
|
46
|
-
*
|
|
47
|
-
* // 드롭다운이 필요하지 않은 옵션 메뉴(ex. 버튼 클릭 시 모달 오픈))
|
|
48
|
-
* <Menu
|
|
49
|
-
* variant="gray"
|
|
50
|
-
* name="옵션 메뉴"
|
|
51
|
-
* isSelected={true}
|
|
52
|
-
* iconButtonName="small_more_options"
|
|
53
|
-
* onClick={() => console.log('메뉴 클릭')}
|
|
54
|
-
* onOptionClick={() => console.log('옵션 클릭', '모달 오픈')}
|
|
55
|
-
* />
|
|
56
|
-
* // 링크로 사용해야하는 메뉴
|
|
57
|
-
* <Menu
|
|
58
|
-
* href="/test"
|
|
59
|
-
* name="테스트 메뉴"
|
|
60
|
-
* />
|
|
61
|
-
*/const u=({type:u="main",variant:c="white",name:d,isSelected:y=!1,badgeCount:I,leadingIcon:g,iconButtonName:h,onClick:j,onOptionClick:C,href:w,items:x,selectedItem:B,onItemClick:f})=>{const b=!!(h&&x&&f&&B),k=!!h&&!!C,D=(t,e,o)=>{const r="sub"===t?"pl-12":"",n={white:s("hover:bg-w-gray-50",o&&"bg-primary-10 text-primary hover:bg-primary-10"),gray:s("hover:bg-w-gray-100",o&&"bg-w-gray-100 hover:bg-w-gray-100")};return i("flex w-full cursor-pointer items-center gap-2 rounded-xl px-4 py-3 text-w-gray-900 text-left",r,n[e])},v=/*#__PURE__*/t(e,{children:[g&&/*#__PURE__*/o(p,{testId:"design-system-menu-leading-icon",name:g,className:s("white"===c&&y&&"text-primary")}),
|
|
62
|
-
/*#__PURE__*/o(a,{"data-testid":"design-system-menu-name",variant:"body16",className:"w-full select-none",children:d}),I&&/*#__PURE__*/o(n,{variant:((t,e)=>"white"!==t?"white_gray":e?"primary":"gray")(c,y),text:I,className:"relative",showZero:!0}),b&&/*#__PURE__*/o(l,{size:"sm",icon:h,items:x,selectedItem:B,onItemClick:f}),k&&/*#__PURE__*/o(m,{size:"sm",className:"shrink-0",onClick:t=>{t.stopPropagation(),C()},children:/*#__PURE__*/o(p,{name:h})})]});return w?/*#__PURE__*/o(r,{href:w,"data-testid":"design-system-menu",className:D(u,c,y),children:v}):/*#__PURE__*/o("button",{"data-testid":"design-system-menu",className:D(u,c,y),onClick:j,children:v})};export{u as Menu};
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
"use strict";var e=require("react/jsx-runtime"),t=require("tailwind-merge"),r=require("next/link");require("../../DataDisplays/Avatar/Avatar.js");var s=require("../../DataDisplays/CountBadge/CountBadge.js"),n=require("../../Base/Typography/Typography.js");require("../../Base/Layouts/Box/Box.js"),require("../../Base/Layouts/FullBleed/FullBleed.js"),require("react");var i=require("../../DataDisplays/SystemIcon/SystemIcon.js");require("../../DataDisplays/SystemIcon/SystemIcon.constants.js"),require("../../DataDisplays/Accordion/Accordion.js"),require("@wishket/yogokit"),require("../../Inputs/AutoCompleteList/AutoCompleteList.parts.js"),require("../../Inputs/Input/Input.js"),require("../../Inputs/Input/PasswordInput.js"),require("../../Inputs/Input/LabelInput.js"),require("../../Inputs/Input/InputTypeSelector.js"),require("../../Inputs/Button/Button.js"),require("../../Inputs/Calendar/Calendar.utils.js"),require("../../Inputs/Checkbox/Checkbox.js"),require("../../Inputs/ChoiceChip/ChoiceChip.js");var a=require("../../Inputs/IconButton/IconButton.js");require("../../Inputs/Radio/Radio.js"),require("../../Inputs/TextField/TextField.js"),require("../../Inputs/CommentArea/CommentArea.js");var o=require("../../Inputs/IconButtonDropdown/IconButtonDropdown.js");require("../../Inputs/FilterChip/FilterChip.js");exports.Menu=({type:u="main",variant:p="white",name:l,isSelected:c=!1,badgeCount:m,leadingIcon:d,iconButtonName:y,onClick:I,onOptionClick:g,href:j,items:h,selectedItem:q,onItemClick:x})=>{const C=!!(y&&h&&x&&q),w=!!y&&!!g,B=(e,r,s)=>{const n="sub"===e?"pl-12":"",i={white:t.twJoin("hover:bg-w-gray-50",s&&"bg-primary-10 text-primary hover:bg-primary-10"),gray:t.twJoin("hover:bg-w-gray-100",s&&"bg-w-gray-100 hover:bg-w-gray-100")};return t.twMerge("flex w-full cursor-pointer items-center gap-2 rounded-xl px-4 py-3 text-w-gray-900 text-left",n,i[r])},v=/*#__PURE__*/e.jsxs(e.Fragment,{children:[d&&/*#__PURE__*/e.jsx(i.SystemIcon,{testId:"design-system-menu-leading-icon",name:d,className:t.twJoin("white"===p&&c&&"text-primary")}),
|
|
2
|
-
/*#__PURE__*/e.jsx(n.Typography,{"data-testid":"design-system-menu-name",variant:"body16",className:"w-full select-none",children:l}),m&&/*#__PURE__*/e.jsx(s.CountBadge,{variant:((e,t)=>"white"!==e?"white_gray":t?"primary":"gray")(p,c),text:m,className:"relative",showZero:!0}),C&&/*#__PURE__*/e.jsx(o.IconButtonDropdown,{size:"sm",icon:y,items:h,selectedItem:q,onItemClick:x}),w&&/*#__PURE__*/e.jsx(a.IconButton,{size:"sm",className:"shrink-0",onClick:e=>{e.stopPropagation(),g()},children:/*#__PURE__*/e.jsx(i.SystemIcon,{name:y})})]});return j?/*#__PURE__*/e.jsx(r,{href:j,"data-testid":"design-system-menu",className:B(u,p,c),children:v}):/*#__PURE__*/e.jsx("button",{"data-testid":"design-system-menu",className:B(u,p,c),onClick:I,children:v})};
|