refactor: table mode and context

This commit is contained in:
Carlos Valente
2026-01-10 09:41:40 +01:00
parent 8cfbcd35cd
commit 261cd25bec
64 changed files with 560 additions and 273 deletions
@@ -1,58 +0,0 @@
.radioGroup {
color: $gray-900;
font-size: calc(1rem - 3px);
color: $label-gray;
}
.item {
display: flex;
align-items: center;
gap: 0.5rem;
line-height: 1.2em;
&:has([data-checked]) {
color: $ui-white;
}
}
.radio {
box-sizing: border-box;
display: flex;
width: 0.75rem;
height: 0.75rem;
align-items: center;
justify-content: center;
border-radius: 100%;
outline: 0;
border: none;
&[data-unchecked] {
background-color: $gray-1200;
}
&[data-checked] {
background-color: $gray-700;
}
&:focus-visible {
outline: 2px solid $blue-500;
outline-offset: 2px;
}
}
.indicator {
display: grid;
place-content: center;
&[data-unchecked] {
display: none;
}
&::before {
content: '';
border-radius: 100%;
width: 0.5em;
height: 0.5em;
background-color: $ui-white;
}
}
@@ -1,35 +0,0 @@
import { Radio } from '@base-ui/react/radio';
import { RadioGroup as BaseRadioGroup } from '@base-ui/react/radio-group';
import style from './BlockRadio.module.scss';
interface BlockRadioProps<T extends string | number | boolean> extends Omit<BaseRadioGroup.Props, 'onValueChange'> {
items: {
value: T;
label: string;
}[];
onValueChange?: (value: T) => void;
}
export default function BlockRadio<T extends string | number | boolean>({
items,
onValueChange,
...elementProps
}: BlockRadioProps<T>) {
return (
<BaseRadioGroup
onValueChange={(value) => onValueChange?.(value as T)}
className={style.radioGroup}
{...elementProps}
>
{items.map((item) => (
<label className={style.item} key={item.value.toString()}>
<Radio.Root value={item.value.toString()} className={style.radio}>
<Radio.Indicator className={style.indicator} />
</Radio.Root>
{item.label}
</label>
))}
</BaseRadioGroup>
);
}
@@ -1,12 +0,0 @@
.delayInput {
display: flex;
gap: $element-spacing;
align-items: center;
}
.inputField {
text-align: center;
letter-spacing: 1px;
max-width: 7em;
color: $ontime-delay-text
}
@@ -1,130 +0,0 @@
import { KeyboardEvent, useEffect, useRef, useState } from 'react';
import { millisToString, parseUserTime } from 'ontime-utils';
import { useRundownEntryActions } from '../../../../features/rundown/context/RundownActionsContext';
import Input from '../input/Input';
import BlockRadio from './BlockRadio';
import style from './DelayInput.module.scss';
interface DelayInputProps {
eventId: string;
duration: number;
}
export default function DelayInput({ eventId, duration }: DelayInputProps) {
const { updateEntry } = useRundownEntryActions();
const [value, setValue] = useState<string>('');
const inputRef = useRef<HTMLInputElement | null>(null);
// avoid wrong submit on cancel
const ignoreChangeRef = useRef(false);
// set internal value on duration change
useEffect(() => {
if (typeof duration === 'undefined') {
return;
}
setValue(millisToString(duration));
}, [duration]);
/**
* @description Prepare delay value for update
* @param {string} newValue string to be parsed
*/
const validateAndSubmit = (newValue: string) => {
if (ignoreChangeRef.current) {
ignoreChangeRef.current = false;
return;
}
const isNegative = newValue.startsWith('-');
let newMillis = parseUserTime(newValue);
if (isNegative) {
newMillis = newMillis * -1;
}
if (newMillis === duration) {
return;
}
submitChange(newMillis);
setValue(millisToString(newMillis));
};
const submitChange = (value: number) => {
updateEntry({
id: eventId,
duration: value,
});
};
/**
* @description Selects input text on focus
*/
const handleFocus = () => inputRef.current?.select();
/**
* @description Handles common keys for submit and cancel
* @param {KeyboardEvent} event
*/
const onKeyDownHandler = (event: KeyboardEvent<HTMLInputElement>) => {
if (event.key === 'Enter') {
inputRef.current?.blur();
validateAndSubmit((event.target as HTMLInputElement).value);
} else if (event.key === 'Tab') {
validateAndSubmit((event.target as HTMLInputElement).value);
} else if (event.key === 'Escape') {
ignoreChangeRef.current = true;
setValue(millisToString(duration));
inputRef.current?.blur();
}
};
/**
* @description handles direction change to delay
* @param newDirection
*/
const handleSlipChange = (newDirection: 'add' | 'subtract') => {
if (newDirection === 'add') {
// add time
if (duration < 0) {
submitChange(duration * -1);
}
} else if (newDirection === 'subtract') {
// subtract time
if (duration > 0) {
submitChange(duration * -1);
}
}
};
const checkedOption = value.startsWith('-') ? 'subtract' : 'add';
return (
<div className={style.delayInput}>
<Input
ref={inputRef}
data-testid='delay-input'
className={style.inputField}
placeholder='-'
onFocus={handleFocus}
onChange={(event) => setValue(event.target.value)}
onBlur={(event) => validateAndSubmit(event.target.value)}
onKeyDown={onKeyDownHandler}
value={value}
maxLength={9}
/>
<BlockRadio
onValueChange={handleSlipChange}
value={checkedOption}
items={[
{ value: 'add', label: 'Add time' },
{ value: 'subtract', label: 'Subtract time' },
]}
/>
</div>
);
}