PluginProbe
Code Snippets / trunk
Code Snippets vtrunk
4.0.0-beta.2 3.10.2 3.10.1 3.10.0 3.10.0-beta.2 3.10.0-beta.1 4.0.0-beta.1 3.9.6 trunk 2.10.0 2.10.1 2.12.0 2.12.1 2.13.0 2.13.1 2.13.2 2.13.3 2.14.0 2.14.1 2.14.2 2.14.3 2.14.4 2.14.5 2.14.6 3.0.0 All 65 releases
code-snippets / js / components / ManageMenu / CloudLibraryDemo / useCloudLibraryDemo.ts

useCloudLibraryDemo.ts in Code Snippets trunk, at js/components/ManageMenu/CloudLibraryDemo/useCloudLibraryDemo.ts

99 lines 2.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
2 import type { DemoStage } from './types'
3
4 /**
5 * How long each stage holds before the driver advances. Each stage carries one
6 * step of commentary, so it is given long enough to read the callout and take
7 * in what the spotlight is pointing at. The preview step holds longest: there
8 * is code on screen to actually look at.
9 */
10 const STAGE_DURATIONS: Partial<Record<DemoStage, number>> = {
11 library: 4800,
12 preview: 5400,
13 download: 4200,
14 synced: 4800
15 }
16
17 const DEFAULT_DURATION = 3000
18
19 /** Uniform pause between stages when the visitor has asked for reduced motion. */
20 const REDUCED_MOTION_DURATION = 250
21
22 const FOLLOW_UP: Partial<Record<DemoStage, DemoStage>> = {
23 library: 'preview',
24 preview: 'download',
25 download: 'synced',
26 synced: 'finished'
27 }
28
29 const prefersReducedMotion = (): boolean =>
30 window.matchMedia('(prefers-reduced-motion: reduce)').matches
31
32 export interface CloudLibraryDemoState {
33 stage: DemoStage
34 hasStarted: boolean
35 isFinished: boolean
36 reducedMotion: boolean
37 play: VoidFunction
38 skip: VoidFunction
39 replay: VoidFunction
40 }
41
42 export const useCloudLibraryDemo = (): CloudLibraryDemoState => {
43 const reducedMotion = useMemo(prefersReducedMotion, [])
44
45 const [stage, setStage] = useState<DemoStage>('idle')
46 const timers = useRef<number[]>([])
47
48 const clearTimers = useCallback(() => {
49 timers.current.forEach(window.clearTimeout)
50 timers.current = []
51 }, [])
52
53 useEffect(() => () => {
54 timers.current.forEach(window.clearTimeout)
55 }, [])
56
57 const advance = useCallback((next: DemoStage) => {
58 setStage(next)
59
60 const upcoming = FOLLOW_UP[next]
61
62 if (!upcoming) {
63 return
64 }
65
66 const delay = reducedMotion
67 ? REDUCED_MOTION_DURATION
68 : STAGE_DURATIONS[next] ?? DEFAULT_DURATION
69
70 timers.current.push(window.setTimeout(() => advance(upcoming), delay))
71 }, [reducedMotion])
72
73 const play = useCallback(() => {
74 clearTimers()
75 advance('library')
76 }, [advance, clearTimers])
77
78 const replay = useCallback(() => {
79 clearTimers()
80 setStage('idle')
81 timers.current.push(window.setTimeout(() => advance('library'), REDUCED_MOTION_DURATION))
82 }, [advance, clearTimers])
83
84 const skip = useCallback(() => {
85 clearTimers()
86 setStage('finished')
87 }, [clearTimers])
88
89 return {
90 stage,
91 hasStarted: 'idle' !== stage,
92 isFinished: 'finished' === stage,
93 reducedMotion,
94 play,
95 skip,
96 replay
97 }
98 }
99