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,307 @@
1
+ /* eslint-disable react/jsx-no-useless-fragment */
2
+ import { ArrowLeftIcon } from '@heroicons/react/24/outline';
3
+ import { LucideIcon } from 'lucide-react';
4
+ import { useEffect, useRef, useState } from 'react';
5
+ import { useMedia } from 'react-use';
6
+
7
+ import { HeroIcon } from '../common/constants/types/common';
8
+ import { cn } from '@/lib/utils';
9
+
10
+ import { Avatar } from './avatar';
11
+ import { Button } from './button/button';
12
+ import { Icon, IconComponent } from './icon';
13
+ import { IconButton } from './button/icon-button';
14
+ import { Link } from './link';
15
+ import { SearchBar } from './search-bar';
16
+ import { Separator } from './separator';
17
+ import { Display, Text } from './typography';
18
+ import FilterIcon from './icons/outline/FilterIcon';
19
+
20
+ type IIcons = {
21
+ icon: HeroIcon | LucideIcon | IconComponent | React.ElementType;
22
+ onClick?: () => void;
23
+ };
24
+ type IActions = {
25
+ label: string;
26
+ onClick?: () => void;
27
+ icon?: HeroIcon | LucideIcon | IconComponent | React.ElementType;
28
+ variant?: 'primary' | 'secondary' | 'static' | 'link' | 'plain';
29
+ color?: 'default' | 'brand' | 'error' | 'chat';
30
+ };
31
+ type ISecondaryNavigationBar = {
32
+ /**
33
+ * 是否展示背景
34
+ * boolean
35
+ *
36
+ * @default true
37
+ */
38
+ hasBackground?: boolean;
39
+ /**
40
+ * 右侧自定义内容
41
+ * ReactNode
42
+ *
43
+ */
44
+ children?: React.ReactNode;
45
+ /**
46
+ * 导航栏title
47
+ * string
48
+ */
49
+ title?: string;
50
+ /**
51
+ * 是否展示搜索框
52
+ * boolean
53
+ *
54
+ * @default false
55
+ */
56
+ showSearchBar?: boolean;
57
+ /**
58
+ * 搜索框值
59
+ * string
60
+ */
61
+ searchValue?: string;
62
+ /**
63
+ * 搜索框占位值
64
+ * string
65
+ */
66
+ searchPlaceholder?: string;
67
+ /**
68
+ * 是否显示底部border
69
+ * boolean
70
+ */
71
+ border?: boolean;
72
+ /**
73
+ * 返回链接,有配置则显示返回icon
74
+ * string
75
+ */
76
+ backUrl?: string;
77
+ /**
78
+ * 是否展示清除按钮
79
+ * boolean
80
+ *
81
+ * @default false
82
+ */
83
+ showClear?: boolean;
84
+ /**
85
+ * 清除按钮文本
86
+ * string
87
+ */
88
+ clearText?: string;
89
+ /**
90
+ * icon按钮配置
91
+ * IIcons
92
+ */
93
+ icons?: IIcons[];
94
+ /**
95
+ * 操作按钮配置
96
+ * IActions
97
+ */
98
+ actions?: IActions[];
99
+ /**
100
+ * 头像配置
101
+ * {
102
+ * logo: string;
103
+ * name?: string;
104
+ * }
105
+ */
106
+ avatar?: {
107
+ logo: string;
108
+ name?: string;
109
+ };
110
+ onSearchChange?: (value: string) => void;
111
+ onClear?: () => void;
112
+ };
113
+
114
+ function SecondaryNavigationBar({
115
+ showSearchBar = true,
116
+ searchValue,
117
+ searchPlaceholder,
118
+ title,
119
+ border,
120
+ backUrl,
121
+ showClear,
122
+ clearText,
123
+ icons,
124
+ actions,
125
+ children,
126
+ hasBackground = true,
127
+ avatar,
128
+ onSearchChange,
129
+ onClear,
130
+ }: ISecondaryNavigationBar) {
131
+ const isMobile = useMedia('(max-width: 768px)');
132
+ const navbarRef = useRef<HTMLDivElement>(null);
133
+ const [showBackground, setShowBackground] = useState(hasBackground);
134
+
135
+ useEffect(() => {
136
+ if (hasBackground) {
137
+ setShowBackground(true);
138
+
139
+ return;
140
+ }
141
+
142
+ const handleScroll = () => {
143
+ if (navbarRef.current) {
144
+ const { height } = navbarRef.current.getBoundingClientRect();
145
+ setShowBackground(window.scrollY > height);
146
+ }
147
+ };
148
+
149
+ window.addEventListener('scroll', handleScroll);
150
+
151
+ return () => window.removeEventListener('scroll', handleScroll);
152
+ }, [hasBackground]);
153
+ const currentClearText = clearText || 'Clear Filters';
154
+
155
+ return (
156
+ <div
157
+ ref={navbarRef}
158
+ className={cn(
159
+ 'w-full flex flex-col md:flex-row justify-center items-center px-4 md:px-6',
160
+ !isMobile && border && 'border-b border-Colors-Border-Default',
161
+ showBackground && 'bg-Colors-Background-Normal-Primary-Default'
162
+ )}
163
+ >
164
+ <div
165
+ className={cn(
166
+ 'relative w-full flex justify-between items-center h-14 md:h-15'
167
+ )}
168
+ >
169
+ <div
170
+ className={cn('flex items-center', avatar?.logo && 'min-w-[76px]')}
171
+ >
172
+ {backUrl && (
173
+ <div className="flex justify-start items-center">
174
+ <Link href={backUrl} className="flex justify-center items-center">
175
+ <IconButton
176
+ variant={!hasBackground ? 'primary' : 'plain'}
177
+ color={!hasBackground ? 'gray' : 'brand'}
178
+ size="md"
179
+ icon={ArrowLeftIcon}
180
+ />
181
+ </Link>
182
+ {!isMobile && title && (
183
+ <Separator orientation="vertical" className="h-5 ml-1.5 mr-3" />
184
+ )}
185
+ </div>
186
+ )}
187
+
188
+ {title &&
189
+ (!showSearchBar || !isMobile) &&
190
+ (!avatar?.logo || !isMobile) && (
191
+ <div className="flex-1 flex-shrink-0 absolute w-full md:relative md:w-auto flex items-center justify-center md:justify-start">
192
+ <Display
193
+ size={isMobile ? 'xs' : 'sm'}
194
+ className={cn(
195
+ 'max-w-[calc(100%-168px)] md:max-w-fit flex-1 flex-shrink-0 absolute w-full md:relative md:w-auto text-center md:text-left line-clamp-1'
196
+ )}
197
+ >
198
+ {title}
199
+ </Display>
200
+ </div>
201
+ )}
202
+ </div>
203
+
204
+ {avatar?.logo && !showSearchBar && (
205
+ <div className="flex-shrink-0 flex relative px-3 justify-center items-center space-x-1.5">
206
+ <Avatar size="sm" src={avatar.logo} />
207
+ {avatar.name && !isMobile && (
208
+ <Text size="lg" weight="medium">
209
+ {avatar.name}
210
+ </Text>
211
+ )}
212
+ </div>
213
+ )}
214
+
215
+ <div
216
+ className={cn(
217
+ 'w-fit flex md:w-auto justify-end',
218
+ !avatar?.logo && 'flex-1'
219
+ )}
220
+ >
221
+ {children ? (
222
+ <div className="w-fit">{children}</div>
223
+ ) : (
224
+ <>
225
+ {icons || actions ? (
226
+ <div
227
+ className={cn(
228
+ 'flex justify-end items-center',
229
+ icons && 'space-x-1 min-w-[76px]',
230
+ actions && 'space-x-3'
231
+ )}
232
+ >
233
+ {icons &&
234
+ icons.map((icon, index) => (
235
+ <IconButton
236
+ key={index}
237
+ variant={!hasBackground ? 'primary' : 'plain'}
238
+ color={!hasBackground ? 'gray' : 'brand'}
239
+ size="md"
240
+ icon={icon.icon}
241
+ onClick={icon.onClick}
242
+ />
243
+ ))}
244
+ {actions &&
245
+ actions.map((action, index) => (
246
+ <Button key={index} size="md" {...action}>
247
+ {action.label}
248
+ </Button>
249
+ ))}
250
+ </div>
251
+ ) : (
252
+ <>
253
+ {showClear && !isMobile && (
254
+ <Button
255
+ aria-label={currentClearText}
256
+ size="md"
257
+ variant="primary"
258
+ color="default"
259
+ className="hidden md:flex relative text-sm blue-30 text-blue-30 font-medium ml-2 mr-3 px-4 justify-center items-center"
260
+ onClick={onClear}
261
+ >
262
+ <div className="relative mr-1.5">
263
+ <FilterIcon className="w-5 h-5" />
264
+ </div>
265
+ <span className="hidden md:block">
266
+ {currentClearText}
267
+ </span>
268
+ </Button>
269
+ )}
270
+ {showSearchBar && (
271
+ <div className="relative w-full md:w-60">
272
+ <SearchBar
273
+ color="gray"
274
+ size="xs"
275
+ searchValue={searchValue}
276
+ placeholder={searchPlaceholder || 'Search'}
277
+ onSearchChange={(value) => {
278
+ onSearchChange && onSearchChange(value);
279
+ }}
280
+ />
281
+ </div>
282
+ )}
283
+ {showClear && isMobile && (
284
+ <IconButton
285
+ className="flex-shrink-0 flex relative text-sm font-medium px-4 justify-center items-center ml-3"
286
+ aria-label={currentClearText}
287
+ size="md"
288
+ variant="primary"
289
+ color="default"
290
+ onClick={onClear}
291
+ >
292
+ <div className="relative">
293
+ <FilterIcon />
294
+ </div>
295
+ </IconButton>
296
+ )}
297
+ </>
298
+ )}
299
+ </>
300
+ )}
301
+ </div>
302
+ </div>
303
+ </div>
304
+ );
305
+ }
306
+
307
+ export { SecondaryNavigationBar };
@@ -0,0 +1,273 @@
1
+ /* eslint-disable @typescript-eslint/no-use-before-define */
2
+ /* eslint-disable react/display-name */
3
+
4
+ 'use client';
5
+
6
+ import { CheckIcon } from '@heroicons/react/24/outline';
7
+ import * as SelectPrimitive from '@radix-ui/react-select';
8
+ import { ChevronDown, ChevronUp } from 'lucide-react';
9
+ import * as React from 'react';
10
+
11
+ import { cn } from '@/lib/utils';
12
+
13
+ export interface ISelectProps extends SelectPrimitive.SelectProps {
14
+ options?: Array<{
15
+ value: string;
16
+ label: string;
17
+ icon?: React.ReactNode | React.ElementType | string;
18
+ disabled?: boolean;
19
+ critical?: boolean;
20
+ }>;
21
+ placeholder?: React.ReactNode;
22
+ triggerClassName?: string;
23
+ }
24
+
25
+ const Select = React.forwardRef<
26
+ React.ElementRef<typeof SelectPrimitive.Root>,
27
+ ISelectProps
28
+ >(({ children, placeholder, options, triggerClassName, ...props }, ref) => {
29
+ // 获取选中选项的图标
30
+ const icon = options?.find((item) => item.value === props.value)?.icon;
31
+
32
+ return (
33
+ <SelectPrimitive.Root {...props}>
34
+ {Array.isArray(options) && options.length ? (
35
+ <>
36
+ <SelectTrigger className={cn(triggerClassName)}>
37
+ <div className="flex items-center">
38
+ <SelectIcon icon={icon} />
39
+ <SelectValue placeholder={placeholder} />
40
+ </div>
41
+ </SelectTrigger>
42
+ <SelectContent>
43
+ {options.map((item, i) => (
44
+ <SelectItem key={`${item.value}_${i}`} {...item}>
45
+ {item.label}
46
+ </SelectItem>
47
+ ))}
48
+ </SelectContent>
49
+ </>
50
+ ) : (
51
+ children
52
+ )}
53
+ </SelectPrimitive.Root>
54
+ );
55
+ });
56
+
57
+ const SelectGroup = SelectPrimitive.Group;
58
+
59
+ const SelectValue = SelectPrimitive.Value;
60
+
61
+ const SelectTrigger = React.forwardRef<
62
+ React.ElementRef<typeof SelectPrimitive.Trigger>,
63
+ React.ComponentPropsWithoutRef<typeof SelectPrimitive.Trigger>
64
+ >(({ className, children, ...props }, ref) => (
65
+ <SelectPrimitive.Trigger
66
+ ref={ref}
67
+ className={cn(
68
+ 'flex h-10 w-full items-center justify-between rounded-md border border-Colors-Border-Default bg-Colors-Background-Neutral-On-Surface-Default text-Colors-Text-Default text-sm p-3 ring-offset-cc-Focus-Rings-Brand-default focus:shadow-cc-Focus-Rings-Brand-default placeholder:text-Colors-Text-Subtlest focus:outline-none focus:ring-0 focus:ring-offset-0 disabled:cursor-not-allowed disabled:opacity-30 [&>span]:line-clamp-1 [&>.select-chevron]:aria-expanded:rotate-180',
69
+ className
70
+ )}
71
+ {...props}
72
+ >
73
+ {children}
74
+ <SelectPrimitive.Icon asChild>
75
+ <ChevronDown className="select-chevron h-5 w-5 text-Colors-Foreground-Subtle duration-200" />
76
+ </SelectPrimitive.Icon>
77
+ </SelectPrimitive.Trigger>
78
+ ));
79
+ SelectTrigger.displayName = SelectPrimitive.Trigger.displayName;
80
+
81
+ const SelectScrollUpButton = React.forwardRef<
82
+ React.ElementRef<typeof SelectPrimitive.ScrollUpButton>,
83
+ React.ComponentPropsWithoutRef<typeof SelectPrimitive.ScrollUpButton>
84
+ >(({ className, ...props }, ref) => (
85
+ <SelectPrimitive.ScrollUpButton
86
+ ref={ref}
87
+ className={cn(
88
+ 'flex cursor-default items-center justify-center py-1',
89
+ className
90
+ )}
91
+ {...props}
92
+ >
93
+ <ChevronUp className="h-4 w-4" />
94
+ </SelectPrimitive.ScrollUpButton>
95
+ ));
96
+ SelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName;
97
+
98
+ const SelectScrollDownButton = React.forwardRef<
99
+ React.ElementRef<typeof SelectPrimitive.ScrollDownButton>,
100
+ React.ComponentPropsWithoutRef<typeof SelectPrimitive.ScrollDownButton>
101
+ >(({ className, ...props }, ref) => (
102
+ <SelectPrimitive.ScrollDownButton
103
+ ref={ref}
104
+ className={cn(
105
+ 'flex cursor-default items-center justify-center py-1',
106
+ className
107
+ )}
108
+ {...props}
109
+ >
110
+ <ChevronDown className="h-4 w-4" />
111
+ </SelectPrimitive.ScrollDownButton>
112
+ ));
113
+ SelectScrollDownButton.displayName =
114
+ SelectPrimitive.ScrollDownButton.displayName;
115
+
116
+ const SelectContent = React.forwardRef<
117
+ React.ElementRef<typeof SelectPrimitive.Content>,
118
+ React.ComponentPropsWithoutRef<typeof SelectPrimitive.Content>
119
+ >(({ className, children, position = 'popper', ...props }, ref) => (
120
+ <SelectPrimitive.Portal>
121
+ <SelectPrimitive.Content
122
+ ref={ref}
123
+ className={cn(
124
+ 'relative z-50 max-h-96 min-w-[8rem] overflow-hidden rounded-md border border-opaque bg-Colors-Background-Normal-Primary-Default text-Colors-Text-Default shadow-cc-Shadows-Modal-default data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',
125
+ position === 'popper' &&
126
+ 'data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1',
127
+ className
128
+ )}
129
+ position={position}
130
+ {...props}
131
+ >
132
+ <SelectPrimitive.Viewport
133
+ className={cn(
134
+ 'p-1.5 space-y-1.5',
135
+ position === 'popper' &&
136
+ 'h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]'
137
+ )}
138
+ >
139
+ {children}
140
+ </SelectPrimitive.Viewport>
141
+ </SelectPrimitive.Content>
142
+ </SelectPrimitive.Portal>
143
+ ));
144
+ SelectContent.displayName = SelectPrimitive.Content.displayName;
145
+
146
+ const SelectLabel = React.forwardRef<
147
+ React.ElementRef<typeof SelectPrimitive.Label>,
148
+ React.ComponentPropsWithoutRef<typeof SelectPrimitive.Label>
149
+ >(({ className, ...props }, ref) => (
150
+ <SelectPrimitive.Label
151
+ ref={ref}
152
+ className={cn(
153
+ 'py-1 px-3 text-sm font-medium text-Colors-Text-Subtler',
154
+ className
155
+ )}
156
+ {...props}
157
+ />
158
+ ));
159
+ SelectLabel.displayName = SelectPrimitive.Label.displayName;
160
+
161
+ interface ISelectIconProps {
162
+ icon?: React.ReactNode | React.ElementType | string;
163
+ critical?: boolean;
164
+ }
165
+
166
+ function SelectIcon(props: ISelectIconProps) {
167
+ const { icon, critical } = props;
168
+
169
+ if (!icon) {
170
+ return null;
171
+ }
172
+
173
+ return (
174
+ <SelectPrimitive.Icon>
175
+ {typeof icon === 'string' ? (
176
+ // 如果是字符串URL,作为背景图片显示
177
+ <div
178
+ className={cn(
179
+ 'aspect-[20/20] w-5 h-5 relative bg-cover bg-no-repeat rounded-md overflow-hidden mr-1.5',
180
+ critical && 'text-Colors-Text-Critical-Default'
181
+ )}
182
+ style={{ backgroundImage: `url('${icon}')` }}
183
+ />
184
+ ) : React.isValidElement(icon) ? (
185
+ // 如果是React元素,直接渲染
186
+ <div
187
+ className={cn(
188
+ 'mr-1.5 w-5 h-5 text-Colors-Foreground-Default',
189
+ critical && 'text-Colors-Foreground-Critical-Default'
190
+ )}
191
+ >
192
+ {icon}
193
+ </div>
194
+ ) : (
195
+ // 如果是组件类型,创建元素
196
+ <div
197
+ className={cn(
198
+ 'mr-1.5 w-5 h-5 text-Colors-Foreground-Default',
199
+ critical && 'text-Colors-Foreground-Critical-Default'
200
+ )}
201
+ >
202
+ {React.createElement(icon as React.ElementType, {
203
+ className: cn(
204
+ 'w-5 h-5 text-Colors-Foreground-Default',
205
+ critical && 'text-Colors-Foreground-Critical-Default'
206
+ ),
207
+ })}
208
+ </div>
209
+ )}
210
+ </SelectPrimitive.Icon>
211
+ );
212
+ }
213
+
214
+ SelectIcon.displayName = SelectPrimitive.Icon.displayName;
215
+
216
+ interface ISelectItemProps
217
+ extends SelectPrimitive.SelectItemProps,
218
+ ISelectIconProps {}
219
+
220
+ const SelectItem = React.forwardRef<
221
+ React.ElementRef<typeof SelectPrimitive.Item>,
222
+ ISelectItemProps
223
+ >(({ className, children, icon, critical, ...props }, ref) => (
224
+ <SelectPrimitive.Item
225
+ ref={ref}
226
+ className={cn(
227
+ 'relative flex justify-between w-full text-sm cursor-pointer select-none items-center rounded-md py-1 px-1.5 outline-none',
228
+ 'bg-Colors-Background-Normal-Secondary-Alt hover:bg-Colors-Background-Normal-Primary-Hover focus:bg-Colors-Background-Neutral-Primary-Default data-[disabled]:opacity-30 data-[disabled]:cursor-not-allowed',
229
+ critical
230
+ ? 'text-Colors-Text-Critical-Default'
231
+ : 'focus:text-Colors-Text-Default',
232
+ className
233
+ )}
234
+ {...props}
235
+ >
236
+ <span className="flex items-center grow">
237
+ <SelectIcon icon={icon} critical={critical} />
238
+ <SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>
239
+ </span>
240
+ <span className="flex h-4 w-4 items-center justify-center">
241
+ <SelectPrimitive.ItemIndicator>
242
+ <CheckIcon className="h-4 w-4 text-Colors-Foreground-Subtle stroke-[2px]" />
243
+ </SelectPrimitive.ItemIndicator>
244
+ </span>
245
+ </SelectPrimitive.Item>
246
+ ));
247
+ SelectItem.displayName = SelectPrimitive.Item.displayName;
248
+
249
+ const SelectSeparator = React.forwardRef<
250
+ React.ElementRef<typeof SelectPrimitive.Separator>,
251
+ React.ComponentPropsWithoutRef<typeof SelectPrimitive.Separator>
252
+ >(({ className, ...props }, ref) => (
253
+ <SelectPrimitive.Separator
254
+ ref={ref}
255
+ className={cn('-mx-1 my-1 h-px bg-border-default', className)}
256
+ {...props}
257
+ />
258
+ ));
259
+ SelectSeparator.displayName = SelectPrimitive.Separator.displayName;
260
+
261
+ export {
262
+ Select,
263
+ SelectGroup,
264
+ SelectIcon,
265
+ SelectValue,
266
+ SelectTrigger,
267
+ SelectContent,
268
+ SelectLabel,
269
+ SelectItem,
270
+ SelectSeparator,
271
+ SelectScrollUpButton,
272
+ SelectScrollDownButton,
273
+ };
@@ -0,0 +1,31 @@
1
+ 'use client';
2
+
3
+ import * as SeparatorPrimitive from '@radix-ui/react-separator';
4
+ import * as React from 'react';
5
+
6
+ import { cn } from '@/lib/utils';
7
+
8
+ const Separator = React.forwardRef<
9
+ React.ElementRef<typeof SeparatorPrimitive.Root>,
10
+ React.ComponentPropsWithoutRef<typeof SeparatorPrimitive.Root>
11
+ >(
12
+ (
13
+ { className, orientation = 'horizontal', decorative = true, ...props },
14
+ ref
15
+ ) => (
16
+ <SeparatorPrimitive.Root
17
+ ref={ref}
18
+ decorative={decorative}
19
+ orientation={orientation}
20
+ className={cn(
21
+ 'shrink-0 border-Colors-Alpha-Black-8',
22
+ orientation === 'horizontal' ? 'w-full border-b' : 'h-full border-r',
23
+ className
24
+ )}
25
+ {...props}
26
+ />
27
+ )
28
+ );
29
+ Separator.displayName = SeparatorPrimitive.Root.displayName;
30
+
31
+ export { Separator };