@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
package/README.md
CHANGED
|
@@ -82,6 +82,64 @@ export const Home = () => {
|
|
|
82
82
|
export default Home;
|
|
83
83
|
```
|
|
84
84
|
|
|
85
|
+
## Navigation 컴포넌트와 라우팅 라이브러리
|
|
86
|
+
|
|
87
|
+
`TextLink`, `Menu`, `GNBList.Item`은 기본적으로 `<a>` 태그로 렌더되며, `as` prop으로 라우팅 라이브러리의 Link를 주입할 수 있습니다.
|
|
88
|
+
|
|
89
|
+
### Next.js와 함께 사용
|
|
90
|
+
|
|
91
|
+
```tsx
|
|
92
|
+
import Link from 'next/link';
|
|
93
|
+
import { TextLink, Menu, GNBList } from '@wishket/design-system';
|
|
94
|
+
|
|
95
|
+
<TextLink as={Link} href="/about" text="About" />
|
|
96
|
+
<Menu as={Link} name="Settings" href="/settings" />
|
|
97
|
+
<GNBList.Item as={Link} href="/projects">프로젝트</GNBList.Item>
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
매 호출마다 `as`를 적기 번거롭다면 컨슈머 앱에서 wrapper를 한 번만 정의해두는 패턴을 권장합니다.
|
|
101
|
+
|
|
102
|
+
```tsx
|
|
103
|
+
// app/components/AppTextLink.tsx
|
|
104
|
+
import { ComponentProps } from 'react';
|
|
105
|
+
import Link from 'next/link';
|
|
106
|
+
import { TextLink } from '@wishket/design-system';
|
|
107
|
+
|
|
108
|
+
export const AppTextLink = (props: ComponentProps<typeof TextLink>) => (
|
|
109
|
+
<TextLink as={Link} {...props} />
|
|
110
|
+
);
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
### react-router와 함께 사용
|
|
114
|
+
|
|
115
|
+
`react-router`의 `Link`는 `to` prop을 쓰므로 `href` → `to` 어댑터가 필요합니다.
|
|
116
|
+
|
|
117
|
+
```tsx
|
|
118
|
+
import { Link as RouterLink } from 'react-router-dom';
|
|
119
|
+
|
|
120
|
+
const RouterLinkAdapter = ({
|
|
121
|
+
href,
|
|
122
|
+
...rest
|
|
123
|
+
}: { href: string } & React.ComponentProps<typeof RouterLink>) => (
|
|
124
|
+
<RouterLink to={href} {...rest} />
|
|
125
|
+
);
|
|
126
|
+
|
|
127
|
+
<TextLink as={RouterLinkAdapter} href="/about" text="About" />
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
### v3 마이그레이션
|
|
131
|
+
|
|
132
|
+
v3.0부터 위 컴포넌트들이 더 이상 자동으로 `next/link`를 사용하지 않습니다. 컨슈머 앱에서 일괄 변환을 원한다면 패키지에 포함된 jscodeshift codemod를 사용하세요.
|
|
133
|
+
|
|
134
|
+
```bash
|
|
135
|
+
npx jscodeshift \
|
|
136
|
+
-t node_modules/@wishket/design-system/scripts/codemods/add-as-link.ts \
|
|
137
|
+
--extensions=tsx,ts --parser=tsx \
|
|
138
|
+
src/
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
자세한 내용은 [`scripts/codemods/README.md`](./scripts/codemods/README.md)를 참고하세요.
|
|
142
|
+
|
|
85
143
|
## Customize Theme in Config File
|
|
86
144
|
|
|
87
145
|
```typescript
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export
|
|
3
|
-
as?:
|
|
4
|
-
}
|
|
1
|
+
import { ComponentPropsWithRef, ElementType, PropsWithChildren } from 'react';
|
|
2
|
+
export type BoxProps<T extends ElementType = 'div'> = {
|
|
3
|
+
as?: T;
|
|
4
|
+
} & PropsWithChildren<Omit<ComponentPropsWithRef<T>, 'as'>>;
|
|
5
5
|
/**
|
|
6
6
|
* @param {Object} props
|
|
7
7
|
* @param {ReactNode} props.children - 박스 내부에 렌더링할 컨텐츠
|
|
8
8
|
* @param {ElementType} [props.as='div'] - 렌더링할 HTML 요소
|
|
9
9
|
* @param {string} [props.className] - 추가적인 CSS 클래스
|
|
10
10
|
*/
|
|
11
|
-
declare const Box: import("react").ForwardRefExoticComponent<BoxProps & import("react").RefAttributes<
|
|
11
|
+
declare const Box: import("react").ForwardRefExoticComponent<Omit<BoxProps<ElementType>, "ref"> & import("react").RefAttributes<any>>;
|
|
12
12
|
export { Box };
|
|
@@ -4,5 +4,6 @@ import{jsx as e}from"react/jsx-runtime";import{twMerge as r}from"tailwind-merge"
|
|
|
4
4
|
* @param {ReactNode} props.children - 박스 내부에 렌더링할 컨텐츠
|
|
5
5
|
* @param {ElementType} [props.as='div'] - 렌더링할 HTML 요소
|
|
6
6
|
* @param {string} [props.className] - 추가적인 CSS 클래스
|
|
7
|
-
*/(e,r);if(Object.getOwnPropertySymbols){var c=Object.getOwnPropertySymbols(e);for(n=0;n<c.length;n++)t=c[n],r.indexOf(t)>=0||Object.prototype.propertyIsEnumerable.call(e,t)&&(o[t]=e[t])}return o}const i=/*#__PURE__*/t(((t
|
|
8
|
-
|
|
7
|
+
*/(e,r);if(Object.getOwnPropertySymbols){var c=Object.getOwnPropertySymbols(e);for(n=0;n<c.length;n++)t=c[n],r.indexOf(t)>=0||Object.prototype.propertyIsEnumerable.call(e,t)&&(o[t]=e[t])}return o}const i=/*#__PURE__*/t(((t,// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
8
|
+
i)=>{var{as:l,className:a,children:u}=t,b=c(t,["as","className","children"]);/*#__PURE__*/
|
|
9
|
+
return e(null!=l?l:"div",o(function(e){for(var r=1;r<arguments.length;r++){var t=null!=arguments[r]?arguments[r]:{},o=Object.keys(t);"function"==typeof Object.getOwnPropertySymbols&&(o=o.concat(Object.getOwnPropertySymbols(t).filter((function(e){return Object.getOwnPropertyDescriptor(t,e).enumerable})))),o.forEach((function(r){n(e,r,t[r])}))}return e}({ref:i,className:r("box-border",a)},b),{children:u}))}));i.displayName="Box";export{i as Box};
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { ElementType } from 'react';
|
|
1
2
|
import { TypographyProps } from './Typography.types';
|
|
2
3
|
/**
|
|
3
4
|
* Typography
|
|
@@ -7,5 +8,5 @@ import { TypographyProps } from './Typography.types';
|
|
|
7
8
|
* @param {string} [props.className] - 추가적인 CSS 클래스
|
|
8
9
|
* @param {keyof JSX.IntrinsicElements} [props.as='span'] - 렌더링할 HTML 요소
|
|
9
10
|
*/
|
|
10
|
-
declare const Typography: ({ children, variant, className, as, ...rest }: TypographyProps) => import("react/jsx-runtime").JSX.Element;
|
|
11
|
+
declare const Typography: <T extends ElementType = "span">({ children, variant, className, as, ...rest }: TypographyProps<T>) => import("react/jsx-runtime").JSX.Element;
|
|
11
12
|
export { Typography };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{jsx as t}from"react/jsx-runtime";import{twMerge as e}from"tailwind-merge";function n(t,e,n){return e in t?Object.defineProperty(t,e,{value:n,enumerable:!0,configurable:!0,writable:!0}):t[e]=n,t}function r(t,e){return e=null!=e?e:{},Object.getOwnPropertyDescriptors?Object.defineProperties(t,Object.getOwnPropertyDescriptors(e)):function(t){var e=Object.keys(t);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(t);e.push.apply(e,n)}return e}(Object(e)).forEach((function(n){Object.defineProperty(t,n,Object.getOwnPropertyDescriptor(e,n))})),t}function o(t,e){if(null==t)return{};var n,r,o=function(t,e){if(null==t)return{};var n,r,o={},c=Object.keys(t);for(r=0;r<c.length;r++)n=c[r],e.indexOf(n)>=0||(o[n]=t[n]);return o}(t,e);if(Object.getOwnPropertySymbols){var c=Object.getOwnPropertySymbols(t);for(r=0;r<c.length;r++)n=c[r],e.indexOf(n)>=0||Object.prototype.propertyIsEnumerable.call(t,n)&&(o[n]=t[n])}return o}const c=[{name:"title48",class:"typo-title48"},{name:"title32",class:"typo-title32"},{name:"title28",class:"typo-title28"},{name:"title24",class:"typo-title24"},{name:"subTitle20",class:"typo-subTitle20"},{name:"subTitle18",class:"typo-subTitle18"},{name:"contents18",class:"typo-contents18"},{name:"contents16",class:"typo-contents16"},{name:"body16",class:"typo-body16"},{name:"body14",class:"typo-body14"},{name:"body13",class:"typo-body13"},{name:"caption12",class:"typo-caption12"},{name:"caption11",class:"typo-caption11"}],a=a=>{var s,{children:l,variant:i,className:p,as:y
|
|
1
|
+
import{jsx as t}from"react/jsx-runtime";import{twMerge as e}from"tailwind-merge";function n(t,e,n){return e in t?Object.defineProperty(t,e,{value:n,enumerable:!0,configurable:!0,writable:!0}):t[e]=n,t}function r(t,e){return e=null!=e?e:{},Object.getOwnPropertyDescriptors?Object.defineProperties(t,Object.getOwnPropertyDescriptors(e)):function(t){var e=Object.keys(t);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(t);e.push.apply(e,n)}return e}(Object(e)).forEach((function(n){Object.defineProperty(t,n,Object.getOwnPropertyDescriptor(e,n))})),t}function o(t,e){if(null==t)return{};var n,r,o=function(t,e){if(null==t)return{};var n,r,o={},c=Object.keys(t);for(r=0;r<c.length;r++)n=c[r],e.indexOf(n)>=0||(o[n]=t[n]);return o}(t,e);if(Object.getOwnPropertySymbols){var c=Object.getOwnPropertySymbols(t);for(r=0;r<c.length;r++)n=c[r],e.indexOf(n)>=0||Object.prototype.propertyIsEnumerable.call(t,n)&&(o[n]=t[n])}return o}const c=[{name:"title48",class:"typo-title48"},{name:"title32",class:"typo-title32"},{name:"title28",class:"typo-title28"},{name:"title24",class:"typo-title24"},{name:"subTitle20",class:"typo-subTitle20"},{name:"subTitle18",class:"typo-subTitle18"},{name:"contents18",class:"typo-contents18"},{name:"contents16",class:"typo-contents16"},{name:"body16",class:"typo-body16"},{name:"body14",class:"typo-body14"},{name:"body13",class:"typo-body13"},{name:"caption12",class:"typo-caption12"},{name:"caption11",class:"typo-caption11"}],a=a=>{var s,{children:l,variant:i,className:p,as:y}=a,b=o(a,["children","variant","className","as"]);const u=null!=y?y:"span",m=(null===(s=c.find((t=>t.name===i)))||void 0===s?void 0:s.class)||"";/*#__PURE__*/
|
|
2
2
|
return t(u,r(function(t){for(var e=1;e<arguments.length;e++){var r=null!=arguments[e]?arguments[e]:{},o=Object.keys(r);"function"==typeof Object.getOwnPropertySymbols&&(o=o.concat(Object.getOwnPropertySymbols(r).filter((function(t){return Object.getOwnPropertyDescriptor(r,t).enumerable})))),o.forEach((function(e){n(t,e,r[e])}))}return t}({className:e("tracking-default",m,p)},b),{children:l}))};
|
|
3
3
|
/**
|
|
4
4
|
* Typography
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ElementType, PropsWithChildren } from 'react';
|
|
1
|
+
import { ComponentPropsWithoutRef, ElementType, PropsWithChildren } from 'react';
|
|
2
2
|
export declare const typographyVariants: {
|
|
3
3
|
readonly title48: "title48";
|
|
4
4
|
readonly title32: "title32";
|
|
@@ -15,9 +15,8 @@ export declare const typographyVariants: {
|
|
|
15
15
|
readonly caption11: "caption11";
|
|
16
16
|
};
|
|
17
17
|
export type TypographyVariants = (typeof typographyVariants)[keyof typeof typographyVariants];
|
|
18
|
-
export
|
|
18
|
+
export type TypographyProps<T extends ElementType = 'span'> = PropsWithChildren<{
|
|
19
19
|
variant: TypographyVariants;
|
|
20
20
|
className?: string;
|
|
21
|
-
as?:
|
|
22
|
-
|
|
23
|
-
}
|
|
21
|
+
as?: T;
|
|
22
|
+
}> & Omit<ComponentPropsWithoutRef<T>, 'as' | 'children' | 'className'>;
|
|
@@ -51,6 +51,6 @@ declare const GNBList: {
|
|
|
51
51
|
UserInfo: ({ imgUrl, username, email, children, onClick, }: import("./GNBList.types").UserInfoProps) => import("react/jsx-runtime").JSX.Element;
|
|
52
52
|
List: ({ children }: import("react").PropsWithChildren) => import("react/jsx-runtime").JSX.Element;
|
|
53
53
|
SubList: ({ children }: import("react").PropsWithChildren) => import("react/jsx-runtime").JSX.Element;
|
|
54
|
-
Item: ({ hasNew, children, className, textClassName, href, ...rest }: import("./GNBList.types").GNBListItemProps) => import("react/jsx-runtime").JSX.Element;
|
|
54
|
+
Item: ({ hasNew, children, className, textClassName, href, as: LinkComponent, ...rest }: import("./GNBList.types").GNBListItemProps) => import("react/jsx-runtime").JSX.Element;
|
|
55
55
|
};
|
|
56
56
|
export { GNBList };
|
|
@@ -99,11 +99,12 @@ declare const SubList: ({ children }: PropsWithChildren) => import("react/jsx-ru
|
|
|
99
99
|
*
|
|
100
100
|
* @description
|
|
101
101
|
* 개별 메뉴 아이템을 렌더링합니다.
|
|
102
|
-
* - Next.js Link
|
|
102
|
+
* - 기본은 `<a>` 태그이며, `as` prop으로 Next.js Link 등 임의의 컴포넌트 주입 가능
|
|
103
103
|
* - 키보드 포커스 및 hover 스타일 지원
|
|
104
104
|
*
|
|
105
105
|
* @param {Object} props
|
|
106
106
|
* @param {string} props.href - 이동할 페이지 경로 (필수)
|
|
107
|
+
* @param {ElementType} [props.as='a'] - anchor로 렌더할 컴포넌트 (예: next/link)
|
|
107
108
|
* @param {ReactNode} props.children - 메뉴 텍스트
|
|
108
109
|
* @param {boolean} [props.hasNew=false] - New 뱃지 표시 여부
|
|
109
110
|
* @param {string} [props.className] - Link에 적용할 추가 CSS 클래스
|
|
@@ -131,5 +132,5 @@ declare const SubList: ({ children }: PropsWithChildren) => import("react/jsx-ru
|
|
|
131
132
|
* 특별 메뉴
|
|
132
133
|
* </Item>
|
|
133
134
|
*/
|
|
134
|
-
declare const Item: ({ hasNew, children, className, textClassName, href, ...rest }: GNBListItemProps) => import("react/jsx-runtime").JSX.Element;
|
|
135
|
+
declare const Item: ({ hasNew, children, className, textClassName, href, as: LinkComponent, ...rest }: GNBListItemProps) => import("react/jsx-runtime").JSX.Element;
|
|
135
136
|
export { Root, UserInfo, List, SubList, Item };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{jsx as e,jsxs as r}from"react/jsx-runtime";import{Children as a,Fragment as t}from"react";import{twMerge as l}from"tailwind-merge";import
|
|
1
|
+
import{jsx as e,jsxs as r}from"react/jsx-runtime";import{Children as a,Fragment as t}from"react";import{twMerge as l}from"tailwind-merge";import{Avatar as n}from"../../DataDisplays/Avatar/Avatar.js";import{Typography as o}from"../../Base/Typography/Typography.js";import{Box as s}from"../../Base/Layouts/Box/Box.js";import"../../Base/Layouts/FullBleed/FullBleed.js";import"../../DataDisplays/SystemIcon/SystemIcon.constants.js";import{NewBadge as i}from"../../DataDisplays/NewBadge/NewBadge.js";import{Divider as c}from"../../DataDisplays/Divider/Divider.js";import"../../DataDisplays/Accordion/Accordion.js";function m(e,r,a){return r in e?Object.defineProperty(e,r,{value:a,enumerable:!0,configurable:!0,writable:!0}):e[r]=a,e}function p(e){for(var r=1;r<arguments.length;r++){var a=null!=arguments[r]?arguments[r]:{},t=Object.keys(a);"function"==typeof Object.getOwnPropertySymbols&&(t=t.concat(Object.getOwnPropertySymbols(a).filter((function(e){return Object.getOwnPropertyDescriptor(a,e).enumerable})))),t.forEach((function(r){m(e,r,a[r])}))}return e}function f(e,r){return r=null!=r?r:{},Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(r)):function(e){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);r.push.apply(r,a)}return r}(Object(r)).forEach((function(a){Object.defineProperty(e,a,Object.getOwnPropertyDescriptor(r,a))})),e}function u(e,r){if(null==e)return{};var a,t,l=function(e,r){if(null==e)return{};var a,t,l={},n=Object.keys(e);for(t=0;t<n.length;t++)a=n[t],r.indexOf(a)>=0||(l[a]=e[a]);return l}
|
|
2
2
|
/**
|
|
3
3
|
* GNB 리스트의 루트 컴포넌트
|
|
4
4
|
*
|
|
@@ -31,16 +31,16 @@ import{jsx as e,jsxs as r}from"react/jsx-runtime";import{Children as a,Fragment
|
|
|
31
31
|
* <Item href="/logout">로그아웃</Item>
|
|
32
32
|
* </SubList>
|
|
33
33
|
* </Root>
|
|
34
|
-
*/(e,r);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);for(t=0;t<n.length;t++)a=n[t],r.indexOf(a)>=0||Object.prototype.propertyIsEnumerable.call(e,a)&&(l[a]=e[a])}return l}const
|
|
35
|
-
/*#__PURE__*/e(
|
|
36
|
-
return e(s,{as:"nav","aria-label":"네비게이션 메뉴",className:l("absolute flex flex-col rounded-xl bg-w-white shadow-graymedium",n?"w-[280px] pb-3 pt-5":"h-auto w-[200px] overflow-hidden py-3",i),children:
|
|
34
|
+
*/(e,r);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);for(t=0;t<n.length;t++)a=n[t],r.indexOf(a)>=0||Object.prototype.propertyIsEnumerable.call(e,a)&&(l[a]=e[a])}return l}const y=({withUserInfo:n=!1,children:o,className:i})=>{const m=a.map(o,((a,l)=>0===l?a:/*#__PURE__*/r(t,{children:[
|
|
35
|
+
/*#__PURE__*/e(c,{className:"my-2"}),a]})));/*#__PURE__*/
|
|
36
|
+
return e(s,{as:"nav","aria-label":"네비게이션 메뉴",className:l("absolute flex flex-col rounded-xl bg-w-white shadow-graymedium",n?"w-[280px] pb-3 pt-5":"h-auto w-[200px] overflow-hidden py-3",i),children:m})},b=({imgUrl:a,username:t,email:l,children:i,onClick:c})=>/*#__PURE__*/r(s,{role:"region","aria-label":"사용자 정보",className:"flex w-full flex-col gap-4 px-6 pb-4",children:[
|
|
37
37
|
/*#__PURE__*/r(s,{className:"flex w-full items-center gap-4",children:[
|
|
38
|
-
/*#__PURE__*/e(
|
|
38
|
+
/*#__PURE__*/e(n,{size:"lg",isEditable:!0,imgUrl:a,onClick:c}),
|
|
39
39
|
/*#__PURE__*/r(s,{className:"flex w-full flex-col gap-0.5",children:[
|
|
40
|
-
/*#__PURE__*/e(
|
|
41
|
-
/*#__PURE__*/e(
|
|
42
|
-
return e(s,{as:"li",children:/*#__PURE__*/r(
|
|
43
|
-
/*#__PURE__*/e(
|
|
40
|
+
/*#__PURE__*/e(o,{variant:"body16",className:"text-w-gray-900 font-medium",children:t}),
|
|
41
|
+
/*#__PURE__*/e(o,{variant:"body14",className:"text-w-gray-600 line-clamp-2 break-all",children:l})]})]}),i&&/*#__PURE__*/e(s,{className:"flex w-full flex-col gap-2",children:i})]}),d=({children:r})=>/*#__PURE__*/e(s,{as:"ul","aria-label":"메인 메뉴",className:"text-w-gray-900 w-full",children:r}),h=({children:r})=>/*#__PURE__*/e(s,{as:"ul","aria-label":"서브 메뉴",className:"text-w-gray-600 w-full",children:r}),g=a=>{var{hasNew:t=!1,children:n,className:c,textClassName:m,href:y,as:b="a"}=a,d=u(a,["hasNew","children","className","textClassName","href","as"]);/*#__PURE__*/
|
|
42
|
+
return e(s,{as:"li",children:/*#__PURE__*/r(b,f(p({href:y,className:l("hover:bg-primary-10 flex w-full cursor-pointer gap-1 px-6 py-2",c)},d),{children:[
|
|
43
|
+
/*#__PURE__*/e(o,{variant:"body14",className:m,children:n}),t&&/*#__PURE__*/e(i,{className:"mt-1","aria-label":"새 소식"})]}))})};
|
|
44
44
|
/**
|
|
45
45
|
* 사용자 정보 컴포넌트
|
|
46
46
|
*
|
|
@@ -64,4 +64,4 @@ return e(s,{as:"li",children:/*#__PURE__*/r(n,u(f({href:b,className:l("hover:bg-
|
|
|
64
64
|
* <Button size="sm">프로필 수정</Button>
|
|
65
65
|
* <Button size="sm" variant="outlined">내 프로젝트</Button>
|
|
66
66
|
* </UserInfo>
|
|
67
|
-
*/export{
|
|
67
|
+
*/export{g as Item,d as List,y as Root,h as SubList,b as UserInfo};
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { ComponentPropsWithoutRef, PropsWithChildren } from 'react';
|
|
1
|
+
import { ComponentPropsWithoutRef, ElementType, PropsWithChildren } from 'react';
|
|
3
2
|
export interface GNBListRootProps extends PropsWithChildren {
|
|
4
3
|
withUserInfo?: boolean;
|
|
5
4
|
className?: string;
|
|
@@ -10,7 +9,13 @@ export interface UserInfoProps extends PropsWithChildren {
|
|
|
10
9
|
email?: string;
|
|
11
10
|
onClick?: () => void;
|
|
12
11
|
}
|
|
13
|
-
export interface GNBListItemProps extends ComponentPropsWithoutRef<
|
|
12
|
+
export interface GNBListItemProps extends Omit<ComponentPropsWithoutRef<'a'>, 'href'> {
|
|
13
|
+
href: string;
|
|
14
14
|
hasNew?: boolean;
|
|
15
15
|
textClassName?: string;
|
|
16
|
+
/**
|
|
17
|
+
* Item에서 anchor로 렌더할 컴포넌트. 기본값은 일반 `<a>` 태그이며,
|
|
18
|
+
* Next.js의 Link 등을 주입할 수 있습니다.
|
|
19
|
+
*/
|
|
20
|
+
as?: ElementType;
|
|
16
21
|
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { ComponentPropsWithoutRef, ElementType, ReactNode } from 'react';
|
|
2
|
+
import { MediumSystemIconName, SmallSystemIconName } from '../../DataDisplays/SystemIcon';
|
|
3
|
+
import { IconButtonDropdownItem } from '../../Inputs';
|
|
4
|
+
/**
|
|
5
|
+
* `MenuLink`와 `MenuButton`이 공유하는 presentation props.
|
|
6
|
+
*
|
|
7
|
+
* - 시맨틱 분기(`href` vs `onClick`)는 각 컴포넌트의 별도 타입으로 표현되며,
|
|
8
|
+
* 이 타입에는 포함되지 않습니다.
|
|
9
|
+
*/
|
|
10
|
+
export interface MenuCommonProps {
|
|
11
|
+
type?: 'main' | 'sub';
|
|
12
|
+
variant?: 'white' | 'gray';
|
|
13
|
+
name: string;
|
|
14
|
+
badgeCount?: string;
|
|
15
|
+
isSelected?: boolean;
|
|
16
|
+
leadingIcon?: MediumSystemIconName;
|
|
17
|
+
iconButtonName?: SmallSystemIconName;
|
|
18
|
+
onOptionClick?: () => void;
|
|
19
|
+
children?: ReactNode;
|
|
20
|
+
items?: IconButtonDropdownItem[];
|
|
21
|
+
selectedItem?: IconButtonDropdownItem;
|
|
22
|
+
onItemClick?: (item: IconButtonDropdownItem) => void;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* `MenuLink`의 props.
|
|
26
|
+
*
|
|
27
|
+
* - `href`가 필수이며, 기본 anchor(`<a>`) 또는 `as`로 주입된 컴포넌트로 렌더링됩니다.
|
|
28
|
+
* - anchor 관련 native 속성(`target`, `rel`, `download` 등)을 그대로 받습니다.
|
|
29
|
+
*/
|
|
30
|
+
export type MenuLinkProps = MenuCommonProps & Omit<ComponentPropsWithoutRef<'a'>, 'href' | 'className' | 'children' | 'as'> & {
|
|
31
|
+
href: string;
|
|
32
|
+
/**
|
|
33
|
+
* anchor 자리에 렌더할 컴포넌트. 기본값은 일반 `<a>` 태그이며,
|
|
34
|
+
* Next.js의 Link 등을 주입할 수 있습니다.
|
|
35
|
+
*/
|
|
36
|
+
as?: ElementType;
|
|
37
|
+
};
|
|
38
|
+
/**
|
|
39
|
+
* `MenuButton`의 props.
|
|
40
|
+
*
|
|
41
|
+
* - `onClick`이 필수이며, 항상 `<button>`으로 렌더링됩니다.
|
|
42
|
+
* - button 관련 native 속성(`disabled`, `type` 등)을 그대로 받습니다.
|
|
43
|
+
*/
|
|
44
|
+
export type MenuButtonProps = MenuCommonProps & Omit<ComponentPropsWithoutRef<'button'>, 'onClick' | 'className' | 'children' | 'type'> & {
|
|
45
|
+
onClick: () => void;
|
|
46
|
+
/**
|
|
47
|
+
* HTML `<button>`의 native `type` 속성. `MenuCommonProps.type`(`'main' | 'sub'`)과
|
|
48
|
+
* 이름이 충돌하므로 별도 prop으로 전달하려면 `htmlType`을 사용하세요.
|
|
49
|
+
*
|
|
50
|
+
* 디자인 시스템의 메뉴 타입(`'main' | 'sub'`)은 `MenuCommonProps`의 `type`을 그대로 사용합니다.
|
|
51
|
+
*/
|
|
52
|
+
htmlType?: 'button' | 'submit' | 'reset';
|
|
53
|
+
};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { ElementType, ReactNode } from 'react';
|
|
2
|
+
import { MenuCommonProps } from './Menu.types';
|
|
3
|
+
interface MenuBaseProps extends MenuCommonProps {
|
|
4
|
+
/**
|
|
5
|
+
* 자식 wrapper로 사용할 element 또는 컴포넌트.
|
|
6
|
+
* `MenuLink`에서는 `'a'` 또는 주입된 컴포넌트, `MenuButton`에서는 `'button'`이 들어옵니다.
|
|
7
|
+
*/
|
|
8
|
+
as: ElementType;
|
|
9
|
+
/**
|
|
10
|
+
* `as` 컴포넌트에 그대로 전달되는 native props (href, onClick, target 등).
|
|
11
|
+
*/
|
|
12
|
+
wrapperProps?: Record<string, unknown>;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* `MenuLink`와 `MenuButton`이 공유하는 내부 컴포넌트.
|
|
16
|
+
* presentation 로직(className 계산, leadingIcon, name, badge, trailing IconButton/Dropdown)을 담당합니다.
|
|
17
|
+
*
|
|
18
|
+
* 외부에 export 되지 않으며, `MenuLink` / `MenuButton`을 통해서만 사용됩니다.
|
|
19
|
+
*/
|
|
20
|
+
declare const MenuBase: ({ type, variant, name, isSelected, badgeCount, leadingIcon, iconButtonName, onOptionClick, items, selectedItem, onItemClick, as: Component, wrapperProps, }: MenuBaseProps) => ReactNode;
|
|
21
|
+
export { MenuBase };
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import{jsxs as t,jsx as e}from"react/jsx-runtime";import{twJoin as r,twMerge as o}from"tailwind-merge";import"../../DataDisplays/Avatar/Avatar.js";import{CountBadge as n}from"../../DataDisplays/CountBadge/CountBadge.js";import{Typography as s}from"../../Base/Typography/Typography.js";import"../../Base/Layouts/Box/Box.js";import"../../Base/Layouts/FullBleed/FullBleed.js";import"react";import{SystemIcon as i}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 p}from"../../Inputs/IconButton/IconButton.js";import"../../Inputs/Radio/Radio.js";import"../../Inputs/TextField/TextField.js";import"../../Inputs/CommentArea/CommentArea.js";import{IconButtonDropdown as a}from"../../Inputs/IconButtonDropdown/IconButtonDropdown.js";import"../../Inputs/FilterChip/FilterChip.js";function m(t,e,r){return e in t?Object.defineProperty(t,e,{value:r,enumerable:!0,configurable:!0,writable:!0}):t[e]=r,t}function c(t,e){return e=null!=e?e:{},Object.getOwnPropertyDescriptors?Object.defineProperties(t,Object.getOwnPropertyDescriptors(e)):function(t){var e=Object.keys(t);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(t);e.push.apply(e,r)}return e}(Object(e)).forEach((function(r){Object.defineProperty(t,r,Object.getOwnPropertyDescriptor(e,r))})),t}const u=(t,e,n)=>{const s="sub"===t?"pl-12":"",i={white:r("hover:bg-w-gray-50",n&&"bg-primary-10 text-primary hover:bg-primary-10"),gray:r("hover:bg-w-gray-100",n&&"bg-w-gray-100 hover:bg-w-gray-100")};return o("flex w-full cursor-pointer items-center gap-2 rounded-xl px-4 py-3 text-w-gray-900 text-left",s,i[e])},l=(t,e)=>"white"!==t?"white_gray":e?"primary":"gray",y=({type:o="main",variant:y="white",name:d,isSelected:g=!1,badgeCount:j,leadingIcon:I,iconButtonName:b,onOptionClick:f,items:h,selectedItem:w,onItemClick:O,as:C,wrapperProps:x})=>{const B=!!(b&&h&&O&&w),D=!!b&&!!f;/*#__PURE__*/
|
|
2
|
+
return t(C,c(function(t){for(var e=1;e<arguments.length;e++){var r=null!=arguments[e]?arguments[e]:{},o=Object.keys(r);"function"==typeof Object.getOwnPropertySymbols&&(o=o.concat(Object.getOwnPropertySymbols(r).filter((function(t){return Object.getOwnPropertyDescriptor(r,t).enumerable})))),o.forEach((function(e){m(t,e,r[e])}))}return t}({"data-testid":"design-system-menu",className:u(o,y,g)},x),{children:[I&&/*#__PURE__*/e(i,{testId:"design-system-menu-leading-icon",name:I,className:r("white"===y&&g&&"text-primary")}),
|
|
3
|
+
/*#__PURE__*/e(s,{"data-testid":"design-system-menu-name",variant:"body16",className:"w-full select-none",children:d}),j&&/*#__PURE__*/e(n,{variant:l(y,g),text:j,className:"relative",showZero:!0}),B&&/*#__PURE__*/e(a,{size:"sm",icon:b,items:h,selectedItem:w,onItemClick:O}),D&&/*#__PURE__*/e(p,{size:"sm",className:"shrink-0",onClick:t=>{t.stopPropagation(),f()},children:/*#__PURE__*/e(i,{name:b})})]}))};export{y as MenuBase};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { MenuButtonProps } from './Menu.types';
|
|
2
|
+
/**
|
|
3
|
+
* 네비게이션에서 사용되는 버튼형 메뉴 항목.
|
|
4
|
+
*
|
|
5
|
+
* @description
|
|
6
|
+
* - 항상 `<button>`으로 렌더링됩니다.
|
|
7
|
+
* - `onClick`이 반드시 필요합니다. 페이지 이동을 위한 anchor가 필요한 경우 `MenuLink`를 사용하세요.
|
|
8
|
+
*
|
|
9
|
+
* @param {Object} props
|
|
10
|
+
* @param {() => void} props.onClick - 메뉴 클릭 시 실행될 함수 (필수)
|
|
11
|
+
* @param {'main' | 'sub'} [props.type='main'] - 메뉴 타입 (HTML `<button>`의 native `type`과는 무관. native `type`을 지정하려면 `htmlType` prop 사용)
|
|
12
|
+
* @param {'button' | 'submit' | 'reset'} [props.htmlType] - `<button>`의 native `type` 속성
|
|
13
|
+
* @param {'white' | 'gray'} [props.variant='white'] - 메뉴 스타일 변형
|
|
14
|
+
* @param {string} props.name - 메뉴에 표시될 텍스트
|
|
15
|
+
* @param {string} [props.badgeCount] - 메뉴 항목에 표시될 뱃지 숫자
|
|
16
|
+
* @param {boolean} [props.isSelected=false] - 메뉴 선택 상태
|
|
17
|
+
* @param {SystemIconName} [props.leadingIcon] - 메뉴 앞쪽에 표시될 아이콘 (medium 사이즈)
|
|
18
|
+
* @param {SystemIconName} [props.iconButtonName] - IconButtonDropdown / IconButton 아이콘 (small 사이즈)
|
|
19
|
+
* @param {() => void} [props.onOptionClick] - 후행 아이콘 클릭 시 실행될 함수 (IconButton 옵션)
|
|
20
|
+
* @param {Item[]} [props.items] - IconButtonDropdown 사용 시 필요
|
|
21
|
+
* @param {Item} [props.selectedItem] - IconButtonDropdown 사용 시 필요
|
|
22
|
+
* @param {(item: Item) => void} [props.onItemClick] - IconButtonDropdown 사용 시 필요
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* // 기본 사용
|
|
26
|
+
* <MenuButton name="설정" onClick={() => openModal()} />
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* // IconButtonDropdown과 함께
|
|
30
|
+
* <MenuButton
|
|
31
|
+
* name="옵션"
|
|
32
|
+
* onClick={() => {}}
|
|
33
|
+
* iconButtonName="small_more_options"
|
|
34
|
+
* items={items}
|
|
35
|
+
* selectedItem={selectedItem}
|
|
36
|
+
* onItemClick={onItemClick}
|
|
37
|
+
* />
|
|
38
|
+
*/
|
|
39
|
+
declare const MenuButton: ({ type, variant, name, badgeCount, isSelected, leadingIcon, iconButtonName, onOptionClick, items, selectedItem, onItemClick, onClick, htmlType, children: _children, ...rest }: MenuButtonProps) => import("react/jsx-runtime").JSX.Element;
|
|
40
|
+
export { MenuButton };
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import{jsx as e}from"react/jsx-runtime";import{MenuBase as t}from"./MenuBase.js";function n(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function r(e){for(var t=1;t<arguments.length;t++){var r=null!=arguments[t]?arguments[t]:{},o=Object.keys(r);"function"==typeof Object.getOwnPropertySymbols&&(o=o.concat(Object.getOwnPropertySymbols(r).filter((function(e){return Object.getOwnPropertyDescriptor(r,e).enumerable})))),o.forEach((function(t){n(e,t,r[t])}))}return e}function o(e,t){if(null==e)return{};var n,r,o=function(e,t){if(null==e)return{};var n,r,o={},i=Object.keys(e);for(r=0;r<i.length;r++)n=i[r],t.indexOf(n)>=0||(o[n]=e[n]);return o}
|
|
2
|
+
/**
|
|
3
|
+
* 네비게이션에서 사용되는 버튼형 메뉴 항목.
|
|
4
|
+
*
|
|
5
|
+
* @description
|
|
6
|
+
* - 항상 `<button>`으로 렌더링됩니다.
|
|
7
|
+
* - `onClick`이 반드시 필요합니다. 페이지 이동을 위한 anchor가 필요한 경우 `MenuLink`를 사용하세요.
|
|
8
|
+
*
|
|
9
|
+
* @param {Object} props
|
|
10
|
+
* @param {() => void} props.onClick - 메뉴 클릭 시 실행될 함수 (필수)
|
|
11
|
+
* @param {'main' | 'sub'} [props.type='main'] - 메뉴 타입 (HTML `<button>`의 native `type`과는 무관. native `type`을 지정하려면 `htmlType` prop 사용)
|
|
12
|
+
* @param {'button' | 'submit' | 'reset'} [props.htmlType] - `<button>`의 native `type` 속성
|
|
13
|
+
* @param {'white' | 'gray'} [props.variant='white'] - 메뉴 스타일 변형
|
|
14
|
+
* @param {string} props.name - 메뉴에 표시될 텍스트
|
|
15
|
+
* @param {string} [props.badgeCount] - 메뉴 항목에 표시될 뱃지 숫자
|
|
16
|
+
* @param {boolean} [props.isSelected=false] - 메뉴 선택 상태
|
|
17
|
+
* @param {SystemIconName} [props.leadingIcon] - 메뉴 앞쪽에 표시될 아이콘 (medium 사이즈)
|
|
18
|
+
* @param {SystemIconName} [props.iconButtonName] - IconButtonDropdown / IconButton 아이콘 (small 사이즈)
|
|
19
|
+
* @param {() => void} [props.onOptionClick] - 후행 아이콘 클릭 시 실행될 함수 (IconButton 옵션)
|
|
20
|
+
* @param {Item[]} [props.items] - IconButtonDropdown 사용 시 필요
|
|
21
|
+
* @param {Item} [props.selectedItem] - IconButtonDropdown 사용 시 필요
|
|
22
|
+
* @param {(item: Item) => void} [props.onItemClick] - IconButtonDropdown 사용 시 필요
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* // 기본 사용
|
|
26
|
+
* <MenuButton name="설정" onClick={() => openModal()} />
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* // IconButtonDropdown과 함께
|
|
30
|
+
* <MenuButton
|
|
31
|
+
* name="옵션"
|
|
32
|
+
* onClick={() => {}}
|
|
33
|
+
* iconButtonName="small_more_options"
|
|
34
|
+
* items={items}
|
|
35
|
+
* selectedItem={selectedItem}
|
|
36
|
+
* onItemClick={onItemClick}
|
|
37
|
+
* />
|
|
38
|
+
*/(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(r=0;r<i.length;r++)n=i[r],t.indexOf(n)>=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(o[n]=e[n])}return o}const i=n=>{var{type:i,variant:c,name:l,badgeCount:a,isSelected:u,leadingIcon:m,iconButtonName:p,onOptionClick:s,items:b,selectedItem:f,onItemClick:y,onClick:O,htmlType:d,children:g}=n,C=o(n,["type","variant","name","badgeCount","isSelected","leadingIcon","iconButtonName","onOptionClick","items","selectedItem","onItemClick","onClick","htmlType","children"]);/*#__PURE__*/
|
|
39
|
+
return e(t,{type:i,variant:c,name:l,badgeCount:a,isSelected:u,leadingIcon:m,iconButtonName:p,onOptionClick:s,items:b,selectedItem:f,onItemClick:y,as:"button",wrapperProps:r({onClick:O,type:d},C)})};export{i as MenuButton};
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { MenuLinkProps } from './Menu.types';
|
|
2
|
+
/**
|
|
3
|
+
* 네비게이션에서 사용되는 링크형 메뉴 항목.
|
|
4
|
+
*
|
|
5
|
+
* @description
|
|
6
|
+
* - 기본은 `<a>` 태그이며, `as` prop으로 Next.js의 Link 등 임의의 컴포넌트를 주입할 수 있습니다.
|
|
7
|
+
* - `href`가 반드시 필요합니다. 클릭 핸들러만 필요한 경우 `MenuButton`을 사용하세요.
|
|
8
|
+
*
|
|
9
|
+
* @param {Object} props
|
|
10
|
+
* @param {string} props.href - 메뉴 링크 주소 (필수)
|
|
11
|
+
* @param {ElementType} [props.as='a'] - anchor 자리에 렌더할 컴포넌트 (예: next/link)
|
|
12
|
+
* @param {'main' | 'sub'} [props.type='main'] - 메뉴 타입
|
|
13
|
+
* @param {'white' | 'gray'} [props.variant='white'] - 메뉴 스타일 변형
|
|
14
|
+
* @param {string} props.name - 메뉴에 표시될 텍스트
|
|
15
|
+
* @param {string} [props.badgeCount] - 메뉴 항목에 표시될 뱃지 숫자
|
|
16
|
+
* @param {boolean} [props.isSelected=false] - 메뉴 선택 상태
|
|
17
|
+
* @param {SystemIconName} [props.leadingIcon] - 메뉴 앞쪽에 표시될 아이콘 (medium 사이즈)
|
|
18
|
+
* @param {SystemIconName} [props.iconButtonName] - IconButtonDropdown / IconButton 아이콘 (small 사이즈)
|
|
19
|
+
* @param {() => void} [props.onOptionClick] - 후행 아이콘 클릭 시 실행될 함수 (IconButton 옵션)
|
|
20
|
+
* @param {Item[]} [props.items] - IconButtonDropdown 사용 시 필요
|
|
21
|
+
* @param {Item} [props.selectedItem] - IconButtonDropdown 사용 시 필요
|
|
22
|
+
* @param {(item: Item) => void} [props.onItemClick] - IconButtonDropdown 사용 시 필요
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* // 기본 사용 (일반 <a>)
|
|
26
|
+
* <MenuLink href="/projects" name="프로젝트" />
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* // Next.js Link 주입
|
|
30
|
+
* import Link from 'next/link';
|
|
31
|
+
* <MenuLink as={Link} href="/projects" name="프로젝트" />
|
|
32
|
+
*
|
|
33
|
+
* @example
|
|
34
|
+
* // 외부 링크
|
|
35
|
+
* <MenuLink
|
|
36
|
+
* href="https://example.com"
|
|
37
|
+
* name="외부 사이트"
|
|
38
|
+
* target="_blank"
|
|
39
|
+
* rel="noopener noreferrer"
|
|
40
|
+
* />
|
|
41
|
+
*/
|
|
42
|
+
declare const MenuLink: ({ type, variant, name, badgeCount, isSelected, leadingIcon, iconButtonName, onOptionClick, items, selectedItem, onItemClick, href, as: LinkComponent, children: _children, ...rest }: MenuLinkProps) => import("react/jsx-runtime").JSX.Element;
|
|
43
|
+
export { MenuLink };
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import{jsx as e}from"react/jsx-runtime";import{MenuBase as t}from"./MenuBase.js";function n(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function r(e){for(var t=1;t<arguments.length;t++){var r=null!=arguments[t]?arguments[t]:{},o=Object.keys(r);"function"==typeof Object.getOwnPropertySymbols&&(o=o.concat(Object.getOwnPropertySymbols(r).filter((function(e){return Object.getOwnPropertyDescriptor(r,e).enumerable})))),o.forEach((function(t){n(e,t,r[t])}))}return e}function o(e,t){if(null==e)return{};var n,r,o=function(e,t){if(null==e)return{};var n,r,o={},i=Object.keys(e);for(r=0;r<i.length;r++)n=i[r],t.indexOf(n)>=0||(o[n]=e[n]);return o}
|
|
2
|
+
/**
|
|
3
|
+
* 네비게이션에서 사용되는 링크형 메뉴 항목.
|
|
4
|
+
*
|
|
5
|
+
* @description
|
|
6
|
+
* - 기본은 `<a>` 태그이며, `as` prop으로 Next.js의 Link 등 임의의 컴포넌트를 주입할 수 있습니다.
|
|
7
|
+
* - `href`가 반드시 필요합니다. 클릭 핸들러만 필요한 경우 `MenuButton`을 사용하세요.
|
|
8
|
+
*
|
|
9
|
+
* @param {Object} props
|
|
10
|
+
* @param {string} props.href - 메뉴 링크 주소 (필수)
|
|
11
|
+
* @param {ElementType} [props.as='a'] - anchor 자리에 렌더할 컴포넌트 (예: next/link)
|
|
12
|
+
* @param {'main' | 'sub'} [props.type='main'] - 메뉴 타입
|
|
13
|
+
* @param {'white' | 'gray'} [props.variant='white'] - 메뉴 스타일 변형
|
|
14
|
+
* @param {string} props.name - 메뉴에 표시될 텍스트
|
|
15
|
+
* @param {string} [props.badgeCount] - 메뉴 항목에 표시될 뱃지 숫자
|
|
16
|
+
* @param {boolean} [props.isSelected=false] - 메뉴 선택 상태
|
|
17
|
+
* @param {SystemIconName} [props.leadingIcon] - 메뉴 앞쪽에 표시될 아이콘 (medium 사이즈)
|
|
18
|
+
* @param {SystemIconName} [props.iconButtonName] - IconButtonDropdown / IconButton 아이콘 (small 사이즈)
|
|
19
|
+
* @param {() => void} [props.onOptionClick] - 후행 아이콘 클릭 시 실행될 함수 (IconButton 옵션)
|
|
20
|
+
* @param {Item[]} [props.items] - IconButtonDropdown 사용 시 필요
|
|
21
|
+
* @param {Item} [props.selectedItem] - IconButtonDropdown 사용 시 필요
|
|
22
|
+
* @param {(item: Item) => void} [props.onItemClick] - IconButtonDropdown 사용 시 필요
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* // 기본 사용 (일반 <a>)
|
|
26
|
+
* <MenuLink href="/projects" name="프로젝트" />
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* // Next.js Link 주입
|
|
30
|
+
* import Link from 'next/link';
|
|
31
|
+
* <MenuLink as={Link} href="/projects" name="프로젝트" />
|
|
32
|
+
*
|
|
33
|
+
* @example
|
|
34
|
+
* // 외부 링크
|
|
35
|
+
* <MenuLink
|
|
36
|
+
* href="https://example.com"
|
|
37
|
+
* name="외부 사이트"
|
|
38
|
+
* target="_blank"
|
|
39
|
+
* rel="noopener noreferrer"
|
|
40
|
+
* />
|
|
41
|
+
*/(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(r=0;r<i.length;r++)n=i[r],t.indexOf(n)>=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(o[n]=e[n])}return o}const i=n=>{var{type:i,variant:c,name:a,badgeCount:l,isSelected:u,leadingIcon:m,iconButtonName:f,onOptionClick:s,items:p,selectedItem:b,onItemClick:O,href:y,as:d="a",children:g}=n,j=o(n,["type","variant","name","badgeCount","isSelected","leadingIcon","iconButtonName","onOptionClick","items","selectedItem","onItemClick","href","as","children"]);/*#__PURE__*/
|
|
42
|
+
return e(t,{type:i,variant:c,name:a,badgeCount:l,isSelected:u,leadingIcon:m,iconButtonName:f,onOptionClick:s,items:p,selectedItem:b,onItemClick:O,as:d,wrapperProps:r({href:y},j)})};export{i as MenuLink};
|
|
@@ -1,66 +1,44 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { ComponentProps } from 'react';
|
|
1
|
+
import { ComponentPropsWithoutRef, ElementType } from 'react';
|
|
3
2
|
import { TextWithIconsProps } from '../../Base/TextWithIcons/TextWithIcons';
|
|
4
|
-
interface TextLinkProps extends
|
|
3
|
+
interface TextLinkProps extends Omit<ComponentPropsWithoutRef<'a'>, 'href'>, TextWithIconsProps {
|
|
4
|
+
href: string;
|
|
5
|
+
as?: ElementType;
|
|
5
6
|
}
|
|
6
7
|
/**
|
|
7
8
|
* 텍스트 기반의 링크 컴포넌트입니다.
|
|
8
9
|
*
|
|
9
10
|
* @description
|
|
10
|
-
* TextButton과 동일한 스타일을 가지지만 `<a>` 태그 기반으로 페이지 네비게이션을 담당합니다.
|
|
11
|
-
* Next.js의 Link
|
|
11
|
+
* TextButton과 동일한 스타일을 가지지만 기본적으로 `<a>` 태그 기반으로 페이지 네비게이션을 담당합니다.
|
|
12
|
+
* `as` prop으로 Next.js의 `Link`나 React Router의 `Link` 등 임의의 컴포넌트를 주입할 수 있습니다.
|
|
12
13
|
*
|
|
13
14
|
* @component
|
|
14
15
|
* @param {Object} props - TextLink 컴포넌트의 props
|
|
15
16
|
* @param {string} props.href - 링크 대상 URL (필수)
|
|
16
17
|
* @param {string} props.text - 표시할 텍스트 (필수)
|
|
18
|
+
* @param {ElementType} [props.as='a'] - 렌더링할 요소/컴포넌트
|
|
17
19
|
* @param {SystemIconName} [props.leadingIcon] - 텍스트 앞에 표시할 아이콘
|
|
18
20
|
* @param {SystemIconName} [props.trailingIcon] - 텍스트 뒤에 표시할 아이콘
|
|
19
21
|
* @param {boolean} [props.isTextSmall=false] - 작은 텍스트 크기 사용 여부
|
|
20
22
|
* @param {boolean} [props.isGray=false] - 텍스트 색상을 회색으로 사용 여부
|
|
21
23
|
* @param {boolean} [props.isUnderline=false] - 텍스트에 밑줄 스타일 적용 여부
|
|
22
|
-
* @param {ComponentProps<typeof Link>} props - Next.js Link 컴포넌트의 모든 props 지원
|
|
23
24
|
*
|
|
24
25
|
* @example
|
|
25
|
-
* // 기본 사용법
|
|
26
|
+
* // 기본 사용법 (일반 <a>)
|
|
26
27
|
* <TextLink href="/about" text="소개 페이지로 이동" />
|
|
27
28
|
*
|
|
28
29
|
* @example
|
|
29
|
-
* //
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
* text="자세히 보기"
|
|
33
|
-
* trailingIcon="medium_arrow_right"
|
|
34
|
-
* />
|
|
30
|
+
* // Next.js Link와 함께 사용
|
|
31
|
+
* import Link from 'next/link';
|
|
32
|
+
* <TextLink as={Link} href="/about" text="소개" />
|
|
35
33
|
*
|
|
36
34
|
* @example
|
|
37
|
-
* // 외부
|
|
35
|
+
* // 외부 링크
|
|
38
36
|
* <TextLink
|
|
39
37
|
* href="https://example.com"
|
|
40
38
|
* text="외부 사이트 방문"
|
|
41
39
|
* target="_blank"
|
|
42
40
|
* rel="noopener noreferrer"
|
|
43
41
|
* />
|
|
44
|
-
*
|
|
45
|
-
* @example
|
|
46
|
-
* // 작은 텍스트 크기로 사용
|
|
47
|
-
* <TextLink
|
|
48
|
-
* href="/help"
|
|
49
|
-
* text="도움말"
|
|
50
|
-
* isTextSmall
|
|
51
|
-
* leadingIcon="small_help"
|
|
52
|
-
* />
|
|
53
|
-
*
|
|
54
|
-
* @example
|
|
55
|
-
* // 프로그래매틱 네비게이션과 함께
|
|
56
|
-
* <TextLink
|
|
57
|
-
* href="/dashboard"
|
|
58
|
-
* text="대시보드"
|
|
59
|
-
* onClick={(e) => {
|
|
60
|
-
* // 추가 로직 실행 가능
|
|
61
|
-
* console.log('대시보드로 이동');
|
|
62
|
-
* }}
|
|
63
|
-
* />
|
|
64
42
|
*/
|
|
65
|
-
declare const TextLink: ({ href, text, leadingIcon, trailingIcon, isTextSmall, isGray, isUnderline, className, ...rest }: TextLinkProps) => import("react/jsx-runtime").JSX.Element;
|
|
43
|
+
declare const TextLink: ({ as: Component, href, text, leadingIcon, trailingIcon, isTextSmall, isGray, isUnderline, className, ...rest }: TextLinkProps) => import("react/jsx-runtime").JSX.Element;
|
|
66
44
|
export { TextLink };
|