Improve props handling and types in components

This commit is contained in:
Bartłomiej Głownia 2022-03-07 13:27:56 +01:00 committed by Matthew Russell
parent 20a053a4f5
commit 33284da828
4 changed files with 92 additions and 85 deletions

View File

@ -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>
);
}
);

View File

@ -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;

View File

@ -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')} />
)
);

View File

@ -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)} />
);