You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
64 lines
1.9 KiB
TypeScript
64 lines
1.9 KiB
TypeScript
import { type Component, type JSX, Show, splitProps } from 'solid-js';
|
|
|
|
/**
|
|
* Props for the reusable Button component.
|
|
*
|
|
* - `primary` (default): indigo background, white text.
|
|
* - `secondary`: white background, gray border, dark text.
|
|
* - `danger`: red background, white text.
|
|
*
|
|
* When `loading` is true, the button is disabled and a spinner replaces the icon.
|
|
*/
|
|
interface ButtonProps extends JSX.ButtonHTMLAttributes<HTMLButtonElement> {
|
|
variant?: 'primary' | 'secondary' | 'danger';
|
|
loading?: boolean;
|
|
icon?: Component<{ class?: string }>;
|
|
}
|
|
|
|
const variantClasses: Record<string, string> = {
|
|
primary:
|
|
'text-white bg-indigo-600 hover:bg-indigo-700 border-transparent focus:ring-indigo-500',
|
|
secondary:
|
|
'text-gray-700 bg-white hover:bg-gray-50 border-gray-300 focus:ring-indigo-500',
|
|
danger:
|
|
'text-white bg-red-600 hover:bg-red-700 border-transparent focus:ring-red-500',
|
|
};
|
|
|
|
/** Styled button with variant theming, optional leading icon, and loading state. */
|
|
const Button: Component<ButtonProps> = (allProps) => {
|
|
const [props, rest] = splitProps(allProps, [
|
|
'variant',
|
|
'loading',
|
|
'icon',
|
|
'children',
|
|
'class',
|
|
]);
|
|
const variant = () => props.variant ?? 'primary';
|
|
|
|
const renderIcon = () => {
|
|
if (props.loading) {
|
|
return (
|
|
<div class="animate-spin rounded-full h-4 w-4 border-b-2 border-current mr-2" />
|
|
);
|
|
}
|
|
if (props.icon) {
|
|
const Icon = props.icon;
|
|
return <Icon class="-ml-1 mr-2 h-5 w-5" />;
|
|
}
|
|
return null;
|
|
};
|
|
|
|
return (
|
|
<button
|
|
{...rest}
|
|
disabled={rest.disabled || props.loading}
|
|
class={`inline-flex items-center justify-center px-4 py-2 border shadow-sm text-sm font-medium rounded-md focus:outline-none focus:ring-2 focus:ring-offset-2 disabled:opacity-50 disabled:cursor-not-allowed ${variantClasses[variant()]} ${props.class ?? ''}`}
|
|
>
|
|
{renderIcon()}
|
|
{props.children}
|
|
</button>
|
|
);
|
|
};
|
|
|
|
export default Button;
|