UNPKG

@primer/react

Version:

An implementation of GitHub's Primer Design System using React

268 lines (263 loc) • 7.09 kB
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 };