deepsea-components 5.15.24 → 5.15.26
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.
- package/README.md +23 -23
- package/dist/components/AutoFit.cjs +6 -6
- package/dist/components/AutoFit.js +6 -6
- package/dist/components/AutoScroll.cjs +7 -7
- package/dist/components/AutoScroll.js +7 -7
- package/dist/components/AutoSizeTextarea.cjs +4 -4
- package/dist/components/AutoSizeTextarea.js +4 -4
- package/dist/components/Flow.cjs +16 -16
- package/dist/components/Flow.js +16 -16
- package/dist/components/InfiniteScroll.cjs +13 -13
- package/dist/components/InfiniteScroll.js +13 -13
- package/dist/components/LoopSwiper.cjs +33 -33
- package/dist/components/LoopSwiper.js +33 -33
- package/dist/components/Scroll.cjs +13 -13
- package/dist/components/Scroll.js +13 -13
- package/dist/components/Skeleton.cjs +17 -17
- package/dist/components/Skeleton.js +17 -17
- package/dist/components/Trapezium.cjs +4 -4
- package/dist/components/Trapezium.js +4 -4
- package/package.json +3 -3
- package/src/components/AutoFit.tsx +104 -104
- package/src/components/AutoScroll.tsx +150 -150
- package/src/components/AutoSizeTextarea.tsx +50 -50
- package/src/components/CircleText.tsx +81 -81
- package/src/components/CopyButton.tsx +31 -31
- package/src/components/Echart.tsx +69 -69
- package/src/components/Flow.tsx +271 -271
- package/src/components/FormLabel.tsx +41 -41
- package/src/components/HlsPlayer.tsx +34 -34
- package/src/components/IconFileType.tsx +162 -162
- package/src/components/InfiniteScroll.tsx +163 -163
- package/src/components/InputFile.tsx +93 -93
- package/src/components/InputFileButton.tsx +122 -122
- package/src/components/LoopSwiper.tsx +125 -125
- package/src/components/ReadExcel.tsx +13 -13
- package/src/components/ReadSheet.tsx +17 -17
- package/src/components/Ring.tsx +30 -30
- package/src/components/Scroll.tsx +97 -97
- package/src/components/ScrollMask.module.css +87 -87
- package/src/components/ScrollMask.tsx +68 -68
- package/src/components/SectionRing.tsx +40 -40
- package/src/components/Skeleton.tsx +45 -45
- package/src/components/Title.tsx +27 -27
- package/src/components/TransitionBox.tsx +44 -44
- package/src/components/TransitionNum.tsx +54 -54
- package/src/components/Trapezium.tsx +60 -60
- package/src/components/Unify.tsx +38 -38
- package/src/components/WriteExcel.tsx +13 -13
- package/src/components/WriteSheet.tsx +25 -25
- package/src/utils/getReactVersion.ts +7 -7
- package/src/utils/index.ts +33 -33
- package/tsconfig.json +16 -16
|
@@ -7,23 +7,23 @@ const Skeleton = /*#__PURE__*/ forwardRef((props, ref)=>{
|
|
|
7
7
|
const { className, children, loading = null == children, ...rest } = props;
|
|
8
8
|
return /*#__PURE__*/ jsx("div", {
|
|
9
9
|
ref: ref,
|
|
10
|
-
className: clsx(css`
|
|
11
|
-
@keyframes shimmer {
|
|
12
|
-
0% {
|
|
13
|
-
background-position: -400px 0px;
|
|
14
|
-
}
|
|
15
|
-
100% {
|
|
16
|
-
background-position: 400px 0px;
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
`, loading && css`
|
|
20
|
-
background-image: linear-gradient(to right, rgba(0, 0, 0, 0.067) 8%, rgba(0, 0, 0, 0.133) 18%, rgba(0, 0, 0, 0.067) 33%);
|
|
21
|
-
animation-duration: 1s;
|
|
22
|
-
animation-fill-mode: forwards;
|
|
23
|
-
animation-iteration-count: infinite;
|
|
24
|
-
animation-name: shimmer;
|
|
25
|
-
animation-timing-function: linear;
|
|
26
|
-
background-size: 800px 104px;
|
|
10
|
+
className: clsx(css`
|
|
11
|
+
@keyframes shimmer {
|
|
12
|
+
0% {
|
|
13
|
+
background-position: -400px 0px;
|
|
14
|
+
}
|
|
15
|
+
100% {
|
|
16
|
+
background-position: 400px 0px;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
`, loading && css`
|
|
20
|
+
background-image: linear-gradient(to right, rgba(0, 0, 0, 0.067) 8%, rgba(0, 0, 0, 0.133) 18%, rgba(0, 0, 0, 0.067) 33%);
|
|
21
|
+
animation-duration: 1s;
|
|
22
|
+
animation-fill-mode: forwards;
|
|
23
|
+
animation-iteration-count: infinite;
|
|
24
|
+
animation-name: shimmer;
|
|
25
|
+
animation-timing-function: linear;
|
|
26
|
+
background-size: 800px 104px;
|
|
27
27
|
`, className),
|
|
28
28
|
...rest,
|
|
29
29
|
children: children
|
|
@@ -45,10 +45,10 @@ const Trapezium = /*#__PURE__*/ (0, external_react_namespaceObject.forwardRef)((
|
|
|
45
45
|
const h = f * Math.sin(2 * a);
|
|
46
46
|
return /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsx)("div", {
|
|
47
47
|
ref: ref,
|
|
48
|
-
className: (0, external_deepsea_tools_namespaceObject.clsx)((0, css_namespaceObject.css)`
|
|
49
|
-
width: var(--width);
|
|
50
|
-
height: var(--height);
|
|
51
|
-
clip-path: var(--clip-path);
|
|
48
|
+
className: (0, external_deepsea_tools_namespaceObject.clsx)((0, css_namespaceObject.css)`
|
|
49
|
+
width: var(--width);
|
|
50
|
+
height: var(--height);
|
|
51
|
+
clip-path: var(--clip-path);
|
|
52
52
|
`, className),
|
|
53
53
|
style: (0, index_cjs_namespaceObject.transformCSSVariable)({
|
|
54
54
|
width: (0, index_cjs_namespaceObject.px)(bottom),
|
|
@@ -17,10 +17,10 @@ const Trapezium = /*#__PURE__*/ forwardRef((props, ref)=>{
|
|
|
17
17
|
const h = f * Math.sin(2 * a);
|
|
18
18
|
return /*#__PURE__*/ jsx("div", {
|
|
19
19
|
ref: ref,
|
|
20
|
-
className: clsx(css`
|
|
21
|
-
width: var(--width);
|
|
22
|
-
height: var(--height);
|
|
23
|
-
clip-path: var(--clip-path);
|
|
20
|
+
className: clsx(css`
|
|
21
|
+
width: var(--width);
|
|
22
|
+
height: var(--height);
|
|
23
|
+
clip-path: var(--clip-path);
|
|
24
24
|
`, className),
|
|
25
25
|
style: transformCSSVariable({
|
|
26
26
|
width: px(bottom),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "deepsea-components",
|
|
3
|
-
"version": "5.15.
|
|
3
|
+
"version": "5.15.26",
|
|
4
4
|
"description": "格数科技自用组件库",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -40,8 +40,8 @@
|
|
|
40
40
|
"echarts": "^5.6.0",
|
|
41
41
|
"hls.js": "^1.6.9",
|
|
42
42
|
"smooth-scrollbar": "^8.8.4",
|
|
43
|
-
"
|
|
44
|
-
"
|
|
43
|
+
"deepsea-tools": "5.40.5",
|
|
44
|
+
"soda-hooks": "6.13.1"
|
|
45
45
|
},
|
|
46
46
|
"devDependencies": {
|
|
47
47
|
"typescript": "^5.9.2"
|
|
@@ -1,104 +1,104 @@
|
|
|
1
|
-
"use client"
|
|
2
|
-
|
|
3
|
-
import { css } from "@emotion/css"
|
|
4
|
-
import { clsx } from "deepsea-tools"
|
|
5
|
-
import { HTMLAttributes, forwardRef, useEffect, useImperativeHandle, useLayoutEffect, useRef, useState } from "react"
|
|
6
|
-
|
|
7
|
-
import { px, transformCSSVariable } from "@/utils"
|
|
8
|
-
|
|
9
|
-
export interface AutoFitProps extends HTMLAttributes<HTMLDivElement> {
|
|
10
|
-
/** 设计稿宽度,默认 1920 */
|
|
11
|
-
width?: number
|
|
12
|
-
/** 设计稿高度,默认 1080 */
|
|
13
|
-
height?: number
|
|
14
|
-
/**
|
|
15
|
-
* 在哪些方向进行缩放,默认缩放所有方向
|
|
16
|
-
* 1. 水平方向是指,宽度按照设计稿进行占满,高度反向缩放
|
|
17
|
-
* 2. 垂直方向是指,高度按照设计稿进行占满,宽度反向缩放
|
|
18
|
-
* 3. 默认是水平和垂直方向都进行缩放,类似于 background-size: contain 的效果
|
|
19
|
-
*/
|
|
20
|
-
direction?: "horizontal" | "vertical" | "both"
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* 自适应缩放组件
|
|
25
|
-
*
|
|
26
|
-
* 注意:
|
|
27
|
-
* 1. 父元素必须设置非 static 定位方式
|
|
28
|
-
* 2. 父元素只能有且仅有一个子元素,那就是 AutoFit
|
|
29
|
-
* 3. 不要设置 AutoFit 的 position、left、top、transform、width、height 属性
|
|
30
|
-
* 4. 在第一次完成缩放前,无论 props 是什么,返回的都是 <div style={{ display: "none" }} />
|
|
31
|
-
* 5. 元素的属性、事件、资源并不是立即加载的,会有一帧的延迟,在第一次完成缩放后才会显示
|
|
32
|
-
*/
|
|
33
|
-
export const AutoFit = forwardRef<HTMLDivElement, AutoFitProps>((props, ref) => {
|
|
34
|
-
const { width: designWidth = 1920, height: designHeight = 1080, direction, className, style, ...rest } = props
|
|
35
|
-
const ele = useRef<HTMLDivElement>(null)
|
|
36
|
-
const [show, setShow] = useState(false)
|
|
37
|
-
const [transform, setTransform] = useState<string | undefined>(undefined)
|
|
38
|
-
const [width, setWidth] = useState<string | undefined>(undefined)
|
|
39
|
-
const [height, setHeight] = useState<string | undefined>(undefined)
|
|
40
|
-
|
|
41
|
-
useImperativeHandle(ref, () => ele.current!, [ele.current])
|
|
42
|
-
useLayoutEffect(() => {
|
|
43
|
-
const element = ele.current
|
|
44
|
-
const parent = element?.parentElement
|
|
45
|
-
if (!element || !parent) return
|
|
46
|
-
function listener(entries: ResizeObserverEntry[]) {
|
|
47
|
-
const entry = entries[0]
|
|
48
|
-
const { contentRect } = entry
|
|
49
|
-
if (direction === "horizontal") {
|
|
50
|
-
const scale = contentRect.width / designWidth
|
|
51
|
-
setTransform(`scale(${scale})`)
|
|
52
|
-
setWidth(px(designWidth))
|
|
53
|
-
setHeight(px(contentRect.height / scale))
|
|
54
|
-
} else if (direction === "vertical") {
|
|
55
|
-
const scale = contentRect.height / designHeight
|
|
56
|
-
setTransform(`scale(${scale})`)
|
|
57
|
-
setWidth(px(contentRect.width / scale))
|
|
58
|
-
setHeight(px(designHeight))
|
|
59
|
-
} else {
|
|
60
|
-
const scale = Math.min(contentRect.width / designWidth, contentRect.height / designHeight)
|
|
61
|
-
setTransform(
|
|
62
|
-
`translateX(${(contentRect.width - designWidth * scale) / 2}px) translateY(${(contentRect.height - designHeight * scale) / 2}px) scale(${scale})`,
|
|
63
|
-
)
|
|
64
|
-
setWidth(px(designWidth))
|
|
65
|
-
setHeight(px(designHeight))
|
|
66
|
-
}
|
|
67
|
-
setShow(true)
|
|
68
|
-
}
|
|
69
|
-
const observer = new ResizeObserver(listener)
|
|
70
|
-
observer.observe(parent)
|
|
71
|
-
return () => observer.disconnect()
|
|
72
|
-
}, [ele.current?.parentElement, designWidth, designHeight, direction])
|
|
73
|
-
|
|
74
|
-
if (process.env.NODE_ENV === "development") {
|
|
75
|
-
useEffect(() => {
|
|
76
|
-
const parent = ele.current?.parentElement
|
|
77
|
-
if (!parent) return
|
|
78
|
-
const style = getComputedStyle(parent)
|
|
79
|
-
if (style.position === "static") {
|
|
80
|
-
console.warn("AutoFit 组件的父元素的 position 属性不应该是 static")
|
|
81
|
-
}
|
|
82
|
-
}, [ele.current?.parentElement])
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
if (!show) return <div ref={ele} style={{ display: "none" }} />
|
|
86
|
-
|
|
87
|
-
return (
|
|
88
|
-
<div
|
|
89
|
-
ref={ele}
|
|
90
|
-
className={clsx(
|
|
91
|
-
css`
|
|
92
|
-
position: absolute;
|
|
93
|
-
transform: var(--transform);
|
|
94
|
-
transform-origin: top left;
|
|
95
|
-
width: var(--width);
|
|
96
|
-
height: var(--height);
|
|
97
|
-
`,
|
|
98
|
-
className,
|
|
99
|
-
)}
|
|
100
|
-
style={transformCSSVariable({ transform, width, height }, style)}
|
|
101
|
-
{...rest}
|
|
102
|
-
/>
|
|
103
|
-
)
|
|
104
|
-
})
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import { css } from "@emotion/css"
|
|
4
|
+
import { clsx } from "deepsea-tools"
|
|
5
|
+
import { HTMLAttributes, forwardRef, useEffect, useImperativeHandle, useLayoutEffect, useRef, useState } from "react"
|
|
6
|
+
|
|
7
|
+
import { px, transformCSSVariable } from "@/utils"
|
|
8
|
+
|
|
9
|
+
export interface AutoFitProps extends HTMLAttributes<HTMLDivElement> {
|
|
10
|
+
/** 设计稿宽度,默认 1920 */
|
|
11
|
+
width?: number
|
|
12
|
+
/** 设计稿高度,默认 1080 */
|
|
13
|
+
height?: number
|
|
14
|
+
/**
|
|
15
|
+
* 在哪些方向进行缩放,默认缩放所有方向
|
|
16
|
+
* 1. 水平方向是指,宽度按照设计稿进行占满,高度反向缩放
|
|
17
|
+
* 2. 垂直方向是指,高度按照设计稿进行占满,宽度反向缩放
|
|
18
|
+
* 3. 默认是水平和垂直方向都进行缩放,类似于 background-size: contain 的效果
|
|
19
|
+
*/
|
|
20
|
+
direction?: "horizontal" | "vertical" | "both"
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* 自适应缩放组件
|
|
25
|
+
*
|
|
26
|
+
* 注意:
|
|
27
|
+
* 1. 父元素必须设置非 static 定位方式
|
|
28
|
+
* 2. 父元素只能有且仅有一个子元素,那就是 AutoFit
|
|
29
|
+
* 3. 不要设置 AutoFit 的 position、left、top、transform、width、height 属性
|
|
30
|
+
* 4. 在第一次完成缩放前,无论 props 是什么,返回的都是 <div style={{ display: "none" }} />
|
|
31
|
+
* 5. 元素的属性、事件、资源并不是立即加载的,会有一帧的延迟,在第一次完成缩放后才会显示
|
|
32
|
+
*/
|
|
33
|
+
export const AutoFit = forwardRef<HTMLDivElement, AutoFitProps>((props, ref) => {
|
|
34
|
+
const { width: designWidth = 1920, height: designHeight = 1080, direction, className, style, ...rest } = props
|
|
35
|
+
const ele = useRef<HTMLDivElement>(null)
|
|
36
|
+
const [show, setShow] = useState(false)
|
|
37
|
+
const [transform, setTransform] = useState<string | undefined>(undefined)
|
|
38
|
+
const [width, setWidth] = useState<string | undefined>(undefined)
|
|
39
|
+
const [height, setHeight] = useState<string | undefined>(undefined)
|
|
40
|
+
|
|
41
|
+
useImperativeHandle(ref, () => ele.current!, [ele.current])
|
|
42
|
+
useLayoutEffect(() => {
|
|
43
|
+
const element = ele.current
|
|
44
|
+
const parent = element?.parentElement
|
|
45
|
+
if (!element || !parent) return
|
|
46
|
+
function listener(entries: ResizeObserverEntry[]) {
|
|
47
|
+
const entry = entries[0]
|
|
48
|
+
const { contentRect } = entry
|
|
49
|
+
if (direction === "horizontal") {
|
|
50
|
+
const scale = contentRect.width / designWidth
|
|
51
|
+
setTransform(`scale(${scale})`)
|
|
52
|
+
setWidth(px(designWidth))
|
|
53
|
+
setHeight(px(contentRect.height / scale))
|
|
54
|
+
} else if (direction === "vertical") {
|
|
55
|
+
const scale = contentRect.height / designHeight
|
|
56
|
+
setTransform(`scale(${scale})`)
|
|
57
|
+
setWidth(px(contentRect.width / scale))
|
|
58
|
+
setHeight(px(designHeight))
|
|
59
|
+
} else {
|
|
60
|
+
const scale = Math.min(contentRect.width / designWidth, contentRect.height / designHeight)
|
|
61
|
+
setTransform(
|
|
62
|
+
`translateX(${(contentRect.width - designWidth * scale) / 2}px) translateY(${(contentRect.height - designHeight * scale) / 2}px) scale(${scale})`,
|
|
63
|
+
)
|
|
64
|
+
setWidth(px(designWidth))
|
|
65
|
+
setHeight(px(designHeight))
|
|
66
|
+
}
|
|
67
|
+
setShow(true)
|
|
68
|
+
}
|
|
69
|
+
const observer = new ResizeObserver(listener)
|
|
70
|
+
observer.observe(parent)
|
|
71
|
+
return () => observer.disconnect()
|
|
72
|
+
}, [ele.current?.parentElement, designWidth, designHeight, direction])
|
|
73
|
+
|
|
74
|
+
if (process.env.NODE_ENV === "development") {
|
|
75
|
+
useEffect(() => {
|
|
76
|
+
const parent = ele.current?.parentElement
|
|
77
|
+
if (!parent) return
|
|
78
|
+
const style = getComputedStyle(parent)
|
|
79
|
+
if (style.position === "static") {
|
|
80
|
+
console.warn("AutoFit 组件的父元素的 position 属性不应该是 static")
|
|
81
|
+
}
|
|
82
|
+
}, [ele.current?.parentElement])
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
if (!show) return <div ref={ele} style={{ display: "none" }} />
|
|
86
|
+
|
|
87
|
+
return (
|
|
88
|
+
<div
|
|
89
|
+
ref={ele}
|
|
90
|
+
className={clsx(
|
|
91
|
+
css`
|
|
92
|
+
position: absolute;
|
|
93
|
+
transform: var(--transform);
|
|
94
|
+
transform-origin: top left;
|
|
95
|
+
width: var(--width);
|
|
96
|
+
height: var(--height);
|
|
97
|
+
`,
|
|
98
|
+
className,
|
|
99
|
+
)}
|
|
100
|
+
style={transformCSSVariable({ transform, width, height }, style)}
|
|
101
|
+
{...rest}
|
|
102
|
+
/>
|
|
103
|
+
)
|
|
104
|
+
})
|
|
@@ -1,150 +1,150 @@
|
|
|
1
|
-
"use client"
|
|
2
|
-
|
|
3
|
-
import { css } from "@emotion/css"
|
|
4
|
-
import { clsx, getArray } from "deepsea-tools"
|
|
5
|
-
import { CSSProperties, MouseEvent as ReactMouseEvent, forwardRef, useEffect, useImperativeHandle, useRef } from "react"
|
|
6
|
-
import Scrollbar from "smooth-scrollbar"
|
|
7
|
-
import { ScrollStatus } from "smooth-scrollbar/interfaces/scrollbar"
|
|
8
|
-
import { useSize } from "soda-hooks"
|
|
9
|
-
|
|
10
|
-
import { px, transformCSSVariable } from "@/utils"
|
|
11
|
-
|
|
12
|
-
import { Scroll, ScrollProps } from "./Scroll"
|
|
13
|
-
|
|
14
|
-
export interface AutoScrollProps extends ScrollProps {
|
|
15
|
-
/** 轮播元素的个数 */
|
|
16
|
-
count: number
|
|
17
|
-
|
|
18
|
-
/** 轮播元素的高度 */
|
|
19
|
-
itemHeight: number
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* 轮播动画的时间,单位毫秒
|
|
23
|
-
* @default 1000
|
|
24
|
-
*/
|
|
25
|
-
animation?: number
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* 每个元素的停留时间,单位毫秒
|
|
29
|
-
* @default 3000
|
|
30
|
-
*/
|
|
31
|
-
duration?: number
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* 元素之间的间距
|
|
35
|
-
* @default 0
|
|
36
|
-
*/
|
|
37
|
-
gap?: number
|
|
38
|
-
|
|
39
|
-
/** 容器类名 */
|
|
40
|
-
containerClassName?: string
|
|
41
|
-
|
|
42
|
-
/** 容器样式 */
|
|
43
|
-
containerStyle?: CSSProperties
|
|
44
|
-
|
|
45
|
-
/**
|
|
46
|
-
* 在鼠标移入时是否继续播放
|
|
47
|
-
* @default false
|
|
48
|
-
*/
|
|
49
|
-
playOnMouseEnter?: boolean
|
|
50
|
-
|
|
51
|
-
/**
|
|
52
|
-
* 是否暂停
|
|
53
|
-
* @default false
|
|
54
|
-
*/
|
|
55
|
-
paused?: boolean
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
export const AutoScroll = forwardRef<HTMLDivElement, AutoScrollProps>((props, ref) => {
|
|
59
|
-
const {
|
|
60
|
-
count,
|
|
61
|
-
itemHeight,
|
|
62
|
-
animation = 1000,
|
|
63
|
-
duration = 3000,
|
|
64
|
-
onMouseEnter,
|
|
65
|
-
onMouseLeave,
|
|
66
|
-
gap = 0,
|
|
67
|
-
containerClassName,
|
|
68
|
-
containerStyle,
|
|
69
|
-
children,
|
|
70
|
-
playOnMouseEnter,
|
|
71
|
-
scrollbar,
|
|
72
|
-
paused,
|
|
73
|
-
...rest
|
|
74
|
-
} = props
|
|
75
|
-
const bar = useRef<Scrollbar | null>(null)
|
|
76
|
-
const timeout = useRef<NodeJS.Timeout | undefined>(undefined)
|
|
77
|
-
const ele = useRef<HTMLDivElement>(null)
|
|
78
|
-
const size = useSize(ele)
|
|
79
|
-
const pausedRef = useRef(false)
|
|
80
|
-
const pausedProps = useRef(paused)
|
|
81
|
-
pausedProps.current = paused
|
|
82
|
-
|
|
83
|
-
useImperativeHandle(ref, () => ele.current!, [])
|
|
84
|
-
useImperativeHandle(scrollbar, () => bar.current!, [])
|
|
85
|
-
|
|
86
|
-
useEffect(() => {
|
|
87
|
-
if (playOnMouseEnter) pausedRef.current = false
|
|
88
|
-
}, [playOnMouseEnter])
|
|
89
|
-
|
|
90
|
-
useEffect(() => {
|
|
91
|
-
if (!size || count === 0) return
|
|
92
|
-
const { height } = size
|
|
93
|
-
const range = getArray(count, index => (itemHeight + gap) * (index + 1) - (index === count - 1 ? gap : 0))
|
|
94
|
-
const scrollHeight = range[range.length - 1]
|
|
95
|
-
if (height >= scrollHeight) return
|
|
96
|
-
function scroll(target: number) {
|
|
97
|
-
clearTimeout(timeout.current)
|
|
98
|
-
timeout.current = setTimeout(() => {
|
|
99
|
-
if (pausedRef.current || pausedProps.current) return scroll(target)
|
|
100
|
-
bar.current?.scrollTo(0, target, animation)
|
|
101
|
-
}, duration)
|
|
102
|
-
}
|
|
103
|
-
scroll(range[0])
|
|
104
|
-
function listener(status: ScrollStatus) {
|
|
105
|
-
const { y } = status.offset
|
|
106
|
-
const scrollToBottom = Math.abs(y + height - scrollHeight) / itemHeight <= 0.05
|
|
107
|
-
const target = scrollToBottom ? 0 : range.find(item => item > y)!
|
|
108
|
-
scroll(target)
|
|
109
|
-
}
|
|
110
|
-
bar.current?.addListener(listener)
|
|
111
|
-
return () => {
|
|
112
|
-
clearTimeout(timeout.current)
|
|
113
|
-
bar.current?.removeListener(listener)
|
|
114
|
-
bar.current?.scrollTo(0, 0)
|
|
115
|
-
}
|
|
116
|
-
}, [size, count, itemHeight, gap, duration, animation])
|
|
117
|
-
|
|
118
|
-
function onContainerMouseEnter(e: ReactMouseEvent<HTMLDivElement, MouseEvent>) {
|
|
119
|
-
if (playOnMouseEnter) return
|
|
120
|
-
pausedRef.current = true
|
|
121
|
-
onMouseEnter?.(e)
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
function onContainerMouseLeave(e: ReactMouseEvent<HTMLDivElement, MouseEvent>) {
|
|
125
|
-
if (playOnMouseEnter) return
|
|
126
|
-
pausedRef.current = false
|
|
127
|
-
onMouseLeave?.(e)
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
return (
|
|
131
|
-
<Scroll ref={ele} scrollbar={bar} onMouseEnter={onContainerMouseEnter} onMouseLeave={onContainerMouseLeave} {...rest}>
|
|
132
|
-
<div
|
|
133
|
-
className={clsx(
|
|
134
|
-
css`
|
|
135
|
-
display: flex;
|
|
136
|
-
flex-direction: column;
|
|
137
|
-
gap: var(--gap, 0);
|
|
138
|
-
& > * {
|
|
139
|
-
flex: none;
|
|
140
|
-
}
|
|
141
|
-
`,
|
|
142
|
-
containerClassName,
|
|
143
|
-
)}
|
|
144
|
-
style={transformCSSVariable({ gap: px(gap) }, containerStyle)}
|
|
145
|
-
>
|
|
146
|
-
{children}
|
|
147
|
-
</div>
|
|
148
|
-
</Scroll>
|
|
149
|
-
)
|
|
150
|
-
})
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import { css } from "@emotion/css"
|
|
4
|
+
import { clsx, getArray } from "deepsea-tools"
|
|
5
|
+
import { CSSProperties, MouseEvent as ReactMouseEvent, forwardRef, useEffect, useImperativeHandle, useRef } from "react"
|
|
6
|
+
import Scrollbar from "smooth-scrollbar"
|
|
7
|
+
import { ScrollStatus } from "smooth-scrollbar/interfaces/scrollbar"
|
|
8
|
+
import { useSize } from "soda-hooks"
|
|
9
|
+
|
|
10
|
+
import { px, transformCSSVariable } from "@/utils"
|
|
11
|
+
|
|
12
|
+
import { Scroll, ScrollProps } from "./Scroll"
|
|
13
|
+
|
|
14
|
+
export interface AutoScrollProps extends ScrollProps {
|
|
15
|
+
/** 轮播元素的个数 */
|
|
16
|
+
count: number
|
|
17
|
+
|
|
18
|
+
/** 轮播元素的高度 */
|
|
19
|
+
itemHeight: number
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* 轮播动画的时间,单位毫秒
|
|
23
|
+
* @default 1000
|
|
24
|
+
*/
|
|
25
|
+
animation?: number
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* 每个元素的停留时间,单位毫秒
|
|
29
|
+
* @default 3000
|
|
30
|
+
*/
|
|
31
|
+
duration?: number
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* 元素之间的间距
|
|
35
|
+
* @default 0
|
|
36
|
+
*/
|
|
37
|
+
gap?: number
|
|
38
|
+
|
|
39
|
+
/** 容器类名 */
|
|
40
|
+
containerClassName?: string
|
|
41
|
+
|
|
42
|
+
/** 容器样式 */
|
|
43
|
+
containerStyle?: CSSProperties
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* 在鼠标移入时是否继续播放
|
|
47
|
+
* @default false
|
|
48
|
+
*/
|
|
49
|
+
playOnMouseEnter?: boolean
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* 是否暂停
|
|
53
|
+
* @default false
|
|
54
|
+
*/
|
|
55
|
+
paused?: boolean
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export const AutoScroll = forwardRef<HTMLDivElement, AutoScrollProps>((props, ref) => {
|
|
59
|
+
const {
|
|
60
|
+
count,
|
|
61
|
+
itemHeight,
|
|
62
|
+
animation = 1000,
|
|
63
|
+
duration = 3000,
|
|
64
|
+
onMouseEnter,
|
|
65
|
+
onMouseLeave,
|
|
66
|
+
gap = 0,
|
|
67
|
+
containerClassName,
|
|
68
|
+
containerStyle,
|
|
69
|
+
children,
|
|
70
|
+
playOnMouseEnter,
|
|
71
|
+
scrollbar,
|
|
72
|
+
paused,
|
|
73
|
+
...rest
|
|
74
|
+
} = props
|
|
75
|
+
const bar = useRef<Scrollbar | null>(null)
|
|
76
|
+
const timeout = useRef<NodeJS.Timeout | undefined>(undefined)
|
|
77
|
+
const ele = useRef<HTMLDivElement>(null)
|
|
78
|
+
const size = useSize(ele)
|
|
79
|
+
const pausedRef = useRef(false)
|
|
80
|
+
const pausedProps = useRef(paused)
|
|
81
|
+
pausedProps.current = paused
|
|
82
|
+
|
|
83
|
+
useImperativeHandle(ref, () => ele.current!, [])
|
|
84
|
+
useImperativeHandle(scrollbar, () => bar.current!, [])
|
|
85
|
+
|
|
86
|
+
useEffect(() => {
|
|
87
|
+
if (playOnMouseEnter) pausedRef.current = false
|
|
88
|
+
}, [playOnMouseEnter])
|
|
89
|
+
|
|
90
|
+
useEffect(() => {
|
|
91
|
+
if (!size || count === 0) return
|
|
92
|
+
const { height } = size
|
|
93
|
+
const range = getArray(count, index => (itemHeight + gap) * (index + 1) - (index === count - 1 ? gap : 0))
|
|
94
|
+
const scrollHeight = range[range.length - 1]
|
|
95
|
+
if (height >= scrollHeight) return
|
|
96
|
+
function scroll(target: number) {
|
|
97
|
+
clearTimeout(timeout.current)
|
|
98
|
+
timeout.current = setTimeout(() => {
|
|
99
|
+
if (pausedRef.current || pausedProps.current) return scroll(target)
|
|
100
|
+
bar.current?.scrollTo(0, target, animation)
|
|
101
|
+
}, duration)
|
|
102
|
+
}
|
|
103
|
+
scroll(range[0])
|
|
104
|
+
function listener(status: ScrollStatus) {
|
|
105
|
+
const { y } = status.offset
|
|
106
|
+
const scrollToBottom = Math.abs(y + height - scrollHeight) / itemHeight <= 0.05
|
|
107
|
+
const target = scrollToBottom ? 0 : range.find(item => item > y)!
|
|
108
|
+
scroll(target)
|
|
109
|
+
}
|
|
110
|
+
bar.current?.addListener(listener)
|
|
111
|
+
return () => {
|
|
112
|
+
clearTimeout(timeout.current)
|
|
113
|
+
bar.current?.removeListener(listener)
|
|
114
|
+
bar.current?.scrollTo(0, 0)
|
|
115
|
+
}
|
|
116
|
+
}, [size, count, itemHeight, gap, duration, animation])
|
|
117
|
+
|
|
118
|
+
function onContainerMouseEnter(e: ReactMouseEvent<HTMLDivElement, MouseEvent>) {
|
|
119
|
+
if (playOnMouseEnter) return
|
|
120
|
+
pausedRef.current = true
|
|
121
|
+
onMouseEnter?.(e)
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function onContainerMouseLeave(e: ReactMouseEvent<HTMLDivElement, MouseEvent>) {
|
|
125
|
+
if (playOnMouseEnter) return
|
|
126
|
+
pausedRef.current = false
|
|
127
|
+
onMouseLeave?.(e)
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
return (
|
|
131
|
+
<Scroll ref={ele} scrollbar={bar} onMouseEnter={onContainerMouseEnter} onMouseLeave={onContainerMouseLeave} {...rest}>
|
|
132
|
+
<div
|
|
133
|
+
className={clsx(
|
|
134
|
+
css`
|
|
135
|
+
display: flex;
|
|
136
|
+
flex-direction: column;
|
|
137
|
+
gap: var(--gap, 0);
|
|
138
|
+
& > * {
|
|
139
|
+
flex: none;
|
|
140
|
+
}
|
|
141
|
+
`,
|
|
142
|
+
containerClassName,
|
|
143
|
+
)}
|
|
144
|
+
style={transformCSSVariable({ gap: px(gap) }, containerStyle)}
|
|
145
|
+
>
|
|
146
|
+
{children}
|
|
147
|
+
</div>
|
|
148
|
+
</Scroll>
|
|
149
|
+
)
|
|
150
|
+
})
|