deepsea-components 5.15.21 → 5.15.23
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 +7 -7
- package/dist/components/AutoFit.js +7 -7
- 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/FormLabel.cjs +1 -1
- package/dist/components/FormLabel.js +1 -1
- 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/ScrollMask.module.cjs +2 -2
- package/dist/components/ScrollMask.module.js +2 -2
- package/dist/components/Skeleton.cjs +17 -17
- package/dist/components/Skeleton.js +17 -17
- package/dist/components/TransitionNum.cjs +1 -1
- package/dist/components/TransitionNum.js +1 -1
- package/dist/components/Trapezium.cjs +4 -4
- package/dist/components/Trapezium.js +4 -4
- package/package.json +4 -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
|
@@ -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
|
+
})
|
|
@@ -1,50 +1,50 @@
|
|
|
1
|
-
"use client"
|
|
2
|
-
|
|
3
|
-
import { css } from "@emotion/css"
|
|
4
|
-
import { clsx } from "deepsea-tools"
|
|
5
|
-
import { TextareaHTMLAttributes, forwardRef, useImperativeHandle, useLayoutEffect, useRef, useState } from "react"
|
|
6
|
-
|
|
7
|
-
import { px, transformCSSVariable } from "@/utils"
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* 自适应高度的文本域
|
|
11
|
-
*/
|
|
12
|
-
export const AutoSizeTextArea = forwardRef<HTMLTextAreaElement, TextareaHTMLAttributes<HTMLTextAreaElement>>((props, ref) => {
|
|
13
|
-
const { className, style, ...rest } = props
|
|
14
|
-
const [height, setHeight] = useState<string | undefined>(undefined)
|
|
15
|
-
const ele = useRef<HTMLTextAreaElement>(null)
|
|
16
|
-
|
|
17
|
-
useImperativeHandle(ref, () => ele.current!, [])
|
|
18
|
-
|
|
19
|
-
useLayoutEffect(() => {
|
|
20
|
-
const textarea = ele.current!
|
|
21
|
-
function resizeTextarea() {
|
|
22
|
-
setHeight("auto")
|
|
23
|
-
setHeight(px(textarea.scrollHeight + textarea.offsetHeight - textarea.clientHeight))
|
|
24
|
-
}
|
|
25
|
-
resizeTextarea()
|
|
26
|
-
textarea.addEventListener("input", resizeTextarea)
|
|
27
|
-
textarea.addEventListener("change", resizeTextarea)
|
|
28
|
-
|
|
29
|
-
return () => {
|
|
30
|
-
textarea.removeEventListener("input", resizeTextarea)
|
|
31
|
-
textarea.removeEventListener("change", resizeTextarea)
|
|
32
|
-
}
|
|
33
|
-
}, [])
|
|
34
|
-
|
|
35
|
-
return (
|
|
36
|
-
<textarea
|
|
37
|
-
ref={ele}
|
|
38
|
-
className={clsx(
|
|
39
|
-
css`
|
|
40
|
-
height: var(--height);
|
|
41
|
-
resize: none;
|
|
42
|
-
overflow-y: hidden;
|
|
43
|
-
`,
|
|
44
|
-
className,
|
|
45
|
-
)}
|
|
46
|
-
style={transformCSSVariable({ height }, style)}
|
|
47
|
-
{...rest}
|
|
48
|
-
/>
|
|
49
|
-
)
|
|
50
|
-
})
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import { css } from "@emotion/css"
|
|
4
|
+
import { clsx } from "deepsea-tools"
|
|
5
|
+
import { TextareaHTMLAttributes, forwardRef, useImperativeHandle, useLayoutEffect, useRef, useState } from "react"
|
|
6
|
+
|
|
7
|
+
import { px, transformCSSVariable } from "@/utils"
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* 自适应高度的文本域
|
|
11
|
+
*/
|
|
12
|
+
export const AutoSizeTextArea = forwardRef<HTMLTextAreaElement, TextareaHTMLAttributes<HTMLTextAreaElement>>((props, ref) => {
|
|
13
|
+
const { className, style, ...rest } = props
|
|
14
|
+
const [height, setHeight] = useState<string | undefined>(undefined)
|
|
15
|
+
const ele = useRef<HTMLTextAreaElement>(null)
|
|
16
|
+
|
|
17
|
+
useImperativeHandle(ref, () => ele.current!, [])
|
|
18
|
+
|
|
19
|
+
useLayoutEffect(() => {
|
|
20
|
+
const textarea = ele.current!
|
|
21
|
+
function resizeTextarea() {
|
|
22
|
+
setHeight("auto")
|
|
23
|
+
setHeight(px(textarea.scrollHeight + textarea.offsetHeight - textarea.clientHeight))
|
|
24
|
+
}
|
|
25
|
+
resizeTextarea()
|
|
26
|
+
textarea.addEventListener("input", resizeTextarea)
|
|
27
|
+
textarea.addEventListener("change", resizeTextarea)
|
|
28
|
+
|
|
29
|
+
return () => {
|
|
30
|
+
textarea.removeEventListener("input", resizeTextarea)
|
|
31
|
+
textarea.removeEventListener("change", resizeTextarea)
|
|
32
|
+
}
|
|
33
|
+
}, [])
|
|
34
|
+
|
|
35
|
+
return (
|
|
36
|
+
<textarea
|
|
37
|
+
ref={ele}
|
|
38
|
+
className={clsx(
|
|
39
|
+
css`
|
|
40
|
+
height: var(--height);
|
|
41
|
+
resize: none;
|
|
42
|
+
overflow-y: hidden;
|
|
43
|
+
`,
|
|
44
|
+
className,
|
|
45
|
+
)}
|
|
46
|
+
style={transformCSSVariable({ height }, style)}
|
|
47
|
+
{...rest}
|
|
48
|
+
/>
|
|
49
|
+
)
|
|
50
|
+
})
|
|
@@ -1,81 +1,81 @@
|
|
|
1
|
-
"use client"
|
|
2
|
-
|
|
3
|
-
import { FC, Fragment, HTMLAttributes } from "react"
|
|
4
|
-
|
|
5
|
-
export interface CircleTextProps extends Omit<HTMLAttributes<HTMLSpanElement>, "children"> {
|
|
6
|
-
/** 每一个方块的宽度 */
|
|
7
|
-
width: number
|
|
8
|
-
/** 每一个方块的高度 */
|
|
9
|
-
height: number
|
|
10
|
-
/** 圆弧的半径,不包含方块的高度 */
|
|
11
|
-
radius: number
|
|
12
|
-
/** 开始旋转的弧度,逆时针增加,0 为 x 轴方向 */
|
|
13
|
-
startAngel?: number
|
|
14
|
-
/** 每一个方块之间间隔的弧度 */
|
|
15
|
-
gapAngel?: number
|
|
16
|
-
/** 文字对齐的方式,默认居中 */
|
|
17
|
-
align?: "left" | "center" | "right"
|
|
18
|
-
/** 文字朝向圆心还是外侧,默认外侧 */
|
|
19
|
-
direction?: "inner" | "outer"
|
|
20
|
-
/** 是否反转文字顺序 */
|
|
21
|
-
reverse?: boolean
|
|
22
|
-
/** 分割文字的方法 */
|
|
23
|
-
separator?: string | RegExp | ((text: string) => string[])
|
|
24
|
-
/** 显示的文字 */
|
|
25
|
-
children: string
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
/** 环形文字 */
|
|
29
|
-
export const CircleText: FC<CircleTextProps> = props => {
|
|
30
|
-
const {
|
|
31
|
-
width,
|
|
32
|
-
height,
|
|
33
|
-
radius,
|
|
34
|
-
startAngel = 0,
|
|
35
|
-
gapAngel = 0,
|
|
36
|
-
align = "center",
|
|
37
|
-
style,
|
|
38
|
-
direction = "outer",
|
|
39
|
-
reverse = false,
|
|
40
|
-
separator,
|
|
41
|
-
children,
|
|
42
|
-
...rest
|
|
43
|
-
} = props
|
|
44
|
-
const unitAngle = Math.atan(width / 2 / radius) * 2
|
|
45
|
-
const totalAngle = (unitAngle + gapAngel) * children.length - gapAngel
|
|
46
|
-
const offsetAngle = align === "left" ? 0 : align === "right" ? totalAngle : totalAngle / 2
|
|
47
|
-
|
|
48
|
-
function getTransform(idx: number) {
|
|
49
|
-
const angle = startAngel - idx * (unitAngle + gapAngel) + offsetAngle - unitAngle / 2
|
|
50
|
-
const x = (radius + height / 2) * Math.cos(angle) - width / 2
|
|
51
|
-
const y = (radius + height / 2) * Math.sin(angle) * -1 - height / 2
|
|
52
|
-
const z = Math.PI / 2 - angle + (direction === "inner" ? Math.PI : 0)
|
|
53
|
-
return `translateX(${x}px) translateY(${y}px) rotateZ(${(z / Math.PI) * 180}deg)`
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
const words = typeof separator === "function" ? separator(children) : children.split(separator ?? "")
|
|
57
|
-
|
|
58
|
-
if (reverse) words.reverse()
|
|
59
|
-
|
|
60
|
-
return (
|
|
61
|
-
<Fragment>
|
|
62
|
-
{words.map((w, idx) => (
|
|
63
|
-
<span
|
|
64
|
-
key={idx}
|
|
65
|
-
style={{
|
|
66
|
-
position: "absolute",
|
|
67
|
-
...style,
|
|
68
|
-
transform: getTransform(idx),
|
|
69
|
-
textAlign: "center",
|
|
70
|
-
width,
|
|
71
|
-
lineHeight: `${height}px`,
|
|
72
|
-
height: height,
|
|
73
|
-
}}
|
|
74
|
-
{...rest}
|
|
75
|
-
>
|
|
76
|
-
{w}
|
|
77
|
-
</span>
|
|
78
|
-
))}
|
|
79
|
-
</Fragment>
|
|
80
|
-
)
|
|
81
|
-
}
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import { FC, Fragment, HTMLAttributes } from "react"
|
|
4
|
+
|
|
5
|
+
export interface CircleTextProps extends Omit<HTMLAttributes<HTMLSpanElement>, "children"> {
|
|
6
|
+
/** 每一个方块的宽度 */
|
|
7
|
+
width: number
|
|
8
|
+
/** 每一个方块的高度 */
|
|
9
|
+
height: number
|
|
10
|
+
/** 圆弧的半径,不包含方块的高度 */
|
|
11
|
+
radius: number
|
|
12
|
+
/** 开始旋转的弧度,逆时针增加,0 为 x 轴方向 */
|
|
13
|
+
startAngel?: number
|
|
14
|
+
/** 每一个方块之间间隔的弧度 */
|
|
15
|
+
gapAngel?: number
|
|
16
|
+
/** 文字对齐的方式,默认居中 */
|
|
17
|
+
align?: "left" | "center" | "right"
|
|
18
|
+
/** 文字朝向圆心还是外侧,默认外侧 */
|
|
19
|
+
direction?: "inner" | "outer"
|
|
20
|
+
/** 是否反转文字顺序 */
|
|
21
|
+
reverse?: boolean
|
|
22
|
+
/** 分割文字的方法 */
|
|
23
|
+
separator?: string | RegExp | ((text: string) => string[])
|
|
24
|
+
/** 显示的文字 */
|
|
25
|
+
children: string
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** 环形文字 */
|
|
29
|
+
export const CircleText: FC<CircleTextProps> = props => {
|
|
30
|
+
const {
|
|
31
|
+
width,
|
|
32
|
+
height,
|
|
33
|
+
radius,
|
|
34
|
+
startAngel = 0,
|
|
35
|
+
gapAngel = 0,
|
|
36
|
+
align = "center",
|
|
37
|
+
style,
|
|
38
|
+
direction = "outer",
|
|
39
|
+
reverse = false,
|
|
40
|
+
separator,
|
|
41
|
+
children,
|
|
42
|
+
...rest
|
|
43
|
+
} = props
|
|
44
|
+
const unitAngle = Math.atan(width / 2 / radius) * 2
|
|
45
|
+
const totalAngle = (unitAngle + gapAngel) * children.length - gapAngel
|
|
46
|
+
const offsetAngle = align === "left" ? 0 : align === "right" ? totalAngle : totalAngle / 2
|
|
47
|
+
|
|
48
|
+
function getTransform(idx: number) {
|
|
49
|
+
const angle = startAngel - idx * (unitAngle + gapAngel) + offsetAngle - unitAngle / 2
|
|
50
|
+
const x = (radius + height / 2) * Math.cos(angle) - width / 2
|
|
51
|
+
const y = (radius + height / 2) * Math.sin(angle) * -1 - height / 2
|
|
52
|
+
const z = Math.PI / 2 - angle + (direction === "inner" ? Math.PI : 0)
|
|
53
|
+
return `translateX(${x}px) translateY(${y}px) rotateZ(${(z / Math.PI) * 180}deg)`
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const words = typeof separator === "function" ? separator(children) : children.split(separator ?? "")
|
|
57
|
+
|
|
58
|
+
if (reverse) words.reverse()
|
|
59
|
+
|
|
60
|
+
return (
|
|
61
|
+
<Fragment>
|
|
62
|
+
{words.map((w, idx) => (
|
|
63
|
+
<span
|
|
64
|
+
key={idx}
|
|
65
|
+
style={{
|
|
66
|
+
position: "absolute",
|
|
67
|
+
...style,
|
|
68
|
+
transform: getTransform(idx),
|
|
69
|
+
textAlign: "center",
|
|
70
|
+
width,
|
|
71
|
+
lineHeight: `${height}px`,
|
|
72
|
+
height: height,
|
|
73
|
+
}}
|
|
74
|
+
{...rest}
|
|
75
|
+
>
|
|
76
|
+
{w}
|
|
77
|
+
</span>
|
|
78
|
+
))}
|
|
79
|
+
</Fragment>
|
|
80
|
+
)
|
|
81
|
+
}
|
|
@@ -1,31 +1,31 @@
|
|
|
1
|
-
"use client"
|
|
2
|
-
|
|
3
|
-
import ClipboardJS, { Event } from "clipboard"
|
|
4
|
-
import { ComponentPropsWithoutRef, forwardRef, useEffect, useImperativeHandle, useRef } from "react"
|
|
5
|
-
import { useLatest } from "soda-hooks"
|
|
6
|
-
|
|
7
|
-
export { Event } from "clipboard"
|
|
8
|
-
|
|
9
|
-
export type CopyButtonProps = ComponentPropsWithoutRef<"button"> & {
|
|
10
|
-
text?: string
|
|
11
|
-
onCopySuccess?: (e: Event) => void
|
|
12
|
-
onCopyError?: (e: Event) => void
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export const CopyButton = forwardRef<HTMLButtonElement, CopyButtonProps>((props, ref) => {
|
|
16
|
-
const { text, onCopySuccess: _onCoptSuccess, onCopyError: _onCopyError, ...rest } = props
|
|
17
|
-
const ele = useRef<HTMLButtonElement>(null)
|
|
18
|
-
const onCopySuccess = useLatest(_onCoptSuccess)
|
|
19
|
-
const onCopyError = useLatest(_onCopyError)
|
|
20
|
-
|
|
21
|
-
useImperativeHandle(ref, () => ele.current!, [ele.current])
|
|
22
|
-
|
|
23
|
-
useEffect(() => {
|
|
24
|
-
const clipboard = new ClipboardJS(ele.current!)
|
|
25
|
-
clipboard.on("success", event => onCopySuccess.current?.(event))
|
|
26
|
-
clipboard.on("error", event => onCopyError.current?.(event))
|
|
27
|
-
return () => clipboard.destroy()
|
|
28
|
-
}, [])
|
|
29
|
-
|
|
30
|
-
return <button ref={ele} {...rest} data-clipboard-text={text} />
|
|
31
|
-
})
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import ClipboardJS, { Event } from "clipboard"
|
|
4
|
+
import { ComponentPropsWithoutRef, forwardRef, useEffect, useImperativeHandle, useRef } from "react"
|
|
5
|
+
import { useLatest } from "soda-hooks"
|
|
6
|
+
|
|
7
|
+
export { Event } from "clipboard"
|
|
8
|
+
|
|
9
|
+
export type CopyButtonProps = ComponentPropsWithoutRef<"button"> & {
|
|
10
|
+
text?: string
|
|
11
|
+
onCopySuccess?: (e: Event) => void
|
|
12
|
+
onCopyError?: (e: Event) => void
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export const CopyButton = forwardRef<HTMLButtonElement, CopyButtonProps>((props, ref) => {
|
|
16
|
+
const { text, onCopySuccess: _onCoptSuccess, onCopyError: _onCopyError, ...rest } = props
|
|
17
|
+
const ele = useRef<HTMLButtonElement>(null)
|
|
18
|
+
const onCopySuccess = useLatest(_onCoptSuccess)
|
|
19
|
+
const onCopyError = useLatest(_onCopyError)
|
|
20
|
+
|
|
21
|
+
useImperativeHandle(ref, () => ele.current!, [ele.current])
|
|
22
|
+
|
|
23
|
+
useEffect(() => {
|
|
24
|
+
const clipboard = new ClipboardJS(ele.current!)
|
|
25
|
+
clipboard.on("success", event => onCopySuccess.current?.(event))
|
|
26
|
+
clipboard.on("error", event => onCopyError.current?.(event))
|
|
27
|
+
return () => clipboard.destroy()
|
|
28
|
+
}, [])
|
|
29
|
+
|
|
30
|
+
return <button ref={ele} {...rest} data-clipboard-text={text} />
|
|
31
|
+
})
|