mirror of
https://github.com/jeffvli/feishin.git
synced 2026-05-08 13:00:13 +02:00
150 lines
4.5 KiB
TypeScript
150 lines
4.5 KiB
TypeScript
import type { ButtonVariant, ButtonProps as MantineButtonProps } from '@mantine/core';
|
|
|
|
import { ElementProps, Button as MantineButton } from '@mantine/core';
|
|
import { useTimeout } from '@mantine/hooks';
|
|
import clsx from 'clsx';
|
|
import { forwardRef, useCallback, useRef, useState } from 'react';
|
|
|
|
import styles from './button.module.css';
|
|
|
|
import { Spinner } from '/@/shared/components/spinner/spinner';
|
|
import { Tooltip, TooltipProps } from '/@/shared/components/tooltip/tooltip';
|
|
import { createPolymorphicComponent } from '/@/shared/utils/create-polymorphic-component';
|
|
|
|
export interface ButtonProps
|
|
extends ElementProps<'button', keyof MantineButtonProps>,
|
|
MantineButtonProps,
|
|
MantineButtonProps {
|
|
tooltip?: Omit<TooltipProps, 'children'>;
|
|
uppercase?: boolean;
|
|
variant?: ExtendedButtonVariant;
|
|
}
|
|
|
|
type ExtendedButtonVariant =
|
|
| 'state-error'
|
|
| 'state-info'
|
|
| 'state-success'
|
|
| 'state-warning'
|
|
| ButtonVariant;
|
|
|
|
export const _Button = forwardRef<HTMLButtonElement, ButtonProps>(
|
|
(
|
|
{
|
|
children,
|
|
classNames,
|
|
loading,
|
|
size = 'sm',
|
|
style,
|
|
tooltip,
|
|
uppercase,
|
|
variant = 'default',
|
|
...props
|
|
}: ButtonProps,
|
|
ref,
|
|
) => {
|
|
if (tooltip) {
|
|
return (
|
|
<Tooltip withinPortal {...tooltip}>
|
|
<MantineButton
|
|
autoContrast
|
|
classNames={{
|
|
label: clsx(styles.label, {
|
|
[styles.uppercase]: uppercase,
|
|
}),
|
|
loader: styles.loader,
|
|
root: styles.root,
|
|
section: styles.section,
|
|
...classNames,
|
|
}}
|
|
ref={ref}
|
|
size={size}
|
|
style={style}
|
|
variant={variant}
|
|
{...props}
|
|
>
|
|
{children}
|
|
{loading && (
|
|
<div className={styles.spinner}>
|
|
<Spinner />
|
|
</div>
|
|
)}
|
|
</MantineButton>
|
|
</Tooltip>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<MantineButton
|
|
classNames={{
|
|
label: clsx(styles.label, {
|
|
[styles.uppercase]: uppercase,
|
|
}),
|
|
loader: styles.loader,
|
|
root: styles.root,
|
|
section: styles.section,
|
|
...classNames,
|
|
}}
|
|
ref={ref}
|
|
size={size}
|
|
style={style}
|
|
variant={variant}
|
|
{...props}
|
|
>
|
|
{children}
|
|
{loading && (
|
|
<div className={styles.spinner}>
|
|
<Spinner />
|
|
</div>
|
|
)}
|
|
</MantineButton>
|
|
);
|
|
},
|
|
);
|
|
|
|
export const Button = createPolymorphicComponent<'button', ButtonProps>(_Button);
|
|
|
|
interface TimeoutButtonProps extends ButtonProps {
|
|
timeoutProps: {
|
|
callback: () => void;
|
|
duration: number;
|
|
};
|
|
}
|
|
|
|
export const TimeoutButton = ({ timeoutProps, ...props }: TimeoutButtonProps) => {
|
|
const [, setTimeoutRemaining] = useState(timeoutProps.duration);
|
|
const [isRunning, setIsRunning] = useState(false);
|
|
const intervalRef = useRef(0);
|
|
|
|
const callback = () => {
|
|
timeoutProps.callback();
|
|
setTimeoutRemaining(timeoutProps.duration);
|
|
clearInterval(intervalRef.current);
|
|
setIsRunning(false);
|
|
};
|
|
|
|
const { clear, start } = useTimeout(callback, timeoutProps.duration);
|
|
|
|
const startTimeout = useCallback(() => {
|
|
if (isRunning) {
|
|
clearInterval(intervalRef.current);
|
|
setIsRunning(false);
|
|
clear();
|
|
} else {
|
|
setIsRunning(true);
|
|
start();
|
|
|
|
const intervalId = window.setInterval(() => {
|
|
setTimeoutRemaining((prev) => prev - 100);
|
|
}, 100);
|
|
|
|
intervalRef.current = intervalId;
|
|
}
|
|
}, [clear, isRunning, start]);
|
|
|
|
return (
|
|
<Button onClick={startTimeout} {...props}>
|
|
{isRunning ? 'Cancel' : props.children}
|
|
</Button>
|
|
);
|
|
};
|