deepsea-components 5.6.0 → 5.7.1

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 (92) hide show
  1. package/README.md +23 -23
  2. package/dist/cjs/components/AutoFit.js +3 -1
  3. package/dist/cjs/components/AutoFit.js.map +2 -2
  4. package/dist/cjs/components/AutoScroll.js +16 -1
  5. package/dist/cjs/components/AutoScroll.js.map +2 -2
  6. package/dist/cjs/components/AutoSizeTextarea.js.map +1 -1
  7. package/dist/cjs/components/CircleText.js +31 -2
  8. package/dist/cjs/components/CircleText.js.map +2 -2
  9. package/dist/cjs/components/CopyButton.d.ts +9 -0
  10. package/dist/cjs/components/CopyButton.js +59 -0
  11. package/dist/cjs/components/CopyButton.js.map +7 -0
  12. package/dist/cjs/components/Echart.js.map +2 -2
  13. package/dist/cjs/components/Flow.js +56 -3
  14. package/dist/cjs/components/Flow.js.map +2 -2
  15. package/dist/cjs/components/FormLabel.js.map +1 -1
  16. package/dist/cjs/components/HlsPlayer.js.map +1 -1
  17. package/dist/cjs/components/InfiniteScroll.js +15 -1
  18. package/dist/cjs/components/InfiniteScroll.js.map +2 -2
  19. package/dist/cjs/components/InputFile.js.map +1 -1
  20. package/dist/cjs/components/LoopSwiper.js +23 -2
  21. package/dist/cjs/components/LoopSwiper.js.map +2 -2
  22. package/dist/cjs/components/Ring.js +13 -1
  23. package/dist/cjs/components/Ring.js.map +2 -2
  24. package/dist/cjs/components/Scroll.js.map +2 -2
  25. package/dist/cjs/components/SectionRing.js +3 -1
  26. package/dist/cjs/components/SectionRing.js.map +2 -2
  27. package/dist/cjs/components/Skeleton.js.map +2 -2
  28. package/dist/cjs/components/Title.js.map +2 -2
  29. package/dist/cjs/components/TransitionBox.js +9 -1
  30. package/dist/cjs/components/TransitionBox.js.map +2 -2
  31. package/dist/cjs/components/TransitionNum.js.map +1 -1
  32. package/dist/cjs/components/Trapezium.js.map +1 -1
  33. package/dist/cjs/index.d.ts +1 -0
  34. package/dist/cjs/index.js +2 -0
  35. package/dist/cjs/index.js.map +2 -2
  36. package/dist/cjs/utils/getReactVersion.js.map +1 -1
  37. package/dist/cjs/utils/index.js.map +1 -1
  38. package/dist/esm/components/AutoFit.js +9 -9
  39. package/dist/esm/components/AutoFit.js.map +1 -1
  40. package/dist/esm/components/AutoScroll.js.map +1 -1
  41. package/dist/esm/components/AutoSizeTextarea.js +2 -2
  42. package/dist/esm/components/AutoSizeTextarea.js.map +1 -1
  43. package/dist/esm/components/CircleText.js.map +1 -1
  44. package/dist/esm/components/CopyButton.d.ts +9 -0
  45. package/dist/esm/components/CopyButton.js +29 -0
  46. package/dist/esm/components/CopyButton.js.map +1 -0
  47. package/dist/esm/components/Echart.js.map +1 -1
  48. package/dist/esm/components/Flow.js.map +1 -1
  49. package/dist/esm/components/FormLabel.js +2 -2
  50. package/dist/esm/components/FormLabel.js.map +1 -1
  51. package/dist/esm/components/HlsPlayer.js.map +1 -1
  52. package/dist/esm/components/InfiniteScroll.js +4 -4
  53. package/dist/esm/components/InfiniteScroll.js.map +1 -1
  54. package/dist/esm/components/InputFile.js.map +1 -1
  55. package/dist/esm/components/LoopSwiper.js.map +1 -1
  56. package/dist/esm/components/Ring.js.map +1 -1
  57. package/dist/esm/components/Scroll.js.map +1 -1
  58. package/dist/esm/components/SectionRing.js.map +1 -1
  59. package/dist/esm/components/Skeleton.js.map +1 -1
  60. package/dist/esm/components/Title.js.map +1 -1
  61. package/dist/esm/components/TransitionBox.js.map +1 -1
  62. package/dist/esm/components/TransitionNum.js.map +1 -1
  63. package/dist/esm/components/Trapezium.js.map +1 -1
  64. package/dist/esm/index.d.ts +1 -0
  65. package/dist/esm/index.js +1 -0
  66. package/dist/esm/index.js.map +1 -1
  67. package/dist/esm/utils/getReactVersion.js.map +1 -1
  68. package/dist/esm/utils/index.js.map +1 -1
  69. package/package.json +4 -2
  70. package/src/components/AutoFit.tsx +103 -101
  71. package/src/components/AutoScroll.tsx +148 -132
  72. package/src/components/AutoSizeTextarea.tsx +49 -49
  73. package/src/components/CircleText.tsx +81 -56
  74. package/src/components/CopyButton.tsx +29 -0
  75. package/src/components/Echart.tsx +67 -44
  76. package/src/components/Flow.tsx +270 -214
  77. package/src/components/FormLabel.tsx +38 -38
  78. package/src/components/HlsPlayer.tsx +34 -34
  79. package/src/components/InfiniteScroll.tsx +161 -135
  80. package/src/components/InputFile.tsx +234 -234
  81. package/src/components/LoopSwiper.tsx +125 -101
  82. package/src/components/Ring.tsx +30 -19
  83. package/src/components/Scroll.tsx +96 -95
  84. package/src/components/SectionRing.tsx +39 -36
  85. package/src/components/Skeleton.tsx +47 -46
  86. package/src/components/Title.tsx +23 -24
  87. package/src/components/TransitionBox.tsx +44 -36
  88. package/src/components/TransitionNum.tsx +54 -54
  89. package/src/components/Trapezium.tsx +59 -59
  90. package/src/index.ts +1 -0
  91. package/src/utils/getReactVersion.ts +7 -7
  92. package/src/utils/index.ts +33 -33
@@ -1,132 +1,148 @@
1
- "use client"
2
-
3
- import { css } from "@emotion/css"
4
- import { useSize } from "ahooks"
5
- import { clsx, getArray } from "deepsea-tools"
6
- import { CSSProperties, forwardRef, MouseEvent as ReactMouseEvent, useEffect, useImperativeHandle, useRef } from "react"
7
- import Scrollbar from "smooth-scrollbar"
8
- import { ScrollStatus } from "smooth-scrollbar/interfaces/scrollbar"
9
- import { px, transformCSSVariable } from "../utils"
10
- import { Scroll, ScrollProps } from "./Scroll"
11
-
12
- export interface AutoScrollProps extends ScrollProps {
13
- /** 轮播元素的个数 */
14
- count: number
15
-
16
- /** 轮播元素的高度 */
17
- itemHeight: number
18
-
19
- /**
20
- * 轮播动画的时间,单位毫秒
21
- * @default 1000
22
- */
23
- animation?: number
24
-
25
- /**
26
- * 每个元素的停留时间,单位毫秒
27
- * @default 3000
28
- */
29
- duration?: number
30
-
31
- /**
32
- * 元素之间的间距
33
- * @default 0
34
- */
35
- gap?: number
36
-
37
- /** 容器类名 */
38
- containerClassName?: string
39
-
40
- /** 容器样式 */
41
- containerStyle?: CSSProperties
42
-
43
- /**
44
- * 在鼠标移入时是否继续播放
45
- * @default false
46
- */
47
- playOnMouseEnter?: boolean
48
-
49
- /**
50
- * 是否暂停
51
- * @default false
52
- */
53
- paused?: boolean
54
- }
55
-
56
- export const AutoScroll = forwardRef<HTMLDivElement, AutoScrollProps>((props, ref) => {
57
- const { count, itemHeight, animation = 1000, duration = 3000, onMouseEnter, onMouseLeave, gap = 0, containerClassName, containerStyle, children, playOnMouseEnter, scrollbar, paused, ...rest } = props
58
- const bar = useRef<Scrollbar | null>(null)
59
- const timeout = useRef<NodeJS.Timeout | undefined>(undefined)
60
- const ele = useRef<HTMLDivElement>(null)
61
- const size = useSize(ele)
62
- const pausedRef = useRef(false)
63
- const pausedProps = useRef(paused)
64
- pausedProps.current = paused
65
-
66
- useImperativeHandle(ref, () => ele.current!, [])
67
- useImperativeHandle(scrollbar, () => bar.current!, [])
68
-
69
- useEffect(() => {
70
- if (playOnMouseEnter) pausedRef.current = false
71
- }, [playOnMouseEnter])
72
-
73
- useEffect(() => {
74
- if (!size || count === 0) return
75
- const { height } = size
76
- const range = getArray(count, index => (itemHeight + gap) * (index + 1) - (index === count - 1 ? gap : 0))
77
- const scrollHeight = range[range.length - 1]
78
- if (height >= scrollHeight) return
79
- function scroll(target: number) {
80
- clearTimeout(timeout.current)
81
- timeout.current = setTimeout(() => {
82
- if (pausedRef.current || pausedProps.current) return scroll(target)
83
- bar.current?.scrollTo(0, target, animation)
84
- }, duration)
85
- }
86
- scroll(range[0])
87
- function listener(status: ScrollStatus) {
88
- const { y } = status.offset
89
- const scrollToBottom = Math.abs(y + height - scrollHeight) / itemHeight <= 0.05
90
- const target = scrollToBottom ? 0 : range.find(item => item > y)!
91
- scroll(target)
92
- }
93
- bar.current?.addListener(listener)
94
- return () => {
95
- clearTimeout(timeout.current)
96
- bar.current?.removeListener(listener)
97
- bar.current?.scrollTo(0, 0)
98
- }
99
- }, [size, count, itemHeight, gap, duration, animation])
100
-
101
- function onContainerMouseEnter(e: ReactMouseEvent<HTMLDivElement, MouseEvent>) {
102
- if (playOnMouseEnter) return
103
- pausedRef.current = true
104
- onMouseEnter?.(e)
105
- }
106
-
107
- function onContainerMouseLeave(e: ReactMouseEvent<HTMLDivElement, MouseEvent>) {
108
- if (playOnMouseEnter) return
109
- pausedRef.current = false
110
- onMouseLeave?.(e)
111
- }
112
-
113
- return (
114
- <Scroll ref={ele} scrollbar={bar} onMouseEnter={onContainerMouseEnter} onMouseLeave={onContainerMouseLeave} {...rest}>
115
- <div
116
- className={clsx(
117
- css`
118
- display: flex;
119
- flex-direction: column;
120
- gap: var(--gap, 0);
121
- & > * {
122
- flex: none;
123
- }
124
- `,
125
- containerClassName
126
- )}
127
- style={transformCSSVariable({ gap: px(gap) }, containerStyle)}>
128
- {children}
129
- </div>
130
- </Scroll>
131
- )
132
- })
1
+ "use client"
2
+
3
+ import { css } from "@emotion/css"
4
+ import { useSize } from "ahooks"
5
+ import { clsx, getArray } from "deepsea-tools"
6
+ import { CSSProperties, forwardRef, MouseEvent as ReactMouseEvent, useEffect, useImperativeHandle, useRef } from "react"
7
+ import Scrollbar from "smooth-scrollbar"
8
+ import { ScrollStatus } from "smooth-scrollbar/interfaces/scrollbar"
9
+ import { px, transformCSSVariable } from "../utils"
10
+ import { Scroll, ScrollProps } from "./Scroll"
11
+
12
+ export interface AutoScrollProps extends ScrollProps {
13
+ /** 轮播元素的个数 */
14
+ count: number
15
+
16
+ /** 轮播元素的高度 */
17
+ itemHeight: number
18
+
19
+ /**
20
+ * 轮播动画的时间,单位毫秒
21
+ * @default 1000
22
+ */
23
+ animation?: number
24
+
25
+ /**
26
+ * 每个元素的停留时间,单位毫秒
27
+ * @default 3000
28
+ */
29
+ duration?: number
30
+
31
+ /**
32
+ * 元素之间的间距
33
+ * @default 0
34
+ */
35
+ gap?: number
36
+
37
+ /** 容器类名 */
38
+ containerClassName?: string
39
+
40
+ /** 容器样式 */
41
+ containerStyle?: CSSProperties
42
+
43
+ /**
44
+ * 在鼠标移入时是否继续播放
45
+ * @default false
46
+ */
47
+ playOnMouseEnter?: boolean
48
+
49
+ /**
50
+ * 是否暂停
51
+ * @default false
52
+ */
53
+ paused?: boolean
54
+ }
55
+
56
+ export const AutoScroll = forwardRef<HTMLDivElement, AutoScrollProps>((props, ref) => {
57
+ const {
58
+ count,
59
+ itemHeight,
60
+ animation = 1000,
61
+ duration = 3000,
62
+ onMouseEnter,
63
+ onMouseLeave,
64
+ gap = 0,
65
+ containerClassName,
66
+ containerStyle,
67
+ children,
68
+ playOnMouseEnter,
69
+ scrollbar,
70
+ paused,
71
+ ...rest
72
+ } = props
73
+ const bar = useRef<Scrollbar | null>(null)
74
+ const timeout = useRef<NodeJS.Timeout | undefined>(undefined)
75
+ const ele = useRef<HTMLDivElement>(null)
76
+ const size = useSize(ele)
77
+ const pausedRef = useRef(false)
78
+ const pausedProps = useRef(paused)
79
+ pausedProps.current = paused
80
+
81
+ useImperativeHandle(ref, () => ele.current!, [])
82
+ useImperativeHandle(scrollbar, () => bar.current!, [])
83
+
84
+ useEffect(() => {
85
+ if (playOnMouseEnter) pausedRef.current = false
86
+ }, [playOnMouseEnter])
87
+
88
+ useEffect(() => {
89
+ if (!size || count === 0) return
90
+ const { height } = size
91
+ const range = getArray(count, index => (itemHeight + gap) * (index + 1) - (index === count - 1 ? gap : 0))
92
+ const scrollHeight = range[range.length - 1]
93
+ if (height >= scrollHeight) return
94
+ function scroll(target: number) {
95
+ clearTimeout(timeout.current)
96
+ timeout.current = setTimeout(() => {
97
+ if (pausedRef.current || pausedProps.current) return scroll(target)
98
+ bar.current?.scrollTo(0, target, animation)
99
+ }, duration)
100
+ }
101
+ scroll(range[0])
102
+ function listener(status: ScrollStatus) {
103
+ const { y } = status.offset
104
+ const scrollToBottom = Math.abs(y + height - scrollHeight) / itemHeight <= 0.05
105
+ const target = scrollToBottom ? 0 : range.find(item => item > y)!
106
+ scroll(target)
107
+ }
108
+ bar.current?.addListener(listener)
109
+ return () => {
110
+ clearTimeout(timeout.current)
111
+ bar.current?.removeListener(listener)
112
+ bar.current?.scrollTo(0, 0)
113
+ }
114
+ }, [size, count, itemHeight, gap, duration, animation])
115
+
116
+ function onContainerMouseEnter(e: ReactMouseEvent<HTMLDivElement, MouseEvent>) {
117
+ if (playOnMouseEnter) return
118
+ pausedRef.current = true
119
+ onMouseEnter?.(e)
120
+ }
121
+
122
+ function onContainerMouseLeave(e: ReactMouseEvent<HTMLDivElement, MouseEvent>) {
123
+ if (playOnMouseEnter) return
124
+ pausedRef.current = false
125
+ onMouseLeave?.(e)
126
+ }
127
+
128
+ return (
129
+ <Scroll ref={ele} scrollbar={bar} onMouseEnter={onContainerMouseEnter} onMouseLeave={onContainerMouseLeave} {...rest}>
130
+ <div
131
+ className={clsx(
132
+ css`
133
+ display: flex;
134
+ flex-direction: column;
135
+ gap: var(--gap, 0);
136
+ & > * {
137
+ flex: none;
138
+ }
139
+ `,
140
+ containerClassName,
141
+ )}
142
+ style={transformCSSVariable({ gap: px(gap) }, containerStyle)}
143
+ >
144
+ {children}
145
+ </div>
146
+ </Scroll>
147
+ )
148
+ })
@@ -1,49 +1,49 @@
1
- "use client"
2
-
3
- import { css } from "@emotion/css"
4
- import { clsx } from "deepsea-tools"
5
- import { forwardRef, TextareaHTMLAttributes, useImperativeHandle, useLayoutEffect, useRef, useState } from "react"
6
- import { px, transformCSSVariable } from "../utils"
7
-
8
- /**
9
- * 自适应高度的文本域
10
- */
11
- export const AutoSizeTextArea = forwardRef<HTMLTextAreaElement, TextareaHTMLAttributes<HTMLTextAreaElement>>((props, ref) => {
12
- const { className, style, ...rest } = props
13
- const [height, setHeight] = useState<string | undefined>(undefined)
14
- const ele = useRef<HTMLTextAreaElement>(null)
15
-
16
- useImperativeHandle(ref, () => ele.current!, [])
17
-
18
- useLayoutEffect(() => {
19
- const textarea = ele.current!
20
- function resizeTextarea() {
21
- setHeight("auto")
22
- setHeight(px(textarea.scrollHeight + textarea.offsetHeight - textarea.clientHeight))
23
- }
24
- resizeTextarea()
25
- textarea.addEventListener("input", resizeTextarea)
26
- textarea.addEventListener("change", resizeTextarea)
27
-
28
- return () => {
29
- textarea.removeEventListener("input", resizeTextarea)
30
- textarea.removeEventListener("change", resizeTextarea)
31
- }
32
- }, [])
33
-
34
- return (
35
- <textarea
36
- ref={ele}
37
- className={clsx(
38
- css`
39
- height: var(--height);
40
- resize: none;
41
- overflow-y: hidden;
42
- `,
43
- className
44
- )}
45
- style={transformCSSVariable({ height }, style)}
46
- {...rest}
47
- />
48
- )
49
- })
1
+ "use client"
2
+
3
+ import { css } from "@emotion/css"
4
+ import { clsx } from "deepsea-tools"
5
+ import { forwardRef, TextareaHTMLAttributes, useImperativeHandle, useLayoutEffect, useRef, useState } from "react"
6
+ import { px, transformCSSVariable } from "../utils"
7
+
8
+ /**
9
+ * 自适应高度的文本域
10
+ */
11
+ export const AutoSizeTextArea = forwardRef<HTMLTextAreaElement, TextareaHTMLAttributes<HTMLTextAreaElement>>((props, ref) => {
12
+ const { className, style, ...rest } = props
13
+ const [height, setHeight] = useState<string | undefined>(undefined)
14
+ const ele = useRef<HTMLTextAreaElement>(null)
15
+
16
+ useImperativeHandle(ref, () => ele.current!, [])
17
+
18
+ useLayoutEffect(() => {
19
+ const textarea = ele.current!
20
+ function resizeTextarea() {
21
+ setHeight("auto")
22
+ setHeight(px(textarea.scrollHeight + textarea.offsetHeight - textarea.clientHeight))
23
+ }
24
+ resizeTextarea()
25
+ textarea.addEventListener("input", resizeTextarea)
26
+ textarea.addEventListener("change", resizeTextarea)
27
+
28
+ return () => {
29
+ textarea.removeEventListener("input", resizeTextarea)
30
+ textarea.removeEventListener("change", resizeTextarea)
31
+ }
32
+ }, [])
33
+
34
+ return (
35
+ <textarea
36
+ ref={ele}
37
+ className={clsx(
38
+ css`
39
+ height: var(--height);
40
+ resize: none;
41
+ overflow-y: hidden;
42
+ `,
43
+ className,
44
+ )}
45
+ style={transformCSSVariable({ height }, style)}
46
+ {...rest}
47
+ />
48
+ )
49
+ })
@@ -1,56 +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 { width, height, radius, startAngel = 0, gapAngel = 0, align = "center", style, direction = "outer", reverse = false, separator, children, ...rest } = props
31
- const unitAngle = Math.atan(width / 2 / radius) * 2
32
- const totalAngle = (unitAngle + gapAngel) * children.length - gapAngel
33
- const offsetAngle = align === "left" ? 0 : align === "right" ? totalAngle : totalAngle / 2
34
-
35
- function getTransform(idx: number) {
36
- const angle = startAngel - idx * (unitAngle + gapAngel) + offsetAngle - unitAngle / 2
37
- const x = (radius + height / 2) * Math.cos(angle) - width / 2
38
- const y = (radius + height / 2) * Math.sin(angle) * -1 - height / 2
39
- const z = Math.PI / 2 - angle + (direction === "inner" ? Math.PI : 0)
40
- return `translateX(${x}px) translateY(${y}px) rotateZ(${(z / Math.PI) * 180}deg)`
41
- }
42
-
43
- const words = typeof separator === "function" ? separator(children) : children.split(separator ?? "")
44
-
45
- if (reverse) words.reverse()
46
-
47
- return (
48
- <Fragment>
49
- {words.map((w, idx) => (
50
- <span key={idx} style={{ position: "absolute", ...style, transform: getTransform(idx), textAlign: "center", width, lineHeight: `${height}px`, height: height }} {...rest}>
51
- {w}
52
- </span>
53
- ))}
54
- </Fragment>
55
- )
56
- }
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
+ }
@@ -0,0 +1,29 @@
1
+ import { useLatest } from "ahooks"
2
+ import ClipboardJS, { Event } from "clipboard"
3
+ import { ComponentProps, forwardRef, useEffect, useImperativeHandle, useRef } from "react"
4
+
5
+ export { Event } from "clipboard"
6
+
7
+ export type CopyButtonProps = ComponentProps<"button"> & {
8
+ text?: string
9
+ onCopySuccess?: (e: Event) => void
10
+ onCopyError?: (e: Event) => void
11
+ }
12
+
13
+ export const CopyButton = forwardRef<HTMLButtonElement, CopyButtonProps>((props, ref) => {
14
+ const { text, onCopySuccess: _onCoptSuccess, onCopyError: _onCopyError, ...rest } = props
15
+ const ele = useRef<HTMLButtonElement>(null)
16
+ const onCopySuccess = useLatest(_onCoptSuccess)
17
+ const onCopyError = useLatest(_onCopyError)
18
+
19
+ useImperativeHandle(ref, () => ele.current!, [ele.current])
20
+
21
+ useEffect(() => {
22
+ const clipboard = new ClipboardJS(ele.current!)
23
+ clipboard.on("success", event => onCopySuccess.current?.(event))
24
+ clipboard.on("error", event => onCopyError.current?.(event))
25
+ return () => clipboard.destroy()
26
+ }, [])
27
+
28
+ return <button ref={ele} {...rest} data-clipboard-text={text} />
29
+ })