@@ -42,8 +42,60 @@ export const Spinner: FC<SpinnerProps> = ({
42
42
unmountedWhileLoading = false ,
43
43
...delegatedProps
44
44
} ) => {
45
- // Doing some mid-render state syncs to minimize re-renders and risks of
46
- // contradictory states. It's ugly, but it's what the React team recommends
45
+ const showSpinner = useShowSpinner ( loading , spinnerStartDelayMs ) ;
46
+ if ( ! showSpinner ) {
47
+ return children ;
48
+ }
49
+
50
+ return (
51
+ < >
52
+ < svg
53
+ // Fill is the only prop that should be allowed to be
54
+ // overridden; all other props must come after destructuring
55
+ fill = "currentColor"
56
+ { ...delegatedProps }
57
+ viewBox = "0 0 24 24"
58
+ xmlns = "http://www.w3.org/2000/svg"
59
+ className = { cn ( className , spinnerVariants ( { size } ) ) }
60
+ >
61
+ < title > Loading…</ title >
62
+ { leavesIterable . map ( ( leafIndex ) => (
63
+ < rect
64
+ key = { leafIndex }
65
+ x = "10.9"
66
+ y = "2"
67
+ width = "2"
68
+ height = "5.5"
69
+ rx = "1"
70
+ className = {
71
+ // 0.8 is hard-coded because of Tailwind; the value
72
+ // should always be (0.1 * SPINNER_LEAF_COUNT)
73
+ isChromatic ( ) ? "" : "animate-[loading_0.8s_ease-in-out_infinite]"
74
+ }
75
+ style = { {
76
+ transform : `rotate(${ leafIndex * ( 360 / SPINNER_LEAF_COUNT ) } deg)` ,
77
+ transformOrigin : "center" ,
78
+ animationDelay : `${ - leafIndex * 0.1 } s` ,
79
+ } }
80
+ />
81
+ ) ) }
82
+ </ svg >
83
+ { ! unmountedWhileLoading && < div className = "sr-only" > { children } </ div > }
84
+ </ >
85
+ ) ;
86
+ } ;
87
+
88
+ // Not a big fan of one-time custom hooks, but it helps insulate the main
89
+ // component from the chaos of handling all these states, when the ultimate
90
+ // result is a simple boolean. V8 will be able to inline the function definition
91
+ // in some cases anyway
92
+ function useShowSpinner (
93
+ loading : boolean ,
94
+ spinnerStartDelayMs : number ,
95
+ ) : boolean {
96
+ // Doing a bunch of mid-render state syncs to minimize risks of
97
+ // contradictory states during re-renders. It's ugly, but it's what the
98
+ // React team officially recommends
47
99
const noDelay = spinnerStartDelayMs === 0 ;
48
100
const [ mountSpinner , setMountSpinner ] = useState ( noDelay ) ;
49
101
const unmountImmediatelyOnRerender = mountSpinner && ! loading ;
@@ -65,42 +117,5 @@ export const Spinner: FC<SpinnerProps> = ({
65
117
return ( ) => window . clearTimeout ( delayId ) ;
66
118
} , [ spinnerStartDelayMs ] ) ;
67
119
68
- // Past this point, only showSpinner should need to be referenced
69
- const showSpinner = loading && mountSpinner ;
70
- if ( ! showSpinner ) {
71
- return children ;
72
- }
73
-
74
- return (
75
- < svg
76
- // Fill is the only prop that should be allowed to be overridden;
77
- // all other props must come after destructuring
78
- fill = "currentColor"
79
- { ...delegatedProps }
80
- viewBox = "0 0 24 24"
81
- xmlns = "http://www.w3.org/2000/svg"
82
- className = { cn ( className , spinnerVariants ( { size } ) ) }
83
- >
84
- < title > Loading…</ title >
85
- { leavesIterable . map ( ( leafIndex ) => (
86
- < rect
87
- key = { leafIndex }
88
- x = "10.9"
89
- y = "2"
90
- width = "2"
91
- height = "5.5"
92
- rx = "1"
93
- // 0.8 = leaves * 0.1
94
- className = {
95
- isChromatic ( ) ? "" : "animate-[loading_0.8s_ease-in-out_infinite]"
96
- }
97
- style = { {
98
- transform : `rotate(${ leafIndex * ( 360 / SPINNER_LEAF_COUNT ) } deg)` ,
99
- transformOrigin : "center" ,
100
- animationDelay : `${ - leafIndex * 0.1 } s` ,
101
- } }
102
- />
103
- ) ) }
104
- </ svg >
105
- ) ;
106
- } ;
120
+ return loading && mountSpinner ;
121
+ }
0 commit comments