diff --git a/services/pcp-tgu-ui-service/src/features/tgu-slots/TguSlotsTab.tsx b/services/pcp-tgu-ui-service/src/features/tgu-slots/TguSlotsTab.tsx index 1cd97b4..fc11bd0 100644 --- a/services/pcp-tgu-ui-service/src/features/tgu-slots/TguSlotsTab.tsx +++ b/services/pcp-tgu-ui-service/src/features/tgu-slots/TguSlotsTab.tsx @@ -781,7 +781,8 @@ export function TguSlotsTab({ range }: TguSlotsTabProps) { return !(slot && slotIsBusy(slot)); }); setSelectedKeys((current) => selectStripRange(current, members, anchorKey, targetKey)); - setAnchorKey(targetKey); + // Якорь НЕ двигаем: остаётся первым кликом, чтобы повторный Shift+клик задавал диапазон + // от того же якоря — ближе к нему «сжимал» выделение, дальше — расширял. return; } setSelectedKeys((current) => diff --git a/services/pcp-tgu-ui-service/src/features/tgu-slots/slotSelection.test.ts b/services/pcp-tgu-ui-service/src/features/tgu-slots/slotSelection.test.ts index 617f364..30d94e4 100644 --- a/services/pcp-tgu-ui-service/src/features/tgu-slots/slotSelection.test.ts +++ b/services/pcp-tgu-ui-service/src/features/tgu-slots/slotSelection.test.ts @@ -60,6 +60,20 @@ describe("selectStripRange", () => { expect([...next]).toEqual(["c"]); }); + it("сжимает выделение: повторный диапазон ближе к якорю снимает хвост полосы", () => { + // якорь "a", сначала растянули до "d" (a..d), потом Shift+клик по "b" → остаётся a..b, c и d сняты. + const expanded = selectStripRange(new Set(), members, "a", "d"); + expect([...expanded].sort()).toEqual(["a", "b", "c", "d"]); + const shrunk = selectStripRange(expanded, members, "a", "b"); + expect([...shrunk].sort()).toEqual(["a", "b"]); + }); + + it("снимает хвост полосы, но не трогает выбор в других полосах", () => { + const current = new Set(["a", "b", "c", "d", "x"]); + const next = selectStripRange(current, members, "a", "b"); + expect([...next].sort()).toEqual(["a", "b", "x"]); + }); + it("сохраняет уже выбранное за пределами полосы", () => { const next = selectStripRange(new Set(["x"]), members, "a", "b"); expect([...next].sort()).toEqual(["a", "b", "x"]); diff --git a/services/pcp-tgu-ui-service/src/features/tgu-slots/slotSelection.ts b/services/pcp-tgu-ui-service/src/features/tgu-slots/slotSelection.ts index 2f3b97c..c636508 100644 --- a/services/pcp-tgu-ui-service/src/features/tgu-slots/slotSelection.ts +++ b/services/pcp-tgu-ui-service/src/features/tgu-slots/slotSelection.ts @@ -40,15 +40,17 @@ export function selectionAfterCheck( } /** - * Новое множество выбора после жеста «полоса от первого к последнему»: добавляет всех членов - * полосы между fromKey и toKey включительно (в порядке времени). Это ВЫДЕЛЕНИЕ, не toggle — - * существующий выбор сохраняется. Жест ограничен одной полосой: вызывающий передаёт members - * именно её (один КА, один крен, подряд по времени); граница полосы — гарантия «нельзя выделить - * не полосу». Если fromKey или toKey не из members — выбор не меняется (страховка). Вход не мутируется. + * Новое множество выбора после жеста «полоса от первого к последнему»: выбор полосы становится + * РОВНО диапазоном [fromKey..toKey] (в порядке времени). Члены полосы вне диапазона снимаются — + * так Shift+клик по слоту ближе к якорю «сжимает» выделение. Выбор за пределами полосы (другие + * полосы/КА) сохраняется: снимаем/добавляем только ключи из members. Жест ограничен одной полосой: + * вызывающий передаёт members именно её (один КА, один крен, подряд по времени); граница полосы — + * гарантия «нельзя выделить не полосу». Если fromKey или toKey не из members — выбор не меняется + * (страховка). Вход не мутируется. * * @param current Текущее множество выбранных ключей. * @param members Ключи членов полосы в порядке времени. - * @param fromKey Ключ первого клика (якорь). + * @param fromKey Ключ якоря (первый клик). * @param toKey Ключ второго клика. */ export function selectStripRange( @@ -63,7 +65,10 @@ export function selectStripRange( if (from < 0 || to < 0) return next const lo = Math.min(from, to) const hi = Math.max(from, to) - for (let i = lo; i <= hi; i++) next.add(members[i]!) + members.forEach((key, i) => { + if (i >= lo && i <= hi) next.add(key) + else next.delete(key) + }) return next }