Files
tabler/shared/ui/Button.astro
T

73 lines
3.0 KiB
Plaintext

---
import Icon from './Icon.astro'
/** core/scss/ui/_buttons.scss `.btn-{size}` modifier classes — 'md' is the default (no class emitted) */
type ButtonSize = 'sm' | 'md' | 'lg' | 'xl'
interface Props {
/** visible label; for iconOnly buttons it becomes the aria-label */
text?: string | undefined
/** passed through literally — the caller owns the relative/absolute convention */
href?: string
/** adds target="_blank" rel="noreferrer" */
external?: boolean
element?: 'a' | 'button'
type?: 'button' | 'reset' | 'submit'
id?: string
color?: string | undefined
/** btn-outline-{color} / btn-ghost-{color} style variants */
variant?: 'outline' | 'ghost'
size?: ButtonSize | undefined
block?: boolean
disabled?: boolean
/** .btn-loading — CSS-only spinner replacing the button content */
loading?: boolean
class?: string
icon?: string | undefined
iconColor?: string
iconEnd?: string
iconOnly?: boolean
/** data-bs-toggle="modal" data-bs-target="#modal-{modalId}" */
modalId?: string
/** data-bs-toggle="toast" data-bs-target="#toast-{toastId}" */
toastId?: string
/** data-bs-dismiss="modal" */
dismiss?: boolean
/** data-bs-toggle="dropdown" */
dropdown?: boolean
/** remaining attributes (data-*, aria-*, …) are forwarded to the root element */
[key: string]: unknown
}
const { text, href, external, element, type, id, color, variant, size, block, disabled, loading, class: className, icon, iconColor, iconEnd, iconOnly, modalId, toastId, dismiss, dropdown, ...rest } = Astro.props
// Actions (dropdown/modal/toast/dismiss) and bare buttons use <button>. Real URLs use <a>.
const isAction = Boolean(dropdown || modalId || toastId || dismiss)
const Element = element ?? (href && !isAction ? 'a' : 'button')
const resolvedHref = Element === 'a' ? href : undefined
const resolvedType = Element === 'button' ? (type ?? 'button') : undefined
const classes = ['btn', color ? `btn-${variant ? `${variant}-` : ''}${color}` : variant && `btn-${variant}`, size && size !== 'md' && `btn-${size}`, className, block && 'w-100', iconOnly && 'btn-icon', loading && 'btn-loading', Element === 'a' && disabled && 'disabled']
---
<Element
href={resolvedHref}
type={resolvedType}
id={id}
class:list={classes}
target={external ? '_blank' : undefined}
rel={external ? 'noreferrer' : undefined}
data-bs-toggle={dropdown ? 'dropdown' : modalId ? 'modal' : toastId ? 'toast' : undefined}
data-bs-target={!dropdown && modalId ? `#modal-${modalId}` : !dropdown && toastId ? `#toast-${toastId}` : undefined}
aria-label={iconOnly ? text : undefined}
data-bs-dismiss={dismiss ? 'modal' : undefined}
disabled={Element === 'button' && disabled ? true : undefined}
aria-disabled={Element === 'a' && disabled ? 'true' : undefined}
tabindex={Element === 'a' && disabled ? -1 : undefined}
{...rest}
>
{icon && <Icon name={icon} color={iconColor} />}
{!iconOnly && text}
{!iconOnly && iconEnd && <Icon name={iconEnd} class="icon-end" />}
</Element>