diff --git a/src/components/atoms/spinner/Spinner.stories.tsx b/src/components/atoms/spinner/Spinner.stories.tsx new file mode 100644 index 0000000..9a23a7f --- /dev/null +++ b/src/components/atoms/spinner/Spinner.stories.tsx @@ -0,0 +1,59 @@ +import type {Meta, StoryObj} from "@storybook/react-vite"; +import {Spinner} from "./Spinner"; + +const meta = { + title: "components/atoms/Spinner", + component: Spinner, +} satisfies Meta; + +export default meta; + +type Story = StoryObj; + +/** + * Default spinner + */ +export const Default: Story = { + args: { + size: "md", + color: "#2563eb", + }, +}; + +/** + * Spinner sizes + */ +export const Sizes: Story = { + render: () => ( +
+ + + +
+ ), +}; + +/** + * Spinner with different colors + */ +export const Colors: Story = { + render: () => ( +
+ {/* red */} + {/* green */} + {/* amber */} + {/* sky */} +
+ ), +}; + +/** + * Spinner inheriting parent color (currentColor) + */ +export const InheritColor: Story = { + render: () => ( +
+ +
+ ), +}; diff --git a/src/components/atoms/spinner/Spinner.tsx b/src/components/atoms/spinner/Spinner.tsx new file mode 100644 index 0000000..4f8824a --- /dev/null +++ b/src/components/atoms/spinner/Spinner.tsx @@ -0,0 +1,76 @@ +import type {SpinnerProps, SpinnerSize} from "./SpinnerProps"; + +const sizeMap: Record = { + sm: 16, + md: 24, + lg: 32, +}; + +/** + * Renders a visually appealing and accessible SVG spinner component. + * This component is typically used to indicate a loading state or ongoing process. + * + * The spinner's size and color can be customized via props. + * It includes accessibility attributes (`role="status"`, `aria-label="Loading"`) + * to ensure screen readers can convey its purpose. + * + * @component + * @param {Readonly} props - The properties for the Spinner component. + * @param {("sm" | "md" | "lg")} [props.size="md"] - The predefined size of the spinner. + * - "sm": Small (16px) + * - "md": Medium (24px) + * - "lg": Large (32px) + * @param {string} [props.color="currentColor"] - The color of the spinner's stroke. + * Can be any valid CSS color string (e.g., "red", "#FF0000", "var(--primary-color)"). + * Defaults to "currentColor", which inherits the text color of its parent. + * @returns {JSX.Element} A React element representing the SVG spinner. + * + * @example + * // Basic usage with default size and color + * + * @example + * // Large spinner with a specific color + * + */ +export function Spinner(props: Readonly) { + const { + size = "md", + color = "currentColor", + className, + } = props; + + const pixelSize = sizeMap[size]; + const strokeWidth = Math.max(2, Math.floor(pixelSize / 6)); + + return ( + + Loading + + + + + + ); +} diff --git a/src/components/atoms/spinner/SpinnerProps.ts b/src/components/atoms/spinner/SpinnerProps.ts new file mode 100644 index 0000000..d25b840 --- /dev/null +++ b/src/components/atoms/spinner/SpinnerProps.ts @@ -0,0 +1,7 @@ +export type SpinnerSize = "sm" | "md" | "lg"; + +export interface SpinnerProps { + size?: SpinnerSize; + color?: string; + className?: string; +}