mirror of
https://github.com/cpvalente/ontime.git
synced 2026-08-20 14:39:06 +00:00
refactor: cleanup picker implementations
This commit is contained in:
committed by
Carlos Valente
parent
d62e314d19
commit
c69bce3e89
@@ -16,11 +16,11 @@ export default function Swatch(props: SwatchProps) {
|
|||||||
const handleClick = () => {
|
const handleClick = () => {
|
||||||
onClick?.(color);
|
onClick?.(color);
|
||||||
};
|
};
|
||||||
const classes = cx([style.swatch, isSelected ? style.selected : null, onClick ? style.selectable : null]);
|
const classes = cx([style.swatch, isSelected && style.selected, onClick && style.selectable]);
|
||||||
|
|
||||||
if (!color) {
|
if (!color) {
|
||||||
return (
|
return (
|
||||||
<div className={`${classes} ${style.center}`} onClick={handleClick}>
|
<div className={classes} onClick={handleClick}>
|
||||||
<IoBan />
|
<IoBan />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
import { useCallback } from 'react';
|
import { useCallback } from 'react';
|
||||||
|
import { useController, UseControllerProps } from 'react-hook-form';
|
||||||
import { IoEyedrop } from '@react-icons/all-files/io5/IoEyedrop';
|
import { IoEyedrop } from '@react-icons/all-files/io5/IoEyedrop';
|
||||||
import Color from 'color';
|
import { ViewSettings } from 'ontime-types';
|
||||||
|
|
||||||
import PopoverPicker from '../../../../common/components/input/popover-picker/PopoverPicker';
|
import PopoverPicker from '../../../../common/components/input/popover-picker/PopoverPicker';
|
||||||
import { debounce } from '../../../../common/utils/debounce';
|
import { debounce } from '../../../../common/utils/debounce';
|
||||||
import { cx } from '../../../utils/styleUtils';
|
import { cx, getAccessibleColour } from '../../../utils/styleUtils';
|
||||||
|
|
||||||
import style from './SwatchSelect.module.scss';
|
import style from './SwatchSelect.module.scss';
|
||||||
|
|
||||||
@@ -15,26 +16,9 @@ interface SwatchPickerProps {
|
|||||||
alwaysDisplayColor?: boolean;
|
alwaysDisplayColor?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const getIconColor = (color: string, isSelected: boolean) => {
|
|
||||||
if (isSelected) {
|
|
||||||
try {
|
|
||||||
const isLight = Color(color).isLight();
|
|
||||||
return isLight ? '#000000' : '#ffffff';
|
|
||||||
} catch (_error) {
|
|
||||||
/* we are not handling the error here */
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return '#ffffff';
|
|
||||||
};
|
|
||||||
|
|
||||||
export default function SwatchPicker(props: SwatchPickerProps) {
|
export default function SwatchPicker(props: SwatchPickerProps) {
|
||||||
const { color, onChange, isSelected, alwaysDisplayColor } = props;
|
const { color, onChange, isSelected, alwaysDisplayColor } = props;
|
||||||
|
|
||||||
const classes = cx([style.swatch, isSelected ? style.selected : null, style.selectable]);
|
|
||||||
|
|
||||||
const iconColor = getIconColor(color, (alwaysDisplayColor || isSelected) ?? false);
|
|
||||||
|
|
||||||
const debouncedOnChange = useCallback(
|
const debouncedOnChange = useCallback(
|
||||||
debounce((newValue: string) => {
|
debounce((newValue: string) => {
|
||||||
onChange(newValue);
|
onChange(newValue);
|
||||||
@@ -43,15 +27,34 @@ export default function SwatchPicker(props: SwatchPickerProps) {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const displayColor = alwaysDisplayColor || isSelected ? color : '';
|
const displayColor = alwaysDisplayColor || isSelected ? color : '';
|
||||||
|
const { color: iconColor } = getAccessibleColour(displayColor);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={classes}>
|
<PopoverPicker color={displayColor} onChange={debouncedOnChange}>
|
||||||
<PopoverPicker
|
<div
|
||||||
color={displayColor}
|
className={cx([style.swatch, isSelected && style.selected, style.selectable])}
|
||||||
onChange={debouncedOnChange}
|
style={{ backgroundColor: displayColor }}
|
||||||
icon={<IoEyedrop color={iconColor} />}
|
>
|
||||||
hasInput
|
<IoEyedrop color={iconColor} />
|
||||||
/>
|
</div>
|
||||||
</div>
|
</PopoverPicker>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function SwatchPickerRHF(props: UseControllerProps<ViewSettings>) {
|
||||||
|
const { name, control } = props;
|
||||||
|
const {
|
||||||
|
field: { onChange, value },
|
||||||
|
} = useController({ control, name });
|
||||||
|
|
||||||
|
const displayColor = typeof value === 'string' ? value : '';
|
||||||
|
const { color: iconColor } = getAccessibleColour(displayColor);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<PopoverPicker color={value as string} onChange={onChange}>
|
||||||
|
<div className={cx([style.swatch, style.selectable])} style={{ backgroundColor: displayColor }}>
|
||||||
|
<IoEyedrop color={iconColor} />
|
||||||
|
</div>
|
||||||
|
</PopoverPicker>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,15 +1,18 @@
|
|||||||
.list {
|
.list {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
gap: 0.5rem
|
gap: 0.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.swatch {
|
.swatch {
|
||||||
width: 2rem;
|
width: 2rem;
|
||||||
height: 2rem;
|
height: 2rem;
|
||||||
aspect-ratio: 1;
|
|
||||||
border-radius: 99px;
|
border-radius: 99px;
|
||||||
border: 2px solid $gray-1200;
|
border: 2px solid $gray-1200;
|
||||||
|
color: $ui-white;
|
||||||
|
|
||||||
|
display: grid;
|
||||||
|
place-content: center;
|
||||||
|
|
||||||
&.selected {
|
&.selected {
|
||||||
border: 2px solid $blue-500;
|
border: 2px solid $blue-500;
|
||||||
@@ -17,11 +20,9 @@
|
|||||||
|
|
||||||
&.selectable {
|
&.selectable {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
border: 2px solid $blue-300;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.center {
|
|
||||||
display: grid;
|
|
||||||
place-content: center;
|
|
||||||
color: $ui-white;
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,26 +1,7 @@
|
|||||||
.swatch {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
width: 24px;
|
|
||||||
height: 24px;
|
|
||||||
border-radius: 12px;
|
|
||||||
border: 1px solid white;
|
|
||||||
box-shadow: 0 0 0 1px $gray-300;
|
|
||||||
cursor: pointer;
|
|
||||||
|
|
||||||
transition-property: box-shadow;
|
|
||||||
transition-duration: $transition-time-action;
|
|
||||||
}
|
|
||||||
|
|
||||||
.swatch:hover {
|
|
||||||
box-shadow: 0 0 0 1px $blue-300;
|
|
||||||
}
|
|
||||||
|
|
||||||
.input {
|
.input {
|
||||||
color: $gray-200;
|
color: $gray-200;
|
||||||
border: 1px solid transparent;
|
border: 1px solid transparent;
|
||||||
border-radius: 0 3px;
|
border-radius: 0 0 8px 8px;
|
||||||
background-color: $gray-1200;
|
background-color: $gray-1200;
|
||||||
padding: 0 0.5rem;
|
padding: 0 0.5rem;
|
||||||
font-size: 1rem;
|
font-size: 1rem;
|
||||||
|
|||||||
@@ -1,40 +1,22 @@
|
|||||||
import { ReactNode } from 'react';
|
import { PropsWithChildren } from 'react';
|
||||||
import { HexAlphaColorPicker, HexColorInput } from 'react-colorful';
|
import { HexAlphaColorPicker, HexColorInput } from 'react-colorful';
|
||||||
import { useController, UseControllerProps } from 'react-hook-form';
|
|
||||||
import { Popover, PopoverContent, PopoverTrigger } from '@chakra-ui/react';
|
import { Popover, PopoverContent, PopoverTrigger } from '@chakra-ui/react';
|
||||||
import { ViewSettings } from 'ontime-types';
|
|
||||||
|
|
||||||
import style from './PopoverPicker.module.scss';
|
import style from './PopoverPicker.module.scss';
|
||||||
|
|
||||||
export function PopoverPickerRHF(props: UseControllerProps<ViewSettings>) {
|
|
||||||
const { name, control } = props;
|
|
||||||
const {
|
|
||||||
field: { onChange, value },
|
|
||||||
} = useController({ control, name });
|
|
||||||
|
|
||||||
return <PopoverPicker color={value as string} onChange={onChange} />;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface PopoverPickerProps {
|
interface PopoverPickerProps {
|
||||||
color: string;
|
color: string;
|
||||||
icon?: ReactNode;
|
|
||||||
hasInput?: boolean;
|
|
||||||
onChange: (color: string) => void;
|
onChange: (color: string) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function PopoverPicker(props: PopoverPickerProps) {
|
export default function PopoverPicker(props: PropsWithChildren<PopoverPickerProps>) {
|
||||||
const { color, icon, hasInput, onChange } = props;
|
const { color, onChange, children } = props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Popover>
|
<Popover>
|
||||||
<PopoverTrigger>
|
<PopoverTrigger>{children}</PopoverTrigger>
|
||||||
<div className={style.swatch} style={{ backgroundColor: color }}>
|
<PopoverContent className={style.small} style={{ borderRadius: '9px', width: 'auto' }}>
|
||||||
{icon ?? null}
|
|
||||||
</div>
|
|
||||||
</PopoverTrigger>
|
|
||||||
<PopoverContent style={{ width: 'auto' }}>
|
|
||||||
<HexAlphaColorPicker color={color} onChange={onChange} />
|
<HexAlphaColorPicker color={color} onChange={onChange} />
|
||||||
{hasInput && <HexColorInput color={color} onChange={onChange} className={style.input} prefixed />}
|
<HexColorInput color={color} onChange={onChange} className={style.input} prefixed />
|
||||||
</PopoverContent>
|
</PopoverContent>
|
||||||
</Popover>
|
</Popover>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { ViewSettings } from 'ontime-types';
|
|||||||
import { maybeAxiosError } from '../../../../common/api/utils';
|
import { maybeAxiosError } from '../../../../common/api/utils';
|
||||||
import { postViewSettings } from '../../../../common/api/viewSettings';
|
import { postViewSettings } from '../../../../common/api/viewSettings';
|
||||||
import ExternalLink from '../../../../common/components/external-link/ExternalLink';
|
import ExternalLink from '../../../../common/components/external-link/ExternalLink';
|
||||||
import { PopoverPickerRHF } from '../../../../common/components/input/popover-picker/PopoverPicker';
|
import { SwatchPickerRHF } from '../../../../common/components/input/colour-input/SwatchPicker';
|
||||||
import useInfo from '../../../../common/hooks-query/useInfo';
|
import useInfo from '../../../../common/hooks-query/useInfo';
|
||||||
import useViewSettings from '../../../../common/hooks-query/useViewSettings';
|
import useViewSettings from '../../../../common/hooks-query/useViewSettings';
|
||||||
import * as Panel from '../../panel-utils/PanelUtils';
|
import * as Panel from '../../panel-utils/PanelUtils';
|
||||||
@@ -111,15 +111,15 @@ export default function ViewSettingsForm() {
|
|||||||
<Panel.ListGroup>
|
<Panel.ListGroup>
|
||||||
<Panel.ListItem>
|
<Panel.ListItem>
|
||||||
<Panel.Field title='Timer colour' description='Default colour of a running timer' />
|
<Panel.Field title='Timer colour' description='Default colour of a running timer' />
|
||||||
<PopoverPickerRHF name='normalColor' control={control} />
|
<SwatchPickerRHF name='normalColor' control={control} />
|
||||||
</Panel.ListItem>
|
</Panel.ListItem>
|
||||||
<Panel.ListItem>
|
<Panel.ListItem>
|
||||||
<Panel.Field title='Warning colour' description='Colour of a running timer in warning mode' />
|
<Panel.Field title='Warning colour' description='Colour of a running timer in warning mode' />
|
||||||
<PopoverPickerRHF name='warningColor' control={control} />
|
<SwatchPickerRHF name='warningColor' control={control} />
|
||||||
</Panel.ListItem>
|
</Panel.ListItem>
|
||||||
<Panel.ListItem>
|
<Panel.ListItem>
|
||||||
<Panel.Field title='Danger colour' description='Colour of a running timer in danger mode' />
|
<Panel.Field title='Danger colour' description='Colour of a running timer in danger mode' />
|
||||||
<PopoverPickerRHF name='dangerColor' control={control} />
|
<SwatchPickerRHF name='dangerColor' control={control} />
|
||||||
</Panel.ListItem>
|
</Panel.ListItem>
|
||||||
</Panel.ListGroup>
|
</Panel.ListGroup>
|
||||||
<Panel.ListGroup>
|
<Panel.ListGroup>
|
||||||
|
|||||||
Reference in New Issue
Block a user