@wishket/design-system 1.11.2 → 1.12.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.
@@ -19,7 +19,7 @@ const AvatarContent = ({ size = 'md', name, imgUrl, isMonogram, avatarStyle })=>
19
19
  alt: "",
20
20
  width: 80,
21
21
  height: 80,
22
- className: "h-full w-full",
22
+ className: "h-full w-full object-cover",
23
23
  "data-testid": "design-system-avatar--image",
24
24
  onError: handleImageError
25
25
  });
@@ -45,11 +45,15 @@ const MultiColumnListContext = react.createContext({
45
45
  ]);
46
46
  const [activeCategory, setActiveCategory] = react.useState(currentActiveCategory);
47
47
  const handleCategoryClick = (index)=>{
48
+ if (index === activeCategory) return; // 현재 카테고리를 다시 클릭하는 경우 방지
48
49
  onCategoryChange === null || onCategoryChange === void 0 ? void 0 : onCategoryChange(index);
49
50
  setActiveCategory(index);
50
51
  };
51
52
  react.useEffect(()=>{
52
- setActiveCategory(currentActiveCategory);
53
+ if (currentActiveCategory !== activeCategory) {
54
+ setActiveCategory(currentActiveCategory);
55
+ }
56
+ // eslint-disable-next-line react-hooks/exhaustive-deps
53
57
  }, [
54
58
  currentActiveCategory
55
59
  ]);
@@ -15,8 +15,8 @@
15
15
  * return (
16
16
  * <BoxTab.Root>
17
17
  * <BoxTab.Tabs>
18
- * <BoxTab.Tab index={0}>첫 번째 탭</BoxTab.Tab>
19
- * <BoxTab.Tab index={1}>두 번째 탭</BoxTab.Tab>
18
+ * <BoxTab.Tab index={0} href="/">첫 번째 탭</BoxTab.Tab>
19
+ * <BoxTab.Tab index={1} href="/">두 번째 탭</BoxTab.Tab>
20
20
  * </BoxTab.Tabs>
21
21
  * <BoxTab.Panel index={0}>첫 번째 패널 내용</BoxTab.Panel>
22
22
  * <BoxTab.Panel index={1}>두 번째 패널 내용</BoxTab.Panel>
@@ -28,7 +28,7 @@
28
28
  declare const BoxTab: {
29
29
  Root: ({ children, currentActiveTab, onChange, }: import("./BoxTab.types").BoxTabRootProps) => import("react/jsx-runtime").JSX.Element;
30
30
  Tabs: ({ children, className }: import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLDivElement>, HTMLDivElement>) => import("react/jsx-runtime").JSX.Element;
31
- Tab: ({ children, index, className, isFirst, isLast, disabled, }: import("./BoxTab.types").BoxTabProps) => import("react/jsx-runtime").JSX.Element;
31
+ Tab: ({ children, index, className, isFirst, isLast, disabled, href, }: import("./BoxTab.types").BoxTabProps) => import("react/jsx-runtime").JSX.Element;
32
32
  Panel: ({ children, index }: import("./BoxTab.types").BoxTabPanelProps) => false | import("react/jsx-runtime").JSX.Element;
33
33
  };
34
34
  export default BoxTab;
@@ -19,8 +19,8 @@ var BoxTab_parts = require('./BoxTab.parts.js');
19
19
  * return (
20
20
  * <BoxTab.Root>
21
21
  * <BoxTab.Tabs>
22
- * <BoxTab.Tab index={0}>첫 번째 탭</BoxTab.Tab>
23
- * <BoxTab.Tab index={1}>두 번째 탭</BoxTab.Tab>
22
+ * <BoxTab.Tab index={0} href="/">첫 번째 탭</BoxTab.Tab>
23
+ * <BoxTab.Tab index={1} href="/">두 번째 탭</BoxTab.Tab>
24
24
  * </BoxTab.Tabs>
25
25
  * <BoxTab.Panel index={0}>첫 번째 패널 내용</BoxTab.Panel>
26
26
  * <BoxTab.Panel index={1}>두 번째 패널 내용</BoxTab.Panel>
@@ -56,7 +56,7 @@ declare const Tabs: ({ children, className }: ComponentProps<'div'>) => import("
56
56
  * </BoxTab.Tab>
57
57
  * ```
58
58
  */
59
- declare const Tab: ({ children, index, className, isFirst, isLast, disabled, }: BoxTabProps) => import("react/jsx-runtime").JSX.Element;
59
+ declare const Tab: ({ children, index, className, isFirst, isLast, disabled, href, }: BoxTabProps) => import("react/jsx-runtime").JSX.Element;
60
60
  /**
61
61
  * 탭 패널 컴포넌트입니다.
62
62
  * 해당 인덱스의 탭이 선택되었을 때만 내용을 표시합니다.
@@ -4,6 +4,7 @@
4
4
  var jsxRuntime = require('react/jsx-runtime');
5
5
  var react = require('react');
6
6
  var tailwindMerge = require('tailwind-merge');
7
+ var Link = require('next/link');
7
8
  var Typography = require('../../Base/Typography/Typography.js');
8
9
  var Box = require('../../Base/Layouts/Box/Box.js');
9
10
  require('../../Base/Layouts/FullBleed/FullBleed.js');
@@ -36,11 +37,15 @@ const BoxTabContext = react.createContext({
36
37
  */ const Root = ({ children, currentActiveTab = 0, onChange })=>{
37
38
  const [activeTab, setActiveTab] = react.useState(currentActiveTab);
38
39
  const handleTabClick = (index)=>{
40
+ if (index === activeTab) return;
39
41
  onChange === null || onChange === void 0 ? void 0 : onChange(index);
40
42
  setActiveTab(index);
41
43
  };
42
44
  react.useEffect(()=>{
43
- setActiveTab(currentActiveTab);
45
+ if (currentActiveTab !== activeTab) {
46
+ setActiveTab(currentActiveTab);
47
+ }
48
+ // eslint-disable-next-line react-hooks/exhaustive-deps
44
49
  }, [
45
50
  currentActiveTab
46
51
  ]);
@@ -105,25 +110,35 @@ const BoxTabContext = react.createContext({
105
110
  * 탭 1
106
111
  * </BoxTab.Tab>
107
112
  * ```
108
- */ const Tab = ({ children, index, className, isFirst, isLast, disabled = false })=>{
113
+ */ const Tab = ({ children, index, className, isFirst, isLast, disabled = false, href })=>{
109
114
  const { handleTabClick, activeTab } = react.useContext(BoxTabContext);
110
115
  const isSelected = index === activeTab;
111
- return jsxRuntime.jsx(Box, {
116
+ const sharedProps = {
112
117
  tabIndex: 0,
113
- "data-testid": `design-system-boxTab--item-${index}`,
118
+ 'data-testid': `design-system-boxTab--item-${index}`,
114
119
  className: tailwindMerge.twMerge('group flex w-full cursor-pointer items-center border border-w-gray-200 bg-w-white px-6 py-4 hover:bg-w-gray-10', 'relative', '-ml-px', isSelected && 'z-[1] border-primary bg-primary-10 hover:bg-primary-10', isFirst && 'ml-0 rounded-l-xl', isLast && 'rounded-r-xl', className),
115
120
  onClick: ()=>{
116
121
  !disabled && handleTabClick(index);
117
- },
118
- children: jsxRuntime.jsx(Box, {
119
- className: tailwindMerge.twMerge('relative flex w-full flex-row items-center justify-center font-normal text-w-gray-600', isSelected && 'font-medium text-primary'),
120
- children: jsxRuntime.jsx(Typography, {
121
- variant: "body1",
122
- className: "relative",
123
- children: children
124
- })
122
+ }
123
+ };
124
+ const content = jsxRuntime.jsx(Box, {
125
+ className: tailwindMerge.twMerge('relative flex w-full flex-row items-center justify-center font-normal text-w-gray-600', isSelected && 'font-medium text-primary'),
126
+ children: jsxRuntime.jsx(Typography, {
127
+ variant: "body1",
128
+ className: "relative",
129
+ children: children
125
130
  })
126
131
  });
132
+ if (href) {
133
+ return jsxRuntime.jsx(Link, Object.assign({
134
+ href: href
135
+ }, sharedProps, {
136
+ children: content
137
+ }));
138
+ }
139
+ return jsxRuntime.jsx(Box, Object.assign({}, sharedProps, {
140
+ children: content
141
+ }));
127
142
  };
128
143
  /**
129
144
  * 탭 패널 컴포넌트입니다.
@@ -9,6 +9,7 @@ export interface BoxTabProps extends PropsWithChildren {
9
9
  className?: string;
10
10
  isFirst?: boolean;
11
11
  isLast?: boolean;
12
+ href?: string;
12
13
  }
13
14
  export interface BoxTabPanelProps extends PropsWithChildren {
14
15
  index: number;
@@ -15,7 +15,7 @@
15
15
  * <TextTab.Root defaultValue="tab1">
16
16
  * <TextTab.Tabs>
17
17
  * <TextTab.Tab value="tab1">첫 번째 탭</TextTab.Tab>
18
- * <TextTab.Tab value="tab2">두 번째 탭</TextTab.Tab>
18
+ * <TextTab.Tab value="tab2" href="/">두 번째 탭</TextTab.Tab>
19
19
  * </TextTab.Tabs>
20
20
  *
21
21
  * <TextTab.Panel value="tab1">
@@ -31,7 +31,7 @@
31
31
  declare const TextTab: {
32
32
  Root: ({ children, currentActiveTab, isFit, hasPadding, isFull, barColor, onChange, }: import("./TextTab.types").TextTabRootProps) => import("react/jsx-runtime").JSX.Element;
33
33
  Tabs: ({ children, className }: import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLDivElement>, HTMLDivElement>) => import("react/jsx-runtime").JSX.Element;
34
- Tab: ({ children, index, className, disabled, badgeNumber, iconName, }: import("./TextTab.types").TextTabProps) => import("react/jsx-runtime").JSX.Element;
34
+ Tab: ({ children, index, className, disabled, badgeNumber, iconName, href, }: import("./TextTab.types").TextTabProps) => import("react/jsx-runtime").JSX.Element;
35
35
  Panel: ({ children, index }: import("./TextTab.types").TextTabPanelProps) => false | import("react/jsx-runtime").JSX.Element;
36
36
  };
37
37
  export default TextTab;
@@ -19,7 +19,7 @@ var TextTab_parts = require('./TextTab.parts.js');
19
19
  * <TextTab.Root defaultValue="tab1">
20
20
  * <TextTab.Tabs>
21
21
  * <TextTab.Tab value="tab1">첫 번째 탭</TextTab.Tab>
22
- * <TextTab.Tab value="tab2">두 번째 탭</TextTab.Tab>
22
+ * <TextTab.Tab value="tab2" href="/">두 번째 탭</TextTab.Tab>
23
23
  * </TextTab.Tabs>
24
24
  *
25
25
  * <TextTab.Panel value="tab1">
@@ -59,7 +59,7 @@ declare const Tabs: ({ children, className }: ComponentProps<'div'>) => import("
59
59
  * 홈
60
60
  * </TextTab.Tab>
61
61
  */
62
- declare const Tab: ({ children, index, className, disabled, badgeNumber, iconName, }: TextTabProps) => import("react/jsx-runtime").JSX.Element;
62
+ declare const Tab: ({ children, index, className, disabled, badgeNumber, iconName, href, }: TextTabProps) => import("react/jsx-runtime").JSX.Element;
63
63
  /**
64
64
  * 탭 패널 컴포넌트입니다.
65
65
  * 해당 탭이 선택되었을 때만 내용을 표시합니다.
@@ -4,6 +4,7 @@
4
4
  var jsxRuntime = require('react/jsx-runtime');
5
5
  var react = require('react');
6
6
  var tailwindMerge = require('tailwind-merge');
7
+ var Link = require('next/link');
7
8
  var Typography = require('../../Base/Typography/Typography.js');
8
9
  var Box = require('../../Base/Layouts/Box/Box.js');
9
10
  require('../../Base/Layouts/FullBleed/FullBleed.js');
@@ -46,11 +47,15 @@ const TextTabContext = react.createContext({
46
47
  */ const Root = ({ children, currentActiveTab = 0, isFit = true, hasPadding = false, isFull = false, barColor = 'bg-w-gray-900', onChange })=>{
47
48
  const [activeTab, setActiveTab] = react.useState(currentActiveTab);
48
49
  const handleTabClick = (index)=>{
50
+ if (index === activeTab) return;
49
51
  onChange === null || onChange === void 0 ? void 0 : onChange(index);
50
52
  setActiveTab(index);
51
53
  };
52
54
  react.useEffect(()=>{
53
- setActiveTab(currentActiveTab);
55
+ if (currentActiveTab !== activeTab) {
56
+ setActiveTab(currentActiveTab);
57
+ }
58
+ // eslint-disable-next-line react-hooks/exhaustive-deps
54
59
  }, [
55
60
  currentActiveTab
56
61
  ]);
@@ -83,41 +88,57 @@ const TextTabContext = react.createContext({
83
88
  const { activeTab, hasPadding, isFull, barColor } = react.useContext(TextTabContext);
84
89
  const tabRefs = react.useRef([]);
85
90
  const underbarRef = react.useRef(null);
91
+ const containerRef = react.useRef(null);
86
92
  const childrenArray = react.Children.toArray(children);
93
+ const [isScrolling, setIsScrolling] = react.useState(false);
94
+ // 터치 이벤트 핸들러
95
+ const handleTouchStart = ()=>{
96
+ setIsScrolling(true);
97
+ };
98
+ const handleTouchEnd = ()=>{
99
+ setIsScrolling(false);
100
+ };
87
101
  react.useEffect(()=>{
88
- if (!tabRefs.current[activeTab] || !underbarRef.current) return;
102
+ if (!tabRefs.current[activeTab] || !underbarRef.current || isScrolling) return;
89
103
  const activeTabElement = tabRefs.current[activeTab];
90
104
  const underbarElement = underbarRef.current;
91
- const containerElement = underbarElement.parentElement;
105
+ const containerElement = containerRef.current;
92
106
  if (activeTabElement && underbarElement && containerElement) {
93
107
  const { offsetLeft, offsetWidth } = activeTabElement;
94
108
  const { scrollLeft, clientWidth } = containerElement;
95
109
  underbarElement.style.width = `${offsetWidth}px`;
96
110
  underbarElement.style.transform = `translateX(${offsetLeft}px)`;
97
- requestAnimationFrame(()=>{
98
- const isVisible = scrollLeft <= offsetLeft && scrollLeft + clientWidth >= offsetLeft + offsetWidth;
99
- if (!isVisible) {
111
+ const isVisible = scrollLeft <= offsetLeft && scrollLeft + clientWidth >= offsetLeft + offsetWidth;
112
+ if (!isVisible) {
113
+ const scrollTimeout = setTimeout(()=>{
100
114
  activeTabElement.scrollIntoView({
101
115
  behavior: 'smooth',
102
116
  block: 'nearest',
103
117
  inline: 'center'
104
118
  });
105
- }
106
- });
119
+ }, 0);
120
+ return ()=>clearTimeout(scrollTimeout);
121
+ }
107
122
  }
108
123
  }, [
109
- activeTab
124
+ activeTab,
125
+ isScrolling
110
126
  ]);
111
127
  return jsxRuntime.jsxs(Box, {
112
- className: tailwindMerge.twJoin('relative block w-full items-start overflow-x-scroll border-b border-w-gray-200 scrollbar-hide', hasPadding && 'px-9', className),
128
+ ref: containerRef,
129
+ className: tailwindMerge.twJoin('relative block w-full touch-pan-x items-start overflow-x-scroll border-b border-w-gray-200 scrollbar-hide', hasPadding && 'px-9', className),
113
130
  "data-testid": "design-system-textTab--container",
131
+ onTouchStart: handleTouchStart,
132
+ onTouchEnd: handleTouchEnd,
114
133
  children: [
115
134
  jsxRuntime.jsx(Box, {
116
135
  className: tailwindMerge.twMerge('inline-flex w-max gap-10', isFull && 'w-full gap-0'),
117
136
  children: childrenArray.map((child, index)=>{
118
137
  return jsxRuntime.jsx(Box, {
119
138
  className: tailwindMerge.twMerge(isFull && 'w-full'),
120
- ref: (el)=>tabRefs.current[index] = el,
139
+ ref: (el)=>{
140
+ tabRefs.current[index] = el;
141
+ },
121
142
  children: child
122
143
  }, index);
123
144
  })
@@ -150,40 +171,50 @@ const TextTabContext = react.createContext({
150
171
  * >
151
172
  * 홈
152
173
  * </TextTab.Tab>
153
- */ const Tab = ({ children, index, className, disabled = false, badgeNumber, iconName })=>{
174
+ */ const Tab = ({ children, index, className, disabled = false, badgeNumber, iconName, href })=>{
154
175
  const { isFit, handleTabClick, activeTab, isFull } = react.useContext(TextTabContext);
155
176
  const isSelected = index === activeTab;
156
- return jsxRuntime.jsx(Box, {
177
+ const sharedProps = {
157
178
  tabIndex: 0,
158
- "data-testid": `design-system-textTab--item-${index}`,
179
+ 'data-testid': `design-system-textTab--item-${index}`,
159
180
  className: tailwindMerge.twMerge('group flex w-max cursor-pointer items-center rounded-xl hover:font-medium', disabled && 'cursor-not-allowed', isFull && `w-full`, className),
160
181
  onClick: ()=>{
161
182
  !disabled && handleTabClick(index);
162
- },
163
- children: jsxRuntime.jsxs(Box, {
164
- className: tailwindMerge.twMerge('relative flex w-full flex-row items-center justify-center gap-2 py-4 font-normal text-w-gray-600 group-hover:text-w-gray-900', !isFit && 'px-[33.5px]', isFull && 'w-full px-0', isSelected && 'font-medium text-w-gray-900'),
165
- children: [
166
- iconName && jsxRuntime.jsx(Box, {
167
- className: "flex size-4 flex-shrink-0 items-center justify-center",
168
- children: jsxRuntime.jsx(SystemIcon, {
169
- name: iconName,
170
- className: tailwindMerge.twMerge('size-4 text-w-gray-600 group-hover:text-w-gray-900', isSelected && 'text-w-gray-900')
171
- })
172
- }),
173
- jsxRuntime.jsx(Typography, {
174
- variant: "body1",
175
- className: "relative",
176
- children: children
177
- }),
178
- badgeNumber !== undefined && jsxRuntime.jsx(CountBadge, {
179
- text: badgeNumber,
180
- showZero: true,
181
- className: "relative",
182
- variant: "gray"
183
+ }
184
+ };
185
+ const content = jsxRuntime.jsxs(Box, {
186
+ className: tailwindMerge.twMerge('relative flex w-full flex-row items-center justify-center gap-2 py-4 font-normal text-w-gray-600 group-hover:text-w-gray-900', !isFit && 'px-[33.5px]', isFull && 'w-full px-0', isSelected && 'font-medium text-w-gray-900'),
187
+ children: [
188
+ iconName && jsxRuntime.jsx(Box, {
189
+ className: "flex size-4 flex-shrink-0 items-center justify-center",
190
+ children: jsxRuntime.jsx(SystemIcon, {
191
+ name: iconName,
192
+ className: tailwindMerge.twMerge('size-4 text-w-gray-600 group-hover:text-w-gray-900', isSelected && 'text-w-gray-900')
183
193
  })
184
- ]
185
- })
194
+ }),
195
+ jsxRuntime.jsx(Typography, {
196
+ variant: "body1",
197
+ className: "relative",
198
+ children: children
199
+ }),
200
+ badgeNumber !== undefined && jsxRuntime.jsx(CountBadge, {
201
+ text: badgeNumber,
202
+ showZero: true,
203
+ className: "relative",
204
+ variant: "gray"
205
+ })
206
+ ]
186
207
  });
208
+ if (href) {
209
+ return jsxRuntime.jsx(Link, Object.assign({
210
+ href: href
211
+ }, sharedProps, {
212
+ children: content
213
+ }));
214
+ }
215
+ return jsxRuntime.jsx(Box, Object.assign({}, sharedProps, {
216
+ children: content
217
+ }));
187
218
  };
188
219
  /**
189
220
  * 탭 패널 컴포넌트입니다.
@@ -14,6 +14,7 @@ export interface TextTabProps extends PropsWithChildren {
14
14
  className?: string;
15
15
  badgeNumber?: string | number;
16
16
  iconName?: SystemIconName;
17
+ href?: string;
17
18
  }
18
19
  export interface TextTabPanelProps extends PropsWithChildren {
19
20
  index: number;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wishket/design-system",
3
- "version": "1.11.2",
3
+ "version": "1.12.0",
4
4
  "description": "Wishket Design System",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",