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,118 @@
1
+ import { cva } from 'class-variance-authority';
2
+ import * as React from 'react';
3
+
4
+ import { cn } from '@/lib/utils';
5
+
6
+ export type InputProps = Omit<
7
+ React.InputHTMLAttributes<HTMLInputElement>,
8
+ 'size'
9
+ > & {
10
+ rounded?:
11
+ | 'default'
12
+ | 'none'
13
+ | 'sm'
14
+ | 'md'
15
+ | 'lg'
16
+ | 'xl'
17
+ | '2xl'
18
+ | '3xl'
19
+ | 'full'
20
+ | null
21
+ | undefined;
22
+ size?: 'xs' | 'sm' | 'md' | 'lg' | null | undefined;
23
+ isFull?: boolean;
24
+ border?: 'none' | 'default' | null | undefined;
25
+ shadow?: 'none' | 'default' | null | undefined;
26
+ outline?: 'none' | 'default' | null | undefined;
27
+ background?: 'none' | 'default' | null | undefined;
28
+ };
29
+
30
+ const Input = React.forwardRef<HTMLInputElement, InputProps>(
31
+ (
32
+ {
33
+ className,
34
+ type,
35
+ autoComplete = 'off',
36
+ isFull = true,
37
+ rounded = 'lg',
38
+ size = 'sm',
39
+ border = 'default',
40
+ outline = 'default',
41
+ background = 'default',
42
+ shadow = 'default',
43
+ ...props
44
+ },
45
+ ref
46
+ ) => {
47
+ const { readOnly } = props;
48
+
49
+ const inputVariants = cva('', {
50
+ variants: {
51
+ rounded: {
52
+ none: 'rounded-none',
53
+ sm: 'rounded-sm',
54
+ default: 'rounded',
55
+ md: 'rounded-md',
56
+ lg: 'rounded-lg',
57
+ xl: 'rounded-xl',
58
+ '2xl': 'rounded-2xl',
59
+ '3xl': 'rounded-3xl',
60
+ full: 'rounded-full',
61
+ },
62
+ size: {
63
+ lg: 'h-14',
64
+ md: 'h-11',
65
+ sm: 'h-10',
66
+ xs: 'h-9',
67
+ },
68
+ border: {
69
+ none: 'border-none shadow-none',
70
+ default: 'border border-cc-Input-border-default ',
71
+ },
72
+ outline: {
73
+ none: 'outline-none',
74
+ default:
75
+ 'focus-visible:outline-none aria-[invalid=true]:focus-visible:ring-error focus-visible:ring-2 focus-visible:ring-cc-Focus-Rings-Brand-default focus-visible:ring-offset-0',
76
+ },
77
+ background: {
78
+ none: 'bg-transparent',
79
+ default:
80
+ 'bg-cc-Input-bg-default hover:bg-cc-Input-bg-hover disabled:bg-cc-Input-bg-disabled',
81
+ },
82
+ shadow: {
83
+ none: 'shadow-none',
84
+ default: '',
85
+ },
86
+ },
87
+ defaultVariants: {
88
+ rounded: 'lg',
89
+ size: 'sm',
90
+ border: 'default',
91
+ outline: 'default',
92
+ background: 'default',
93
+ },
94
+ });
95
+
96
+ return (
97
+ <input
98
+ type={type}
99
+ className={cn(
100
+ 'flex space-x-2 p-3 text-base text-Colors-Text-Default placeholder:text-Colors-Text-Subtlest ring-offset-cc-Focus-Rings-Brand-default',
101
+ 'aria-[invalid=true]:border-Colors-Border-Critical aria-[invalid=true]:hover:bg-Colors-Background-Critical-Subtle',
102
+ 'file:border-0 file:bg-transparent file:text-sm file:font-medium disabled:cursor-not-allowed disabled:opacity-30',
103
+ inputVariants({ rounded, size, border, outline, background, shadow }),
104
+ readOnly &&
105
+ 'focus-visible:ring-offset-0 focus-visible:ring-transparent',
106
+ isFull && 'w-full',
107
+ className
108
+ )}
109
+ autoComplete={autoComplete}
110
+ ref={ref}
111
+ {...props}
112
+ />
113
+ );
114
+ }
115
+ );
116
+ Input.displayName = 'Input';
117
+
118
+ export { Input };
@@ -0,0 +1,26 @@
1
+ 'use client';
2
+
3
+ import * as LabelPrimitive from '@radix-ui/react-label';
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 labelVariants = cva(
10
+ 'text-sm font-medium peer-disabled:cursor-not-allowed peer-disabled:opacity-30'
11
+ );
12
+
13
+ const Label = React.forwardRef<
14
+ React.ElementRef<typeof LabelPrimitive.Root>,
15
+ React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root> &
16
+ VariantProps<typeof labelVariants>
17
+ >(({ className, ...props }, ref) => (
18
+ <LabelPrimitive.Root
19
+ ref={ref}
20
+ className={cn(labelVariants(), className)}
21
+ {...props}
22
+ />
23
+ ));
24
+ Label.displayName = LabelPrimitive.Root.displayName;
25
+
26
+ export { Label };
@@ -0,0 +1,123 @@
1
+ import LinkComponent from 'next/link';
2
+ import { useMemo } from 'react';
3
+
4
+ import { useNativeBridge } from '@/common/hooks/useNativeBridge';
5
+ import { isClient, isIosApp } from '@/common/utils/common-helper';
6
+
7
+ import { UrlObject } from 'url';
8
+ import { useMedia } from 'react-use';
9
+
10
+ export interface LinkProps {
11
+ /**
12
+ * 跳转地址
13
+ * '/about'
14
+ * {
15
+ * pathname: '/about',
16
+ query: { name: 'test' },
17
+ * }
18
+ */
19
+ href: string | UrlObject;
20
+ /**
21
+ * 图像宽度
22
+ * string | number
23
+ */
24
+ scroll?: boolean;
25
+
26
+ /**
27
+ * 替换url
28
+ */
29
+ replace?: boolean;
30
+ /**
31
+ * true:进入视区预先获取herf数据
32
+ * false:进入视区不获取,悬浮时获取
33
+ */
34
+ prefetch?: boolean;
35
+ /**
36
+ * Forces `Link` to send the `href` property to its child.
37
+ *
38
+ * @defaultValue `false`
39
+ */
40
+ passHref?: boolean;
41
+ /**
42
+ * 样式覆盖
43
+ */
44
+ className?: string;
45
+ /**
46
+ * 是否是返回link
47
+ */
48
+ back?: boolean;
49
+ onClick?: (e: any) => void;
50
+ children?: React.ReactNode;
51
+ [key: string]: any;
52
+ }
53
+
54
+ export function Link({
55
+ className,
56
+ href,
57
+ scroll = false,
58
+ replace = false,
59
+ prefetch = true,
60
+ back = false,
61
+ children,
62
+ onClick,
63
+ ...props
64
+ }: LinkProps) {
65
+ const path = typeof href === 'string' ? href : `${href.pathname}`;
66
+ // ref:外链添加nofollow,noreferrer,告诉搜索引擎不要抓取以免降低自身站点权重;内链添加dofollow标签,保证权重的传递
67
+ const externalLink = /^(https?:\/\/)/.test(path);
68
+ const { getIosUrl, nativeGoBack } = useNativeBridge();
69
+ const isIos = isClient() && isIosApp();
70
+ const isMobile = useMedia('(max-width: 768px)');
71
+
72
+ // 通过url是否有platform参数判断是否是ios
73
+ const pathname = useMemo(() => {
74
+ // 站外链接直接返回
75
+ if (externalLink) return path;
76
+ // 白名单直接返回
77
+ // if (
78
+ // NotMatchWhitelist.some((whitelist: string) => path.startsWith(whitelist))
79
+ // ) {
80
+ // return path;
81
+ // }
82
+
83
+ let formatPath = path;
84
+ // 统一做m&pc路由跳转优化
85
+ const isMobPath = path.startsWith('/m/');
86
+ if (isMobile) {
87
+ formatPath = isMobPath ? path : `/m${path}`;
88
+ } else {
89
+ formatPath = isMobPath ? path.slice(2) : path;
90
+ }
91
+
92
+ return isIos ? getIosUrl(formatPath) || formatPath : formatPath;
93
+ }, [path]);
94
+
95
+ // 此处兼容ios跳转返回后失效问题
96
+ const Com = isIos ? 'a' : LinkComponent;
97
+
98
+ return (
99
+ <Com
100
+ href={pathname}
101
+ scroll={scroll}
102
+ replace={replace}
103
+ prefetch={prefetch}
104
+ onClick={(e) => {
105
+ if (isIos && back) {
106
+ e.stopPropagation();
107
+ e.preventDefault();
108
+ nativeGoBack();
109
+ } else {
110
+ onClick?.(e);
111
+ }
112
+ }}
113
+ className={className}
114
+ rel={externalLink ? 'nofollow,noreferrer' : 'dofollow'}
115
+ target={externalLink ? '_blank' : undefined}
116
+ {...props}
117
+ >
118
+ {children}
119
+ </Com>
120
+ );
121
+ }
122
+
123
+ export default Link;
@@ -0,0 +1,15 @@
1
+ .ms-marquee-pause-hover:hover div{
2
+ animation-play-state: paused !important;
3
+ }
4
+ .ms-marquee-pause-running:hover div{
5
+ animation-play-state: running !important;
6
+ }
7
+
8
+ @keyframes scroll {
9
+ 0% {
10
+ transform: translateX(0%);
11
+ }
12
+ 100% {
13
+ transform: translateX(-100%);
14
+ }
15
+ }
@@ -0,0 +1,220 @@
1
+ /* eslint-disable import/no-unassigned-import */
2
+ import React, {
3
+ Fragment,
4
+ useEffect,
5
+ useState,
6
+ useRef,
7
+ useCallback,
8
+ useMemo,
9
+ ReactNode,
10
+ CSSProperties,
11
+ FC,
12
+ forwardRef,
13
+ Children,
14
+ MutableRefObject,
15
+ RefAttributes,
16
+ } from 'react';
17
+
18
+ import { cn } from '@/lib/utils';
19
+
20
+ import './index.css';
21
+
22
+ type MarqueeProps = {
23
+ style?: CSSProperties;
24
+ className?: string;
25
+ autoFill?: boolean;
26
+ play?: boolean;
27
+ pauseOnHover?: boolean;
28
+ direction?: 'left' | 'right' | 'up' | 'down';
29
+ speed?: number;
30
+ delay?: number;
31
+ loop?: number;
32
+ onFinish?: () => void;
33
+ onCycleComplete?: () => void;
34
+ children?: ReactNode;
35
+ } & RefAttributes<HTMLDivElement>;
36
+
37
+ const Marquee: FC<MarqueeProps> = forwardRef(function Marquee(
38
+ {
39
+ style = {},
40
+ className = '',
41
+ autoFill = false,
42
+ play = true,
43
+ pauseOnHover = false,
44
+ direction = 'left',
45
+ speed = 50,
46
+ delay = 0,
47
+ loop = 0,
48
+ onFinish,
49
+ onCycleComplete,
50
+ children,
51
+ },
52
+ ref
53
+ ) {
54
+ const [containerWidth, setContainerWidth] = useState(0);
55
+ const [marqueeWidth, setMarqueeWidth] = useState(0);
56
+ const [multiplier, setMultiplier] = useState(1);
57
+ const [isMounted, setIsMounted] = useState(false);
58
+ const rootRef = useRef<HTMLDivElement>(null);
59
+ const containerRef = (ref as MutableRefObject<HTMLDivElement>) || rootRef;
60
+ const marqueeRef = useRef<HTMLDivElement>(null);
61
+
62
+ const calculateWidth = useCallback(() => {
63
+ if (marqueeRef.current && containerRef.current) {
64
+ const containerRect = containerRef.current.getBoundingClientRect();
65
+ const marqueeRect = marqueeRef.current.getBoundingClientRect();
66
+ let containerWidth = containerRect.width;
67
+ let marqueeWidth = marqueeRect.width;
68
+
69
+ if (direction === 'up' || direction === 'down') {
70
+ containerWidth = containerRect.height;
71
+ marqueeWidth = marqueeRect.height;
72
+ }
73
+
74
+ if (autoFill && containerWidth && marqueeWidth) {
75
+ setMultiplier(
76
+ marqueeWidth < containerWidth
77
+ ? Math.ceil(containerWidth / marqueeWidth)
78
+ : 1
79
+ );
80
+ } else {
81
+ setMultiplier(1);
82
+ }
83
+
84
+ setContainerWidth(containerWidth);
85
+ setMarqueeWidth(marqueeWidth);
86
+ }
87
+ }, [autoFill, containerRef, direction]);
88
+
89
+ useEffect(() => {
90
+ if (!isMounted) return;
91
+
92
+ calculateWidth();
93
+ if (marqueeRef.current && containerRef.current) {
94
+ const resizeObserver = new ResizeObserver(() => calculateWidth());
95
+ resizeObserver.observe(containerRef.current);
96
+ resizeObserver.observe(marqueeRef.current);
97
+
98
+ return () => {
99
+ if (!resizeObserver) return;
100
+ resizeObserver.disconnect();
101
+ };
102
+ }
103
+ }, [calculateWidth, containerRef, isMounted]);
104
+
105
+ useEffect(() => {
106
+ calculateWidth();
107
+ }, [calculateWidth, children]);
108
+
109
+ useEffect(() => {
110
+ setIsMounted(true);
111
+ }, []);
112
+
113
+ // Animation duration
114
+ const duration = useMemo(() => {
115
+ if (autoFill) {
116
+ return (marqueeWidth * multiplier) / speed;
117
+ } else {
118
+ return marqueeWidth < containerWidth
119
+ ? containerWidth / speed
120
+ : marqueeWidth / speed;
121
+ }
122
+ }, [autoFill, containerWidth, marqueeWidth, multiplier, speed]);
123
+
124
+ const containerStyle = useMemo(
125
+ () => ({
126
+ ...style,
127
+ width: direction === 'up' || direction === 'down' ? `100vh` : '100%',
128
+ transform:
129
+ direction === 'up'
130
+ ? 'rotate(-90deg)'
131
+ : direction === 'down'
132
+ ? 'rotate(90deg)'
133
+ : 'none',
134
+ }),
135
+ [style, play, pauseOnHover, direction]
136
+ );
137
+
138
+ const marqueeStyle = useMemo(
139
+ () => ({
140
+ flex: '0 0 auto',
141
+ animationDuration: `${duration}s`,
142
+ animationDirection: direction === 'left' ? 'normal' : 'reverse',
143
+ animationDelay: `${delay}s`,
144
+ animation: !!loop
145
+ ? `scroll ${duration}s linear ${delay}s ${loop}`
146
+ : `scroll ${duration}s linear ${delay}s infinite`,
147
+ minWidth: autoFill ? `auto` : '100%',
148
+ }),
149
+ [play, direction, duration, delay, loop, autoFill]
150
+ );
151
+
152
+ const childStyle = useMemo(
153
+ () => ({
154
+ transform:
155
+ direction === 'up'
156
+ ? 'rotate(90deg)'
157
+ : direction === 'down'
158
+ ? 'rotate(-90deg)'
159
+ : 'none',
160
+ }),
161
+ [direction]
162
+ );
163
+
164
+ // Render {multiplier} number of children
165
+ const multiplyChildren = useCallback(
166
+ (multiplier: number) => {
167
+ return [
168
+ ...Array(
169
+ Number.isFinite(multiplier) && multiplier >= 0 ? multiplier : 0
170
+ ),
171
+ ].map((_, i) => (
172
+ <Fragment key={i}>
173
+ {Children.map(children, (child) => {
174
+ return (
175
+ <div key={`children${i}`} style={childStyle} className="ms-child">
176
+ {child}
177
+ </div>
178
+ );
179
+ })}
180
+ </Fragment>
181
+ ));
182
+ },
183
+ [childStyle, children]
184
+ );
185
+
186
+ return !isMounted ? null : (
187
+ <div
188
+ ref={containerRef}
189
+ style={containerStyle}
190
+ className={cn(
191
+ `ms-marquee-container overflow-x-hidden flex flex-row relative${className}`,
192
+ !play || pauseOnHover ? 'ms-marquee-pause-hover' : ''
193
+ )}
194
+ >
195
+ <div
196
+ className={cn(' z-[1] flex flex-row items-center')}
197
+ style={marqueeStyle}
198
+ onAnimationIteration={onCycleComplete}
199
+ onAnimationEnd={onFinish}
200
+ >
201
+ <div
202
+ className="flex flex-row items-center min-w-[auto]"
203
+ ref={marqueeRef}
204
+ >
205
+ {Children.map(children, (child) => {
206
+ return (
207
+ <div style={childStyle} className="ms-child">
208
+ {child}
209
+ </div>
210
+ );
211
+ })}
212
+ </div>
213
+ {multiplyChildren(multiplier - 1)}
214
+ </div>
215
+ <div style={marqueeStyle}>{multiplyChildren(multiplier)}</div>
216
+ </div>
217
+ );
218
+ });
219
+
220
+ export default Marquee;
@@ -0,0 +1,138 @@
1
+ 'use client';
2
+
3
+ import { indexOf, min } from 'lodash-es';
4
+ import React, { Children, useEffect, useState } from 'react';
5
+
6
+ import { cn } from '@/lib/utils';
7
+
8
+ interface BreakpointColsObject {
9
+ default: number;
10
+ [key: number]: number;
11
+ }
12
+
13
+ interface MasonryProps {
14
+ breakpointCols?: number | BreakpointColsObject;
15
+ className?: string;
16
+ columnClassName?: string;
17
+ children?: React.ReactNode;
18
+ columnAttrs?: React.HTMLAttributes<HTMLDivElement>;
19
+ heights?: number[];
20
+ }
21
+
22
+ const Masonry: React.FC<MasonryProps> = ({
23
+ breakpointCols = {
24
+ default: 0,
25
+ 3280: 5,
26
+ 1919: 4,
27
+ 1439: 3,
28
+ 1199: 2,
29
+ },
30
+ className = '',
31
+ columnClassName = '',
32
+ children,
33
+ columnAttrs = {},
34
+ heights,
35
+ }) => {
36
+ const [columnCount, setColumnCount] = useState<number>(
37
+ typeof breakpointCols === 'number' ? breakpointCols : breakpointCols.default
38
+ );
39
+
40
+ const reCalculateColumnCount = () => {
41
+ const windowWidth = window?.innerWidth || Infinity;
42
+ let newColumnCount = columnCount;
43
+
44
+ if (typeof breakpointCols === 'object') {
45
+ let matchedBreakpoint = Infinity;
46
+
47
+ for (const breakpoint in breakpointCols) {
48
+ const optBreakpoint = parseInt(breakpoint);
49
+ const isCurrentBreakpoint =
50
+ optBreakpoint > 0 && windowWidth <= optBreakpoint;
51
+
52
+ if (isCurrentBreakpoint && optBreakpoint < matchedBreakpoint) {
53
+ matchedBreakpoint = optBreakpoint;
54
+ newColumnCount = breakpointCols[breakpoint];
55
+ }
56
+ }
57
+
58
+ newColumnCount = Math.max(1, newColumnCount || 1);
59
+ }
60
+
61
+ if (columnCount !== newColumnCount) {
62
+ setColumnCount(newColumnCount);
63
+ }
64
+ };
65
+
66
+ useEffect(() => {
67
+ const handleResize = () => {
68
+ reCalculateColumnCount();
69
+ };
70
+
71
+ window.addEventListener('resize', handleResize);
72
+
73
+ // Invoke immediately to set the initial column count
74
+ handleResize();
75
+
76
+ return () => {
77
+ window.removeEventListener('resize', handleResize);
78
+ };
79
+ }, [breakpointCols, columnCount]);
80
+
81
+ const itemsInColumns = () => {
82
+ const columns = new Array<React.ReactNode[]>(columnCount);
83
+ const totals = new Array<number>(columnCount).fill(0);
84
+ const items = Children.toArray(children);
85
+
86
+ if (heights) {
87
+ for (let i = 0; i < items.length; i++) {
88
+ const columnIndex = indexOf(totals, min(totals));
89
+
90
+ if (!columns[columnIndex]) {
91
+ columns[columnIndex] = [];
92
+ }
93
+ columns[columnIndex].push(items[i]);
94
+ totals[columnIndex] += heights[i];
95
+ }
96
+ } else {
97
+ for (let i = 0; i < items.length; i++) {
98
+ const columnIndex = i % columnCount;
99
+ if (!columns[columnIndex]) {
100
+ columns[columnIndex] = [];
101
+ }
102
+ columns[columnIndex].push(items[i]);
103
+ }
104
+ }
105
+
106
+ return columns;
107
+ };
108
+
109
+ const renderColumns = () => {
110
+ const childrenInColumns = itemsInColumns();
111
+ const columnWidth = `${100 / childrenInColumns.length}%`;
112
+
113
+ return childrenInColumns.map((items, i) => (
114
+ <div
115
+ {...columnAttrs}
116
+ style={{ ...columnAttrs.style, width: columnWidth }}
117
+ className={cn(
118
+ 'space-y-2 bg-clip-padding pl-2 md:space-y-5 md:pl-5',
119
+ columnClassName
120
+ )}
121
+ key={i}
122
+ >
123
+ {items}
124
+ </div>
125
+ ));
126
+ };
127
+
128
+ return (
129
+ <div
130
+ {...columnAttrs}
131
+ className={cn('mr-4 flex w-auto md:mr-6', className || 'my-masonry-grid')}
132
+ >
133
+ {columnCount > 0 && renderColumns()}
134
+ </div>
135
+ );
136
+ };
137
+
138
+ export default Masonry;