Compose form design changes

Signed-off-by: marcin mikołajczak <git@mkljczk.pl>
This commit is contained in:
marcin mikołajczak
2024-05-15 18:56:18 +02:00
parent 1aafe10e99
commit 4ceef0fe81
9 changed files with 60 additions and 92 deletions

View File

@ -22,9 +22,10 @@ interface IDropdownMenuItem {
index: number;
item: MenuItem | null;
onClick?(): void;
autoFocus?: boolean;
}
const DropdownMenuItem = ({ index, item, onClick }: IDropdownMenuItem) => {
const DropdownMenuItem = ({ index, item, onClick, autoFocus }: IDropdownMenuItem) => {
const history = useHistory();
const itemRef = useRef<HTMLAnchorElement>(null);
@ -63,7 +64,7 @@ const DropdownMenuItem = ({ index, item, onClick }: IDropdownMenuItem) => {
useEffect(() => {
const firstItem = index === 0;
if (itemRef.current && firstItem) {
if (itemRef.current && (autoFocus ? firstItem : item?.active)) {
itemRef.current.focus({ preventScroll: true });
}
}, [itemRef.current, index]);

View File

@ -262,6 +262,8 @@ const DropdownMenu = (props: IDropdownMenu) => {
return null;
}
const autoFocus = !items.some((item) => item?.active);
return (
<>
{children ? (
@ -309,6 +311,7 @@ const DropdownMenu = (props: IDropdownMenu) => {
item={item}
index={idx}
onClick={handleClose}
autoFocus={autoFocus}
/>
))}
</ul>

View File

@ -8,19 +8,16 @@ import { useButtonStyles } from './useButtonStyles';
import type { ButtonSizes, ButtonThemes } from './useButtonStyles';
interface IButton {
interface IButton extends Pick<
React.ComponentProps<'button'>,
'children' | 'className' | 'disabled' | 'onClick' | 'onMouseDown' | 'onKeyDown' | 'title' | 'type'
> {
/** Whether this button expands the width of its container. */
block?: boolean;
/** Elements inside the <button> */
children?: React.ReactNode;
/** Extra class names for the button. */
className?: string;
/** Prevent the button from being clicked. */
disabled?: boolean;
/** URL to an SVG icon to render inside the button. */
icon?: string;
/** Action when the button is clicked. */
onClick?: (event: React.MouseEvent<HTMLButtonElement>) => void;
/** URL to an SVG icon to render inside the button next to the text. */
secondaryIcon?: string;
/** A predefined button size. */
size?: ButtonSizes;
/** Text inside the button. Takes precedence over `children`. */
@ -29,26 +26,24 @@ interface IButton {
to?: string;
/** Styles the button visually with a predefined theme. */
theme?: ButtonThemes;
/** Whether this button should submit a form by default. */
type?: 'button' | 'submit';
}
/** Customizable button element with various themes. */
const Button = React.forwardRef<HTMLButtonElement, IButton>((props, ref): JSX.Element => {
const {
block = false,
children,
disabled = false,
icon,
onClick,
size = 'md',
text,
theme = 'secondary',
to,
type = 'button',
className,
} = props;
const Button = React.forwardRef<HTMLButtonElement, IButton>(({
block = false,
children,
disabled = false,
icon,
secondaryIcon,
onClick,
size = 'md',
text,
theme = 'secondary',
to,
type = 'button',
className,
...props
}, ref): JSX.Element => {
const body = text || children;
const themeClass = useButtonStyles({
@ -58,14 +53,6 @@ const Button = React.forwardRef<HTMLButtonElement, IButton>((props, ref): JSX.El
size,
});
const renderIcon = () => {
if (!icon) {
return null;
}
return <Icon src={icon} className='h-4 w-4' />;
};
const handleClick: React.MouseEventHandler<HTMLButtonElement> = React.useCallback((event) => {
if (onClick && !disabled) {
onClick(event);
@ -74,18 +61,21 @@ const Button = React.forwardRef<HTMLButtonElement, IButton>((props, ref): JSX.El
const renderButton = () => (
<button
className={clsx('space-x-2 rtl:space-x-reverse', themeClass, className)}
{...props}
className={clsx('rtl:space-x-reverse', themeClass, className)}
disabled={disabled}
onClick={handleClick}
ref={ref}
type={type}
data-testid='button'
>
{renderIcon()}
{icon ? <Icon src={icon} className='h-4 w-4' /> : null}
{body && (
<span>{body}</span>
)}
{secondaryIcon ? <Icon src={secondaryIcon} className='h-4 w-4' /> : null}
</button>
);

View File

@ -11,14 +11,14 @@ const themes = {
danger: 'border-transparent bg-danger-100 dark:bg-danger-900 text-danger-600 dark:text-danger-200 hover:bg-danger-600 hover:text-gray-100 dark:hover:text-gray-100 dark:hover:bg-danger-500 focus:ring-danger-500',
transparent: 'border-transparent bg-transparent text-primary-600 dark:text-accent-blue dark:bg-transparent hover:bg-gray-200 dark:hover:bg-gray-800/50',
outline: 'border-gray-100 border-2 bg-transparent text-gray-100 hover:bg-white/10',
muted: 'border border-solid bg-transparent border-gray-400 dark:border-gray-800 hover:border-primary-300 dark:hover:border-primary-700 focus:border-primary-500 text-gray-900 dark:text-gray-100 focus:ring-primary-500',
muted: 'border border-solid bg-transparent border-gray-400 dark:border-gray-800 hover:border-primary-300 dark:hover:border-primary-700 focus:border-primary-500 text-gray-800 dark:text-gray-100 focus:ring-primary-500',
};
const sizes = {
xs: 'px-3 py-1 text-xs',
sm: 'px-3 py-1.5 text-xs leading-4',
md: 'px-4 py-2 text-sm',
lg: 'px-6 py-3 text-base',
xs: 'gap-x-1.5 px-2 py-1 text-xs',
sm: 'gap-x-2 px-3 py-1.5 text-xs leading-4',
md: 'gap-x-2 px-4 py-2 text-sm',
lg: 'gap-x-2 px-6 py-3 text-base',
};
type ButtonSizes = keyof typeof sizes