feat: dont moove slider cursor if tcp command fails

This commit is contained in:
2026-07-28 13:53:57 +02:00
parent 54fda168cc
commit eab62e151e
6 changed files with 376 additions and 353 deletions
+68 -49
View File
@@ -13,8 +13,8 @@ type VerticalSliderProps = {
switchStepValue?: number;
switchStep?: number;
value?: number;
onChange?: (value: number) => void;
value: number;
onChange?: (value: number) => Promise<boolean>;
unit?: string;
@@ -34,23 +34,18 @@ export default function VerticalSlider({
step,
switchStepValue,
switchStep,
value: controlledValue,
value,
onChange,
unit = '',
className,
style,
}: VerticalSliderProps) {
const [internalValue, setInternalValue] = useState(min);
const value = controlledValue ?? internalValue;
const [editingText, setEditingText] = useState<string | null>(null);
const inputRef = useRef<HTMLInputElement>(null);
const sliderRef = useRef<HTMLDivElement>(null);
const lastValue = useRef(value);
const keyboardValue = useRef<number | null>(null);
const [displayValue, setDisplayValue] = useState(value);
const dragValue = useRef(value);
const rafRef = useRef<number | null>(null);
const dragging = useRef(false);
const thumbRef = useRef<HTMLDivElement>(null);
const keyboardEditing = useRef(false);
@@ -74,16 +69,20 @@ export default function VerticalSlider({
}, []);
useEffect(() => {
if (dragging.current) return;
if (keyboardEditing.current) return;
setDisplayValue(value);
dragValue.current = value;
}, [value]);
const currentStep =
switchStepValue !== undefined && switchStep !== undefined && value >= switchStepValue
? switchStep
: step;
function getStep(value: number) {
if (switchStepValue !== undefined && switchStep !== undefined && value >= switchStepValue) {
return switchStep;
}
return step;
}
const decimals = useMemo(
() => Math.max(getDecimals(step), switchStep !== undefined ? getDecimals(switchStep) : 0),
@@ -92,30 +91,25 @@ export default function VerticalSlider({
function normalizeValue(input: number) {
const clamped = Math.min(max, Math.max(min, input));
const snapped = Math.round(clamped / currentStep) * currentStep;
return Number(snapped.toFixed(6));
const currentStep = getStep(clamped);
const snapped = min + Math.round((clamped - min) / currentStep) * currentStep;
return Number(Math.min(max, Math.max(min, snapped)).toFixed(6));
}
function updateValue(v: number) {
const next = normalizeValue(v);
function updateValue(input: number) {
const next = normalizeValue(input);
dragValue.current = next;
lastValue.current = next;
if (thumbRef.current) {
const percent = (next - min) / (max - min);
thumbRef.current.style.bottom = `${percent * 100}%`;
}
if (rafRef.current === null) {
rafRef.current = requestAnimationFrame(() => {
setDisplayValue(dragValue.current);
rafRef.current = null;
});
}
setDisplayValue(next);
}
function handleKeyDown(e: React.KeyboardEvent<HTMLDivElement>) {
if (!['ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight'].includes(e.key)) return;
if (!['ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight'].includes(e.key)) {
return;
}
keyboardEditing.current = true;
@@ -123,33 +117,39 @@ export default function VerticalSlider({
const base = keyboardValue.current ?? value;
const currentStep = getStep(base);
let next = base;
if (e.key === 'ArrowUp' || e.key === 'ArrowRight') next += currentStep;
if (e.key === 'ArrowUp' || e.key === 'ArrowRight') {
next += currentStep;
}
if (e.key === 'ArrowDown' || e.key === 'ArrowLeft') next -= currentStep;
if (e.key === 'ArrowDown' || e.key === 'ArrowLeft') {
next -= currentStep;
}
next = normalizeValue(next);
keyboardValue.current = next;
updateValue(next); // updates displayValue only
updateValue(next);
}
function handleKeyUp() {
async function handleKeyUp() {
if (keyboardValue.current == null) return;
keyboardEditing.current = false;
const next = keyboardValue.current;
if (controlledValue === undefined) {
setInternalValue(next);
}
onChange?.(next);
const success = await onChange?.(next);
keyboardEditing.current = false;
keyboardValue.current = null;
if (success === false) {
setDisplayValue(value);
dragValue.current = value;
}
}
function valueFromPointer(clientY: number) {
@@ -172,28 +172,47 @@ export default function VerticalSlider({
valueFromPointer(e.clientY);
}
function endDrag(e: React.PointerEvent<HTMLDivElement>) {
dragging.current = false;
async function endDrag(e: React.PointerEvent<HTMLDivElement>) {
if (!dragging.current) return;
const finalValue = dragValue.current;
if (controlledValue === undefined) setInternalValue(finalValue);
setDisplayValue(finalValue);
onChange?.(finalValue);
const success = await onChange?.(finalValue);
dragging.current = false;
if (success === false) {
setDisplayValue(value);
dragValue.current = value;
}
if (e.currentTarget.hasPointerCapture(e.pointerId)) {
e.currentTarget.releasePointerCapture(e.pointerId);
}
}
function commitEditing() {
async function commitEditing() {
if (editingText !== null && editingText !== '' && editingText !== '-') {
const parsed = Number(editingText);
if (!Number.isNaN(parsed)) updateValue(parsed);
if (!Number.isNaN(parsed)) {
const next = normalizeValue(parsed);
setDisplayValue(next);
const success = await onChange?.(next);
if (success === false) {
setDisplayValue(value);
dragValue.current = value;
}
}
}
setEditingText(null);
}
const percent = (value - min) / (max - min);
const percent = (displayValue - min) / (max - min);
return (
<div