brf/client/shared/components/button_factory.tsx
2025-12-13 21:12:08 +01:00

86 lines
2.1 KiB
TypeScript

import { h, type FunctionComponent, type PointerEventHandler } from 'preact'
import cn from 'classnames'
type Styles<C extends string, D extends string, S extends string> = {
base: string
autoHeight?: string
fullWidth?: string
icon?: string
invert?: string
} & Record<C, string> &
Record<D, string> &
Record<S, string>
type Props<C extends string, D extends string, I extends string, S extends string> = {
autoHeight?: boolean
className?: string
color?: C
design?: D
disabled?: boolean
fullWidth?: boolean
icon?: I
iconSize?: string
invert?: boolean
onClick?: PointerEventHandler<HTMLButtonElement>
size?: S
tabIndex?: number
tag?: string
title?: string
type?: 'button' | 'reset' | 'submit'
}
type Options<C extends string, D extends string, I extends string, S extends string> = {
defaults: Partial<Props<C, D, I, S>>
icons: Record<I, string>
styles: Styles<C, D, S>
}
export default function buttonFactory<
C extends string = never,
D extends string = never,
I extends string = never,
S extends string = never,
>({ defaults, icons, styles }: Options<C, D, I, S>) {
const Button: FunctionComponent<Props<C, D, I, S>> = ({
autoHeight = defaults?.autoHeight,
children,
className,
color = defaults?.color,
design = defaults?.design,
disabled,
fullWidth = defaults?.fullWidth,
icon = defaults?.icon,
iconSize,
invert = defaults?.invert,
onClick,
size = defaults?.size,
tabIndex,
title,
type,
}) => (
<button
className={cn(
styles.base,
design && styles[design],
size && styles[size],
color && styles[color],
autoHeight && styles.autoHeight,
fullWidth && styles.fullWidth,
invert && styles.invert,
className,
)}
style={iconSize && { '--icon-size': iconSize }}
disabled={disabled}
onClick={onClick}
tabIndex={tabIndex}
title={title}
type={type}
>
{icon && <i className={styles.icon} style={{ maskImage: `url(${icons[icon] || icon})` }} />}
{children && <span>{children}</span>}
</button>
)
return Button
}