feat: add table navigation to cuesheet table (#1483)

* refactor: cuesheet table body
* feat: add table navigation
This commit is contained in:
Shobhit Nagpal
2025-03-03 02:10:43 +05:30
committed by Carlos Valente
parent 8465b04c89
commit e5b30770ce
15 changed files with 255 additions and 119 deletions
@@ -11,6 +11,10 @@ interface TimeInputDurationProps {
onSubmit: (value: string) => void;
}
interface ParentFocusableInput extends HTMLInputElement {
focusParentElement: () => void;
}
export default memo(TimeInputDuration);
function TimeInputDuration(props: PropsWithChildren<TimeInputDurationProps>) {
@@ -18,7 +22,8 @@ function TimeInputDuration(props: PropsWithChildren<TimeInputDurationProps>) {
const [isEditing, setIsEditing] = useState(false);
const [value, setValue] = useState(initialValue);
const inputRef = useRef<HTMLInputElement>(null);
const inputRef = useRef<ParentFocusableInput>(null);
const textRef = useRef<ParentFocusableInput>(null);
// when we go into edit mode, set focus to the input
useEffect(() => {
@@ -36,7 +41,10 @@ function TimeInputDuration(props: PropsWithChildren<TimeInputDurationProps>) {
}, [initialValue, isEditing]);
const handleFakeFocus = () => setIsEditing(true);
const handleFakeBlur = () => setIsEditing(false);
const handleFakeBlur = () => {
setIsEditing(false);
setTimeout(() => textRef.current?.focusParentElement()); // Immediate timeout to ensure state change takes place first
};
const handleUpdate = useCallback(
(newValue: string) => {
@@ -45,28 +53,33 @@ function TimeInputDuration(props: PropsWithChildren<TimeInputDurationProps>) {
// if the user sends an empty string, we want to clear the value
if (newValue === '') {
onSubmit(newValue);
inputRef.current?.focusParentElement();
return;
}
// we dont know the values in the rundown, escalate to handler
if (newValue.startsWith('p') || newValue.startsWith('+')) {
onSubmit(newValue);
inputRef.current?.focusParentElement();
return;
}
const valueInMillis = parseUserTime(newValue);
if (valueInMillis < 0 || isNaN(valueInMillis)) {
setValue(initialValue);
setTimeout(() => textRef.current?.focusParentElement()); // Immediate timeout to ensure state change takes place first
return;
}
// if the value is the same, we may still want to push the lock change
if (valueInMillis === initialValue && lockedValue) {
inputRef.current?.focusParentElement();
return;
}
onSubmit(newValue);
setValue(Number(newValue));
setTimeout(() => textRef.current?.focusParentElement()); // Immediate timeout to ensure state change takes place first
},
[initialValue, lockedValue, onSubmit],
);
@@ -82,7 +95,13 @@ function TimeInputDuration(props: PropsWithChildren<TimeInputDurationProps>) {
handleCancelUpdate={handleFakeBlur}
/>
) : (
<TextLikeInput onClick={handleFakeFocus} onFocus={handleFakeFocus} muted={!lockedValue} delayed={delayed}>
<TextLikeInput
onClick={handleFakeFocus}
onFocus={handleFakeFocus}
muted={!lockedValue}
delayed={delayed}
ref={textRef}
>
{children}
</TextLikeInput>
);