mirror of
https://github.com/wavetermdev/waveterm
synced 2026-04-21 14:37:16 +00:00
fix: retry giveFocus on next animation frame for newly created blocks (#3100)
Fixes #2926 ## Problem Keyboard scrolling (arrow keys) does not work in newly opened blocks until the user mouse-clicks. This is a race condition in BlockFull's focus handling. When `isFocused` becomes true for a newly created block, `setFocusTarget()` calls `viewModel.giveFocus()`. But the view's DOM element (terminal, Monaco editor, webview) may not be mounted yet, so `giveFocus()` returns false and focus falls back to the hidden dummy `<input>` element — which cannot handle arrow key scrolling. ## Fix After falling back to the dummy focus element, schedule a `requestAnimationFrame` callback that retries `viewModel.giveFocus()`. This gives React one more frame to flush pending renders and mount the view's DOM, so focus transfers to the real element once it's ready. ## Test Plan - Open a new terminal block — verify arrow keys scroll immediately without clicking - Open a file preview — verify arrow keys scroll the file content - Open a webview — verify keyboard scrolling works - Switch focus between blocks using keyboard shortcuts — verify scrolling works in each - `npx tsc --noEmit` passes clean --------- Signed-off-by: majiayu000 <1835304752@qq.com>
This commit is contained in:
parent
4805c598ca
commit
9ed86e9e18
1 changed files with 19 additions and 0 deletions
|
|
@ -144,6 +144,7 @@ const BlockFull = memo(({ nodeModel, viewModel }: FullBlockProps) => {
|
|||
const focusElemRef = useRef<HTMLInputElement>(null);
|
||||
const blockRef = useRef<HTMLDivElement>(null);
|
||||
const contentRef = useRef<HTMLDivElement>(null);
|
||||
const pendingFocusRafRef = useRef<number | null>(null);
|
||||
const [blockClicked, setBlockClicked] = useState(false);
|
||||
const blockView = useAtomValue(waveEnv.getBlockMetaKeyAtom(nodeModel.blockId, "view")) ?? "";
|
||||
const isFocused = useAtomValue(nodeModel.isFocused);
|
||||
|
|
@ -156,6 +157,14 @@ const BlockFull = memo(({ nodeModel, viewModel }: FullBlockProps) => {
|
|||
const innerRect = useDebouncedNodeInnerRect(nodeModel);
|
||||
const noPadding = useAtomValueSafe(viewModel.noPadding);
|
||||
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
if (pendingFocusRafRef.current != null) {
|
||||
cancelAnimationFrame(pendingFocusRafRef.current);
|
||||
}
|
||||
};
|
||||
}, []);
|
||||
|
||||
useLayoutEffect(() => {
|
||||
setBlockClicked(isFocused);
|
||||
}, [isFocused]);
|
||||
|
|
@ -221,11 +230,21 @@ const BlockFull = memo(({ nodeModel, viewModel }: FullBlockProps) => {
|
|||
);
|
||||
|
||||
const setFocusTarget = useCallback(() => {
|
||||
if (pendingFocusRafRef.current != null) {
|
||||
cancelAnimationFrame(pendingFocusRafRef.current);
|
||||
pendingFocusRafRef.current = null;
|
||||
}
|
||||
const ok = viewModel?.giveFocus?.();
|
||||
if (ok) {
|
||||
return;
|
||||
}
|
||||
focusElemRef.current?.focus({ preventScroll: true });
|
||||
pendingFocusRafRef.current = requestAnimationFrame(() => {
|
||||
pendingFocusRafRef.current = null;
|
||||
if (blockRef.current?.contains(document.activeElement)) {
|
||||
viewModel?.giveFocus?.();
|
||||
}
|
||||
});
|
||||
}, [viewModel]);
|
||||
|
||||
const focusFromPointerEnter = useCallback(
|
||||
|
|
|
|||
Loading…
Reference in a new issue