все проблемы с кнопочками вроде ушли
This commit is contained in:
@@ -67,6 +67,39 @@ function drop(row: HTMLTableRowElement): void {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Положение строк ОТНОСИТЕЛЬНО их <tbody>, а не окна.
|
||||||
|
*
|
||||||
|
* Это принципиально: getBoundingClientRect() отдаёт координату относительно
|
||||||
|
* ОКНА, а прокрутка страницы не вызывает ре-рендер — значит, запомненные с
|
||||||
|
* прошлого рендера позиции остаются от прежнего положения прокрутки. Стоит
|
||||||
|
* пользователю прокрутить страницу к маршруту и нажать стрелку, как в сдвиг
|
||||||
|
* попадает вся дельта прокрутки (сотни пикселей), и строки улетают за пределы
|
||||||
|
* таблицы. Именно так выглядел баг «первое нажатие ломает, дальше нормально»:
|
||||||
|
* повторные клики уже сравнивались с позициями, замеренными при текущей
|
||||||
|
* прокрутке, поэтому шли правильно.
|
||||||
|
*
|
||||||
|
* Относительная база от этого избавляет: и строка, и <tbody> замеряются в
|
||||||
|
* одном кадре, поэтому прокрутка сокращается. Заодно это чинит и случай, когда
|
||||||
|
* блок маршрута целиком уезжает из-за изменений НАД ним (например, свернулся
|
||||||
|
* блок вложений): tbody уезжает вместе со строками, и мнимого сдвига нет.
|
||||||
|
*/
|
||||||
|
function measureTops(nodes: Map<string, HTMLTableRowElement>): Map<string, number> {
|
||||||
|
const tops = new Map<string, number>();
|
||||||
|
|
||||||
|
let firstRow: HTMLTableRowElement | undefined;
|
||||||
|
nodes.forEach((el) => {
|
||||||
|
if (!firstRow) firstRow = el;
|
||||||
|
});
|
||||||
|
if (!firstRow) return tops;
|
||||||
|
|
||||||
|
const container = (firstRow as HTMLTableRowElement).parentElement;
|
||||||
|
const base = container ? container.getBoundingClientRect().top : 0;
|
||||||
|
|
||||||
|
nodes.forEach((el, key) => tops.set(key, el.getBoundingClientRect().top - base));
|
||||||
|
return tops;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Возвращает функцию, которая по ключу строки отдаёт ref-колбэк для её <tr>.
|
* Возвращает функцию, которая по ключу строки отдаёт ref-колбэк для её <tr>.
|
||||||
* @param activeKeys ключи строк того шага, который двигает пользователь —
|
* @param activeKeys ключи строк того шага, который двигает пользователь —
|
||||||
@@ -126,31 +159,28 @@ export function useFlipRows(
|
|||||||
if (!orderChanged) {
|
if (!orderChanged) {
|
||||||
// Порядок тот же — просто обновляем базовую геометрию для следующего
|
// Порядок тот же — просто обновляем базовую геометрию для следующего
|
||||||
// ДЕЙСТВИТЕЛЬНОГО сравнения, без единой анимации на этом проходе.
|
// ДЕЙСТВИТЕЛЬНОГО сравнения, без единой анимации на этом проходе.
|
||||||
const current = new Map<string, number>();
|
prevTops.current = measureTops(nodes.current);
|
||||||
nodes.current.forEach((el, key) => current.set(key, el.getBoundingClientRect().top));
|
|
||||||
prevTops.current = current;
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Сначала запоминаем, где строка ВИДНА сейчас: при быстрых повторных кликах
|
// Сначала запоминаем, где строка ВИДНА сейчас: при быстрых повторных кликах
|
||||||
// она в этот момент ещё летит, и новая анимация обязана стартовать от её
|
// она в этот момент ещё летит, и новая анимация обязана стартовать от её
|
||||||
// текущего положения на экране, иначе будет рывок.
|
// текущего положения на экране, иначе будет рывок.
|
||||||
const visualTops = new Map<string, number>();
|
const visualTops = measureTops(nodes.current);
|
||||||
nodes.current.forEach((el, key) => visualTops.set(key, el.getBoundingClientRect().top));
|
|
||||||
|
|
||||||
running.current.forEach((a) => a.cancel());
|
running.current.forEach((a) => a.cancel());
|
||||||
running.current = [];
|
running.current = [];
|
||||||
nodes.current.forEach(drop);
|
nodes.current.forEach(drop);
|
||||||
|
|
||||||
const previous = prevTops.current;
|
const previous = prevTops.current;
|
||||||
const current = new Map<string, number>();
|
// После отмены анимаций transform снят — это истинные новые положения.
|
||||||
|
const current = measureTops(nodes.current);
|
||||||
const moved: Array<{ el: HTMLTableRowElement; key: string; shift: number }> = [];
|
const moved: Array<{ el: HTMLTableRowElement; key: string; shift: number }> = [];
|
||||||
const entered: HTMLTableRowElement[] = [];
|
const entered: HTMLTableRowElement[] = [];
|
||||||
|
|
||||||
nodes.current.forEach((el, key) => {
|
nodes.current.forEach((el, key) => {
|
||||||
// После отмены анимаций transform снят — это истинное новое положение.
|
const layoutTop = current.get(key);
|
||||||
const layoutTop = el.getBoundingClientRect().top;
|
if (layoutTop === undefined) return;
|
||||||
current.set(key, layoutTop);
|
|
||||||
|
|
||||||
const before = previous.get(key);
|
const before = previous.get(key);
|
||||||
if (before === undefined) {
|
if (before === undefined) {
|
||||||
|
|||||||
Reference in New Issue
Block a user