fix(tgu-ui): двуручейный слайдер — выбор ползунка по направлению при наложении
При наложении в крайней точке хват попадал на верхний ползунок to, который на правом краю заперт fromIndex и не уходит влево, а нижний from был недоступен. Теперь при fromIndex === toIndex управляемый ползунок выбирается по направлению движения (влево → from, вправо → to). Чинит и «Сутки» (SlotDaySelector), и «Витки» (MapPassSelector).
This commit is contained in:
@@ -42,21 +42,34 @@ export function MapPassSelector({ passes, fromIndex, toIndex, onFromChange, onTo
|
|||||||
return Math.round(Math.max(0, Math.min(1, (clientX - rect.left) / rect.width)) * (n - 1)) + 1;
|
return Math.round(Math.max(0, Math.min(1, (clientX - rect.left) / rect.width)) * (n - 1)) + 1;
|
||||||
};
|
};
|
||||||
|
|
||||||
const makeHandlers = (isFrom: boolean) => ({
|
// Какой ползунок реально тянем. При наложении (fromIndex === toIndex) хват попадает на верхний
|
||||||
|
// (to), но на правом краю он заперт fromIndex и не может уйти влево — поэтому при наложении
|
||||||
|
// выбираем ползунок по направлению движения: влево → from (открыть диапазон), вправо → to.
|
||||||
|
const dragRef = useRef<"from" | "to" | null>(null);
|
||||||
|
|
||||||
|
const makeHandlers = (which: "from" | "to") => ({
|
||||||
onPointerDown(e: React.PointerEvent<HTMLDivElement>) {
|
onPointerDown(e: React.PointerEvent<HTMLDivElement>) {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
|
dragRef.current = which;
|
||||||
(e.currentTarget as HTMLDivElement).setPointerCapture(e.pointerId);
|
(e.currentTarget as HTMLDivElement).setPointerCapture(e.pointerId);
|
||||||
},
|
},
|
||||||
onPointerMove(e: React.PointerEvent<HTMLDivElement>) {
|
onPointerMove(e: React.PointerEvent<HTMLDivElement>) {
|
||||||
if (!(e.currentTarget as HTMLDivElement).hasPointerCapture(e.pointerId)) return;
|
if (!(e.currentTarget as HTMLDivElement).hasPointerCapture(e.pointerId)) return;
|
||||||
const idx = indexAt(e.clientX);
|
const idx = indexAt(e.clientX);
|
||||||
if (isFrom) {
|
let active = dragRef.current;
|
||||||
|
if (fromIndex === toIndex) {
|
||||||
|
if (idx < fromIndex) active = "from";
|
||||||
|
else if (idx > toIndex) active = "to";
|
||||||
|
dragRef.current = active;
|
||||||
|
}
|
||||||
|
if (active === "from") {
|
||||||
onFromChange(Math.max(1, Math.min(idx, toIndex)));
|
onFromChange(Math.max(1, Math.min(idx, toIndex)));
|
||||||
} else {
|
} else {
|
||||||
onToChange(Math.min(n, Math.max(idx, fromIndex)));
|
onToChange(Math.min(n, Math.max(idx, fromIndex)));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onPointerUp(e: React.PointerEvent<HTMLDivElement>) {
|
onPointerUp(e: React.PointerEvent<HTMLDivElement>) {
|
||||||
|
dragRef.current = null;
|
||||||
(e.currentTarget as HTMLDivElement).releasePointerCapture(e.pointerId);
|
(e.currentTarget as HTMLDivElement).releasePointerCapture(e.pointerId);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@@ -111,13 +124,13 @@ export function MapPassSelector({ passes, fromIndex, toIndex, onFromChange, onTo
|
|||||||
<div
|
<div
|
||||||
className="map-pass-selector__thumb"
|
className="map-pass-selector__thumb"
|
||||||
style={{ left: `${fromPct}%` }}
|
style={{ left: `${fromPct}%` }}
|
||||||
{...makeHandlers(true)}
|
{...makeHandlers("from")}
|
||||||
/>
|
/>
|
||||||
{/* To handle */}
|
{/* To handle */}
|
||||||
<div
|
<div
|
||||||
className="map-pass-selector__thumb"
|
className="map-pass-selector__thumb"
|
||||||
style={{ left: `${toPct}%` }}
|
style={{ left: `${toPct}%` }}
|
||||||
{...makeHandlers(false)}
|
{...makeHandlers("to")}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -37,21 +37,34 @@ export function SlotDaySelector({ days, fromIndex, toIndex, onFromChange, onToCh
|
|||||||
return Math.round(Math.max(0, Math.min(1, (clientX - rect.left) / rect.width)) * (n - 1)) + 1;
|
return Math.round(Math.max(0, Math.min(1, (clientX - rect.left) / rect.width)) * (n - 1)) + 1;
|
||||||
};
|
};
|
||||||
|
|
||||||
const makeHandlers = (isFrom: boolean) => ({
|
// Какой ползунок реально тянем. При наложении (fromIndex === toIndex) хват попадает на верхний
|
||||||
|
// (to), но на правом краю он заперт fromIndex и не может уйти влево — поэтому при наложении
|
||||||
|
// выбираем ползунок по направлению движения: влево → from (открыть диапазон), вправо → to.
|
||||||
|
const dragRef = useRef<"from" | "to" | null>(null);
|
||||||
|
|
||||||
|
const makeHandlers = (which: "from" | "to") => ({
|
||||||
onPointerDown(e: React.PointerEvent<HTMLDivElement>) {
|
onPointerDown(e: React.PointerEvent<HTMLDivElement>) {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
|
dragRef.current = which;
|
||||||
(e.currentTarget as HTMLDivElement).setPointerCapture(e.pointerId);
|
(e.currentTarget as HTMLDivElement).setPointerCapture(e.pointerId);
|
||||||
},
|
},
|
||||||
onPointerMove(e: React.PointerEvent<HTMLDivElement>) {
|
onPointerMove(e: React.PointerEvent<HTMLDivElement>) {
|
||||||
if (!(e.currentTarget as HTMLDivElement).hasPointerCapture(e.pointerId)) return;
|
if (!(e.currentTarget as HTMLDivElement).hasPointerCapture(e.pointerId)) return;
|
||||||
const idx = indexAt(e.clientX);
|
const idx = indexAt(e.clientX);
|
||||||
if (isFrom) {
|
let active = dragRef.current;
|
||||||
|
if (fromIndex === toIndex) {
|
||||||
|
if (idx < fromIndex) active = "from";
|
||||||
|
else if (idx > toIndex) active = "to";
|
||||||
|
dragRef.current = active;
|
||||||
|
}
|
||||||
|
if (active === "from") {
|
||||||
onFromChange(Math.max(1, Math.min(idx, toIndex)));
|
onFromChange(Math.max(1, Math.min(idx, toIndex)));
|
||||||
} else {
|
} else {
|
||||||
onToChange(Math.min(n, Math.max(idx, fromIndex)));
|
onToChange(Math.min(n, Math.max(idx, fromIndex)));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onPointerUp(e: React.PointerEvent<HTMLDivElement>) {
|
onPointerUp(e: React.PointerEvent<HTMLDivElement>) {
|
||||||
|
dragRef.current = null;
|
||||||
(e.currentTarget as HTMLDivElement).releasePointerCapture(e.pointerId);
|
(e.currentTarget as HTMLDivElement).releasePointerCapture(e.pointerId);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@@ -101,12 +114,12 @@ export function SlotDaySelector({ days, fromIndex, toIndex, onFromChange, onToCh
|
|||||||
<div
|
<div
|
||||||
className="map-pass-selector__thumb"
|
className="map-pass-selector__thumb"
|
||||||
style={{ left: `${fromPct}%` }}
|
style={{ left: `${fromPct}%` }}
|
||||||
{...makeHandlers(true)}
|
{...makeHandlers("from")}
|
||||||
/>
|
/>
|
||||||
<div
|
<div
|
||||||
className="map-pass-selector__thumb"
|
className="map-pass-selector__thumb"
|
||||||
style={{ left: `${toPct}%` }}
|
style={{ left: `${toPct}%` }}
|
||||||
{...makeHandlers(false)}
|
{...makeHandlers("to")}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user