Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(spinner): new spinner variants #4555

Open
wants to merge 11 commits into
base: canary
Choose a base branch
from
6 changes: 6 additions & 0 deletions .changeset/clever-pets-arrive.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@heroui/spinner": patch
"@heroui/theme": patch
---

Adding variants to the Spinner Component.
2 changes: 2 additions & 0 deletions apps/docs/content/components/spinner/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@ import sizes from "./sizes";
import colors from "./colors";
import label from "./label";
import labelColors from "./label-colors";
import variants from "./variants";

export const spinnerContent = {
usage,
sizes,
colors,
label,
labelColors,
variants,
};
17 changes: 17 additions & 0 deletions apps/docs/content/components/spinner/variants.raw.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import {Spinner} from "@heroui/react";

export default function App() {
return (
<div className="flex flex-wrap items-end gap-8">
<Spinner classNames={{label: "text-primary-400 mt-4"}} label="default" variant="default" />
<Spinner classNames={{label: "text-primary-400 mt-4"}} label="gradient" variant="gradient" />
<Spinner classNames={{label: "text-primary-400 mt-4"}} label="star" variant="star" />
<Spinner classNames={{label: "text-primary-400 mt-4"}} label="dots" variant="dots" />
<Spinner
classNames={{label: "text-primary-400 mt-4"}}
label="dots-blink"
variant="dots-blink"
/>
</div>
);
}
9 changes: 9 additions & 0 deletions apps/docs/content/components/spinner/variants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import App from "./variants.raw.jsx?raw";

const react = {
"/App.jsx": App,
};

export default {
...react,
};
18 changes: 15 additions & 3 deletions apps/docs/content/docs/components/spinner.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -60,12 +60,18 @@ Spinner express an unspecified wait time or display the length of a process.

<CodeDemo title="Label colors" files={spinnerContent.labelColors} />

### Variants

<CodeDemo title="Label colors" files={spinnerContent.variants} />
macci001 marked this conversation as resolved.
Show resolved Hide resolved

## Slots

- **base**: The base slot of the spinner, it wraps the circles and the label.
- **wrapper**: The wrapper of the circles.
- **circle1**: The first circle of the spinner.
- **circle2**: The second circle of the spinner.
- **circle1**: The first circle of the spinner. (Effective only when variant is `default` or `gradient`)
- **circle2**: The second circle of the spinner. (Effective only when variant is `default` or `gradient`)
- **dots**: Dots of the component. (Effective only when variant is `dots` or `dots-blink`)
- **bars**: Bars of the component. (Effective only when variant is `bars`)
- **label**: The label content.

<Spacer y={4} />
Expand Down Expand Up @@ -94,6 +100,12 @@ Spinner express an unspecified wait time or display the length of a process.
description: "The color of the spinner circles.",
default: "primary"
},
{
attribute: "variant",
type: "default | gradient | dots | dots-blink | star",
description: "The variant of the spinner",
default: "default"
},
{
attribute: "labelColor",
type: "default | primary | secondary | success | warning | danger",
Expand All @@ -102,7 +114,7 @@ Spinner express an unspecified wait time or display the length of a process.
},
{
attribute: "classNames",
type: "Partial<Record<\"base\"|\"wrapper\"|\"circle1\"|\"circle2\"|\"label\", string>>",
type: "Partial<Record<'base' | 'wrapper' | 'circle1' | 'circle2' | 'dots' | 'bars' | 'label', string>>",
description: "Allows to set custom class names for the spinner slots.",
default: "-"
}
Expand Down
44 changes: 43 additions & 1 deletion packages/components/spinner/src/spinner.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,49 @@ import {UseSpinnerProps, useSpinner} from "./use-spinner";
export interface SpinnerProps extends UseSpinnerProps {}

const Spinner = forwardRef<"div", SpinnerProps>((props, ref) => {
const {slots, classNames, label, getSpinnerProps} = useSpinner({...props});
const {slots, classNames, label, variant, getSpinnerProps} = useSpinner({...props});

if (variant === "dots" || variant === "dots-blink") {
return (
<div ref={ref} {...getSpinnerProps()}>
<div className={slots.wrapper({class: classNames?.wrapper})}>
{[...new Array(3)].map((_, index) => (
<i
key={`dot-${index}`}
className={slots.dots({class: classNames?.dots})}
style={
{
"--dot-index": index,
} as React.CSSProperties
}
/>
))}
</div>
{label && <span className={slots.label({class: classNames?.label})}>{label}</span>}
</div>
);
}

if (variant === "star") {
return (
<div ref={ref} {...getSpinnerProps()}>
<div className={slots.wrapper({class: classNames?.wrapper})}>
{[...new Array(12)].map((_, index) => (
<i
key={`star-${index}`}
className={slots.bars({class: classNames?.bars})}
style={
{
"--bar-index": index,
} as React.CSSProperties
}
/>
))}
</div>
{label && <span className={slots.label({class: classNames?.label})}>{label}</span>}
</div>
);
}
macci001 marked this conversation as resolved.
Show resolved Hide resolved

return (
<div ref={ref} {...getSpinnerProps()}>
Expand Down
3 changes: 2 additions & 1 deletion packages/components/spinner/src/use-spinner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ export type UseSpinnerProps = Props & SpinnerVariantProps;

export function useSpinner(originalProps: UseSpinnerProps) {
const [props, variantProps] = mapPropsVariants(originalProps, spinner.variantKeys);
const {variant} = originalProps;
macci001 marked this conversation as resolved.
Show resolved Hide resolved

const {children, className, classNames, label: labelProp, ...otherProps} = props;

Expand Down Expand Up @@ -65,7 +66,7 @@ export function useSpinner(originalProps: UseSpinnerProps) {
[ariaLabel, slots, baseStyles, otherProps],
);

return {label, slots, classNames, getSpinnerProps};
return {label, slots, classNames, variant, getSpinnerProps};
}

export type UseSpinnerReturn = ReturnType<typeof useSpinner>;
25 changes: 25 additions & 0 deletions packages/components/spinner/stories/spinner.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,22 @@ const defaultProps = {
...spinner.defaultVariants,
};

const VariantsTemplate = () => {
return (
<div className="flex flex-wrap items-end gap-8 py-4">
<Spinner classNames={{label: "text-primary-400 mt-4"}} label="default" variant="default" />
<Spinner classNames={{label: "text-primary-400 mt-4"}} label="gradient" variant="gradient" />
<Spinner classNames={{label: "text-primary-400 mt-4"}} label="star" variant="star" />
<Spinner classNames={{label: "text-primary-400 mt-4"}} label="dots" variant="dots" />
<Spinner
classNames={{label: "text-primary-400 mt-4"}}
label="dots-blink"
variant="dots-blink"
/>
</div>
);
};
macci001 marked this conversation as resolved.
Show resolved Hide resolved

export const Default = {
args: {
...defaultProps,
Expand All @@ -52,3 +68,12 @@ export const WithLabel = {
label: "Loading...",
},
};

export const Variants = {
args: {
...defaultProps,
className: "h-full w-full",
},

render: VariantsTemplate,
};
33 changes: 33 additions & 0 deletions packages/core/theme/src/animations/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ export const animations = {
"drip-expand": "drip-expand 420ms linear",
"spinner-ease-spin": "spinner-spin 0.8s ease infinite",
"spinner-linear-spin": "spinner-spin 0.8s linear infinite",
sway: "sway 750ms ease infinite",
blink: "blink 1.4s infinite both",
"fade-out": "fade-out 1.2s linear 0s infinite normal none running",
"appearance-in": "appearance-in 250ms ease-out normal both",
"appearance-out": "appearance-out 60ms ease-in normal both",
"indeterminate-bar":
Expand Down Expand Up @@ -67,5 +70,35 @@ export const animations = {
transform: "translateX(100%) scaleX(1)",
},
},
sway: {
"0%": {
transform: "translate(0px, 0px)",
},
"50%": {
transform: "translate(0px, -150%)",
},
"100%": {
transform: "translate(0px, 0px)",
},
},
blink: {
"0%": {
opacity: "0.2",
},
"20%": {
opacity: "1",
},
"100%": {
opacity: "0.2",
},
},
"fade-out": {
"0%": {
opacity: "1",
},
"100%": {
opacity: "0.15",
},
},
},
};
Loading
Loading