Spaces:
Sleeping
Sleeping
| /** | |
| * LoadingSpinner - Configurable loading indicator. | |
| * | |
| * Sizes: sm | md | lg | xl | |
| * Can display an optional message below the spinner. | |
| * Supports full-screen overlay mode. | |
| */ | |
| import PropTypes from "prop-types"; | |
| const SIZE_MAP = { | |
| sm: "h-5 w-5", | |
| md: "h-8 w-8", | |
| lg: "h-12 w-12", | |
| xl: "h-16 w-16", | |
| }; | |
| function LoadingSpinner({ | |
| size = "md", | |
| message, | |
| fullScreen = false, | |
| className = "", | |
| }) { | |
| const spinner = ( | |
| <div | |
| className={`flex flex-col items-center justify-center gap-3 ${className}`} | |
| role="status" | |
| aria-label={message || "Loading"} | |
| > | |
| <svg | |
| className={`animate-spin text-primary-600 ${SIZE_MAP[size] || SIZE_MAP.md}`} | |
| xmlns="http://www.w3.org/2000/svg" | |
| fill="none" | |
| viewBox="0 0 24 24" | |
| aria-hidden="true" | |
| > | |
| <circle | |
| className="opacity-25" | |
| cx="12" | |
| cy="12" | |
| r="10" | |
| stroke="currentColor" | |
| strokeWidth="4" | |
| /> | |
| <path | |
| className="opacity-75" | |
| fill="currentColor" | |
| d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z" | |
| /> | |
| </svg> | |
| {message && ( | |
| <p className="text-sm text-neutral-600">{message}</p> | |
| )} | |
| </div> | |
| ); | |
| if (fullScreen) { | |
| return ( | |
| <div className="fixed inset-0 z-40 flex items-center justify-center bg-white/80 backdrop-blur-sm"> | |
| {spinner} | |
| </div> | |
| ); | |
| } | |
| return spinner; | |
| } | |
| LoadingSpinner.propTypes = { | |
| size: PropTypes.oneOf(["sm", "md", "lg", "xl"]), | |
| message: PropTypes.string, | |
| fullScreen: PropTypes.bool, | |
| className: PropTypes.string, | |
| }; | |
| export default LoadingSpinner; | |