Spaces:
Running
Running
| <script lang="ts" module> | |
| import { cn, type WithElementRef } from '$lib/utils.js'; | |
| import type { HTMLAnchorAttributes, HTMLButtonAttributes } from 'svelte/elements'; | |
| import { type VariantProps, tv } from 'tailwind-variants'; | |
| export const buttonVariants = tv({ | |
| base: "focus-visible:border-ring focus-visible:ring-ring/30 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:aria-invalid:border-destructive/50 rounded-md border border-transparent bg-clip-padding text-xs/relaxed font-medium focus-visible:ring-2 active:not-aria-[haspopup]:translate-y-px aria-invalid:ring-2 [&_svg:not([class*='size-'])]:size-4 group/button inline-flex shrink-0 items-center justify-center whitespace-nowrap transition-all outline-none select-none disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0", | |
| variants: { | |
| variant: { | |
| default: 'bg-primary text-primary-foreground hover:bg-primary/80', | |
| outline: | |
| 'border-border dark:bg-input/30 hover:bg-input/50 hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground', | |
| secondary: | |
| 'bg-secondary text-secondary-foreground hover:bg-secondary/80 aria-expanded:bg-secondary aria-expanded:text-secondary-foreground', | |
| ghost: | |
| 'hover:bg-muted hover:text-foreground dark:hover:bg-muted/50 aria-expanded:bg-muted aria-expanded:text-foreground', | |
| destructive: | |
| 'bg-destructive/10 hover:bg-destructive/20 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/20 text-destructive focus-visible:border-destructive/40 dark:hover:bg-destructive/30', | |
| link: 'text-primary underline-offset-4 hover:underline' | |
| }, | |
| size: { | |
| default: | |
| "h-7 gap-1 px-2 text-xs/relaxed has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3.5", | |
| xs: "h-5 gap-1 rounded-sm px-2 text-[0.625rem] has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-2.5", | |
| sm: "h-6 gap-1 px-2 text-xs/relaxed has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3", | |
| lg: "h-8 gap-1 px-2.5 text-xs/relaxed has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2 [&_svg:not([class*='size-'])]:size-4", | |
| icon: "size-7 [&_svg:not([class*='size-'])]:size-3.5", | |
| 'icon-xs': "size-5 rounded-sm [&_svg:not([class*='size-'])]:size-2.5", | |
| 'icon-sm': "size-6 [&_svg:not([class*='size-'])]:size-3", | |
| 'icon-lg': "size-8 [&_svg:not([class*='size-'])]:size-4" | |
| } | |
| }, | |
| defaultVariants: { | |
| variant: 'default', | |
| size: 'default' | |
| } | |
| }); | |
| export type ButtonVariant = VariantProps<typeof buttonVariants>['variant']; | |
| export type ButtonSize = VariantProps<typeof buttonVariants>['size']; | |
| export type ButtonProps = WithElementRef<HTMLButtonAttributes> & | |
| WithElementRef<HTMLAnchorAttributes> & { | |
| variant?: ButtonVariant; | |
| size?: ButtonSize; | |
| }; | |
| </script> | |
| <script lang="ts"> | |
| let { | |
| class: className, | |
| variant = 'default', | |
| size = 'default', | |
| ref = $bindable(null), | |
| href = undefined, | |
| type = 'button', | |
| disabled, | |
| children, | |
| ...restProps | |
| }: ButtonProps = $props(); | |
| </script> | |
| {#if href} | |
| <a | |
| bind:this={ref} | |
| data-slot="button" | |
| class={cn(buttonVariants({ variant, size }), className)} | |
| href={disabled ? undefined : href} | |
| aria-disabled={disabled} | |
| role={disabled ? 'link' : undefined} | |
| tabindex={disabled ? -1 : undefined} | |
| {...restProps} | |
| > | |
| {@render children?.()} | |
| </a> | |
| {:else} | |
| <button | |
| bind:this={ref} | |
| data-slot="button" | |
| class={cn(buttonVariants({ variant, size }), className)} | |
| {type} | |
| {disabled} | |
| {...restProps} | |
| > | |
| {@render children?.()} | |
| </button> | |
| {/if} | |