mirror of
https://github.com/cpvalente/ontime.git
synced 2026-08-10 09:53:48 +00:00
refactor: style cleanups and migrations
fix: close modal with button refactor: small type improvements refactor: migrate time inputs refactor: migrate tooltips refactor: prevent component resizing
This commit is contained in:
committed by
Carlos Valente
parent
6facd6a666
commit
17a7d035bf
@@ -1,4 +1,4 @@
|
||||
import { ButtonHTMLAttributes } from 'react';
|
||||
import { ButtonHTMLAttributes, forwardRef } from 'react';
|
||||
|
||||
import { cx } from '../../utils/styleUtils';
|
||||
|
||||
@@ -17,20 +17,21 @@ interface IconButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
|
||||
size?: 'small' | 'medium' | 'large' | 'xlarge';
|
||||
}
|
||||
|
||||
export default function IconButton({
|
||||
className,
|
||||
children,
|
||||
variant = 'subtle',
|
||||
size = 'medium',
|
||||
...buttonProps
|
||||
}: IconButtonProps) {
|
||||
return (
|
||||
<button
|
||||
className={cx([style.baseIconButton, style[variant], style[size], className])}
|
||||
type='button'
|
||||
{...buttonProps}
|
||||
>
|
||||
{children}
|
||||
</button>
|
||||
);
|
||||
}
|
||||
const IconButton = forwardRef<HTMLButtonElement, IconButtonProps>(
|
||||
({ className, children, variant = 'subtle', size = 'medium', ...buttonProps }, ref) => {
|
||||
return (
|
||||
<button
|
||||
ref={ref}
|
||||
className={cx([style.baseIconButton, style[variant], style[size], className])}
|
||||
type='button'
|
||||
{...buttonProps}
|
||||
>
|
||||
{children}
|
||||
</button>
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
IconButton.displayName = 'IconButton';
|
||||
|
||||
export default IconButton;
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
import { MouseEvent } from 'react';
|
||||
import { IconButton, IconButtonProps, Tooltip } from '@chakra-ui/react';
|
||||
|
||||
interface TooltipActionBtnProps extends IconButtonProps {
|
||||
clickHandler: (event: MouseEvent) => void | Promise<void>;
|
||||
tooltip: string;
|
||||
openDelay?: number;
|
||||
}
|
||||
|
||||
export default function TooltipActionBtn(props: TooltipActionBtnProps) {
|
||||
const { clickHandler, icon, size = 'xs', tooltip, openDelay = 0, className, ...rest } = props;
|
||||
return (
|
||||
<Tooltip label={tooltip} openDelay={openDelay}>
|
||||
<IconButton {...rest} size={size} icon={icon} onClick={clickHandler} className={className} />
|
||||
</Tooltip>
|
||||
);
|
||||
}
|
||||
@@ -1,8 +1,7 @@
|
||||
import { IoChevronDown, IoChevronUp } from 'react-icons/io5';
|
||||
import { Tooltip } from '@chakra-ui/react';
|
||||
|
||||
import { tooltipDelayFast } from '../../../ontimeConfig';
|
||||
import { millisToDelayString } from '../../utils/dateConfig';
|
||||
import Tooltip from '../tooltip/Tooltip';
|
||||
|
||||
import style from './DelayIndicator.module.scss';
|
||||
|
||||
@@ -23,8 +22,8 @@ export default function DelayIndicator(props: DelayIndicatorProps) {
|
||||
: millisToDelayString(delayValue);
|
||||
|
||||
return (
|
||||
<Tooltip openDelay={tooltipDelayFast} label={delayString}>
|
||||
<span className={style.delaySymbol}>{delayValue < 0 ? <IoChevronDown /> : <IoChevronUp />}</span>
|
||||
<Tooltip text={delayString} render={<span />} className={style.delaySymbol}>
|
||||
{delayValue < 0 ? <IoChevronDown /> : <IoChevronUp />}
|
||||
</Tooltip>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -18,8 +18,20 @@ interface NullableTimeInputProps<T extends string> {
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export default function NullableTimeInput<T extends string>(props: NullableTimeInputProps<T>) {
|
||||
const { id, name, submitHandler, time, emptyDisplay, placeholder, disabled, align = 'center', className } = props;
|
||||
/**
|
||||
* Similar to TimeInput, but allows clearing the time value
|
||||
*/
|
||||
export default function NullableTimeInput<T extends string>({
|
||||
id,
|
||||
name,
|
||||
submitHandler,
|
||||
time,
|
||||
emptyDisplay,
|
||||
placeholder,
|
||||
disabled,
|
||||
align = 'center',
|
||||
className,
|
||||
}: NullableTimeInputProps<T>) {
|
||||
const inputRef = useRef<HTMLInputElement | null>(null);
|
||||
const [value, setValue] = useState<string>('');
|
||||
const ignoreChange = useRef(false);
|
||||
|
||||
@@ -3,4 +3,8 @@
|
||||
max-width: 7.5em;
|
||||
letter-spacing: 0.5px;
|
||||
font-variant-numeric: tabular-nums;
|
||||
|
||||
&.delayed {
|
||||
border: 1px solid $ontime-delay-text;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,11 +14,21 @@ interface TimeInputProps<T extends string> {
|
||||
placeholder?: string;
|
||||
disabled?: boolean;
|
||||
align?: 'left' | 'center';
|
||||
delayed?: boolean;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export default function TimeInput<T extends string>(props: TimeInputProps<T>) {
|
||||
const { id, name, submitHandler, time, placeholder, disabled, align = 'center', className } = props;
|
||||
export default function TimeInput<T extends string>({
|
||||
id,
|
||||
name,
|
||||
submitHandler,
|
||||
time,
|
||||
placeholder,
|
||||
disabled,
|
||||
align = 'center',
|
||||
delayed,
|
||||
className,
|
||||
}: TimeInputProps<T>) {
|
||||
const inputRef = useRef<HTMLInputElement | null>(null);
|
||||
const [value, setValue] = useState<string>('');
|
||||
const ignoreChange = useRef(false);
|
||||
@@ -122,7 +132,7 @@ export default function TimeInput<T extends string>(props: TimeInputProps<T>) {
|
||||
disabled={disabled}
|
||||
ref={inputRef}
|
||||
data-testid={`time-input-${name}`}
|
||||
className={cx([style.timeInput, className])}
|
||||
className={cx([style.timeInput, delayed && style.delayed, className])}
|
||||
placeholder={placeholder}
|
||||
onFocus={handleFocus}
|
||||
onChange={(event) => setValue(event.target.value)}
|
||||
@@ -130,7 +140,6 @@ export default function TimeInput<T extends string>(props: TimeInputProps<T>) {
|
||||
onKeyDown={onKeyDownHandler}
|
||||
value={value}
|
||||
maxLength={8}
|
||||
autoComplete='off'
|
||||
style={{
|
||||
textAlign: align,
|
||||
}}
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
$input-delayed-border-color: $ontime-delay-text;
|
||||
|
||||
.timeInput {
|
||||
border: 1px solid transparent;
|
||||
color: $label-gray;
|
||||
|
||||
&.delayed {
|
||||
border: 1px solid $input-delayed-border-color;
|
||||
}
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
import { PropsWithChildren } from 'react';
|
||||
import { InputGroup } from '@chakra-ui/react';
|
||||
|
||||
import { cx } from '../../../utils/styleUtils';
|
||||
|
||||
import TimeInput from './TimeInput';
|
||||
|
||||
import style from './TimeInputWithButton.module.scss';
|
||||
|
||||
interface TimeInputWithButtonProps<T extends string> {
|
||||
name: T;
|
||||
submitHandler: (field: T, value: string) => void;
|
||||
time?: number;
|
||||
hasDelay?: boolean;
|
||||
disabled?: boolean;
|
||||
placeholder: string;
|
||||
}
|
||||
|
||||
export default function TimeInputWithButton<T extends string>(props: PropsWithChildren<TimeInputWithButtonProps<T>>) {
|
||||
const { name, submitHandler, time, hasDelay, placeholder, disabled, children } = props;
|
||||
|
||||
const inputClasses = cx([style.timeInput, hasDelay ? style.delayed : null]);
|
||||
|
||||
return (
|
||||
<InputGroup size='sm' className={inputClasses} width='fit-content'>
|
||||
<TimeInput<T>
|
||||
name={name}
|
||||
submitHandler={submitHandler}
|
||||
time={time}
|
||||
placeholder={placeholder}
|
||||
align='left'
|
||||
disabled={disabled}
|
||||
/>
|
||||
{children}
|
||||
</InputGroup>
|
||||
);
|
||||
}
|
||||
@@ -15,4 +15,9 @@
|
||||
&:hover {
|
||||
color: $ontime-color;
|
||||
}
|
||||
|
||||
&:focus {
|
||||
outline: none;
|
||||
box-shadow: 0 1px 0 0 currentColor;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
border-radius: 3px;
|
||||
box-shadow: $box-shadow-l1;
|
||||
border: 1px solid $gray-1100;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.backdrop {
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
.tooltip {
|
||||
font-size: calc(1rem - 3px);
|
||||
background-color: $ui-white;
|
||||
color: $ui-black;
|
||||
padding: 0.125rem 0.5rem;
|
||||
border-radius: 2px;
|
||||
line-height: 1.25em;
|
||||
max-width: 200px;
|
||||
|
||||
transform-origin: var(--transform-origin);
|
||||
transition:
|
||||
transform 150ms,
|
||||
opacity 150ms;
|
||||
|
||||
&[data-starting-style],
|
||||
&[data-ending-style] {
|
||||
opacity: 0;
|
||||
transform: scale(0.9);
|
||||
}
|
||||
|
||||
&[data-instant] {
|
||||
transition-duration: 0ms;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
import { PropsWithChildren } from 'react';
|
||||
import { Tooltip as BaseTooltip } from '@base-ui-components/react/tooltip';
|
||||
|
||||
import style from './Tooltip.module.scss';
|
||||
|
||||
interface TooltipProps extends BaseTooltip.Trigger.Props {
|
||||
text: string;
|
||||
}
|
||||
|
||||
export default function Tooltip({ text, children, ...triggerProps }: PropsWithChildren<TooltipProps>) {
|
||||
return (
|
||||
<BaseTooltip.Root>
|
||||
<BaseTooltip.Trigger {...triggerProps}>{children}</BaseTooltip.Trigger>
|
||||
<BaseTooltip.Portal>
|
||||
<BaseTooltip.Positioner side='bottom' sideOffset={4}>
|
||||
<BaseTooltip.Popup className={style.tooltip}>
|
||||
<BaseTooltip.Arrow />
|
||||
{text}
|
||||
</BaseTooltip.Popup>
|
||||
</BaseTooltip.Positioner>
|
||||
</BaseTooltip.Portal>
|
||||
</BaseTooltip.Root>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user