import React, { useEffect, useRef } from 'react'; import { hostProps } from '../utils/host-props'; import { useBooleanProperty } from '../utils/use-boolean-prop'; type BuiltinFlavor = 'primary' | 'success' | 'danger' | 'warning' | 'neutral'; type ShadedFlavor = BuiltinFlavor | `${BuiltinFlavor}+` | `${BuiltinFlavor}-`; type ButtonAppearance = 'solid' | 'outlined' | 'ghost'; export interface TyButtonCSSProperties extends React.CSSProperties { '--ty-button-bg'?: string; '--ty-button-bg-hover'?: string; '--ty-button-color'?: string; '--ty-button-border'?: string; } export interface TyButtonProps extends Omit, 'style'> { style?: TyButtonCSSProperties; /** * Semantic styling variant. Built-in flavors get themed styles; append `+` * for a stronger shade or `-` for a softer one (e.g. `"primary+"`, * `"danger-"`). Any other string is passed through as-is — theme it via * `--ty-button-*` CSS variables. */ flavor?: ShadedFlavor | (string & {}); /** * Visual appearance: * - `"solid"` (default) — saturated brand fill with paired text color * - `"outlined"` — transparent background, text === border * - `"ghost"` — text only with hover background */ appearance?: ButtonAppearance; /** Button size */ size?: 'xs' | 'sm' | 'md' | 'lg' | 'xl'; /** Button type for form submission */ type?: 'button' | 'submit' | 'reset'; /** Disable the button */ disabled?: boolean; /** Loading state — shows a centered spinner, blocks click, preserves width */ loading?: boolean; /** Pill-shaped button (rounded ends) */ pill?: boolean; /** Action (icon-only square) */ action?: boolean; /** Accessible label for screen readers */ label?: string; /** Form field name for form submission */ name?: string; /** Form field value for form submission */ value?: string; /** Full-width button */ wide?: boolean; /** Button content */ children?: React.ReactNode; } export const TyButton = React.forwardRef( ({ children, type, appearance, disabled, loading, pill, action, wide, label, name, value, onClick, ...props }, ref) => { const elementRef = useRef(null); // Imperatively attach the click listener so onClick reliably fires for the // CustomEvent('click') that re-dispatches on its host (the // inner