@moises.ai/design-system 4.15.2 → 4.15.4
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/dist/index.js +3597 -3556
- package/package.json +1 -1
- package/src/components/PreviewCard/PreviewCard.jsx +147 -50
- package/src/components/PreviewCard/PreviewCard.module.css +31 -26
package/package.json
CHANGED
|
@@ -1,27 +1,41 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
useState,
|
|
3
|
+
useCallback,
|
|
4
|
+
useEffect,
|
|
5
|
+
useRef,
|
|
6
|
+
forwardRef,
|
|
7
|
+
useImperativeHandle,
|
|
8
|
+
} from 'react'
|
|
2
9
|
import WavesurferPlayer from '@wavesurfer/react'
|
|
3
10
|
import classNames from 'classnames'
|
|
4
11
|
import { PlayIcon, PauseIcon } from '../../icons'
|
|
5
12
|
import styles from './PreviewCard.module.css'
|
|
6
13
|
|
|
7
|
-
export function PreviewCard(
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
14
|
+
export const PreviewCard = forwardRef(function PreviewCard(
|
|
15
|
+
{
|
|
16
|
+
audio,
|
|
17
|
+
selected = false,
|
|
18
|
+
loading = false,
|
|
19
|
+
wavegroup,
|
|
20
|
+
onSelect,
|
|
21
|
+
onPlayStateChange,
|
|
22
|
+
actions,
|
|
23
|
+
autoRepeat = false,
|
|
24
|
+
waveColor = 'rgba(90, 97, 105, 1)',
|
|
25
|
+
progressColor = 'rgba(255, 255, 255, 1)',
|
|
26
|
+
className,
|
|
27
|
+
id,
|
|
28
|
+
tabIndex: tabIndexProp,
|
|
29
|
+
},
|
|
30
|
+
ref,
|
|
31
|
+
) {
|
|
20
32
|
const [isPlaying, setIsPlaying] = useState(false)
|
|
21
33
|
const [isReady, setIsReady] = useState(false)
|
|
22
34
|
const [isSafari, setIsSafari] = useState(false)
|
|
23
35
|
|
|
24
36
|
const waveSurferRef = useRef(null)
|
|
37
|
+
const skipTimeSyncRef = useRef(false)
|
|
38
|
+
const pendingSeekTimeRef = useRef(null)
|
|
25
39
|
|
|
26
40
|
const showSkeleton = loading || !isReady
|
|
27
41
|
|
|
@@ -51,6 +65,44 @@ export function PreviewCard({
|
|
|
51
65
|
waveSurferRef.current.play()
|
|
52
66
|
}, [isSafari, progressColor, wavegroup])
|
|
53
67
|
|
|
68
|
+
const startPlay = useCallback(() => {
|
|
69
|
+
if (showSkeleton || !waveSurferRef.current) return
|
|
70
|
+
|
|
71
|
+
if (
|
|
72
|
+
wavegroup?.state?.active &&
|
|
73
|
+
wavegroup.state.active !== waveSurferRef.current
|
|
74
|
+
) {
|
|
75
|
+
wavegroup.state.active.pause()
|
|
76
|
+
|
|
77
|
+
wavegroup.setter((prev) => ({
|
|
78
|
+
...prev,
|
|
79
|
+
isPlaying: false,
|
|
80
|
+
active: null,
|
|
81
|
+
}))
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if (
|
|
85
|
+
wavegroup?.state?.active &&
|
|
86
|
+
wavegroup.state.active !== waveSurferRef.current &&
|
|
87
|
+
wavegroup.state.active.getCurrentTime
|
|
88
|
+
) {
|
|
89
|
+
waveSurferRef.current.setTime(wavegroup.state.active.getCurrentTime())
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
play()
|
|
93
|
+
}, [showSkeleton, play, wavegroup])
|
|
94
|
+
|
|
95
|
+
useImperativeHandle(
|
|
96
|
+
ref,
|
|
97
|
+
() => ({
|
|
98
|
+
play: startPlay,
|
|
99
|
+
pause,
|
|
100
|
+
isReady: () => isReady,
|
|
101
|
+
isPlaying: () => Boolean(waveSurferRef.current?.isPlaying()),
|
|
102
|
+
}),
|
|
103
|
+
[startPlay, pause, isReady],
|
|
104
|
+
)
|
|
105
|
+
|
|
54
106
|
useEffect(() => {
|
|
55
107
|
if (waveSurferRef.current && audio) {
|
|
56
108
|
waveSurferRef.current.load(audio)
|
|
@@ -66,16 +118,31 @@ export function PreviewCard({
|
|
|
66
118
|
|
|
67
119
|
const onPlay = useCallback(
|
|
68
120
|
(ws) => {
|
|
121
|
+
const isSeekHandoff = skipTimeSyncRef.current
|
|
122
|
+
|
|
69
123
|
setIsPlaying(true)
|
|
70
124
|
onPlayStateChange?.(true)
|
|
125
|
+
if (!isSeekHandoff) {
|
|
126
|
+
onSelect?.()
|
|
127
|
+
}
|
|
71
128
|
|
|
72
129
|
if (wavegroup) {
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
130
|
+
const previousActive = wavegroup.state.active
|
|
131
|
+
|
|
132
|
+
if (previousActive && previousActive !== ws) {
|
|
133
|
+
const currentTime = previousActive.getCurrentTime()
|
|
134
|
+
previousActive.pause()
|
|
135
|
+
|
|
136
|
+
if (!skipTimeSyncRef.current) {
|
|
137
|
+
ws.setTime(currentTime)
|
|
138
|
+
} else if (pendingSeekTimeRef.current != null) {
|
|
139
|
+
ws.setTime(pendingSeekTimeRef.current)
|
|
140
|
+
pendingSeekTimeRef.current = null
|
|
141
|
+
}
|
|
77
142
|
}
|
|
78
143
|
|
|
144
|
+
skipTimeSyncRef.current = false
|
|
145
|
+
|
|
79
146
|
wavegroup.setter((prev) => ({
|
|
80
147
|
...prev,
|
|
81
148
|
isPlaying: true,
|
|
@@ -84,7 +151,7 @@ export function PreviewCard({
|
|
|
84
151
|
}))
|
|
85
152
|
}
|
|
86
153
|
},
|
|
87
|
-
[onPlayStateChange, wavegroup],
|
|
154
|
+
[onPlayStateChange, onSelect, wavegroup],
|
|
88
155
|
)
|
|
89
156
|
|
|
90
157
|
const onPause = useCallback(() => {
|
|
@@ -92,6 +159,10 @@ export function PreviewCard({
|
|
|
92
159
|
onPlayStateChange?.(false)
|
|
93
160
|
|
|
94
161
|
if (wavegroup) {
|
|
162
|
+
if (wavegroup.state.active !== waveSurferRef.current) {
|
|
163
|
+
return
|
|
164
|
+
}
|
|
165
|
+
|
|
95
166
|
wavegroup.setter((prev) => ({
|
|
96
167
|
...prev,
|
|
97
168
|
isPlaying: false,
|
|
@@ -111,6 +182,55 @@ export function PreviewCard({
|
|
|
111
182
|
[wavegroup],
|
|
112
183
|
)
|
|
113
184
|
|
|
185
|
+
const handleInteraction = useCallback(
|
|
186
|
+
(ws, newTime) => {
|
|
187
|
+
if (!wavegroup) return
|
|
188
|
+
|
|
189
|
+
const previousActive = wavegroup.state.active
|
|
190
|
+
const isHandoff = previousActive && previousActive !== ws
|
|
191
|
+
|
|
192
|
+
if (!isHandoff) {
|
|
193
|
+
wavegroup.setter((prev) => ({
|
|
194
|
+
...prev,
|
|
195
|
+
active: ws,
|
|
196
|
+
lastPlayed: ws,
|
|
197
|
+
}))
|
|
198
|
+
return
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
const seekTime =
|
|
202
|
+
typeof newTime === 'number' ? newTime : (ws.getCurrentTime?.() ?? 0)
|
|
203
|
+
const wasPlaying =
|
|
204
|
+
Boolean(previousActive?.isPlaying?.()) || wavegroup.state.isPlaying
|
|
205
|
+
|
|
206
|
+
pendingSeekTimeRef.current = seekTime
|
|
207
|
+
skipTimeSyncRef.current = true
|
|
208
|
+
|
|
209
|
+
wavegroup.state.active = ws
|
|
210
|
+
wavegroup.state.lastPlayed = ws
|
|
211
|
+
wavegroup.state.isPlaying = wasPlaying
|
|
212
|
+
|
|
213
|
+
ws.setTime(seekTime)
|
|
214
|
+
previousActive.pause()
|
|
215
|
+
|
|
216
|
+
if (!isSafari) {
|
|
217
|
+
ws.setOptions({ progressColor })
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
if (wasPlaying) {
|
|
221
|
+
ws.play()
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
wavegroup.setter((prev) => ({
|
|
225
|
+
...prev,
|
|
226
|
+
active: ws,
|
|
227
|
+
lastPlayed: ws,
|
|
228
|
+
isPlaying: wasPlaying,
|
|
229
|
+
}))
|
|
230
|
+
},
|
|
231
|
+
[wavegroup, isSafari, progressColor],
|
|
232
|
+
)
|
|
233
|
+
|
|
114
234
|
const onFinish = useCallback(() => {
|
|
115
235
|
if (autoRepeat) {
|
|
116
236
|
waveSurferRef.current?.play()
|
|
@@ -136,29 +256,8 @@ export function PreviewCard({
|
|
|
136
256
|
return
|
|
137
257
|
}
|
|
138
258
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
wavegroup.state.active !== waveSurferRef.current
|
|
142
|
-
) {
|
|
143
|
-
wavegroup.state.active.pause()
|
|
144
|
-
|
|
145
|
-
wavegroup.setter((prev) => ({
|
|
146
|
-
...prev,
|
|
147
|
-
isPlaying: false,
|
|
148
|
-
active: null,
|
|
149
|
-
}))
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
if (
|
|
153
|
-
wavegroup?.state?.active &&
|
|
154
|
-
wavegroup.state.active !== waveSurferRef.current &&
|
|
155
|
-
wavegroup.state.active.getCurrentTime
|
|
156
|
-
) {
|
|
157
|
-
waveSurferRef.current.setTime(wavegroup.state.active.getCurrentTime())
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
play()
|
|
161
|
-
}, [showSkeleton, pause, play, wavegroup])
|
|
259
|
+
startPlay()
|
|
260
|
+
}, [showSkeleton, pause, startPlay])
|
|
162
261
|
|
|
163
262
|
const handleCardClick = useCallback(() => {
|
|
164
263
|
if (showSkeleton) return
|
|
@@ -167,6 +266,7 @@ export function PreviewCard({
|
|
|
167
266
|
|
|
168
267
|
return (
|
|
169
268
|
<div
|
|
269
|
+
id={id}
|
|
170
270
|
className={classNames(
|
|
171
271
|
styles.previewCard,
|
|
172
272
|
selected && styles.selected,
|
|
@@ -175,8 +275,9 @@ export function PreviewCard({
|
|
|
175
275
|
className,
|
|
176
276
|
)}
|
|
177
277
|
onClick={handleCardClick}
|
|
178
|
-
role="
|
|
179
|
-
|
|
278
|
+
role="option"
|
|
279
|
+
aria-selected={selected}
|
|
280
|
+
tabIndex={showSkeleton ? -1 : (tabIndexProp ?? 0)}
|
|
180
281
|
onKeyDown={(e) => {
|
|
181
282
|
if (e.key === 'Enter' || e.key === ' ') {
|
|
182
283
|
e.preventDefault()
|
|
@@ -230,6 +331,7 @@ export function PreviewCard({
|
|
|
230
331
|
cursorWidth={0}
|
|
231
332
|
height={40}
|
|
232
333
|
onReady={onReady}
|
|
334
|
+
onInteraction={handleInteraction}
|
|
233
335
|
onSeeking={onSeeking}
|
|
234
336
|
onPlay={onPlay}
|
|
235
337
|
onPause={onPause}
|
|
@@ -237,11 +339,6 @@ export function PreviewCard({
|
|
|
237
339
|
backend={isSafari ? 'WebAudio' : 'MediaElement'}
|
|
238
340
|
/>
|
|
239
341
|
)}
|
|
240
|
-
{showSkeleton && (
|
|
241
|
-
<span className={styles.skeletonWaveform} aria-hidden="true">
|
|
242
|
-
<span className={styles.skeletonMask} />
|
|
243
|
-
</span>
|
|
244
|
-
)}
|
|
245
342
|
</div>
|
|
246
343
|
|
|
247
344
|
{actions && (
|
|
@@ -251,6 +348,6 @@ export function PreviewCard({
|
|
|
251
348
|
)}
|
|
252
349
|
</div>
|
|
253
350
|
)
|
|
254
|
-
}
|
|
351
|
+
})
|
|
255
352
|
|
|
256
353
|
PreviewCard.displayName = 'PreviewCard'
|
|
@@ -1,9 +1,12 @@
|
|
|
1
|
-
@keyframes
|
|
1
|
+
@keyframes skeletonShimmer {
|
|
2
2
|
0% {
|
|
3
|
-
|
|
3
|
+
background-position: 200% 50%;
|
|
4
|
+
}
|
|
5
|
+
99.999% {
|
|
6
|
+
background-position: -200% 50%;
|
|
4
7
|
}
|
|
5
8
|
100% {
|
|
6
|
-
|
|
9
|
+
background-position: 200% 50%;
|
|
7
10
|
}
|
|
8
11
|
}
|
|
9
12
|
|
|
@@ -62,7 +65,6 @@
|
|
|
62
65
|
align-items: center;
|
|
63
66
|
gap: 12px;
|
|
64
67
|
width: 100%;
|
|
65
|
-
max-width: 344px;
|
|
66
68
|
padding: 4px;
|
|
67
69
|
border-radius: 8px;
|
|
68
70
|
border: 1px solid var(--neutral-alpha-4);
|
|
@@ -70,6 +72,12 @@
|
|
|
70
72
|
cursor: pointer;
|
|
71
73
|
box-sizing: border-box;
|
|
72
74
|
transition: background-color 0.2s ease, border-color 0.2s ease;
|
|
75
|
+
outline: none;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
.previewCard:focus,
|
|
79
|
+
.previewCard:focus-visible {
|
|
80
|
+
outline: none;
|
|
73
81
|
}
|
|
74
82
|
|
|
75
83
|
.previewCard:hover:not(.selected):not(.skeleton):not(.playing),
|
|
@@ -87,8 +95,12 @@
|
|
|
87
95
|
background: linear-gradient(
|
|
88
96
|
90deg,
|
|
89
97
|
var(--neutral-alpha-3) 0%,
|
|
90
|
-
rgba(221, 235, 236, 0.
|
|
98
|
+
rgba(221, 235, 236, 0.14) 35%,
|
|
99
|
+
rgba(221, 235, 236, 0.02) 70%,
|
|
100
|
+
var(--neutral-alpha-3) 100%
|
|
91
101
|
);
|
|
102
|
+
background-size: 200% 100%;
|
|
103
|
+
animation: skeletonShimmer 2.5s linear infinite;
|
|
92
104
|
pointer-events: none;
|
|
93
105
|
border-color: transparent;
|
|
94
106
|
}
|
|
@@ -106,6 +118,12 @@
|
|
|
106
118
|
background: var(--neutral-alpha-3);
|
|
107
119
|
color: var(--neutral-alpha-11);
|
|
108
120
|
cursor: pointer;
|
|
121
|
+
outline: none;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
.leftButton:focus,
|
|
125
|
+
.leftButton:focus-visible {
|
|
126
|
+
outline: none;
|
|
109
127
|
}
|
|
110
128
|
|
|
111
129
|
.leftButton:disabled {
|
|
@@ -114,7 +132,8 @@
|
|
|
114
132
|
}
|
|
115
133
|
|
|
116
134
|
.skeleton .leftButton,
|
|
117
|
-
.skeleton .actions
|
|
135
|
+
.skeleton .actions,
|
|
136
|
+
.skeleton .waveformContainer {
|
|
118
137
|
opacity: 0;
|
|
119
138
|
}
|
|
120
139
|
|
|
@@ -201,26 +220,6 @@
|
|
|
201
220
|
visibility: hidden;
|
|
202
221
|
}
|
|
203
222
|
|
|
204
|
-
.skeletonWaveform {
|
|
205
|
-
position: absolute;
|
|
206
|
-
inset: 0;
|
|
207
|
-
overflow: hidden;
|
|
208
|
-
border-radius: 4px;
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
.skeletonMask {
|
|
212
|
-
width: 100%;
|
|
213
|
-
height: 100%;
|
|
214
|
-
display: block;
|
|
215
|
-
background-repeat: repeat-x;
|
|
216
|
-
background-position: 30px center;
|
|
217
|
-
mask-image: radial-gradient(circle, blue 50%, rgba(255, 255, 0, 0.5) 50%);
|
|
218
|
-
mask-size: 400% 400%;
|
|
219
|
-
animation: skeleton_animate_mask 3s linear infinite;
|
|
220
|
-
-webkit-animation: skeleton_animate_mask 3s linear infinite;
|
|
221
|
-
background-image: url('/waveform.svg');
|
|
222
|
-
}
|
|
223
|
-
|
|
224
223
|
.actions {
|
|
225
224
|
display: flex;
|
|
226
225
|
flex-shrink: 0;
|
|
@@ -243,6 +242,12 @@
|
|
|
243
242
|
min-height: 24px !important;
|
|
244
243
|
padding: 0 !important;
|
|
245
244
|
border-radius: 4px;
|
|
245
|
+
outline: none;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
.actions :global(button:focus),
|
|
249
|
+
.actions :global(button:focus-visible) {
|
|
250
|
+
outline: none;
|
|
246
251
|
}
|
|
247
252
|
|
|
248
253
|
.actions :global(svg) {
|