mirror of
https://github.com/jeffvli/feishin.git
synced 2026-06-13 15:53:18 +02:00
Migrate to Mantine v8 and Design Changes (#961)
* mantine v8 migration * various design changes and improvements
This commit is contained in:
@@ -0,0 +1,156 @@
|
||||
.root {
|
||||
font-weight: 500;
|
||||
border: 1px solid transparent;
|
||||
transition:
|
||||
background-color 0.2s ease-in-out,
|
||||
border-color 0.2s ease-in-out;
|
||||
|
||||
&[data-disabled='true'] {
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
&[data-variant='default'] {
|
||||
color: var(--theme-colors-foreground);
|
||||
background-color: var(--theme-colors-surface);
|
||||
|
||||
&:hover {
|
||||
background: lighten(var(--theme-colors-surface), 5%);
|
||||
}
|
||||
|
||||
&:focus-visible {
|
||||
background: lighten(var(--theme-colors-surface), 10%);
|
||||
}
|
||||
}
|
||||
|
||||
&[data-variant='outline'] {
|
||||
--button-border: var(--theme-colors-border);
|
||||
|
||||
color: var(--theme-colors-foreground);
|
||||
border: 1px solid var(--theme-colors-border);
|
||||
|
||||
&:hover {
|
||||
border: 1px solid lighten(var(--theme-colors-border), 10%);
|
||||
}
|
||||
|
||||
&:focus-visible {
|
||||
border: 1px solid lighten(var(--theme-colors-border), 10%);
|
||||
}
|
||||
}
|
||||
|
||||
&[data-variant='filled'] {
|
||||
color: var(--theme-colors-primary-contrast);
|
||||
background: var(--theme-colors-primary-filled);
|
||||
border: 1px solid transparent;
|
||||
transition: background-color 0.2s ease-in-out;
|
||||
|
||||
&:hover,
|
||||
&:focus-visible {
|
||||
background: darken(var(--theme-colors-primary-filled), 10%);
|
||||
}
|
||||
}
|
||||
|
||||
&[data-variant='state-error'] {
|
||||
background: var(--theme-colors-state-error);
|
||||
|
||||
&:hover,
|
||||
&:focus-visible {
|
||||
background: darken(var(--theme-colors-state-error), 10%);
|
||||
}
|
||||
}
|
||||
|
||||
&[data-variant='state-info'] {
|
||||
background: var(--theme-colors-state-info);
|
||||
|
||||
&:hover,
|
||||
&:focus-visible {
|
||||
background: darken(var(--theme-colors-state-info), 10%);
|
||||
}
|
||||
}
|
||||
|
||||
&[data-variant='state-success'] {
|
||||
background: var(--theme-colors-state-success);
|
||||
|
||||
&:hover,
|
||||
&:focus-visible {
|
||||
background: darken(var(--theme-colors-state-success), 10%);
|
||||
}
|
||||
}
|
||||
|
||||
&[data-variant='state-warning'] {
|
||||
background: var(--theme-colors-state-warning);
|
||||
|
||||
&:hover,
|
||||
&:focus-visible {
|
||||
background: darken(var(--theme-colors-state-warning), 10%);
|
||||
}
|
||||
}
|
||||
|
||||
&[data-variant='subtle'] {
|
||||
color: var(--theme-colors-foreground);
|
||||
background: transparent;
|
||||
|
||||
&:hover,
|
||||
&:active,
|
||||
&:focus-visible {
|
||||
background-color: lighten(var(--button-bg), 10%);
|
||||
}
|
||||
}
|
||||
|
||||
&[data-variant='secondary'] {
|
||||
border: 1px solid transparent;
|
||||
|
||||
&:hover {
|
||||
background-color: darken(var(--button-bg), 5%);
|
||||
}
|
||||
|
||||
&:focus-visible {
|
||||
background-color: darken(var(--button-bg), 10%);
|
||||
}
|
||||
}
|
||||
|
||||
&[data-variant='transparent'] {
|
||||
color: var(--theme-colors-foreground);
|
||||
border: 1px solid transparent;
|
||||
transition: color 0.2s ease-in-out;
|
||||
|
||||
&:hover {
|
||||
background-color: transparent;
|
||||
|
||||
@mixin dark {
|
||||
color: lighten(var(--theme-colors-foreground), 10%);
|
||||
}
|
||||
|
||||
@mixin light {
|
||||
color: darken(var(--theme-colors-foreground), 10%);
|
||||
}
|
||||
}
|
||||
|
||||
&:focus-visible {
|
||||
border: 1px solid lighten(var(--theme-colors-border), 10%);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.loader {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.section {
|
||||
display: flex;
|
||||
margin-inline-end: var(--theme-spacing-sm);
|
||||
}
|
||||
|
||||
.button-inner.loading {
|
||||
color: transparent;
|
||||
}
|
||||
|
||||
.spinner {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate3d(-50%, -50%, 0);
|
||||
}
|
||||
|
||||
.uppercase {
|
||||
text-transform: uppercase;
|
||||
}
|
||||
@@ -0,0 +1,155 @@
|
||||
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>
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user