-
Notifications
You must be signed in to change notification settings - Fork 5.1k
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(components): add spinner component #1583
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,102 @@ | ||
--- | ||
title: Spinner | ||
description: Indicates an action running in the background. | ||
component: true | ||
--- | ||
|
||
<ComponentPreview name="spinner-demo" /> | ||
|
||
## Installation | ||
|
||
<Tabs defaultValue="cli"> | ||
|
||
<TabsList> | ||
<TabsTrigger value="cli">CLI</TabsTrigger> | ||
<TabsTrigger value="manual">Manual</TabsTrigger> | ||
</TabsList> | ||
|
||
<TabsContent value="cli"> | ||
|
||
<Steps> | ||
|
||
<Step>Run the following command:</Step> | ||
|
||
```bash | ||
npx shadcn-ui@latest add spinner | ||
``` | ||
|
||
<Step>Update `tailwind.config.js`</Step> | ||
|
||
Add the following animations to your `tailwind.config.js` file: | ||
|
||
```js title="tailwind.config.js" {5-13} | ||
/** @type {import('tailwindcss').Config} */ | ||
module.exports = { | ||
theme: { | ||
extend: { | ||
keyframes: { | ||
spinner: { | ||
from: { opacity: "1" }, | ||
to: { opacity: "0.15" }, | ||
}, | ||
}, | ||
animation: { | ||
spinner: "spinner 1.2s linear infinite", | ||
}, | ||
}, | ||
}, | ||
} | ||
``` | ||
|
||
</Steps> | ||
|
||
</TabsContent> | ||
|
||
<TabsContent value="manual"> | ||
|
||
<Steps> | ||
|
||
<Step>Copy and paste the following code into your project.</Step> | ||
|
||
<ComponentSource name="spinner" /> | ||
|
||
<Step>Update the import paths to match your project setup.</Step> | ||
|
||
<Step>Update `tailwind.config.js`</Step> | ||
|
||
Add the following animations to your `tailwind.config.js` file: | ||
|
||
```js title="tailwind.config.js" {5-13} | ||
/** @type {import('tailwindcss').Config} */ | ||
module.exports = { | ||
theme: { | ||
extend: { | ||
keyframes: { | ||
spinner: { | ||
from: { opacity: "1" }, | ||
to: { opacity: "0.15" }, | ||
}, | ||
}, | ||
animation: { | ||
spinner: "spinner 1.2s linear infinite", | ||
}, | ||
}, | ||
}, | ||
} | ||
``` | ||
|
||
</Steps> | ||
|
||
</TabsContent> | ||
|
||
</Tabs> | ||
|
||
## Usage | ||
|
||
```tsx | ||
import { Spinner } from "@/components/ui/spinner" | ||
``` | ||
|
||
```tsx | ||
<Spinner /> | ||
``` |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
{ | ||
"name": "spinner", | ||
"files": [ | ||
{ | ||
"name": "spinner.tsx", | ||
"content": "import * as React from \"react\"\n\nimport { cn } from \"@/lib/utils\"\n\ninterface SpinnerBar {\n animationDelay: number\n rotate: number\n}\n\nconst Spinner = React.forwardRef<\n HTMLDivElement,\n React.HTMLAttributes<HTMLDivElement>\n>(({ className, ...props }, ref) => {\n const spinnerBars: SpinnerBar[] = [\n { animationDelay: -1.2, rotate: 0.0001 },\n { animationDelay: -1.1, rotate: 30 },\n { animationDelay: -1, rotate: 60 },\n { animationDelay: -0.9, rotate: 90 },\n { animationDelay: -0.8, rotate: 120 },\n { animationDelay: -0.7, rotate: 150 },\n { animationDelay: -0.6, rotate: 180 },\n { animationDelay: -0.5, rotate: 210 },\n { animationDelay: -0.4, rotate: 240 },\n { animationDelay: -0.3, rotate: 270 },\n { animationDelay: -0.2, rotate: 300 },\n { animationDelay: -0.1, rotate: 330 },\n ]\n\n return (\n <div ref={ref} className={cn(\"h-5 w-5\", className)} {...props}>\n <div className=\"relative left-1/2 top-1/2 h-full\">\n {spinnerBars.map((bar) => (\n <div\n key={bar.rotate}\n className=\"absolute left-[-10%] top-[-3.9%] h-[8%] w-[24%] animate-spinner rounded-md bg-muted-foreground\"\n style={{\n animationDelay: `${bar.animationDelay}s`,\n transform: `rotate(${bar.rotate}deg) translate(146%)`,\n }}\n />\n ))}\n </div>\n </div>\n )\n})\nSpinner.displayName = \"Spinner\"\n\nexport { Spinner }\n" | ||
} | ||
], | ||
"type": "components:ui" | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
{ | ||
"name": "spinner", | ||
"files": [ | ||
{ | ||
"name": "spinner.tsx", | ||
"content": "import * as React from \"react\"\n\nimport { cn } from \"@/lib/utils\"\n\ninterface SpinnerBar {\n animationDelay: number\n rotate: number\n}\n\nconst Spinner = React.forwardRef<\n HTMLDivElement,\n React.HTMLAttributes<HTMLDivElement>\n>(({ className, ...props }, ref) => {\n const spinnerBars: SpinnerBar[] = [\n { animationDelay: -1.2, rotate: 0.0001 },\n { animationDelay: -1.1, rotate: 30 },\n { animationDelay: -1, rotate: 60 },\n { animationDelay: -0.9, rotate: 90 },\n { animationDelay: -0.8, rotate: 120 },\n { animationDelay: -0.7, rotate: 150 },\n { animationDelay: -0.6, rotate: 180 },\n { animationDelay: -0.5, rotate: 210 },\n { animationDelay: -0.4, rotate: 240 },\n { animationDelay: -0.3, rotate: 270 },\n { animationDelay: -0.2, rotate: 300 },\n { animationDelay: -0.1, rotate: 330 },\n ]\n\n return (\n <div ref={ref} className={cn(\"h-5 w-5\", className)} {...props}>\n <div className=\"relative left-1/2 top-1/2 h-full\">\n {spinnerBars.map((bar) => (\n <div\n key={bar.rotate}\n className=\"absolute left-[-10%] top-[-3.9%] h-[8%] w-[24%] animate-spinner rounded-md bg-muted-foreground\"\n style={{\n animationDelay: `${bar.animationDelay}s`,\n transform: `rotate(${bar.rotate}deg) translate(146%)`,\n }}\n />\n ))}\n </div>\n </div>\n )\n})\nSpinner.displayName = \"Spinner\"\n\nexport { Spinner }\n" | ||
} | ||
], | ||
"type": "components:ui" | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
import { Spinner } from "@/registry/default/ui/spinner" | ||
|
||
export default function SpinnerDemo() { | ||
return <Spinner /> | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
import * as React from "react" | ||
|
||
import { cn } from "@/lib/utils" | ||
|
||
interface SpinnerBar { | ||
animationDelay: number | ||
rotate: number | ||
} | ||
|
||
const Spinner = React.forwardRef< | ||
HTMLDivElement, | ||
React.HTMLAttributes<HTMLDivElement> | ||
>(({ className, ...props }, ref) => { | ||
const spinnerBars: SpinnerBar[] = [ | ||
{ animationDelay: -1.2, rotate: 0.0001 }, | ||
{ animationDelay: -1.1, rotate: 30 }, | ||
{ animationDelay: -1, rotate: 60 }, | ||
{ animationDelay: -0.9, rotate: 90 }, | ||
{ animationDelay: -0.8, rotate: 120 }, | ||
{ animationDelay: -0.7, rotate: 150 }, | ||
{ animationDelay: -0.6, rotate: 180 }, | ||
{ animationDelay: -0.5, rotate: 210 }, | ||
{ animationDelay: -0.4, rotate: 240 }, | ||
{ animationDelay: -0.3, rotate: 270 }, | ||
{ animationDelay: -0.2, rotate: 300 }, | ||
{ animationDelay: -0.1, rotate: 330 }, | ||
] | ||
|
||
return ( | ||
<div ref={ref} className={cn("h-5 w-5", className)} {...props}> | ||
<div className="relative left-1/2 top-1/2 h-full"> | ||
{spinnerBars.map((bar) => ( | ||
<div | ||
key={bar.rotate} | ||
className="absolute left-[-10%] top-[-3.9%] h-[8%] w-[24%] animate-spinner rounded-md bg-muted-foreground" | ||
style={{ | ||
animationDelay: `${bar.animationDelay}s`, | ||
transform: `rotate(${bar.rotate}deg) translate(146%)`, | ||
}} | ||
Comment on lines
+36
to
+39
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm less of a fan of mixing
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I can't do that, because There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. you could use Arbitray Properties: like There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. but still think its better to rotate the icon using There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That's a good point, I'll give it a try |
||
/> | ||
))} | ||
</div> | ||
</div> | ||
) | ||
}) | ||
Spinner.displayName = "Spinner" | ||
|
||
export { Spinner } |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
import { Spinner } from "@/registry/default/ui/spinner" | ||
|
||
export default function SpinnerDemo() { | ||
return <Spinner /> | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
import * as React from "react" | ||
|
||
import { cn } from "@/lib/utils" | ||
|
||
interface SpinnerBar { | ||
animationDelay: number | ||
rotate: number | ||
} | ||
|
||
const Spinner = React.forwardRef< | ||
HTMLDivElement, | ||
React.HTMLAttributes<HTMLDivElement> | ||
>(({ className, ...props }, ref) => { | ||
const spinnerBars: SpinnerBar[] = [ | ||
{ animationDelay: -1.2, rotate: 0.0001 }, | ||
{ animationDelay: -1.1, rotate: 30 }, | ||
{ animationDelay: -1, rotate: 60 }, | ||
{ animationDelay: -0.9, rotate: 90 }, | ||
{ animationDelay: -0.8, rotate: 120 }, | ||
{ animationDelay: -0.7, rotate: 150 }, | ||
{ animationDelay: -0.6, rotate: 180 }, | ||
{ animationDelay: -0.5, rotate: 210 }, | ||
{ animationDelay: -0.4, rotate: 240 }, | ||
{ animationDelay: -0.3, rotate: 270 }, | ||
{ animationDelay: -0.2, rotate: 300 }, | ||
{ animationDelay: -0.1, rotate: 330 }, | ||
] | ||
|
||
return ( | ||
<div ref={ref} className={cn("h-5 w-5", className)} {...props}> | ||
<div className="relative left-1/2 top-1/2 h-full"> | ||
{spinnerBars.map((bar) => ( | ||
<div | ||
key={bar.rotate} | ||
className="absolute left-[-10%] top-[-3.9%] h-[8%] w-[24%] animate-spinner rounded-md bg-muted-foreground" | ||
style={{ | ||
animationDelay: `${bar.animationDelay}s`, | ||
transform: `rotate(${bar.rotate}deg) translate(146%)`, | ||
}} | ||
/> | ||
))} | ||
</div> | ||
</div> | ||
) | ||
}) | ||
Spinner.displayName = "Spinner" | ||
|
||
export { Spinner } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Little cleaner implementations of the
spinnerBars
? Could also move it outside the context of the Component to save some performance 👍