Improve props handling and types in components
This commit is contained in:
parent
20a053a4f5
commit
33284da828
@ -1,29 +1,32 @@
|
||||
import {
|
||||
AnchorHTMLAttributes,
|
||||
ButtonHTMLAttributes,
|
||||
InputHTMLAttributes,
|
||||
forwardRef,
|
||||
} from 'react';
|
||||
import classNames from 'classnames';
|
||||
import { Icon, IconName } from '../icon';
|
||||
export interface ButtonProps {
|
||||
tag?: 'a' | 'button';
|
||||
|
||||
interface CommonProps {
|
||||
children?: React.ReactNode;
|
||||
onClick?: React.MouseEventHandler<HTMLButtonElement> &
|
||||
React.MouseEventHandler<HTMLAnchorElement>;
|
||||
variant?: 'primary' | 'secondary' | 'accent' | 'inline';
|
||||
disabled?: boolean;
|
||||
className?: string;
|
||||
prependIconName?: IconName;
|
||||
appendIconName?: IconName;
|
||||
}
|
||||
export interface ButtonProps
|
||||
extends ButtonHTMLAttributes<HTMLButtonElement>,
|
||||
CommonProps {}
|
||||
|
||||
export function Button({
|
||||
tag = 'button',
|
||||
variant = 'primary',
|
||||
children,
|
||||
onClick,
|
||||
disabled,
|
||||
className,
|
||||
prependIconName,
|
||||
appendIconName,
|
||||
}: ButtonProps) {
|
||||
const ButtonTag: keyof JSX.IntrinsicElements = tag;
|
||||
const effectiveClassName = classNames(
|
||||
export interface AnchorButtonProps
|
||||
extends AnchorHTMLAttributes<HTMLAnchorElement>,
|
||||
CommonProps {}
|
||||
|
||||
const getClassName = (
|
||||
className: CommonProps['className'],
|
||||
variant: CommonProps['variant']
|
||||
) =>
|
||||
classNames(
|
||||
[
|
||||
'inline-flex',
|
||||
'items-center',
|
||||
@ -53,7 +56,7 @@ export function Button({
|
||||
'border-black/60 dark:border-white/60':
|
||||
variant === 'primary' || variant === 'secondary',
|
||||
'text-white dark:text-black': variant === 'primary',
|
||||
'hover:bg-black/70 dark:hover:bg-white/70': variant === 'primary',
|
||||
'hover:bg-black/80 dark:hover:bg-white/80': variant === 'primary',
|
||||
'active:bg-white dark:active:bg-black':
|
||||
variant === 'primary' || variant === 'accent',
|
||||
'active:text-black dark:active:text-white':
|
||||
@ -96,24 +99,66 @@ export function Button({
|
||||
},
|
||||
className
|
||||
);
|
||||
let icon;
|
||||
|
||||
const getContent = (
|
||||
children: React.ReactNode,
|
||||
prependIconName?: IconName,
|
||||
appendIconName?: IconName
|
||||
) => {
|
||||
const iconName = prependIconName || appendIconName;
|
||||
if (iconName !== undefined) {
|
||||
const iconClassName = classNames(['fill-current'], {
|
||||
'mr-8': prependIconName,
|
||||
'ml-8': appendIconName,
|
||||
});
|
||||
icon = <Icon name={iconName} className={iconClassName} size={16} />;
|
||||
if (iconName === undefined) {
|
||||
return children;
|
||||
}
|
||||
const iconClassName = classNames(['fill-current'], {
|
||||
'mr-8': prependIconName,
|
||||
'ml-8': appendIconName,
|
||||
});
|
||||
const icon = <Icon name={iconName} className={iconClassName} size={16} />;
|
||||
return (
|
||||
<ButtonTag
|
||||
onClick={onClick}
|
||||
className={effectiveClassName}
|
||||
disabled={disabled}
|
||||
>
|
||||
<>
|
||||
{prependIconName && icon}
|
||||
{children}
|
||||
{appendIconName && icon}
|
||||
</ButtonTag>
|
||||
</>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export const Button = forwardRef<HTMLButtonElement, ButtonProps>(
|
||||
(
|
||||
{
|
||||
variant = 'primary',
|
||||
children,
|
||||
className,
|
||||
prependIconName,
|
||||
appendIconName,
|
||||
...prosp
|
||||
},
|
||||
ref
|
||||
) => {
|
||||
return (
|
||||
<button ref={ref} className={getClassName(className, variant)} {...prosp}>
|
||||
{getContent(children, prependIconName, appendIconName)}
|
||||
</button>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
export const AnchorButton = forwardRef<HTMLAnchorElement, AnchorButtonProps>(
|
||||
(
|
||||
{
|
||||
variant = 'primary',
|
||||
children,
|
||||
className,
|
||||
prependIconName,
|
||||
appendIconName,
|
||||
...prosp
|
||||
},
|
||||
ref
|
||||
) => {
|
||||
return (
|
||||
<a ref={ref} className={getClassName(className, variant)} {...prosp}>
|
||||
{getContent(children, prependIconName, appendIconName)}
|
||||
</a>
|
||||
);
|
||||
}
|
||||
);
|
@ -11,11 +11,9 @@ interface InputProps extends InputHTMLAttributes<HTMLInputElement> {
|
||||
}
|
||||
export const inputClassNames = ({
|
||||
hasError,
|
||||
disabled,
|
||||
className,
|
||||
}: {
|
||||
hasError?: boolean;
|
||||
disabled?: boolean;
|
||||
className?: string;
|
||||
}) =>
|
||||
classNames(
|
||||
@ -23,7 +21,6 @@ export const inputClassNames = ({
|
||||
'inline-flex',
|
||||
'items-center',
|
||||
'box-border',
|
||||
'h-28',
|
||||
'border',
|
||||
'bg-clip-padding',
|
||||
'border-black/60 dark:border-white/60',
|
||||
@ -71,7 +68,6 @@ export const Input = forwardRef<HTMLInputElement, InputProps>(
|
||||
{...props}
|
||||
ref={ref}
|
||||
className={classNames(inputClassNames({ className, ...props }))}
|
||||
style={{} /*inputStyle(props)*/}
|
||||
/>
|
||||
);
|
||||
const iconName = prependIconName || appendIconName;
|
||||
|
@ -1,34 +1,17 @@
|
||||
import { SelectHTMLAttributes, forwardRef } from 'react';
|
||||
import classNames from 'classnames';
|
||||
import { inputClassNames, inputStyle } from '../input/input';
|
||||
import { inputClassNames } from '../input/input';
|
||||
|
||||
/* eslint-disable-next-line */
|
||||
export interface TextAreaProps {
|
||||
onChange?: React.FormEventHandler<HTMLSelectElement>;
|
||||
export interface SelectProps extends SelectHTMLAttributes<HTMLSelectElement> {
|
||||
hasError?: boolean;
|
||||
disabled?: boolean;
|
||||
className?: string;
|
||||
value?: string | number;
|
||||
children?: React.ReactNode;
|
||||
}
|
||||
|
||||
export function Select({
|
||||
hasError,
|
||||
onChange,
|
||||
disabled,
|
||||
className,
|
||||
children,
|
||||
}: TextAreaProps) {
|
||||
return (
|
||||
<select
|
||||
onChange={onChange}
|
||||
className={classNames(
|
||||
inputClassNames({ hasError, disabled, className }),
|
||||
'h-28'
|
||||
)}
|
||||
disabled={disabled}
|
||||
style={inputStyle({ disabled })}
|
||||
>
|
||||
{children}
|
||||
</select>
|
||||
);
|
||||
}
|
||||
export const Select = forwardRef<HTMLTextAreaElement, SelectProps>(
|
||||
(props, ref) => (
|
||||
<select {...props} className={classNames(inputClassNames(props), 'h-28')} />
|
||||
)
|
||||
);
|
||||
|
@ -1,30 +1,13 @@
|
||||
import { inputClassNames, inputStyle } from '../input/input';
|
||||
import { TextareaHTMLAttributes, forwardRef } from 'react';
|
||||
import { inputClassNames } from '../input/input';
|
||||
|
||||
/* eslint-disable-next-line */
|
||||
export interface TextAreaProps {
|
||||
onChange?: React.FormEventHandler<HTMLTextAreaElement>;
|
||||
export interface TextAreaProps
|
||||
extends TextareaHTMLAttributes<HTMLTextAreaElement> {
|
||||
hasError?: boolean;
|
||||
disabled?: boolean;
|
||||
className?: string;
|
||||
value?: string | number;
|
||||
children?: React.ReactNode;
|
||||
}
|
||||
|
||||
export function TextArea({
|
||||
hasError,
|
||||
onChange,
|
||||
disabled,
|
||||
className,
|
||||
children,
|
||||
}: TextAreaProps) {
|
||||
return (
|
||||
<textarea
|
||||
onChange={onChange}
|
||||
className={inputClassNames({ hasError, disabled, className })}
|
||||
disabled={disabled}
|
||||
style={inputStyle({ disabled })}
|
||||
>
|
||||
{children}
|
||||
</textarea>
|
||||
);
|
||||
}
|
||||
export const TextArea = forwardRef<HTMLTextAreaElement, TextAreaProps>(
|
||||
(props, ref) => <textarea {...props} className={inputClassNames(props)} />
|
||||
);
|
||||
|
Loading…
Reference in New Issue
Block a user