feat(tgu-ui): выбор полосы слотов мышью на карте (Shift+drag)
This commit is contained in:
@@ -39,7 +39,7 @@ import {
|
||||
} from "./coverage";
|
||||
import { applySlotFilters, slotStatusKey, type SlotStatusKey } from "./slotFilters";
|
||||
import { selectionAfterCheck, selectStripRange } from "./slotSelection";
|
||||
import { groupStrips } from "./slotStrips";
|
||||
import { groupStrips, pickStripAnchor, pickStripEnd, type PolosaId } from "./slotStrips";
|
||||
import { runChunked } from "./runChunked";
|
||||
import { ChunkProgress } from "./ChunkProgress";
|
||||
|
||||
@@ -298,10 +298,61 @@ export function TguSlotsTab({ range }: TguSlotsTabProps) {
|
||||
// времени (виток не граница). idByKey — id полосы по ключу слота; membersOf — члены полосы по времени.
|
||||
const polosa = useMemo(() => groupStrips(displaySlots, slotKey), [displaySlots]);
|
||||
|
||||
// Состояние жеста «выбор полосы мышью на карте» (Shift+drag). Держим в ref: правки идут на каждом
|
||||
// движении указателя, перерисовка не нужна. members — члены полосы якоря без BUSY (их не бронируем).
|
||||
const mapStripRef = useRef<{ anchorKey: string; anchorId: PolosaId; members: string[] } | null>(null);
|
||||
|
||||
// Shift+нажатие по слотам на карте: якорь полосы = самый ранний по времени выбираемый слот под
|
||||
// курсором. Выбор этой полосы сбрасываем к одному якорю (далее drag растянет), как и в таблице.
|
||||
const handleStripAnchor = useCallback(
|
||||
(keys: string[]) => {
|
||||
const tnMsOf = (key: string) => {
|
||||
const slot = slotByKey.get(key);
|
||||
return slot ? parseLocal(slot.tn) : Number.NaN;
|
||||
};
|
||||
const selectable = keys.filter((key) => {
|
||||
const slot = slotByKey.get(key);
|
||||
return slot && !slotIsBusy(slot);
|
||||
});
|
||||
const anchor = pickStripAnchor(selectable, tnMsOf);
|
||||
const anchorId = anchor ? polosa.idByKey.get(anchor) : undefined;
|
||||
if (!anchor || !anchorId) {
|
||||
mapStripRef.current = null;
|
||||
return;
|
||||
}
|
||||
const members = (polosa.membersOf.get(anchorId) ?? []).filter((key) => {
|
||||
const slot = slotByKey.get(key);
|
||||
return !(slot && slotIsBusy(slot));
|
||||
});
|
||||
mapStripRef.current = { anchorKey: anchor, anchorId, members };
|
||||
setAnchorKey(anchor);
|
||||
setSelectedKeys((current) => selectStripRange(current, members, anchor, anchor));
|
||||
},
|
||||
[polosa, slotByKey]
|
||||
);
|
||||
|
||||
// Движение указателя в жесте: конец полосы = самый поздний по времени член ТОЙ ЖЕ полосы под
|
||||
// курсором (граница полосы соблюдается). Никого из полосы под курсором — оставляем прежний конец.
|
||||
const handleStripDrag = useCallback(
|
||||
(keys: string[]) => {
|
||||
const gesture = mapStripRef.current;
|
||||
if (!gesture) return;
|
||||
const end = pickStripEnd(polosa, gesture.anchorId, keys);
|
||||
if (!end) return;
|
||||
setSelectedKeys((current) => selectStripRange(current, gesture.members, gesture.anchorKey, end));
|
||||
},
|
||||
[polosa]
|
||||
);
|
||||
|
||||
const handleStripCommit = useCallback(() => {
|
||||
mapStripRef.current = null;
|
||||
}, []);
|
||||
|
||||
// Якорь жеста привязан к ВИДИМОМУ набору: смена фильтров/окна суток/брони делает прежний якорь
|
||||
// неактуальным → сбрасываем (как при смене сортировки), иначе Shift выделил бы не ту полосу.
|
||||
useEffect(() => {
|
||||
setAnchorKey(null);
|
||||
mapStripRef.current = null;
|
||||
}, [filteredSlots]);
|
||||
|
||||
// Загрузка заявок интервала (на карту); вызывается на входе, при смене диапазона и из фильтра.
|
||||
@@ -1205,8 +1256,8 @@ export function TguSlotsTab({ range }: TguSlotsTabProps) {
|
||||
</span>
|
||||
)}
|
||||
</span>
|
||||
<span className="tgu-slots-selbar__sub" title="Кликните первый слот, затем Shift+клик по последнему — выделится полоса между ними. Выйти за полосу (другой КА/крен/разрыв по времени) нельзя.">
|
||||
Полоса: клик + Shift+клик
|
||||
<span className="tgu-slots-selbar__sub" title="Таблица: кликните первый слот, затем Shift+клик по последнему — выделится полоса между ними. На карте: Shift+зажать на слоте и протянуть мышью вдоль полосы. Выйти за полосу (другой КА/крен/разрыв по времени) нельзя.">
|
||||
Полоса: клик+Shift в таблице · Shift+тянуть на карте
|
||||
</span>
|
||||
<div className="tgu-slots-selbar__actions">
|
||||
<button
|
||||
@@ -1533,6 +1584,9 @@ export function TguSlotsTab({ range }: TguSlotsTabProps) {
|
||||
enableObjectChooser
|
||||
onObjectsAtPoint={handleObjectsAtPoint}
|
||||
onObjectSelect={handleObjectSelect}
|
||||
onStripAnchor={hasRequest ? handleStripAnchor : undefined}
|
||||
onStripDrag={hasRequest ? handleStripDrag : undefined}
|
||||
onStripCommit={hasRequest ? handleStripCommit : undefined}
|
||||
selectedObjectId={hasRequest ? activeSlotId : activeHitId}
|
||||
markedObjectIds={hasRequest ? markedSlotIds : undefined}
|
||||
focusPoint={focusPoint}
|
||||
|
||||
Reference in New Issue
Block a user