@primer/react
Version:
An implementation of GitHub's Primer Design System using React
268 lines (263 loc) • 7.09 kB
JavaScript
import { c } from 'react-compiler-runtime';
import { clsx } from 'clsx';
import { useState, useEffect, useRef, useSyncExternalStore } from 'react';
import classes from './Spinner.module.css.js';
import { useMedia } from '../hooks/useMedia.js';
import { jsxs, jsx } from 'react/jsx-runtime';
import { useFeatureFlag } from '../FeatureFlags/useFeatureFlag.js';
import { useId } from '../hooks/useId.js';
import { VisuallyHidden } from '../VisuallyHidden/VisuallyHidden.js';
const sizeMap = {
small: '16px',
medium: '32px',
large: '64px'
};
function Spinner(t0) {
var _ariaLabel;
const $ = c(30);
let ariaLabel;
let className;
let props;
let style;
let t1;
let t2;
let t3;
if ($[0] !== t0) {
({
size: t1,
srText: t2,
"aria-label": ariaLabel,
className,
style,
delay: t3,
...props
} = t0);
$[0] = t0;
$[1] = ariaLabel;
$[2] = className;
$[3] = props;
$[4] = style;
$[5] = t1;
$[6] = t2;
$[7] = t3;
} else {
ariaLabel = $[1];
className = $[2];
props = $[3];
style = $[4];
t1 = $[5];
t2 = $[6];
t3 = $[7];
}
const sizeKey = t1 === undefined ? "medium" : t1;
const srText = t2 === undefined ? "Loading" : t2;
const delay = t3 === undefined ? false : t3;
const syncAnimationsEnabled = useFeatureFlag("primer_react_spinner_synchronize_animations");
const animationRef = useSpinnerAnimation();
const size = sizeMap[sizeKey];
const hasHiddenLabel = srText !== null && ariaLabel === undefined;
const labelId = useId();
const [isVisible, setIsVisible] = useState(!delay);
let t4;
let t5;
if ($[8] !== delay) {
t4 = () => {
if (delay) {
const timeoutId = setTimeout(() => {
setIsVisible(true);
}, 1000);
return () => clearTimeout(timeoutId);
}
};
t5 = [delay];
$[8] = delay;
$[9] = t4;
$[10] = t5;
} else {
t4 = $[9];
t5 = $[10];
}
useEffect(t4, t5);
if (!isVisible) {
return null;
}
const t6 = syncAnimationsEnabled ? animationRef : undefined;
const t7 = (_ariaLabel = ariaLabel) !== null && _ariaLabel !== void 0 ? _ariaLabel : undefined;
const t8 = hasHiddenLabel ? labelId : undefined;
let t9;
if ($[11] !== className) {
t9 = clsx(className, classes.SpinnerAnimation);
$[11] = className;
$[12] = t9;
} else {
t9 = $[12];
}
let t10;
let t11;
if ($[13] === Symbol.for("react.memo_cache_sentinel")) {
t10 = /*#__PURE__*/jsx("circle", {
cx: "8",
cy: "8",
r: "7",
stroke: "currentColor",
strokeOpacity: "0.25",
strokeWidth: "2",
vectorEffect: "non-scaling-stroke"
});
t11 = /*#__PURE__*/jsx("path", {
d: "M15 8a7.002 7.002 0 00-7-7",
stroke: "currentColor",
strokeWidth: "2",
strokeLinecap: "round",
vectorEffect: "non-scaling-stroke"
});
$[13] = t10;
$[14] = t11;
} else {
t10 = $[13];
t11 = $[14];
}
let t12;
if ($[15] !== props || $[16] !== size || $[17] !== style || $[18] !== t6 || $[19] !== t7 || $[20] !== t8 || $[21] !== t9) {
t12 = /*#__PURE__*/jsxs("svg", {
ref: t6,
height: size,
width: size,
viewBox: "0 0 16 16",
fill: "none",
"aria-hidden": true,
"aria-label": t7,
"aria-labelledby": t8,
className: t9,
style: style,
...props,
children: [t10, t11]
});
$[15] = props;
$[16] = size;
$[17] = style;
$[18] = t6;
$[19] = t7;
$[20] = t8;
$[21] = t9;
$[22] = t12;
} else {
t12 = $[22];
}
let t13;
if ($[23] !== hasHiddenLabel || $[24] !== labelId || $[25] !== srText) {
t13 = hasHiddenLabel ? /*#__PURE__*/jsx(VisuallyHidden, {
id: labelId,
children: srText
}) : null;
$[23] = hasHiddenLabel;
$[24] = labelId;
$[25] = srText;
$[26] = t13;
} else {
t13 = $[26];
}
let t14;
if ($[27] !== t12 || $[28] !== t13) {
t14 = /*#__PURE__*/jsxs("span", {
className: classes.Box,
children: [t12, t13]
});
$[27] = t12;
$[28] = t13;
$[29] = t14;
} else {
t14 = $[29];
}
return t14;
}
Spinner.displayName = 'Spinner';
const animationTimingStore = {
subscribers: new Set(),
value: {
startTime: null
},
update(startTime) {
const value = {
startTime
};
animationTimingStore.value = value;
for (const subscriber of animationTimingStore.subscribers) {
subscriber();
}
},
subscribe(subscriber) {
animationTimingStore.subscribers.add(subscriber);
return () => {
animationTimingStore.subscribers.delete(subscriber);
};
},
getSnapshot() {
return animationTimingStore.value;
},
getServerSnapshot() {
return animationTimingStore.value;
}
};
/**
* A utility hook for reading a common `startTime` value so that all animations
* are in sync. This is a global value and is coordinated through `useSyncExternalStore`.
*/
function useAnimationTiming() {
return useSyncExternalStore(animationTimingStore.subscribe, animationTimingStore.getSnapshot, animationTimingStore.getServerSnapshot);
}
/**
* Uses a technique from Spectrum to coordinate animations:
* @see https://github.com/adobe/react-spectrum/blob/ab5e6f3dba4235dafab9f81f8b5c506ce5f11230/packages/%40react-spectrum/s2/src/Skeleton.tsx#L21
*/
function useSpinnerAnimation() {
const $ = c(3);
const ref = useRef(null);
const noMotionPreference = useMedia("(prefers-reduced-motion: no-preference)", false);
const animationTiming = useAnimationTiming();
let t0;
if ($[0] !== animationTiming || $[1] !== noMotionPreference) {
t0 = element => {
if (!element) {
return;
}
if (ref.current !== null) {
return;
}
if (noMotionPreference) {
const cssAnimation = element.getAnimations().find(_temp);
cssAnimation === null || cssAnimation === void 0 ? void 0 : cssAnimation.pause();
ref.current = element.animate([{
transform: "rotate(0deg)"
}, {
transform: "rotate(360deg)"
}], {
duration: 1000,
easing: "cubic-bezier(0,0,1,1)",
iterations: Infinity
});
if (animationTiming.startTime === null) {
var _cssAnimation$startTi;
const startTime = (_cssAnimation$startTi = cssAnimation === null || cssAnimation === void 0 ? void 0 : cssAnimation.startTime) !== null && _cssAnimation$startTi !== void 0 ? _cssAnimation$startTi : 0;
animationTimingStore.update(startTime);
ref.current.startTime = startTime;
} else {
ref.current.startTime = animationTiming.startTime;
}
}
};
$[0] = animationTiming;
$[1] = noMotionPreference;
$[2] = t0;
} else {
t0 = $[2];
}
return t0;
}
function _temp(animation) {
if (animation instanceof CSSAnimation) {
return animation.animationName.startsWith("Spinner") && animation.animationName.endsWith("rotate-keyframes");
}
return false;
}
export { Spinner as default };