myshell-react-lib 0.1.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 (151) hide show
  1. package/README.md +268 -0
  2. package/dist/assets/audio-playing.json +3657 -0
  3. package/dist/index.cjs +9654 -0
  4. package/dist/index.cjs.map +1 -0
  5. package/dist/index.d.cts +1431 -0
  6. package/dist/index.d.ts +1431 -0
  7. package/dist/index.js +8788 -0
  8. package/dist/index.js.map +1 -0
  9. package/package.json +140 -0
  10. package/src/common/assets/audio-playing.json +3657 -0
  11. package/src/common/constants/constants.ts +24 -0
  12. package/src/common/constants/types/common.ts +10 -0
  13. package/src/common/hooks/useAudioPlayer.tsx +198 -0
  14. package/src/common/hooks/useDevice.ts +26 -0
  15. package/src/common/hooks/useNativeBridge.ts +42 -0
  16. package/src/common/hooks/useNotification.tsx +179 -0
  17. package/src/common/hooks/useWindowWidth.ts +19 -0
  18. package/src/common/utils/common-helper.ts +81 -0
  19. package/src/components/ItemDemo.tsx +15 -0
  20. package/src/components/accordion.tsx +126 -0
  21. package/src/components/alert-dialog.tsx +148 -0
  22. package/src/components/alert.tsx +65 -0
  23. package/src/components/aspect-ratio.tsx +7 -0
  24. package/src/components/audio-player.tsx +58 -0
  25. package/src/components/avatar.tsx +133 -0
  26. package/src/components/badge.tsx +65 -0
  27. package/src/components/button/button.styles.ts +258 -0
  28. package/src/components/button/button.tsx +215 -0
  29. package/src/components/button/icon-button.styles.ts +101 -0
  30. package/src/components/button/icon-button.tsx +100 -0
  31. package/src/components/button/index.tsx +3 -0
  32. package/src/components/button/link-button.tsx +184 -0
  33. package/src/components/cascader.tsx +175 -0
  34. package/src/components/checkbox.tsx +135 -0
  35. package/src/components/command.tsx +155 -0
  36. package/src/components/context-menu.tsx +198 -0
  37. package/src/components/count-down.tsx +83 -0
  38. package/src/components/custom-notification.tsx +95 -0
  39. package/src/components/dialog.tsx +158 -0
  40. package/src/components/drawer.tsx +116 -0
  41. package/src/components/dropdown-menu.tsx +196 -0
  42. package/src/components/energy-progress.tsx +55 -0
  43. package/src/components/form.tsx +201 -0
  44. package/src/components/group.tsx +9 -0
  45. package/src/components/guide.tsx +243 -0
  46. package/src/components/icon.tsx +89 -0
  47. package/src/components/icons/outline/DownIcon.tsx +18 -0
  48. package/src/components/icons/outline/FilterIcon.tsx +21 -0
  49. package/src/components/icons/outline/arrow-left.tsx +16 -0
  50. package/src/components/icons/outline/arrow-up-tray.tsx +16 -0
  51. package/src/components/icons/outline/check-circle.tsx +17 -0
  52. package/src/components/icons/outline/config.tsx +42 -0
  53. package/src/components/icons/outline/pencil-square.tsx +16 -0
  54. package/src/components/icons/outline/trash.tsx +17 -0
  55. package/src/components/icons/outline/window.tsx +16 -0
  56. package/src/components/icons/outline/x-circle.tsx +17 -0
  57. package/src/components/icons/outline/x-mark.tsx +16 -0
  58. package/src/components/icons/solid/audio-playing.tsx +31 -0
  59. package/src/components/icons/solid/caret-down.tsx +14 -0
  60. package/src/components/icons/solid/code.tsx +18 -0
  61. package/src/components/icons/solid/drag.tsx +14 -0
  62. package/src/components/icons/solid/phone.tsx +23 -0
  63. package/src/components/icons/solid/rectangle-group.tsx +14 -0
  64. package/src/components/image.tsx +151 -0
  65. package/src/components/input.tsx +118 -0
  66. package/src/components/label.tsx +26 -0
  67. package/src/components/link.tsx +123 -0
  68. package/src/components/marquee/index.css +15 -0
  69. package/src/components/marquee/marquee.tsx +220 -0
  70. package/src/components/masonry.tsx +138 -0
  71. package/src/components/menubar.tsx +234 -0
  72. package/src/components/mobile/m-tooltip.tsx +34 -0
  73. package/src/components/modal.tsx +561 -0
  74. package/src/components/navigation-bar.tsx +100 -0
  75. package/src/components/number-input.tsx +143 -0
  76. package/src/components/page-content.tsx +16 -0
  77. package/src/components/popover.tsx +191 -0
  78. package/src/components/progress.tsx +80 -0
  79. package/src/components/radio-group.tsx +44 -0
  80. package/src/components/scroll-area.tsx +49 -0
  81. package/src/components/search-bar.tsx +140 -0
  82. package/src/components/secondary-navigation-bar.tsx +307 -0
  83. package/src/components/select.tsx +273 -0
  84. package/src/components/separator.tsx +31 -0
  85. package/src/components/sheet.tsx +143 -0
  86. package/src/components/skeleton.tsx +20 -0
  87. package/src/components/slider.tsx +160 -0
  88. package/src/components/spinner.tsx +48 -0
  89. package/src/components/swiper/index.module.scss +88 -0
  90. package/src/components/swiper/index.tsx +319 -0
  91. package/src/components/switch.tsx +67 -0
  92. package/src/components/tabs.tsx +325 -0
  93. package/src/components/textarea.tsx +71 -0
  94. package/src/components/toast/toast.tsx +182 -0
  95. package/src/components/toast/toaster.tsx +160 -0
  96. package/src/components/toast/use-toast.tsx +248 -0
  97. package/src/components/toggle-group.tsx +64 -0
  98. package/src/components/toggle.tsx +46 -0
  99. package/src/components/tooltip.tsx +283 -0
  100. package/src/components/typography.tsx +437 -0
  101. package/src/index.ts +66 -0
  102. package/src/lib/utils.ts +62 -0
  103. package/src/stories/Accordion.stories.tsx +64 -0
  104. package/src/stories/AccordionItem.stories.tsx +48 -0
  105. package/src/stories/Avatar.stories.ts +58 -0
  106. package/src/stories/Badge.stories.tsx +40 -0
  107. package/src/stories/BannerSwiper.stories.tsx +102 -0
  108. package/src/stories/Button.stories.tsx +543 -0
  109. package/src/stories/Checkbox.stories.tsx +161 -0
  110. package/src/stories/Configure.mdx +341 -0
  111. package/src/stories/CssProperties.mdx +30 -0
  112. package/src/stories/Description.stories.ts +70 -0
  113. package/src/stories/Display.stories.ts +64 -0
  114. package/src/stories/FeaturedSwiper.stories.tsx +6978 -0
  115. package/src/stories/GridSwiper.stories.tsx +1407 -0
  116. package/src/stories/Guide.stories.tsx +247 -0
  117. package/src/stories/Heading.stories.ts +89 -0
  118. package/src/stories/Icon.stories.ts +77 -0
  119. package/src/stories/IconButton.stories.tsx +301 -0
  120. package/src/stories/IconTextButton.stories.ts +59 -0
  121. package/src/stories/Image.stories.ts +55 -0
  122. package/src/stories/Input.stories.tsx +203 -0
  123. package/src/stories/Modal.stories.tsx +144 -0
  124. package/src/stories/NavigationBar.stories.tsx +81 -0
  125. package/src/stories/Notification.stories.tsx +276 -0
  126. package/src/stories/Popover.stories.tsx +100 -0
  127. package/src/stories/SearchBar.stories.ts +43 -0
  128. package/src/stories/SecondaryNavigationBar.stories.tsx +199 -0
  129. package/src/stories/Select.stories.tsx +107 -0
  130. package/src/stories/Separator.stories.tsx +49 -0
  131. package/src/stories/Spinner.stories.tsx +48 -0
  132. package/src/stories/SubHeading.stories.ts +64 -0
  133. package/src/stories/Swich.stories.tsx +69 -0
  134. package/src/stories/Tabs.stories.tsx +90 -0
  135. package/src/stories/Text.stories.ts +78 -0
  136. package/src/stories/Textarea.stories.tsx +155 -0
  137. package/src/stories/Toast.stories.tsx +424 -0
  138. package/src/stories/Tooltip.stories.tsx +244 -0
  139. package/src/stories/ViewAutoSwiper.stories.tsx +1408 -0
  140. package/src/styles/components-dark.scss +212 -0
  141. package/src/styles/components-light.scss +210 -0
  142. package/src/styles/design-dark.scss +330 -0
  143. package/src/styles/design-light.scss +345 -0
  144. package/src/styles/design2-dark.scss +319 -0
  145. package/src/styles/design2-light.scss +364 -0
  146. package/src/styles/font.css +19 -0
  147. package/src/styles/global.scss +251 -0
  148. package/src/styles/md-viewer.scss +155 -0
  149. package/src/styles/new-tokens.scss +255 -0
  150. package/src/styles/tokens.scss +401 -0
  151. package/src/types/scss.d.ts +24 -0
@@ -0,0 +1,319 @@
1
+ /* eslint-disable import/order */
2
+
3
+ 'use client';
4
+
5
+ import { cva } from 'class-variance-authority';
6
+ import { useEffect, useMemo, useRef, useState } from 'react';
7
+ import type { Swiper as SwiperType } from 'swiper';
8
+ import { useRouter } from 'next/navigation';
9
+ import {
10
+ Autoplay,
11
+ Navigation,
12
+ FreeMode,
13
+ Scrollbar,
14
+ Mousewheel,
15
+ Grid,
16
+ } from 'swiper/modules';
17
+ import { Swiper as SwiperComponent, SwiperSlide } from 'swiper/react';
18
+ import type { AutoplayOptions } from 'swiper/types';
19
+
20
+ /* eslint-disable import/no-unassigned-import */
21
+ import 'swiper/css';
22
+ // import 'swiper/swiper-bundle.css';
23
+ import 'swiper/css/navigation';
24
+ import 'swiper/css/free-mode';
25
+ import 'swiper/css/grid';
26
+ import 'swiper/css/pagination';
27
+ // import 'swiper/css/scrollbar';
28
+
29
+ import { cn } from '@/lib/utils';
30
+ import styles from './index.module.scss';
31
+
32
+ const swiperVariants = cva('', {
33
+ variants: {
34
+ rounded: {
35
+ none: 'rounded-none',
36
+ sm: 'rounded-sm',
37
+ default: 'rounded',
38
+ md: 'rounded-md',
39
+ lg: 'rounded-lg',
40
+ xl: 'rounded-xl',
41
+ '2xl': 'rounded-2xl',
42
+ '3xl': 'rounded-3xl',
43
+ full: 'rounded-full',
44
+ },
45
+ },
46
+ defaultVariants: {
47
+ rounded: 'none',
48
+ },
49
+ });
50
+
51
+ export interface SwiperProps {
52
+ /**
53
+ * 轮播间隔延迟毫秒
54
+ */
55
+ delay?: number;
56
+ /**
57
+ * 是否自动播放
58
+ */
59
+ autoplay?: boolean;
60
+ /**
61
+ * 是否循环
62
+ */
63
+ loop?: boolean;
64
+ /**
65
+ * 一屏显示slider数量,number|'auto'
66
+ */
67
+ slidesPerView?: number | 'auto';
68
+ /**
69
+ * 轮播数据
70
+ */
71
+ dataList: any;
72
+ /**
73
+ * 轮播类型 ,目前是特殊的'banner' | 'featured',其他自由配置
74
+ */
75
+ swiperType: 'default' | 'banner' | 'featured' | 'grid';
76
+ /**
77
+ * 行数,仅在swiperType为'grid'时生效
78
+ */
79
+ gridRows?: number;
80
+ /**
81
+ * 轮播子组件
82
+ */
83
+ component?: React.ElementType;
84
+ /**
85
+ * Swiperbox样式覆盖
86
+ */
87
+ className?: string;
88
+ /**
89
+ * Swiper item样式覆盖
90
+ */
91
+ componentClassName?: string;
92
+ /**
93
+ * Swiper Slide item样式覆盖
94
+ */
95
+ slideClassName?: string;
96
+ // todo del
97
+ rounded?:
98
+ | 'none'
99
+ | 'sm'
100
+ | 'default'
101
+ | 'md'
102
+ | 'lg'
103
+ | 'xl'
104
+ | '2xl'
105
+ | '3xl'
106
+ | 'full';
107
+ /**
108
+ * 从哪里来,点击跳转时带在url上
109
+ */
110
+ from?: string;
111
+
112
+ /**
113
+ * 是否居中显示
114
+ */
115
+ centeredSlides?: boolean;
116
+
117
+ // 间距
118
+ spaceBetween?: number;
119
+ /**
120
+ * 是否是移动端
121
+ */
122
+ isMobile?: boolean;
123
+ }
124
+
125
+ export function Swiper(props: SwiperProps) {
126
+ const {
127
+ className,
128
+ componentClassName,
129
+ slideClassName,
130
+ rounded = 'none',
131
+ delay = 4000,
132
+ loop = true,
133
+ slidesPerView = 1,
134
+ dataList = [],
135
+ swiperType = 'default',
136
+ from = '',
137
+ component,
138
+ autoplay,
139
+ spaceBetween = 12,
140
+ centeredSlides = true,
141
+ gridRows = 3,
142
+ isMobile = false,
143
+ } = props;
144
+ const isBanner = swiperType === 'banner';
145
+ const isFeatured = swiperType === 'featured';
146
+ const isGrid = swiperType === 'grid';
147
+ const router = useRouter();
148
+ const Com = component || 'div';
149
+ const swiperList = useMemo(() => {
150
+ const { length } = dataList;
151
+ if (length < 2 || isGrid || isFeatured) {
152
+ return dataList;
153
+ }
154
+
155
+ // 抽出最后一个元素放在开头,确保UI逻辑正确
156
+ const newList = dataList.slice(0);
157
+ const last = newList.pop()!;
158
+ newList.unshift(last);
159
+
160
+ return newList;
161
+ }, [dataList]);
162
+
163
+ const autoPlayOptions: AutoplayOptions = {
164
+ delay,
165
+ };
166
+
167
+ const swiperRef = useRef<SwiperType>();
168
+ const fgconfigs = {
169
+ 0: {
170
+ slidesPerView: 1.014,
171
+ spaceBetween: 12,
172
+ },
173
+ 590: {
174
+ slidesPerView: 2.1,
175
+ slidesPerGroup: 2,
176
+ },
177
+ 768: {
178
+ slidesPerView: 1,
179
+ },
180
+ 1024: {
181
+ slidesPerView: 2,
182
+ slidesPerGroup: 2,
183
+ },
184
+ 1280: {
185
+ slidesPerView: 3,
186
+ slidesPerGroup: 3,
187
+ },
188
+ 1536: {
189
+ slidesPerView: 4,
190
+ slidesPerGroup: 4,
191
+ },
192
+ };
193
+ const breakpoints = {
194
+ default: {},
195
+ banner: {
196
+ 769: {
197
+ spaceBetween: 20,
198
+ },
199
+ },
200
+ featured: fgconfigs,
201
+ grid: fgconfigs,
202
+ };
203
+ const swiperConfigs = {
204
+ spaceBetween: isFeatured ? 20 : isGrid ? 0 : spaceBetween,
205
+ autoplay:
206
+ swiperList?.length > 1 && (autoplay || isBanner)
207
+ ? autoPlayOptions
208
+ : false,
209
+ loop: !isFeatured && !isGrid && swiperList?.length > 1 ? loop : false,
210
+ slidesPerView: isBanner ? 'auto' : slidesPerView,
211
+ centeredSlides: isGrid || isFeatured ? false : centeredSlides,
212
+ initialSlide: isBanner ? 1 : 0,
213
+ navigation: !isGrid,
214
+ freeMode: (isFeatured || isGrid) && !isMobile,
215
+ scrollbar: (isFeatured || isGrid) && !isMobile,
216
+
217
+ ...((isFeatured || isGrid) && !isMobile
218
+ ? {
219
+ mousewheel: {
220
+ forceToAxis: true, // 仅水平滚动
221
+ },
222
+ }
223
+ : {}),
224
+ ...(isGrid ? { grid: { rows: gridRows } } : {}),
225
+ modules: [Autoplay, Navigation, FreeMode, Scrollbar, Mousewheel, Grid],
226
+ };
227
+
228
+ const [animate, setAnimate] = useState(false);
229
+ useEffect(() => {
230
+ // delay transition animation.
231
+ // because swiper.js add `.swiper-slide-prev`/`.swiper-slide-next` in a delay,
232
+ // which will introduced an unexpected animated when first load.
233
+ setTimeout(() => {
234
+ setAnimate(true);
235
+ }, 200);
236
+ }, []);
237
+
238
+ const handleSlideItemClick = (
239
+ e: React.MouseEvent<HTMLDivElement>,
240
+ gotoUrl: string,
241
+ index: number
242
+ ) => {
243
+ const swiper = swiperRef.current;
244
+ if (!swiper) {
245
+ return;
246
+ }
247
+ if (swiper.realIndex === index) {
248
+ const hasParam = gotoUrl.includes('?') ? '&' : '?';
249
+ // goToHandle?.(`${gotoUrl}${hasParam}from=Explore_Banner`);
250
+ const fromParam = from ? `from=${from}` : '';
251
+ const url = `${gotoUrl}${hasParam}${fromParam}`;
252
+ if (!(url.startsWith('https') || url.startsWith('http'))) {
253
+ router.push(url);
254
+ } else {
255
+ window.open(url);
256
+ }
257
+ } else if (e.target) {
258
+ const rect = (e.target as HTMLElement)?.getBoundingClientRect();
259
+ const screenWidth = window.innerWidth;
260
+ if (rect.left < screenWidth / 2 && rect.right < screenWidth / 2) {
261
+ // 元素离左边屏幕更近
262
+ swiper.slidePrev();
263
+ } else if (rect.left > screenWidth / 2 && rect.right > screenWidth / 2) {
264
+ // 元素离右边屏幕更近
265
+ swiper.slideNext();
266
+ }
267
+ }
268
+ };
269
+
270
+ return (
271
+ <div className={styles.swiperBox}>
272
+ <SwiperComponent
273
+ observer
274
+ observeParents
275
+ {...swiperConfigs}
276
+ breakpoints={breakpoints[swiperType]}
277
+ className={cn(
278
+ 'w-[100vw] md:w-full h-auto flex-shrink-0',
279
+ isBanner && 'banner-swiper h-[220px] md:h-auto',
280
+ isFeatured && 'feature-swiper !pl-4 !pr-3 md:!px-4',
281
+ isGrid && 'grid-swiper h-full !ml-auto !mr-auto !pl-4 !pr-3 md:!px-0',
282
+ animate && styles.animate,
283
+ className
284
+ )}
285
+ onInit={(swiper) => {
286
+ swiperRef.current = swiper;
287
+ }}
288
+ >
289
+ {swiperList.map((item: any, index: number) => (
290
+ <SwiperSlide
291
+ /* eslint-disable-next-line react/no-array-index-key */
292
+ key={index}
293
+ className={cn(
294
+ isBanner &&
295
+ 'banner-swiper-slide !w-[90%] md:!w-[98%] h-[220px] md:h-auto max-w-[1200px] aspect-[4/1] rounded-2xl opacity-30',
296
+ isFeatured && 'rounded-2xl !w-[100%-32px] md:!w-[100%-72px]',
297
+ isGrid &&
298
+ 'grid-swiper-slide !w-[100%-32px] md:!w-[100%-72px] !h-[100%/3] flex justify-center items-center rounded-2xl',
299
+ 'text-clip',
300
+ slideClassName
301
+ )}
302
+ >
303
+ <Com
304
+ item={item}
305
+ index={index}
306
+ onClick={handleSlideItemClick}
307
+ className={cn(
308
+ swiperVariants({ rounded }),
309
+ (isBanner || isFeatured) && 'rounded-2xl',
310
+ componentClassName
311
+ )}
312
+ isLine={gridRows && (index + 1) % gridRows !== 0}
313
+ />
314
+ </SwiperSlide>
315
+ ))}
316
+ </SwiperComponent>
317
+ </div>
318
+ );
319
+ }
@@ -0,0 +1,67 @@
1
+ 'use client';
2
+
3
+ import * as SwitchPrimitives from '@radix-ui/react-switch';
4
+ import { cva, type VariantProps } from 'class-variance-authority';
5
+ import * as React from 'react';
6
+
7
+ import { cn } from '@/lib/utils';
8
+
9
+ const switchSize = {
10
+ sm: 'w-7 h-4',
11
+ md: 'w-[34px] h-5',
12
+ lg: 'w-10 h-6',
13
+ } as const;
14
+
15
+ const switchRootVariants = cva(
16
+ 'peer inline-flex shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-cc-Focus-Rings-Brand-default focus-visible:ring-offset-2 data-[state=unchecked]:focus-visible:bg-cc-Switch-bg-default disabled:cursor-not-allowed disabled:opacity-30 data-[state="checked"]:bg-cc-Switch-bg-on data-[state="unchecked"]:bg-cc-Switch-bg-default',
17
+ {
18
+ variants: {
19
+ size: switchSize,
20
+ },
21
+ defaultVariants: {
22
+ size: 'lg',
23
+ },
24
+ }
25
+ );
26
+
27
+ const switchThumbVariants = cva(
28
+ 'pointer-events-none block rounded-full bg-cc-Switch-fg-on ring-0 transition-transform data-[state=unchecked]:translate-x-0',
29
+ {
30
+ variants: {
31
+ size: {
32
+ sm: 'w-3 h-3 data-[state=checked]:translate-x-3',
33
+ md: 'w-4 h-4 data-[state=checked]:translate-x-[14px]',
34
+ lg: 'w-5 h-5 data-[state=checked]:translate-x-4',
35
+ },
36
+ },
37
+ defaultVariants: {
38
+ size: 'lg',
39
+ },
40
+ }
41
+ );
42
+
43
+ const Switch = React.forwardRef<
44
+ React.ElementRef<typeof SwitchPrimitives.Root>,
45
+ React.ComponentPropsWithoutRef<typeof SwitchPrimitives.Root> &
46
+ VariantProps<typeof switchRootVariants> & {
47
+ label?: string;
48
+ labelClassName?: string;
49
+ }
50
+ >(({ className, size, label, labelClassName, ...props }, ref) => {
51
+ const Component = label ? 'div' : React.Fragment;
52
+ return (
53
+ <Component className="flex items-center justify-center space-x-1.5 text-Colors-Text-Default">
54
+ <SwitchPrimitives.Root
55
+ className={cn(switchRootVariants({ size }), className, '')}
56
+ {...props}
57
+ ref={ref}
58
+ >
59
+ <SwitchPrimitives.Thumb className={cn(switchThumbVariants({ size }))} />
60
+ </SwitchPrimitives.Root>
61
+ {label && <span className={cn('text-sm', labelClassName)}>{label}</span>}
62
+ </Component>
63
+ );
64
+ });
65
+ Switch.displayName = SwitchPrimitives.Root.displayName;
66
+
67
+ export { Switch };
@@ -0,0 +1,325 @@
1
+ 'use client';
2
+
3
+ import * as TabsPrimitive from '@radix-ui/react-tabs';
4
+ import { cva } from 'class-variance-authority';
5
+ import * as React from 'react';
6
+
7
+ import { cn } from '@/lib/utils';
8
+
9
+ import Badge from './badge';
10
+ import { Icon } from './icon';
11
+ import Link from './link';
12
+ import { Tooltip } from './tooltip';
13
+ import { Text } from './typography';
14
+
15
+ type TItems = {
16
+ label?: string;
17
+ value: string;
18
+ count?: number | string;
19
+ tooltip?: {
20
+ description?: string;
21
+ open?: boolean;
22
+ variant?: 'default' | 'info' | 'message';
23
+ };
24
+ icon?: React.ElementType;
25
+ disabled?: boolean;
26
+ link?: string;
27
+ hasUnRead?: boolean;
28
+ children?: string | React.ReactNode;
29
+ className?: string;
30
+ onClickCallback?: (value: string) => void;
31
+ };
32
+
33
+ const tabListVariants = cva(
34
+ 'relative inline-flex items-center justify-center overflow-hidden',
35
+ {
36
+ variants: {
37
+ size: {
38
+ sm: 'h-6',
39
+ md: 'h-9',
40
+ lg: 'h-12',
41
+ },
42
+ variant: {
43
+ button:
44
+ 'w-full border border-Colors-Border-Default bg-Colors-Background-Neutral-Primary-Default p-0.5',
45
+ underline: 'border-b border-Colors-Border-Default space-x-6',
46
+ icon: 'border border-Colors-Border-Default bg-Colors-Background-Neutral-Primary-Default p-0.5',
47
+ },
48
+ rounded: {
49
+ default: '',
50
+ full: 'rounded-full',
51
+ },
52
+ },
53
+ compoundVariants: [
54
+ {
55
+ variant: 'button',
56
+ size: 'lg',
57
+ className: 'min-w-[320px]',
58
+ },
59
+ {
60
+ variant: 'button',
61
+ size: 'md',
62
+ className: 'min-w-[218px]',
63
+ },
64
+ {
65
+ variant: 'button',
66
+ size: 'sm',
67
+ className: 'min-w-[180px]',
68
+ },
69
+ {
70
+ variant: 'underline',
71
+ size: 'lg',
72
+ className: 'h-8',
73
+ },
74
+ {
75
+ variant: 'underline',
76
+ size: 'md',
77
+ className: 'h-7',
78
+ },
79
+ {
80
+ variant: 'underline',
81
+ size: 'sm',
82
+ className: 'h-6',
83
+ },
84
+ {
85
+ rounded: 'default',
86
+ size: 'lg',
87
+ className: 'rounded-xl',
88
+ },
89
+ {
90
+ rounded: 'default',
91
+ size: 'md',
92
+ className: 'rounded-lg',
93
+ },
94
+ {
95
+ rounded: 'default',
96
+ size: 'sm',
97
+ className: 'rounded-md',
98
+ },
99
+ ],
100
+ defaultVariants: {
101
+ size: 'lg',
102
+ variant: 'button',
103
+ rounded: 'default',
104
+ },
105
+ }
106
+ );
107
+
108
+ const tabVariants = cva(
109
+ 'relative inline-flex w-full h-full items-center justify-center overflow-hidden whitespace-nowrap font-medium text-Colors-Text-Subtler aria-[selected=true]:text-Colors-Text-Brand-Default aria-[selected=true]:bg-Colors-Background-Normal-Primary-Active aria-[selected=true]:shadow-background-default [&>div>span]:hover:text-Colors-Text-Brand-Default disabled:pointer-events-none disabled:text-Colors-Text-Disabled [&>div>span]:disabled:text-Colors-Text-Disabled data-[state=active]:text-Colors-Text-Brand-Default',
110
+ {
111
+ variants: {
112
+ size: {
113
+ sm: 'text-xs',
114
+ md: 'text-sm',
115
+ lg: 'text-base',
116
+ },
117
+ variant: {
118
+ button:
119
+ 'px-3 py-2.5 flex-1 rounded-full transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-0 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:bg-Colors-Background-Normal-Primary-Active data-[state=active]:shadow-background-default ',
120
+ underline:
121
+ 'flex items-baseline pb-2 rounded-none data-[state=active]:border-b-2 data-[state=active]:border-b-brand',
122
+ icon: 'px-4',
123
+ },
124
+ rounded: {
125
+ default: '',
126
+ full: 'rounded-full',
127
+ },
128
+ },
129
+ compoundVariants: [
130
+ {
131
+ rounded: 'default',
132
+ size: 'lg',
133
+ className: 'rounded-xl',
134
+ },
135
+ {
136
+ rounded: 'default',
137
+ size: 'md',
138
+ className: 'rounded-lg',
139
+ },
140
+ {
141
+ rounded: 'default',
142
+ size: 'sm',
143
+ className: 'rounded-md',
144
+ },
145
+ ],
146
+ defaultVariants: {
147
+ size: 'lg',
148
+ variant: 'button',
149
+ rounded: 'default',
150
+ },
151
+ }
152
+ );
153
+
154
+ const Tabs = React.forwardRef<
155
+ React.ElementRef<typeof TabsPrimitive.Root>,
156
+ React.ComponentPropsWithoutRef<typeof TabsPrimitive.Root> & {
157
+ variant?: 'button' | 'underline' | 'icon';
158
+ size?: 'sm' | 'md' | 'lg';
159
+ rounded?: 'default' | 'full';
160
+ /**
161
+ * 是否为链接
162
+ */
163
+ isLink?: boolean;
164
+ listClassName?: string;
165
+ items?: TItems[];
166
+ }
167
+ >(
168
+ (
169
+ {
170
+ className,
171
+ listClassName,
172
+ variant = 'button',
173
+ size,
174
+ isLink = false,
175
+ rounded = 'default',
176
+ items,
177
+ ...props
178
+ },
179
+ ref
180
+ ) => (
181
+ <TabsPrimitive.Root ref={ref} className={cn(className)} {...props}>
182
+ <div className="w-full flex justify-center items-center">
183
+ <TabsPrimitive.List
184
+ ref={ref}
185
+ className={cn(
186
+ '',
187
+ tabListVariants({ variant, size, rounded }),
188
+ listClassName
189
+ )}
190
+ {...props}
191
+ >
192
+ {items?.map((item) => {
193
+ return (
194
+ <Tab
195
+ key={item.value}
196
+ isLink={isLink}
197
+ variant={variant}
198
+ size={size}
199
+ rounded={rounded}
200
+ {...item}
201
+ />
202
+ );
203
+ })}
204
+ </TabsPrimitive.List>
205
+ </div>
206
+ {!isLink && (
207
+ <div className="w-full">
208
+ {items?.map((item) => {
209
+ return (
210
+ <TabsContent value={item.value}>{item.children}</TabsContent>
211
+ );
212
+ })}
213
+ </div>
214
+ )}
215
+ </TabsPrimitive.Root>
216
+ )
217
+ );
218
+ Tabs.displayName = TabsPrimitive.Root.displayName;
219
+
220
+ const Tab = React.forwardRef<
221
+ React.ElementRef<typeof TabsPrimitive.Trigger>,
222
+ React.ComponentPropsWithoutRef<typeof TabsPrimitive.Trigger> & {
223
+ variant?: 'button' | 'underline' | 'icon';
224
+ size?: 'sm' | 'md' | 'lg';
225
+ rounded?: 'default' | 'full';
226
+ isLink?: boolean;
227
+ link?: string;
228
+ count?: number | string;
229
+ label?: string;
230
+ icon?: React.ElementType;
231
+ tooltip?: {
232
+ description?: string;
233
+ open?: boolean;
234
+ variant?: 'default' | 'info' | 'message';
235
+ };
236
+ hasUnRead?: boolean;
237
+ onClickCallback?: (value: string) => void;
238
+ }
239
+ >(
240
+ (
241
+ {
242
+ className,
243
+ variant,
244
+ size,
245
+ isLink,
246
+ link,
247
+ count,
248
+ label,
249
+ icon,
250
+ tooltip,
251
+ hasUnRead,
252
+ rounded = 'default',
253
+ onClickCallback,
254
+ ...props
255
+ },
256
+ ref
257
+ ) => {
258
+ return (
259
+ <Tooltip triggerClassName="w-full h-full flex-1" {...tooltip}>
260
+ <TabsPrimitive.Trigger
261
+ ref={ref}
262
+ className={cn(
263
+ 'tabtrigger min-w-fit',
264
+ tabVariants({ variant, size, rounded }),
265
+ className
266
+ )}
267
+ {...props}
268
+ onClick={(e) => {
269
+ e.stopPropagation();
270
+ onClickCallback?.(props.value);
271
+ }}
272
+ >
273
+ {isLink && link ? (
274
+ <Link
275
+ prefetch
276
+ href={link}
277
+ className={cn(
278
+ 'relative h-full w-full flex justify-center items-center'
279
+ )}
280
+ onClick={(e) => () => {
281
+ e.preventDefault();
282
+ }}
283
+ >
284
+ {label}
285
+ {hasUnRead && <Badge className="-mt-2.5" />}
286
+ </Link>
287
+ ) : (
288
+ <div className="relative flex justify-center items-center">
289
+ {label && <Text className="text-inherit">{label}</Text>}
290
+ {icon && (
291
+ <Icon component={icon} size="md" className="text-inherit" />
292
+ )}
293
+ {count ? (
294
+ <Text size="sm" className="ml-1 text-Colors-Text-Subtlest">
295
+ {count}
296
+ </Text>
297
+ ) : null}
298
+ {hasUnRead && <Badge className="-mt-2.5" />}
299
+ </div>
300
+ )}
301
+ </TabsPrimitive.Trigger>
302
+ </Tooltip>
303
+ );
304
+ }
305
+ );
306
+ Tab.displayName = TabsPrimitive.Trigger.displayName;
307
+
308
+ const TabsContent = React.forwardRef<
309
+ React.ElementRef<typeof TabsPrimitive.Content>,
310
+ React.ComponentPropsWithoutRef<typeof TabsPrimitive.Content>
311
+ >(({ className, ...props }, ref) => {
312
+ return (
313
+ <TabsPrimitive.Content
314
+ ref={ref}
315
+ className={cn(
316
+ 'mt-2 ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-0',
317
+ className
318
+ )}
319
+ {...props}
320
+ />
321
+ );
322
+ });
323
+ TabsContent.displayName = TabsPrimitive.Content.displayName;
324
+
325
+ export { Tabs, Tab, TabsContent };