mirror of
https://github.com/kuaifan/dootask.git
synced 2026-09-15 16:38:51 +00:00
新增到期、排期、逾期与未排期分组,按项目缓存显示选择并至少保留一项。 统一分组复选下拉菜单,接入 Cascader 桌面悬停展开,增加勾选后的滚动定位校正。 升级 view-design-hi 至 4.7.0-81,同步多语言、知识库和回归测试。
61 lines
2.8 KiB
JavaScript
Vendored
61 lines
2.8 KiB
JavaScript
Vendored
// Lazy-rendered rows can move a target after the browser has started scrolling.
|
|
export function scrollToStableTarget({container, target, inputRoot = container, render, isActive}) {
|
|
let active = true;
|
|
let timer;
|
|
let deadline;
|
|
const startedAt = Date.now();
|
|
let stableSince = startedAt;
|
|
let previousTop = null;
|
|
let previousHeight = null;
|
|
const document = container.ownerDocument;
|
|
|
|
const desiredTop = () => Math.max(0, Math.min(
|
|
container.scrollHeight - container.clientHeight,
|
|
container.scrollTop + target.getBoundingClientRect().top - container.getBoundingClientRect().top - container.clientTop,
|
|
));
|
|
const move = (top, behavior) => container.scrollTo({top, left: container.scrollLeft, behavior});
|
|
const valid = () => active && container.isConnected && target.isConnected && isActive();
|
|
const finish = (stopMotion = false) => {
|
|
if (!active) return;
|
|
active = false;
|
|
clearTimeout(timer);
|
|
clearTimeout(deadline);
|
|
for (const event of ['wheel', 'touchstart', 'pointerdown']) inputRoot.removeEventListener(event, cancel, true);
|
|
document.removeEventListener('keydown', onKeyDown, true);
|
|
if (stopMotion && container.isConnected) move(container.scrollTop, 'instant');
|
|
};
|
|
const cancel = () => finish(true);
|
|
const onKeyDown = event => {
|
|
if (event.target?.closest?.('input, textarea, select, [contenteditable="true"]')) return;
|
|
if (['ArrowUp', 'ArrowDown', 'PageUp', 'PageDown', 'Home', 'End', ' '].includes(event.key)) cancel();
|
|
};
|
|
const tick = async () => {
|
|
if (!valid()) return cancel();
|
|
try {
|
|
await render();
|
|
if (!valid()) return cancel();
|
|
const now = Date.now();
|
|
const top = desiredTop();
|
|
const changed = previousTop === null || Math.abs(top - previousTop) > 1 || container.scrollHeight !== previousHeight;
|
|
const aligned = Math.abs(container.scrollTop - top) <= 1;
|
|
if (changed || !aligned) stableSince = now;
|
|
previousTop = top;
|
|
previousHeight = container.scrollHeight;
|
|
// Give the first smooth scroll time to finish; corrections must not start another animation.
|
|
if (!aligned && now - startedAt >= 600) move(top, 'instant');
|
|
if (aligned && now - startedAt >= 600 && now - stableSince >= 240) return finish();
|
|
timer = setTimeout(tick, 80);
|
|
} catch {
|
|
cancel();
|
|
}
|
|
};
|
|
|
|
if (!valid()) return cancel;
|
|
for (const event of ['wheel', 'touchstart', 'pointerdown']) inputRoot.addEventListener(event, cancel, {capture: true, passive: true});
|
|
document.addEventListener('keydown', onKeyDown, true);
|
|
deadline = setTimeout(cancel, 2500);
|
|
move(desiredTop(), 'smooth');
|
|
timer = setTimeout(tick, 80);
|
|
return cancel;
|
|
}
|