ui
ui copied to clipboard
feat: Add loading state to the Button
This PR adds simple handling for the loading state: the original content becomes invisible to ensure that the element's width is not changed; the loading indicator is shown instead of the original content with pulse animation. Later on it other handlers can be added, such as custom loading indicator/icon/text.
@malakhov-dmitrii is attempting to deploy a commit to the shadcn-pro Team on Vercel.
A member of the Team first needs to authorize it.
The latest updates on your projects. Learn more about Vercel for Git ↗︎
| Name | Status | Preview | Comments | Updated |
|---|---|---|---|---|
| next-template | ❌ Failed (Inspect) | Feb 17, 2023 at 4:15AM (UTC) |
I think it's useful cause the button loading state it's common practice, but I want to simplify the code structure a bit, instead of adding a loading CVA variant, I think more propper just disable the Button and already defined disabled styles will be applied to the button when loading prop is true. You can see an example of creating your own button with a loading state in docs here. So I suggest to refactor that to smth like this:
button.tsx
index 088678c..52fe801 100644
--- a/templates/next-template/components/ui/button.tsx
+++ b/templates/next-template/components/ui/button.tsx
@@ -1,9 +1,8 @@
import * as React from "react"
-import type { VariantProps } from "class-variance-authority"
-import { cva } from "class-variance-authority"
+import { VariantProps, cva } from "class-variance-authority"
+import { Loader2 } from "lucide-react"
import { cn } from "@/lib/utils"
-import { Loader2 } from "lucide-react"
const buttonVariants = cva(
"active:scale-95 inline-flex items-center justify-center rounded-md text-sm font-medium transition-colors focus:outline-none focus:ring-2 focus:ring-slate-400 focus:ring-offset-2 dark:hover:bg-slate-800 dark:hover:text-slate-100 disabled:opacity-50 dark:focus:ring-slate-400 disabled:pointer-events-none dark:focus:ring-offset-slate-900 data-[state=open]:bg-slate-100 dark:data-[state=open]:bg-slate-800",
@@ -27,10 +26,6 @@ const buttonVariants = cva(
sm: "h-9 px-2 rounded-md",
lg: "h-11 px-8 rounded-md",
},
-
- modifiers: {
- loading: "opacity-50 animate-pulse pointer-events-none",
- },
},
defaultVariants: {
variant: "default",
@@ -46,23 +41,24 @@ export interface ButtonProps
}
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
- ({ className, variant, size, loading, children, ...props }, ref) => {
+ (
+ { className, variant, size, loading, disabled, children, ...props },
+ ref
+ ) => {
return (
<button
className={cn(
buttonVariants({
variant,
size,
- ...(loading && { modifiers: "loading" }),
className,
})
)}
+ disabled={loading || disabled}
ref={ref}
{...props}
>
- {!loading && children}
- {/* Maintain same width of element */}
- {loading && <LoadingIndicator>{children}</LoadingIndicator>}
+ {loading ? <LoadingIndicator>{children}</LoadingIndicator> : children}
</button>
)
}
@@ -74,10 +70,10 @@ export { Button, buttonVariants }
const LoadingIndicator = ({ children }: { children: React.ReactNode }) => {
return (
<div className="relative flex items-center justify-center">
- <div className="absolute top-0 left-0 flex h-full w-full transform items-center justify-center">
- <Loader2 className="animate-spin animate-pulse" />
+ <div className="absolute top-0 left-0 flex h-full w-full items-center justify-center">
+ <Loader2 className="animate-spin" />
</div>
<div className="invisible">{children}</div>
</div>
)
-}
+}
And also I think a good idea would be to add a loading text. You can pass it through loadingText prop and when loading is true display loader + if loadingText is exists display it or if there is no loadingText display invisible button content.
actually I've made this weeks ago... https://github.com/shadcn/ui/pull/63
@malakhov-dmitrii I like this but let's go with @its-monotype implementation which is simpler. I'll create a PR for it.