Skip to content

Commit c333305

Browse files
committed
add timestamp selector
1 parent 236a73f commit c333305

1 file changed

Lines changed: 46 additions & 3 deletions

File tree

src/pages/visualizer/TimestampsCard.tsx

Lines changed: 46 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
import { Slider, SliderProps, Text } from '@mantine/core';
1+
import { Group, NumberInput, Slider, SliderProps, Text, Title } from '@mantine/core';
22
import { useHotkeys } from '@mantine/hooks';
3-
import { ReactNode, useState } from 'react';
3+
import { KeyboardEvent, ReactNode, useEffect, useState } from 'react';
44
import { AlgorithmDataRow } from '../../models.ts';
55
import { useStore } from '../../store.ts';
66
import { formatNumber } from '../../utils/format.ts';
@@ -24,6 +24,11 @@ export function TimestampsCard(): ReactNode {
2424
// const timestampStep = 100;
2525

2626
const [timestamp, setTimestamp] = useState(timestampMin);
27+
const [inputValue, setInputValue] = useState<number | string>(timestampMin);
28+
29+
useEffect(() => {
30+
setInputValue(timestamp);
31+
}, [timestamp]);
2732

2833
const marks: SliderProps['marks'] = [];
2934
for (let i = timestampMin; i < timestampMax; i += (timestampMax + 100) / 4) {
@@ -33,13 +38,51 @@ export function TimestampsCard(): ReactNode {
3338
});
3439
}
3540

41+
function snapToNearest(value: number): number {
42+
const clamped = Math.max(timestampMin, Math.min(timestampMax, value));
43+
return Math.round((clamped - timestampMin) / timestampStep) * timestampStep + timestampMin;
44+
}
45+
46+
function commit(): void {
47+
const parsed = typeof inputValue === 'number' ? inputValue : Number(inputValue);
48+
if (!isNaN(parsed)) {
49+
setTimestamp(snapToNearest(parsed));
50+
}
51+
}
52+
53+
function handleKeyDown(e: KeyboardEvent<HTMLInputElement>): void {
54+
if (e.key === 'Enter') commit();
55+
}
56+
3657
useHotkeys([
3758
['ArrowLeft', () => setTimestamp(timestamp === timestampMin ? timestamp : timestamp - timestampStep)],
3859
['ArrowRight', () => setTimestamp(timestamp === timestampMax ? timestamp : timestamp + timestampStep)],
3960
]);
4061

4162
return (
42-
<VisualizerCard title="Timestamps">
63+
<VisualizerCard>
64+
<Group align="center" gap="xs" mb="xs">
65+
<Title order={4}>Timestamps</Title>
66+
<NumberInput
67+
value={inputValue}
68+
onChange={value => {
69+
setInputValue(value);
70+
// Stepper buttons produce a valid snapped timestamp — commit immediately.
71+
// Partial typed values (e.g. 273 when heading to 27300) won't match and are left pending.
72+
if (typeof value === 'number' && snapToNearest(value) === value) {
73+
setTimestamp(value);
74+
}
75+
}}
76+
onBlur={commit}
77+
onKeyDown={handleKeyDown}
78+
min={timestampMin}
79+
max={timestampMax}
80+
step={timestampStep}
81+
style={{ width: 150 }}
82+
styles={{ input: { fontWeight: 700, fontSize: 'var(--mantine-font-size-sm)' } }}
83+
/>
84+
</Group>
85+
4386
<Slider
4487
min={timestampMin}
4588
max={timestampMax}

0 commit comments

Comments
 (0)