@wishket/design-system 2.2.0 → 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.
Files changed (63) hide show
  1. package/README.md +58 -0
  2. package/dist/Components/Base/Layouts/Box/Box.d.ts +5 -5
  3. package/dist/Components/Base/Layouts/Box/Box.js +3 -2
  4. package/dist/Components/Base/Typography/Typography.d.ts +2 -1
  5. package/dist/Components/Base/Typography/Typography.js +1 -1
  6. package/dist/Components/Base/Typography/Typography.types.d.ts +4 -5
  7. package/dist/Components/Inputs/Calendar/Calendar.d.ts +2 -1
  8. package/dist/Components/Inputs/Calendar/Calendar.js +9 -8
  9. package/dist/Components/Inputs/Calendar/Calendar.parts.js +1 -1
  10. package/dist/Components/Inputs/Calendar/Calendar.types.d.ts +1 -0
  11. package/dist/Components/Navigations/GNBList/GNBList.d.ts +1 -1
  12. package/dist/Components/Navigations/GNBList/GNBList.parts.d.ts +3 -2
  13. package/dist/Components/Navigations/GNBList/GNBList.parts.js +10 -10
  14. package/dist/Components/Navigations/GNBList/GNBList.types.d.ts +8 -3
  15. package/dist/Components/Navigations/Menu/Menu.types.d.ts +53 -0
  16. package/dist/Components/Navigations/Menu/MenuBase.d.ts +21 -0
  17. package/dist/Components/Navigations/Menu/MenuBase.js +3 -0
  18. package/dist/Components/Navigations/Menu/MenuButton.d.ts +40 -0
  19. package/dist/Components/Navigations/Menu/MenuButton.js +39 -0
  20. package/dist/Components/Navigations/Menu/MenuLink.d.ts +43 -0
  21. package/dist/Components/Navigations/Menu/MenuLink.js +42 -0
  22. package/dist/Components/Navigations/Menu/index.d.ts +3 -1
  23. package/dist/Components/Navigations/TextLink/TextLink.d.ts +13 -35
  24. package/dist/Components/Navigations/TextLink/TextLink.js +11 -34
  25. package/dist/cjs/Components/Base/Layouts/Box/Box.js +3 -2
  26. package/dist/cjs/Components/Base/Typography/Typography.js +2 -2
  27. package/dist/cjs/Components/Inputs/Calendar/Calendar.js +7 -7
  28. package/dist/cjs/Components/Inputs/Calendar/Calendar.parts.js +6 -6
  29. package/dist/cjs/Components/Navigations/GNBList/GNBList.parts.js +11 -11
  30. package/dist/cjs/Components/Navigations/Menu/MenuBase.js +3 -0
  31. package/dist/cjs/Components/Navigations/Menu/MenuButton.js +39 -0
  32. package/dist/cjs/Components/Navigations/Menu/MenuLink.js +42 -0
  33. package/dist/cjs/Components/Navigations/TextLink/TextLink.js +11 -34
  34. package/dist/cjs/index.js +1 -1
  35. package/dist/index.js +1 -1
  36. package/package.json +7 -5
  37. package/scripts/codemods/README.md +178 -0
  38. package/scripts/codemods/__tests__/__fixtures__/add-as-link/already-has-as.input.tsx +6 -0
  39. package/scripts/codemods/__tests__/__fixtures__/add-as-link/already-has-as.output.tsx +6 -0
  40. package/scripts/codemods/__tests__/__fixtures__/add-as-link/basic.input.tsx +9 -0
  41. package/scripts/codemods/__tests__/__fixtures__/add-as-link/basic.output.tsx +10 -0
  42. package/scripts/codemods/__tests__/__fixtures__/add-as-link/existing-next-link-import.input.tsx +9 -0
  43. package/scripts/codemods/__tests__/__fixtures__/add-as-link/existing-next-link-import.output.tsx +9 -0
  44. package/scripts/codemods/__tests__/__fixtures__/add-as-link/menu-button-fallback.input.tsx +5 -0
  45. package/scripts/codemods/__tests__/__fixtures__/add-as-link/menu-button-fallback.output.tsx +5 -0
  46. package/scripts/codemods/__tests__/__fixtures__/add-as-link/unrelated-import.input.tsx +3 -0
  47. package/scripts/codemods/__tests__/__fixtures__/add-as-link/unrelated-import.output.tsx +3 -0
  48. package/scripts/codemods/__tests__/__fixtures__/menu-split/basic.input.tsx +8 -0
  49. package/scripts/codemods/__tests__/__fixtures__/menu-split/basic.output.tsx +8 -0
  50. package/scripts/codemods/__tests__/__fixtures__/menu-split/conflict.input.tsx +5 -0
  51. package/scripts/codemods/__tests__/__fixtures__/menu-split/conflict.output.tsx +6 -0
  52. package/scripts/codemods/__tests__/__fixtures__/menu-split/href-only.input.tsx +3 -0
  53. package/scripts/codemods/__tests__/__fixtures__/menu-split/href-only.output.tsx +3 -0
  54. package/scripts/codemods/__tests__/__fixtures__/menu-split/onclick-only.input.tsx +3 -0
  55. package/scripts/codemods/__tests__/__fixtures__/menu-split/onclick-only.output.tsx +3 -0
  56. package/scripts/codemods/__tests__/__fixtures__/menu-split/spread-props.input.tsx +3 -0
  57. package/scripts/codemods/__tests__/__fixtures__/menu-split/spread-props.output.tsx +4 -0
  58. package/scripts/codemods/__tests__/run-fixtures.cjs +100 -0
  59. package/scripts/codemods/add-as-link.ts +110 -0
  60. package/scripts/codemods/menu-split.ts +252 -0
  61. package/dist/Components/Navigations/Menu/Menu.d.ts +0 -81
  62. package/dist/Components/Navigations/Menu/Menu.js +0 -62
  63. 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 { ComponentPropsWithoutRef, ElementType, PropsWithChildren } from 'react';
2
- export interface BoxProps extends PropsWithChildren<ComponentPropsWithoutRef<'div'>> {
3
- as?: ElementType;
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<HTMLDivElement>>;
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,i)=>{var{className:l,children:a,as:b="div"}=t,f=c(t,["className","children","as"]);/*#__PURE__*/
8
- return e(b,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",l)},f),{children:a}))}));i.displayName="Box";export{i as Box};
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="span"}=a,b=o(a,["children","variant","className","as"]);const u=y||"span",m=(null===(s=c.find((t=>t.name===i)))||void 0===s?void 0:s.class)||"";/*#__PURE__*/
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 interface TypographyProps extends PropsWithChildren {
18
+ export type TypographyProps<T extends ElementType = 'span'> = PropsWithChildren<{
19
19
  variant: TypographyVariants;
20
20
  className?: string;
21
- as?: ElementType;
22
- [key: string]: any;
23
- }
21
+ as?: T;
22
+ }> & Omit<ComponentPropsWithoutRef<T>, 'as' | 'children' | 'className'>;
@@ -14,6 +14,7 @@ import { CalendarProps } from './Calendar.types';
14
14
  * @param {boolean} [props.isError] - 에러 상태 표시 여부
15
15
  * @param {string} [props.className] - 컨테이너에 머지될 클래스 (예: 부모 너비에 맞추기 위한 `w-full`)
16
16
  * @param {string} [props.triggerClassName] - 트리거(TextFieldContainer)에 머지될 클래스
17
+ * @param {string} [props.dropdownClassName] - dropdown popup 영역에 머지될 클래스 (예: 모바일 풀너비 `w-full`)
17
18
  * @param {string} [props.minDate] - 선택 가능한 최소 날짜 (YYYY-MM-DD). 이전 날짜 셀은 disabled 처리
18
19
  * @param {string} [props.maxDate] - 선택 가능한 최대 날짜 (YYYY-MM-DD). 이후 날짜 셀은 disabled 처리
19
20
  *
@@ -52,5 +53,5 @@ import { CalendarProps } from './Calendar.types';
52
53
  * />
53
54
  * ```
54
55
  */
55
- declare const Calendar: ({ defaultDate, selected, onChange, disabled, placeholder, isError, className, triggerClassName, minDate, maxDate, }: CalendarProps) => import("react/jsx-runtime").JSX.Element;
56
+ declare const Calendar: ({ defaultDate, selected, onChange, disabled, placeholder, isError, className, triggerClassName, dropdownClassName, minDate, maxDate, }: CalendarProps) => import("react/jsx-runtime").JSX.Element;
56
57
  export { Calendar };
@@ -1,4 +1,4 @@
1
- "use client";import{jsxs as e,jsx as a}from"react/jsx-runtime";import{useState as t,useEffect as r}from"react";import{twMerge as s}from"tailwind-merge";import"../../DataDisplays/Avatar/Avatar.js";import{Typography as n}from"../../Base/Typography/Typography.js";import{Box as i}from"../../Base/Layouts/Box/Box.js";import"../../Base/Layouts/FullBleed/FullBleed.js";import{SystemIcon as o}from"../../DataDisplays/SystemIcon/SystemIcon.js";import"../../DataDisplays/SystemIcon/SystemIcon.constants.js";import"../../DataDisplays/Accordion/Accordion.js";import{TextFieldContainer as l}from"../TextFieldContainer/TextFieldContainer.js";import{SelectHeader as d,WeekHeader as m,DateBox as c}from"./Calendar.parts.js";import{generateDate as p,today as y,getKoreanTimeString as g,getMonthDate as x,isSameDate as h,isDateOutOfRange as u}from"./Calendar.utils.js";
1
+ "use client";import{jsxs as e,jsx as a}from"react/jsx-runtime";import{useState as t,useEffect as r}from"react";import{twMerge as s}from"tailwind-merge";import"../../DataDisplays/Avatar/Avatar.js";import{Typography as n}from"../../Base/Typography/Typography.js";import{Box as o}from"../../Base/Layouts/Box/Box.js";import"../../Base/Layouts/FullBleed/FullBleed.js";import{SystemIcon as i}from"../../DataDisplays/SystemIcon/SystemIcon.js";import"../../DataDisplays/SystemIcon/SystemIcon.constants.js";import"../../DataDisplays/Accordion/Accordion.js";import{TextFieldContainer as l}from"../TextFieldContainer/TextFieldContainer.js";import{SelectHeader as d,WeekHeader as m,DateBox as c}from"./Calendar.parts.js";import{generateDate as p,today as y,getKoreanTimeString as g,getMonthDate as x,isSameDate as h,isDateOutOfRange as u}from"./Calendar.utils.js";
2
2
  /**
3
3
  * 날짜를 선택할 수 있는 캘린더 컴포넌트입니다.
4
4
  *
@@ -14,6 +14,7 @@
14
14
  * @param {boolean} [props.isError] - 에러 상태 표시 여부
15
15
  * @param {string} [props.className] - 컨테이너에 머지될 클래스 (예: 부모 너비에 맞추기 위한 `w-full`)
16
16
  * @param {string} [props.triggerClassName] - 트리거(TextFieldContainer)에 머지될 클래스
17
+ * @param {string} [props.dropdownClassName] - dropdown popup 영역에 머지될 클래스 (예: 모바일 풀너비 `w-full`)
17
18
  * @param {string} [props.minDate] - 선택 가능한 최소 날짜 (YYYY-MM-DD). 이전 날짜 셀은 disabled 처리
18
19
  * @param {string} [props.maxDate] - 선택 가능한 최대 날짜 (YYYY-MM-DD). 이후 날짜 셀은 disabled 처리
19
20
  *
@@ -51,12 +52,12 @@
51
52
  * placeholder="시작일"
52
53
  * />
53
54
  * ```
54
- */const f=({defaultDate:f,selected:w,onChange:D,disabled:b,placeholder:j,isError:C,className:N,triggerClassName:$,minDate:B,maxDate:v})=>{const[A,F]=t(!1),I=()=>{b||F((e=>!e))},S=f?p(f):y,[T,L]=t(w?p(w):S),k=w?p(w):S,{year:z,month:E,date:_}=g(k),{dateList:q}=x(T.year,T.month),G=[...Array.from({length:q[0].day},(()=>null)),...q],H=e=>{if(w&&h(e,k))return;if(u(e,B,v))return;const a=`${e.year}-${e.month}-${e.date}`;null==D||D(a),F(!1)};return r((()=>{!A&&w&&L(p(w));
55
+ */const f=({defaultDate:f,selected:w,onChange:D,disabled:b,placeholder:j,isError:C,className:N,triggerClassName:$,dropdownClassName:B,minDate:v,maxDate:A})=>{const[F,I]=t(!1),S=()=>{b||I((e=>!e))},T=f?p(f):y,[L,k]=t(w?p(w):T),z=w?p(w):T,{year:E,month:_,date:q}=g(z),{dateList:G}=x(L.year,L.month),H=[...Array.from({length:G[0].day},(()=>null)),...G],J=e=>{if(w&&h(e,z))return;if(u(e,v,A))return;const a=`${e.year}-${e.month}-${e.date}`;null==D||D(a),I(!1)};return r((()=>{!F&&w&&k(p(w));
55
56
  // eslint-disable-next-line react-hooks/exhaustive-deps
56
- }),[A]),/*#__PURE__*/e(i,{className:s("relative h-fit w-fit select-none",N),"data-testid":"design-system-calendar--container",children:[
57
- /*#__PURE__*/a(i,{className:"cursor-pointer",children:/*#__PURE__*/e(l,{isFocused:A,isError:C,isDisabled:b,testId:"design-system-calendar--trigger",onClick:I,className:$,children:[
58
- /*#__PURE__*/a(o,{name:"medium_calendar",className:b||!w?"text-w-gray-300":"text-w-gray-400"}),
59
- /*#__PURE__*/a(n,{variant:"body16",className:s("text-w-gray-600 min-w-[178px]",!w&&"text-w-gray-300",b&&"text-w-gray-300"),"data-testid":"design-system-calendar--selected-date",children:w?`${z} ${E} ${_}`:j})]})}),A&&/*#__PURE__*/a("div",{className:"fixed top-0 left-0 z-10 h-screen w-screen bg-transparent",onClick:I}),A&&/*#__PURE__*/e(i,{"data-testid":"design-system-calendar--content",className:"border-w-gray-200 bg-w-white shadow-graymedium absolute top-[54px] z-30 flex w-[350px] flex-col gap-4 rounded-xl border px-[15px] py-[23px]",children:[
60
- /*#__PURE__*/a(d,{visibleDate:T,onChange:e=>{L(e)}}),
57
+ }),[F]),/*#__PURE__*/e(o,{className:s("relative h-fit w-fit select-none",N),"data-testid":"design-system-calendar--container",children:[
58
+ /*#__PURE__*/a(o,{className:"cursor-pointer",children:/*#__PURE__*/e(l,{isFocused:F,isError:C,isDisabled:b,testId:"design-system-calendar--trigger",onClick:S,className:$,children:[
59
+ /*#__PURE__*/a(i,{name:"medium_calendar",className:b||!w?"text-w-gray-300":"text-w-gray-400"}),
60
+ /*#__PURE__*/a(n,{variant:"body16",className:s("text-w-gray-600 min-w-[178px]",!w&&"text-w-gray-300",b&&"text-w-gray-300"),"data-testid":"design-system-calendar--selected-date",children:w?`${E} ${_} ${q}`:j})]})}),F&&/*#__PURE__*/a("div",{className:"fixed top-0 left-0 z-10 h-screen w-screen bg-transparent",onClick:S}),F&&/*#__PURE__*/e(o,{"data-testid":"design-system-calendar--content",className:s("border-w-gray-200 bg-w-white shadow-graymedium absolute top-[54px] z-30 flex w-[350px] flex-col gap-4 rounded-xl border px-[15px] py-[23px]",B),children:[
61
+ /*#__PURE__*/a(d,{visibleDate:L,onChange:e=>{k(e)}}),
61
62
  /*#__PURE__*/a(m,{}),
62
- /*#__PURE__*/a(i,{className:"grid w-full grid-cols-7 place-content-center place-items-center gap-1",children:G.map(((e,t)=>{const r=!(!e||!w)&&h(e,k);return e?/*#__PURE__*/a(c,{date:e,selected:r,disabled:u(e,B,v),onChange:H},`${e.year}-${e.month}-${e.date}`):/*#__PURE__*/a(c,{disabled:!0},t)}))})]})]})};export{f as Calendar};
63
+ /*#__PURE__*/a(o,{className:"grid w-full grid-cols-7 place-content-center place-items-center gap-1",children:H.map(((e,t)=>{const r=!(!e||!w)&&h(e,z);return e?/*#__PURE__*/a(c,{date:e,selected:r,disabled:u(e,v,A),onChange:J},`${e.year}-${e.month}-${e.date}`):/*#__PURE__*/a(c,{disabled:!0},t)}))})]})]})};export{f as Calendar};
@@ -4,4 +4,4 @@ return e(l,{className:"flex h-[50px] w-full gap-4",children:[
4
4
  /*#__PURE__*/t(l,{"data-testid":"design-system-calendar--to-prev-month",className:"size-6 cursor-pointer text-gray-600",onClick:()=>{i?null==o||o({year:a.year-1,month:12,date:1}):null==o||o({year:a.year,month:a.month-1,date:1})},children:/*#__PURE__*/t(n,{name:"large_arrow_left",className:"text-gray-600"})}),
5
5
  /*#__PURE__*/e(r,{variant:"subTitle18",className:"h-fit w-full text-center leading-normal text-gray-900",children:[c," ",d]}),
6
6
  /*#__PURE__*/t(l,{"data-testid":"design-system-calendar--to-next-month",className:"size-6 cursor-pointer text-gray-600",onClick:()=>{m?null==o||o({year:a.year+1,month:1,date:1}):null==o||o({year:a.year,month:a.month+1,date:1})},children:/*#__PURE__*/t(n,{name:"large_arrow_right",className:"text-gray-600"})})]})},c=({selected:e,date:n,disabled:s=!1,onChange:o})=>{const i=1===String(null==n?void 0:n.date).length?`0${null==n?void 0:n.date}`:null==n?void 0:n.date;/*#__PURE__*/
7
- return t(l,{className:a("bg-w-white flex size-[42px] cursor-pointer items-center justify-center rounded-full p-2 text-center",!s&&!e&&"hover:bg-primary-10",e&&"bg-primary",s&&"cursor-default"),onClick:()=>{s||null==o||o(n)},children:/*#__PURE__*/t(r,{variant:"body16",className:a("text-w-gray-900",e&&"text-w-white",s&&"text-w-gray-300"),children:i})})};export{c as DateBox,m as SelectHeader,i as WeekHeader};
7
+ return t(l,{className:a("bg-w-white flex aspect-square w-full max-w-[42px] cursor-pointer items-center justify-center rounded-full p-2 text-center",!s&&!e&&"hover:bg-primary-10",e&&"bg-primary",s&&"cursor-default"),onClick:()=>{s||null==o||o(n)},children:/*#__PURE__*/t(r,{variant:"body16",className:a("text-w-gray-900",e&&"text-w-white",s&&"text-w-gray-300"),children:i})})};export{c as DateBox,m as SelectHeader,i as WeekHeader};
@@ -7,6 +7,7 @@ export interface CalendarProps {
7
7
  isError?: boolean;
8
8
  className?: string;
9
9
  triggerClassName?: string;
10
+ dropdownClassName?: string;
10
11
  minDate?: string;
11
12
  maxDate?: string;
12
13
  }
@@ -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 n from"next/link";import{Avatar as o}from"../../DataDisplays/Avatar/Avatar.js";import{Typography as i}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 c}from"../../DataDisplays/NewBadge/NewBadge.js";import{Divider as m}from"../../DataDisplays/Divider/Divider.js";import"../../DataDisplays/Accordion/Accordion.js";function p(e,r,a){return r in e?Object.defineProperty(e,r,{value:a,enumerable:!0,configurable:!0,writable:!0}):e[r]=a,e}function f(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){p(e,r,a[r])}))}return e}function u(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 y(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}
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 b=({withUserInfo:n=!1,children:o,className:i})=>{const c=a.map(o,((a,l)=>0===l?a:/*#__PURE__*/r(t,{children:[
35
- /*#__PURE__*/e(m,{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:c})},d=({imgUrl:a,username:t,email:l,children:n,onClick:c})=>/*#__PURE__*/r(s,{role:"region","aria-label":"사용자 정보",className:"flex w-full flex-col gap-4 px-6 pb-4",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(o,{size:"lg",isEditable:!0,imgUrl:a,onClick:c}),
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(i,{variant:"body16",className:"text-w-gray-900 font-medium",children:t}),
41
- /*#__PURE__*/e(i,{variant:"body14",className:"text-w-gray-600 line-clamp-2 break-all",children:l})]})]}),n&&/*#__PURE__*/e(s,{className:"flex w-full flex-col gap-2",children:n})]}),h=({children:r})=>/*#__PURE__*/e(s,{as:"ul","aria-label":"메인 메뉴",className:"text-w-gray-900 w-full",children:r}),g=({children:r})=>/*#__PURE__*/e(s,{as:"ul","aria-label":"서브 메뉴",className:"text-w-gray-600 w-full",children:r}),w=a=>{var{hasNew:t=!1,children:o,className:m,textClassName:p,href:b}=a,d=y(a,["hasNew","children","className","textClassName","href"]);/*#__PURE__*/
42
- return e(s,{as:"li",children:/*#__PURE__*/r(n,u(f({href:b,className:l("hover:bg-primary-10 flex w-full cursor-pointer gap-1 px-6 py-2",m)},d),{children:[
43
- /*#__PURE__*/e(i,{variant:"body14",className:p,children:o}),t&&/*#__PURE__*/e(c,{className:"mt-1","aria-label":"새 소식"})]}))})};
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{w as Item,h as List,b as Root,g as SubList,d as UserInfo};
67
+ */export{g as Item,d as List,y as Root,h as SubList,b as UserInfo};
@@ -1,5 +1,4 @@
1
- import Link from 'next/link';
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<typeof Link> {
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 +1,3 @@
1
- export * from './Menu';
1
+ export * from './MenuLink';
2
+ export * from './MenuButton';
3
+ export type { MenuLinkProps, MenuButtonProps } from './Menu.types';