This repository has no description
0

Configure Feed

Select the types of activity you want to include in your feed.

core / web / src / lib / components / ui / ButtonCard.svelte
3.1 kB 99 lines
1<script module lang="ts"> 2 import { tv, type VariantProps } from "tailwind-variants"; 3 4 export const buttonCard = tv({ 5 base: "flex gap-3 rounded-(--btn-radius) [--btn-radius:var(--radius-sm)] px-4 py-3 w-48 h-40 cursor-pointer select-none no-underline transition-colors duration-150 ease-in-out \ 6 hover:no-underline \ 7 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-border-focus \ 8 disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50", 9 variants: { 10 variant: { 11 default: "border border-border-default bg-background-default hover:bg-background-subtle", 12 ghost: "border border-transparent bg-transparent hover:bg-background-inset" 13 } 14 }, 15 compoundVariants: [ 16 // raised variants draw inset depth via a ::before layer. 17 { 18 variant: "default", 19 class: 20 "relative z-0 before:absolute before:inset-0 before:-z-10 before:rounded-[max(calc(var(--btn-radius)-2px),0px)] before:content-[''] before:shadow-[inset_0_-2px_0_0_var(--color-shadow-inner-subtle)] before:transition-all before:duration-150 hover:before:shadow-[inset_0_-2px_0_0_var(--color-shadow-inner-strong-hover)] active:translate-y-0.5 active:before:shadow-[inset_0_2px_2px_0_var(--color-shadow-inner-subtle)] disabled:active:translate-y-0" 21 } 22 ], 23 defaultVariants: { 24 variant: "default" 25 } 26 }); 27 28 export type ButtonCardVariants = VariantProps<typeof buttonCard>; 29</script> 30 31<script lang="ts"> 32 import type { ResolvedPathname } from "$app/types"; 33 import type { Component, Snippet } from "svelte"; 34 import type { 35 HTMLAnchorAttributes, 36 HTMLButtonAttributes, 37 SvelteHTMLElements 38 } from "svelte/elements"; 39 40 interface Props extends Omit<HTMLAnchorAttributes & HTMLButtonAttributes, "class" | "children"> { 41 variant?: ButtonCardVariants["variant"]; 42 href?: ResolvedPathname; 43 type?: "button" | "submit" | "reset"; 44 disabled?: boolean; 45 title?: string; 46 description?: string; 47 icon?: Component<SvelteHTMLElements["svg"]>; 48 class?: string; 49 children?: Snippet; 50 } 51 52 let { 53 variant = "default", 54 href, 55 type = "button", 56 disabled = false, 57 title, 58 description, 59 icon, 60 class: className, 61 children, 62 ...rest 63 }: Props = $props(); 64 65 const classes = $derived(buttonCard({ variant, class: className })); 66 const Icon = $derived(icon); 67</script> 68 69{#snippet content()} 70 <div class="flex flex-col items-start gap-1.5"> 71 {#if title} 72 <div class="flex flex-row gap-2 items-center"> 73 {#if Icon} 74 <Icon class="size-4 shrink-0" aria-hidden="true" /> 75 {/if} 76 <span class="text-sm font-medium text-foreground-default">{title}</span> 77 </div> 78 {/if} 79 {#if description} 80 <span class="text-xs text-foreground-muted">{description}</span> 81 {/if} 82 </div> 83{/snippet} 84 85{#if href} 86 <a 87 {href} 88 class={classes} 89 aria-disabled={disabled ? "true" : undefined} 90 tabindex={disabled ? -1 : undefined} 91 {...rest} 92 > 93 {@render (children ?? content)()} 94 </a> 95{:else} 96 <button {type} {disabled} class={classes} {...rest}> 97 {@render (children ?? content)()} 98 </button> 99{/if}