Coves frontend - a photon fork
at main 399 lines 9.3 kB view raw
1<script module lang="ts"> 2 import { Spinner } from 'mono-svelte' 3 import type { Snippet } from 'svelte' 4 import { type IconSource, Icon } from 'svelte-hero-icons/dist' 5 import type { ClassValue, HTMLButtonAttributes } from 'svelte/elements' 6 7 export type ButtonColor = keyof typeof buttonColor 8 export type ButtonAlignment = keyof typeof buttonAlignment 9 export type ButtonShadow = keyof typeof buttonShadow 10 11 export const buttonAlignment = { 12 left: 'justify-start text-left origin-left', 13 center: 'justify-center', 14 right: 'justify-end text-right origin-right', 15 } 16 17 export const buttonColor = { 18 primary: 'btn-primary', 19 secondary: 'btn-secondary', 20 tertiary: 'btn-tertiary', 21 danger: 'btn-danger', 22 ghost: 'btn-ghost', 23 'danger-subtle': 'text-red-500 hover:bg-red-500 hover:text-inherit!', 24 'success-subtle': 'text-green-500 hover:bg-green-500 hover:text-inherit!', 25 'warning-subtle': 'text-yellow-500 hover:bg-yellow-500 hover:text-inherit!', 26 'blue-subtle': `text-blue-500 hover:bg-blue-500 hover:text-inherit!`, 27 28 none: '', 29 } 30 31 export const buttonShadow = { 32 sm: 'shadow-xs', 33 none: 'shadow-none', 34 } 35 36 export type ButtonSize = keyof typeof buttonSize 37 38 export const buttonSize = { 39 xs: 'btn-xs', 40 sm: 'btn-sm', 41 md: 'btn-md', 42 lg: 'btn-lg', 43 xl: 'btn-xl', 44 'square-sm': 'btn-square-sm', 45 'square-md': 'btn-square-md', 46 'square-lg': 'btn-square-lg', 47 'square-xl': 'btn-square-xl', 48 custom: '', 49 } 50 51 const buttonRounding = { 52 pill: 'rounded-full', 53 '2xl': 'rounded-2xl', 54 xl: 'rounded-xl', 55 lg: 'rounded-lg', 56 md: 'rounded-md', 57 inherit: 'rounded-[inherit]', 58 none: '', 59 } 60 type ButtonRoundness = keyof typeof buttonRounding 61 62 const buttonWeight = { 63 md: 'font-medium', 64 none: '', 65 } 66 type ButtonWeight = keyof typeof buttonWeight 67 68 const buttonGap = { 69 xl: 'gap-3', 70 lg: 'gap-2', 71 md: 'gap-1.5', 72 none: '', 73 } 74 type ButtonGap = keyof typeof buttonGap 75 76 interface Props { 77 loading?: boolean 78 submit?: boolean 79 type?: 'button' | 'none' 80 color?: ButtonColor 81 size?: ButtonSize 82 rounding?: ButtonRoundness 83 alignment?: ButtonAlignment 84 shadow?: ButtonShadow 85 gap?: ButtonGap 86 loaderWidth?: number | undefined 87 href?: string | undefined 88 class?: ClassValue 89 prefix?: Snippet 90 children?: Snippet 91 suffix?: Snippet 92 icon?: IconSource 93 weight?: ButtonWeight 94 disabled?: boolean 95 onclick?: HTMLButtonAttributes['onclick'] 96 // Common HTML attributes 97 id?: string 98 title?: string 99 tabindex?: number 100 'aria-label'?: string 101 'aria-describedby'?: string 102 'aria-expanded'?: boolean 103 'aria-haspopup'?: boolean | 'menu' | 'listbox' | 'tree' | 'grid' | 'dialog' 104 'aria-pressed'?: boolean | 'mixed' 105 'aria-controls'?: string 106 target?: string 107 rel?: string 108 download?: string | boolean 109 // Allow rest props 110 [key: string]: unknown 111 } 112 113 export type { Props as ButtonProps } 114</script> 115 116<script lang="ts"> 117 let { 118 loading = false, 119 submit = false, 120 type = 'button', 121 color = 'secondary', 122 size = 'md', 123 rounding = size == 'lg' || size == 'square-lg' ? '2xl' : 'xl', 124 alignment = 'center', 125 shadow = color == 'primary' || color == 'secondary' ? 'sm' : 'none', 126 gap = 'md', 127 disabled, 128 loaderWidth = undefined, 129 href = undefined, 130 class: clazz = '', 131 prefix, 132 children, 133 suffix, 134 icon, 135 weight = 'md', 136 ...rest 137 }: Props = $props() 138</script> 139 140<svelte:element 141 this={href ? 'a' : 'button'} 142 role={href ? 'link' : 'button'} 143 {href} 144 {...rest} 145 tabindex={disabled ? -1 : undefined} 146 class={[ 147 type == 'button' && 'btn', 148 buttonSize[size], 149 buttonRounding[rounding], 150 buttonShadow[shadow], 151 buttonColor[color], 152 buttonAlignment[alignment], 153 buttonWeight[weight], 154 buttonGap[gap], 155 (disabled || loading) && 'btn-disabled', 156 alignment == 'center' 157 ? 'origin-center' 158 : alignment == 'left' 159 ? 'origin-left' 160 : 'origin-right', 161 clazz, 162 ]} 163 type={submit ? 'submit' : 'button'} 164> 165 {#if loading} 166 <Spinner width={loaderWidth ?? 16} /> 167 {:else if prefix} 168 {@render prefix?.()} 169 {:else if icon} 170 <Icon 171 src={icon} 172 size="16" 173 mini 174 class={[color == 'secondary' && 'text-slate-600 dark:text-zinc-400']} 175 /> 176 {/if} 177 {@render children?.()} 178 {@render suffix?.()} 179</svelte:element> 180 181<!-- 182 @component 183 184 @slot `prefix` -- Will be replaced if `loading` is `true`. 185 @slot `suffix` 186--> 187 188<style> 189 @reference "../../../../app.css"; 190 191 :global { 192 .btn { 193 display: flex; 194 flex-direction: row; 195 align-items: center; 196 font-size: var(--text-sm); 197 198 transition: 75ms cubic-bezier(0.075, 0.82, 0.165, 1); 199 200 @variant hover { 201 cursor: pointer; 202 } 203 } 204 205 .no-hover:hover { 206 cursor: normal !important; 207 } 208 209 .btn-primary { 210 border: 1px solid transparent; 211 background: radial-gradient( 212 circle at 20% 0%, 213 var(--color-primary-800), 214 var(--color-primary-900) 215 ); 216 background-size: 110% 110% !important; 217 color: var(--color-slate-50); 218 219 @variant dark { 220 background: radial-gradient( 221 circle at center right, 222 var(--color-primary-200), 223 var(--color-primary-100) 224 ); 225 color: var(--color-zinc-900); 226 } 227 228 @variant hover { 229 filter: brightness(120%); 230 @variant dark { 231 filter: brightness(90%); 232 } 233 } 234 235 @variant active { 236 filter: brightness(95%); 237 @variant dark { 238 filter: brightness(85%); 239 } 240 } 241 } 242 243 .btn-secondary { 244 border: 1px solid var(--color-slate-200); 245 border-bottom-color: var(--color-slate-300); 246 background-color: var(--color-white); 247 248 @variant dark { 249 border: 1px solid var(--color-zinc-800); 250 background-color: var(--color-zinc-900); 251 } 252 253 @variant hover { 254 background-color: color-mix( 255 in oklab, 256 var(--color-white), 257 var(--color-slate-50) 258 ); 259 @variant dark { 260 background-color: color-mix( 261 in oklab, 262 var(--color-zinc-925), 263 var(--color-zinc-900) 264 ); 265 } 266 } 267 268 @variant active { 269 background-color: var(--color-slate-100); 270 271 @variant dark { 272 background-color: var(--color-zinc-925); 273 } 274 } 275 } 276 277 .btn-tertiary { 278 background-color: transparent; 279 280 @variant hover { 281 background-color: --alpha(var(--color-slate-200) / 50%); 282 @variant dark { 283 background-color: --alpha(var(--color-zinc-700) / 30%); 284 } 285 } 286 287 @variant active { 288 background-color: --alpha(var(--color-slate-300) / 50%); 289 @variant dark { 290 background-color: --alpha(var(--color-zinc-800) / 30%); 291 } 292 } 293 } 294 295 .btn-danger { 296 /* border border-red-500 bg-red-500 hover:text-red-500 hover:bg-transparent text-white */ 297 298 background-color: var(--color-red-600); 299 color: var(--color-white); 300 301 @variant dark { 302 background-color: var(--color-red-400); 303 color: var(--color-black); 304 } 305 306 @variant hover { 307 filter: brightness(120%); 308 @variant dark { 309 filter: brightness(90%); 310 } 311 } 312 } 313 314 .btn-ghost { 315 border: 1px solid var(--color-slate-200); 316 317 @variant hover { 318 background-color: var(--color-slate-100); 319 } 320 321 @variant active { 322 background-color: --alpha(var(--color-slate-200) / 75%); 323 } 324 325 @variant dark { 326 border-color: var(--color-zinc-800); 327 @variant hover { 328 background-color: var(--color-zinc-800); 329 border-color: var(--color-zinc-700); 330 color: var(--color-zinc-200); 331 } 332 @variant active { 333 background-color: var(--color-zinc-900); 334 } 335 } 336 } 337 338 .btn-xs { 339 padding: calc(var(--spacing) * 1) calc(var(--spacing) * 2); 340 font-size: var(--text-xs); 341 } 342 343 .btn-sm { 344 padding: calc(var(--spacing) * 1.5) calc(var(--spacing) * 3); 345 font-size: 12px; 346 } 347 348 .btn-md { 349 padding-block: calc(var(--spacing) * 1.5); 350 padding-inline: calc(var(--spacing) * 3); 351 font-size: var(--text-sm); 352 } 353 354 .btn-lg { 355 padding-block: calc(var(--spacing) * 2); 356 padding-inline: calc(var(--spacing) * 5); 357 font-size: var(--text-sm); 358 } 359 360 .btn-xl { 361 padding: calc(var(--spacing) * 3) calc(var(--spacing) * 6); 362 font-size: var(--text-base); 363 } 364 365 .btn-square-sm { 366 width: calc(var(--spacing) * 6); 367 height: calc(var(--spacing) * 6); 368 padding: 0; 369 } 370 371 .btn-square-md { 372 width: calc(var(--spacing) * 8); 373 height: calc(var(--spacing) * 8); 374 padding: 0; 375 } 376 377 .btn-square-lg { 378 width: calc(var(--spacing) * 9.5); 379 height: calc(var(--spacing) * 9.5); 380 padding: 0; 381 } 382 383 .btn-square-xl { 384 width: calc(var(--spacing) * 12); 385 height: calc(var(--spacing) * 12); 386 padding: 0; 387 } 388 389 .btn-disabled { 390 pointer-events: none; 391 opacity: 0.6; 392 cursor: normal; 393 394 @variant dark { 395 opacity: 0.5; 396 } 397 } 398 } 399</style>