refactor: style and ux tweaks

This commit is contained in:
Carlos Valente
2025-07-21 07:07:26 +02:00
committed by Carlos Valente
parent 9ebb3decdb
commit 3747dbc1a8
7 changed files with 26 additions and 34 deletions
@@ -1,4 +1,4 @@
import { PropsWithChildren, useState } from 'react'; import { PropsWithChildren, useRef, useState } from 'react';
import { IoCheckmark } from 'react-icons/io5'; import { IoCheckmark } from 'react-icons/io5';
import { IoCopy } from 'react-icons/io5'; import { IoCopy } from 'react-icons/io5';
@@ -24,32 +24,29 @@ export default function CopyTag({
onClick, onClick,
}: PropsWithChildren<CopyTagProps>) { }: PropsWithChildren<CopyTagProps>) {
const [copied, setCopied] = useState(false); const [copied, setCopied] = useState(false);
const timeoutRef = useRef<NodeJS.Timeout | null>(null);
const handleClick = () => { const handleClick = () => {
copyToClipboard(copyValue); copyToClipboard(copyValue);
setCopied(true); setCopied(true);
// reset copied state // reset copied state
setTimeout(() => setCopied(false), 1000); if (timeoutRef.current) {
clearTimeout(timeoutRef.current);
}
timeoutRef.current = setTimeout(() => setCopied(false), 2000);
}; };
return ( return (
<div className={style.copytag}> <div className={style.copytag}>
{onClick !== undefined ? ( {onClick !== undefined ? (
<Button className={style.action} size={size} tabIndex={-1} onClick={onClick} disabled={disabled}> <Button className={style.action} size={size} onClick={onClick} disabled={disabled}>
{children} {children}
</Button> </Button>
) : ( ) : (
<div className={cx([style.label, style[size]])}>{children}</div> <div className={cx([style.label, style[size]])}>{children}</div>
)} )}
<IconButton <IconButton className={style.copy} variant='primary' size={size} onClick={handleClick} disabled={disabled}>
className={style.copy}
variant='primary'
size={size}
tabIndex={-1}
onClick={handleClick}
disabled={disabled}
>
{copied ? <IoCheckmark /> : <IoCopy />} {copied ? <IoCheckmark /> : <IoCopy />}
</IconButton> </IconButton>
</div> </div>
@@ -13,6 +13,7 @@
border-radius: 3px; border-radius: 3px;
box-shadow: $box-shadow-l1; box-shadow: $box-shadow-l1;
border: 1px solid $gray-1100; border: 1px solid $gray-1100;
outline: none;
} }
.backdrop { .backdrop {
@@ -31,11 +31,7 @@
} }
&[data-checked] { &[data-checked] {
background-color: $gray-1200; background-color: $gray-700;
&:hover {
border-color: $gray-1000;
}
} }
&:focus-visible { &:focus-visible {
@@ -57,6 +53,6 @@
border-radius: 100%; border-radius: 100%;
width: 0.5em; width: 0.5em;
height: 0.5em; height: 0.5em;
background-color: $blue-500; background-color: $ui-white;
} }
} }
@@ -1,6 +1,5 @@
.radioGroup { .radioGroup {
display: flex; display: flex;
gap: 0.25rem;
color: $gray-900; color: $gray-900;
font-size: calc(1rem - 2px); font-size: calc(1rem - 2px);
color: $ui-white; color: $ui-white;
@@ -46,10 +45,7 @@
} }
&[data-checked] { &[data-checked] {
background-color: $ui-white; background-color: $blue-700;
&:hover {
border-color: $gray-1000;
}
} }
&:focus-visible { &:focus-visible {
@@ -71,6 +67,6 @@
border-radius: 100%; border-radius: 100%;
width: 0.5rem; width: 0.5rem;
height: 0.5rem; height: 0.5rem;
background-color: $gray-1200; background-color: $ui-white;
} }
} }
@@ -1,4 +1,3 @@
import { memo } from 'react';
import { IoChevronDown } from 'react-icons/io5'; import { IoChevronDown } from 'react-icons/io5';
import { useLocalStorage } from '@mantine/hooks'; import { useLocalStorage } from '@mantine/hooks';
@@ -7,6 +7,7 @@ import IconButton from '../../../../common/components/buttons/IconButton';
import Info from '../../../../common/components/info/Info'; import Info from '../../../../common/components/info/Info';
import ExternalLink from '../../../../common/components/link/external-link/ExternalLink'; import ExternalLink from '../../../../common/components/link/external-link/ExternalLink';
import Switch from '../../../../common/components/switch/Switch'; import Switch from '../../../../common/components/switch/Switch';
import Tag from '../../../../common/components/tag/Tag';
import useUrlPresets, { useUpdateUrlPreset } from '../../../../common/hooks-query/useUrlPresets'; import useUrlPresets, { useUpdateUrlPreset } from '../../../../common/hooks-query/useUrlPresets';
import { handleLinks } from '../../../../common/utils/linkUtils'; import { handleLinks } from '../../../../common/utils/linkUtils';
import * as Panel from '../../panel-utils/PanelUtils'; import * as Panel from '../../panel-utils/PanelUtils';
@@ -57,9 +58,8 @@ export default function URLPresets() {
<thead> <thead>
<tr> <tr>
<th>Enabled</th> <th>Enabled</th>
<th>Alias</th>
<th>Target view</th> <th>Target view</th>
<th>URL parameters</th> <th>Alias</th>
<th /> <th />
</tr> </tr>
</thead> </thead>
@@ -71,9 +71,10 @@ export default function URLPresets() {
<td> <td>
<Switch defaultChecked={preset.enabled} onCheckedChange={() => {}} /> <Switch defaultChecked={preset.enabled} onCheckedChange={() => {}} />
</td> </td>
<td>{preset.alias}</td> <td>
<td>{preset.target}</td> <Tag>{preset.target}</Tag>
<td>{preset.search}</td> </td>
<td style={{ width: '100%' }}>{preset.alias}</td>
<Panel.InlineElements relation='inner' as='td'> <Panel.InlineElements relation='inner' as='td'>
<IconButton <IconButton
variant='ghosted-white' variant='ghosted-white'
@@ -1,6 +1,6 @@
import { useEffect, useRef } from 'react'; import { useEffect, useRef } from 'react';
import { useForm } from 'react-hook-form'; import { useForm } from 'react-hook-form';
import { OntimeView, URLPreset } from 'ontime-types'; import { OntimeView, OntimeViewPresettable, URLPreset } from 'ontime-types';
import { maybeAxiosError, unwrapError } from '../../../../../common/api/utils'; import { maybeAxiosError, unwrapError } from '../../../../../common/api/utils';
import Button from '../../../../../common/components/buttons/Button'; import Button from '../../../../../common/components/buttons/Button';
@@ -12,8 +12,9 @@ import { generateUrlPresetOptions } from '../../../../../common/utils/urlPresets
import * as Panel from '../../../panel-utils/PanelUtils'; import * as Panel from '../../../panel-utils/PanelUtils';
import style from './URLPresetForm.module.scss'; import style from './URLPresetForm.module.scss';
import Textarea from '../../../../../common/components/input/textarea/Textarea';
const targetOptions: SelectOption[] = [ const targetOptions: SelectOption<OntimeViewPresettable>[] = [
{ value: OntimeView.Cuesheet, label: 'Cuesheet' }, { value: OntimeView.Cuesheet, label: 'Cuesheet' },
{ value: OntimeView.Operator, label: 'Operator' }, { value: OntimeView.Operator, label: 'Operator' },
{ value: OntimeView.Timer, label: 'Timer' }, { value: OntimeView.Timer, label: 'Timer' },
@@ -126,14 +127,15 @@ export default function URLPresetForm({ urlPreset, onClose }: URLPresetFormProps
<Select <Select
options={targetOptions} options={targetOptions}
{...register('target', { required: 'Target is required' })} {...register('target', { required: 'Target is required' })}
value={watch('target') as OntimeView} value={watch('target')}
onValueChange={(value) => setValue('target', value)} onValueChange={(value) => setValue('target', value, { shouldDirty: true })}
/> />
</div> </div>
<div> <div>
<Panel.Description>Parameters</Panel.Description> <Panel.Description>Parameters</Panel.Description>
<Input <Textarea
fluid fluid
rows={3}
{...register('search', { {...register('search', {
validate: validateParams, validate: validateParams,
})} })}