Skip to content

Commit 2f356bc

Browse files
committed
wip: get majority of component updated
1 parent c9841f2 commit 2f356bc

File tree

1 file changed

+56
-41
lines changed

1 file changed

+56
-41
lines changed

site/src/components/Spinner/Spinner.tsx

Lines changed: 56 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,60 @@ export const Spinner: FC<SpinnerProps> = ({
4242
unmountedWhileLoading = false,
4343
...delegatedProps
4444
}) => {
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&hellip;</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
4799
const noDelay = spinnerStartDelayMs === 0;
48100
const [mountSpinner, setMountSpinner] = useState(noDelay);
49101
const unmountImmediatelyOnRerender = mountSpinner && !loading;
@@ -65,42 +117,5 @@ export const Spinner: FC<SpinnerProps> = ({
65117
return () => window.clearTimeout(delayId);
66118
}, [spinnerStartDelayMs]);
67119

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&hellip;</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

Comments
 (0)