diff --git a/apps/docs/src/examples/button.tsx b/apps/docs/src/examples/button.tsx index 4cded248b..e84402541 100644 --- a/apps/docs/src/examples/button.tsx +++ b/apps/docs/src/examples/button.tsx @@ -1,37 +1,48 @@ +'use client'; + import { Button } from '@vitnode/core/components/ui/button'; import { Card } from '@vitnode/core/components/ui/card'; import { ArrowRight, CheckCircle, Eye, Home, Star, Trash2 } from 'lucide-react'; +import React from 'react'; export default function ButtonExample() { + const [isLoading, setIsLoading] = React.useState(false); + return ( - - - - - - - + ); } diff --git a/packages/vitnode/package.json b/packages/vitnode/package.json index af2dff761..855de85f1 100644 --- a/packages/vitnode/package.json +++ b/packages/vitnode/package.json @@ -32,6 +32,7 @@ "react": "19.1.x", "react-dom": "19.1.x", "react-hook-form": "^7.x.x", + "motion": "^12.x.x", "typescript": "^5.8.x", "zod": "4.x.x" }, @@ -115,6 +116,7 @@ "clsx": "^2.1.1", "cmdk": "^1.1.1", "input-otp": "^1.4.2", + "motion": "^12.23.6", "next-themes": "^0.4.6", "nodemailer": "^7.0.5", "postgres": "^3.4.7", diff --git a/packages/vitnode/src/components/ui/button-client.tsx b/packages/vitnode/src/components/ui/button-client.tsx new file mode 100644 index 000000000..7b74a49fc --- /dev/null +++ b/packages/vitnode/src/components/ui/button-client.tsx @@ -0,0 +1,57 @@ +'use client'; + +import { AnimatePresence, motion } from 'motion/react'; +import { useTranslations } from 'next-intl'; +import { Slot } from 'radix-ui'; + +import { cn } from '../../lib/utils'; +import { type ButtonProps, buttonVariants } from './button'; +import { Loader } from './loader'; + +export function ClientButton({ + className, + variant, + size, + asChild = false, + isLoading, + children, + ...props +}: ButtonProps) { + const Comp = asChild ? Slot.Root : 'button'; + const t = useTranslations('core.global'); + + return ( + +
+
+ {children} +
+ + + {isLoading && ( + + + + )} + +
+
+ ); +} diff --git a/packages/vitnode/src/components/ui/button.tsx b/packages/vitnode/src/components/ui/button.tsx index 19ad46cf8..2422d5bb3 100644 --- a/packages/vitnode/src/components/ui/button.tsx +++ b/packages/vitnode/src/components/ui/button.tsx @@ -1,14 +1,10 @@ import { cva, type VariantProps } from 'class-variance-authority'; -import { useTranslations } from 'next-intl'; -import { Slot } from 'radix-ui'; import * as React from 'react'; -import { cn } from '@/lib/utils'; - -import { Loader } from './loader'; +import { ClientButton } from './button-client'; const buttonVariants = cva( - "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive cursor-pointer", + "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive cursor-pointer overflow-hidden", { variants: { variant: { @@ -40,52 +36,17 @@ const buttonVariants = cva( }, ); -type ButtonProps = React.ComponentProps<'button'> & +export type ButtonProps = React.ComponentProps<'button'> & VariantProps & { asChild?: boolean; isLoading?: boolean; - loadingText?: string; } & ( | { 'aria-label': string; size: 'icon' } | { 'aria-label'?: string; size?: 'default' | 'lg' | 'sm' } ); -function Button({ - className, - variant, - size, - asChild = false, - isLoading, - loadingText, - ...props -}: ButtonProps) { - const t = useTranslations('core.global'); - const Comp = asChild ? Slot.Root : 'button'; - - if (isLoading) { - const text = loadingText ?? t('loading'); - - return ( - - - {size !== 'icon' && text} - - ); - } - - return ( - - ); +function Button(props: ButtonProps) { + return ; } export { Button, buttonVariants }; diff --git a/packages/vitnode/src/components/ui/loader.tsx b/packages/vitnode/src/components/ui/loader.tsx index 4e627cd15..43bc75d4d 100644 --- a/packages/vitnode/src/components/ui/loader.tsx +++ b/packages/vitnode/src/components/ui/loader.tsx @@ -10,7 +10,7 @@ export const Loader = ({ small?: boolean; }) => { if (small) { - return ; + return ; } return ( diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 51e751452..a967b1d02 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -321,6 +321,9 @@ importers: input-otp: specifier: ^1.4.2 version: 1.4.2(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + motion: + specifier: ^12.23.6 + version: 12.23.6(react-dom@19.1.0(react@19.1.0))(react@19.1.0) next-themes: specifier: ^0.4.6 version: 0.4.6(react-dom@19.1.0(react@19.1.0))(react@19.1.0)