personal web client for Bluesky
typescript solidjs bluesky atcute
at trunk 111 lines 2.4 kB view raw
1import type { JSX } from 'solid-js'; 2 3import { useFieldset } from './fieldset'; 4 5export interface ButtonProps { 6 title?: string; 7 href?: string; 8 disabled?: boolean; 9 type?: 'button' | 'submit' | 'reset'; 10 role?: JSX.AriaAttributes['role']; 11 onClick?: (ev: MouseEvent) => void; 12 13 children: JSX.Element; 14 15 size?: 'sm' | 'md' | 'lg'; 16 variant?: 'outline' | 'primary' | 'danger' | 'ghost'; 17 class?: string; 18} 19 20const Button = (props: ButtonProps) => { 21 const fieldset = useFieldset(); 22 const isDisabled = (): boolean => fieldset.disabled || !!props.disabled; 23 24 if ('href' in props) { 25 return ( 26 <a 27 role={props.role} 28 href={!isDisabled() ? props.href : undefined} 29 title={props.title} 30 onClick={props.onClick} 31 class={buttonClassNames(isDisabled, props)} 32 > 33 {props.children} 34 </a> 35 ); 36 } 37 38 return ( 39 <button 40 role={props.role} 41 type={props.type || 'button'} 42 disabled={isDisabled()} 43 title={props.title} 44 onClick={props.onClick} 45 class={buttonClassNames(isDisabled, props)} 46 > 47 {props.children} 48 </button> 49 ); 50}; 51 52export default Button; 53 54const buttonClassNames = ( 55 isDisabled: () => boolean, 56 { size = 'sm', variant = 'outline', class: className }: ButtonProps, 57): string => { 58 var cn = `flex select-none items-center justify-center rounded text-sm font-semibold leading-none`; 59 60 if (isDisabled()) { 61 cn += ` pointer-events-none`; 62 } 63 64 if (size === 'sm') { 65 cn += ` h-8 px-4`; 66 } else if (size === 'md') { 67 cn += ` h-9 px-4`; 68 } else if (size === 'lg') { 69 cn += ` h-10 px-4`; 70 } 71 72 if (variant === 'primary') { 73 cn += ` bg-accent text-accent-fg`; 74 75 if (!isDisabled()) { 76 cn += ` hover:bg-accent-hover active:bg-accent-active`; 77 } else { 78 cn += ` opacity-50`; 79 } 80 } else if (variant === 'outline') { 81 cn += ` border border-outline-lg text-contrast`; 82 83 if (!isDisabled()) { 84 cn += ` hover:bg-contrast-hinted/md active:bg-contrast-hinted/md-pressed`; 85 } else { 86 cn += ` opacity-50`; 87 } 88 } else if (variant === 'ghost') { 89 cn += ` text-accent`; 90 91 if (!isDisabled()) { 92 cn += ` hover:bg-accent/md active:bg-accent/md-pressed`; 93 } else { 94 cn += ` opacity-50`; 95 } 96 } else if (variant === 'danger') { 97 cn += ` text-white bg-p-red-600`; 98 99 if (!isDisabled()) { 100 cn += ` hover:bg-p-red-700 active:bg-p-red-800`; 101 } else { 102 cn += ` opacity-50`; 103 } 104 } 105 106 if (className) { 107 cn += ` ${className}`; 108 } 109 110 return cn; 111};