From 11dfc46e84d58327f426aea9b2e09b4fa62f89a6 Mon Sep 17 00:00:00 2001 From: moklick Date: Wed, 26 Mar 2025 09:26:36 +0100 Subject: [PATCH 1/4] chore(useKeyPress): prevent event swallowing for buttons #5037 --- packages/system/src/utils/dom.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/system/src/utils/dom.ts b/packages/system/src/utils/dom.ts index 2311153f..4195ea3a 100644 --- a/packages/system/src/utils/dom.ts +++ b/packages/system/src/utils/dom.ts @@ -35,7 +35,7 @@ export const getDimensions = (node: HTMLDivElement): Dimensions => ({ export const getHostForElement = (element: HTMLElement | EventTarget | null): Document | ShadowRoot => ((element as Partial | null)?.getRootNode?.() as Document | ShadowRoot) || window?.document; -const inputTags = ['INPUT', 'SELECT', 'TEXTAREA']; +const inputTags = ['INPUT', 'SELECT', 'TEXTAREA', 'BUTTON']; export function isInputDOMNode(event: KeyboardEvent): boolean { // using composed path for handling shadow dom From 5d15b01ba8cb349d6397a6ed8162848b4dfec293 Mon Sep 17 00:00:00 2001 From: moklick Date: Wed, 26 Mar 2025 09:27:45 +0100 Subject: [PATCH 2/4] chore(changeset): add --- .changeset/great-pillows-drive.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/great-pillows-drive.md diff --git a/.changeset/great-pillows-drive.md b/.changeset/great-pillows-drive.md new file mode 100644 index 00000000..0b099cf8 --- /dev/null +++ b/.changeset/great-pillows-drive.md @@ -0,0 +1,5 @@ +--- +'@xyflow/system': patch +--- + +Do not swallow key events when a button is focused From 238664e32229a6ff544770680b7103f7fb904a07 Mon Sep 17 00:00:00 2001 From: moklick Date: Thu, 27 Mar 2025 10:55:22 +0100 Subject: [PATCH 3/4] refactor(useKeyPress): update preventDefault handling --- packages/react/src/hooks/useKeyPress.ts | 14 ++++++++++++-- packages/system/src/utils/dom.ts | 2 +- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/packages/react/src/hooks/useKeyPress.ts b/packages/react/src/hooks/useKeyPress.ts index b2928baa..9dda2aff 100644 --- a/packages/react/src/hooks/useKeyPress.ts +++ b/packages/react/src/hooks/useKeyPress.ts @@ -8,6 +8,7 @@ type KeyOrCode = 'key' | 'code'; export type UseKeyPressOptions = { target?: Window | Document | HTMLElement | ShadowRoot | null; actInsideInputWithModifier?: boolean; + preventDefault?: boolean; }; const defaultDoc = typeof document !== 'undefined' ? document : null; @@ -88,7 +89,7 @@ export function useKeyPress( if (keyCode !== null) { const downHandler = (event: KeyboardEvent) => { - modifierPressed.current = event.ctrlKey || event.metaKey || event.shiftKey; + modifierPressed.current = event.ctrlKey || event.metaKey || event.shiftKey || event.altKey; const preventAction = (!modifierPressed.current || (modifierPressed.current && !options.actInsideInputWithModifier)) && isInputDOMNode(event); @@ -100,7 +101,16 @@ export function useKeyPress( pressedKeys.current.add(event[keyOrCode]); if (isMatchingKey(keyCodes, pressedKeys.current, false)) { - event.preventDefault(); + const target = (event.composedPath?.()?.[0] || event.target) as Element | null; + const isInteractiveElement = target?.nodeName === 'BUTTON' || target?.nodeName === 'A'; + + if ( + (options.preventDefault || typeof options.preventDefault === 'undefined') && + (modifierPressed.current || !isInteractiveElement) + ) { + event.preventDefault(); + } + setKeyPressed(true); } }; diff --git a/packages/system/src/utils/dom.ts b/packages/system/src/utils/dom.ts index 4195ea3a..2311153f 100644 --- a/packages/system/src/utils/dom.ts +++ b/packages/system/src/utils/dom.ts @@ -35,7 +35,7 @@ export const getDimensions = (node: HTMLDivElement): Dimensions => ({ export const getHostForElement = (element: HTMLElement | EventTarget | null): Document | ShadowRoot => ((element as Partial | null)?.getRootNode?.() as Document | ShadowRoot) || window?.document; -const inputTags = ['INPUT', 'SELECT', 'TEXTAREA', 'BUTTON']; +const inputTags = ['INPUT', 'SELECT', 'TEXTAREA']; export function isInputDOMNode(event: KeyboardEvent): boolean { // using composed path for handling shadow dom From 9b1c638de8569119519dc4d8a75d3b9adfa01ed0 Mon Sep 17 00:00:00 2001 From: moklick Date: Thu, 27 Mar 2025 11:00:22 +0100 Subject: [PATCH 4/4] refactor(useKeyPress): cleanup --- packages/react/src/hooks/useKeyPress.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/packages/react/src/hooks/useKeyPress.ts b/packages/react/src/hooks/useKeyPress.ts index 9dda2aff..ec12e562 100644 --- a/packages/react/src/hooks/useKeyPress.ts +++ b/packages/react/src/hooks/useKeyPress.ts @@ -104,10 +104,7 @@ export function useKeyPress( const target = (event.composedPath?.()?.[0] || event.target) as Element | null; const isInteractiveElement = target?.nodeName === 'BUTTON' || target?.nodeName === 'A'; - if ( - (options.preventDefault || typeof options.preventDefault === 'undefined') && - (modifierPressed.current || !isInteractiveElement) - ) { + if (options.preventDefault !== false && (modifierPressed.current || !isInteractiveElement)) { event.preventDefault(); }