Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[No QA] [TS migration] Migrate 'useKeyboardShortcut.js' hook to TypeScript #29226

Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 0 additions & 56 deletions src/hooks/useKeyboardShortcut.js

This file was deleted.

62 changes: 62 additions & 0 deletions src/hooks/useKeyboardShortcut.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import {useEffect} from 'react';
import {ValueOf} from 'type-fest';
import KeyboardShortcut from '@libs/KeyboardShortcut';
import CONST from '@src/CONST';

type Shortcut = ValueOf<typeof CONST.KEYBOARD_SHORTCUTS>;
type KeyboardShortcutConfig = {
/* Should we capture the event on inputs too? */
captureOnInputs?: boolean;
kubabutkiewicz marked this conversation as resolved.
Show resolved Hide resolved
/* Should we bubble the event? */
shouldBubble?: boolean;
/* The position the callback should take in the stack. 0 means top priority, and 1 means less priority than the most recently added. */
priority?: number;
/* Should call event.preventDefault after callback? */
shouldPreventDefault?: boolean;
/* Do not capture key events targeting excluded nodes (i.e. do not prevent default and let the event bubble) */
excludedNodes?: string[];
/* Is keyboard shortcut is already active */
isActive?: boolean;
};

/**
* Register a keyboard shortcut handler.
* Recommendation: To ensure stability, wrap the `callback` function with the useCallback hook before using it with this hook.
*
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
*

*/
export default function useKeyboardShortcut(shortcut: Shortcut, callback: () => void, config: KeyboardShortcutConfig | Record<string, never> = {}) {
const {
captureOnInputs = true,
shouldBubble = false,
priority = 0,
shouldPreventDefault = true,

// The "excludedNodes" array needs to be stable to prevent the "useEffect" hook from being recreated unnecessarily.
// Hence the use of CONST.EMPTY_ARRAY.
excludedNodes = CONST.EMPTY_ARRAY,
isActive = true,
} = config;

useEffect(() => {
if (!isActive) {
return () => {};
}

const unsubscribe = KeyboardShortcut.subscribe(
shortcut.shortcutKey,
callback,
shortcut.descriptionKey ?? '',
shortcut.modifiers,
captureOnInputs,
shouldBubble,
priority,
shouldPreventDefault,
excludedNodes,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
excludedNodes,
excludedNodes as string[],

);

return () => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

or return unsubscribe;
also i think taht should work - little bit shorter version

return KeyboardShortcut.subscribe(
            shortcut.shortcutKey,
            callback,
            shortcut.descriptionKey ?? '',
            shortcut.modifiers,
            captureOnInputs,
            shouldBubble,
            priority,
            shouldPreventDefault,
            excludedNodes,
        );

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would prefer no changing it to be sure to not introduce regressions, theoretically there shouldn't be be we never know 😅

unsubscribe();
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [isActive, callback, captureOnInputs, excludedNodes, priority, shortcut.descriptionKey, shortcut.modifiers.join(), shortcut.shortcutKey, shouldBubble, shouldPreventDefault]);
}
14 changes: 8 additions & 6 deletions src/libs/KeyboardShortcut/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,13 @@ type EventHandler = {
// Handlers for the various keyboard listeners we set up
const eventHandlers: Record<string, EventHandler[]> = {};

type ShortcutModifiers = readonly ['CTRL'] | readonly ['CTRL', 'SHIFT'] | readonly [];

type Shortcut = {
displayName: string;
shortcutKey: string;
descriptionKey: string;
modifiers: string[];
modifiers: ShortcutModifiers;
};

// Documentation information for keyboard shortcuts that are displayed in the keyboard shortcuts informational modal
Expand Down Expand Up @@ -102,13 +104,13 @@ function unsubscribe(displayName: string, callbackID: string) {
/**
* Return platform specific modifiers for keys like Control (CMD on macOS)
*/
function getPlatformEquivalentForKeys(keys: string[]): string[] {
function getPlatformEquivalentForKeys(keys: ShortcutModifiers): string[] {
return keys.map((key) => {
if (!(key in CONST.PLATFORM_SPECIFIC_KEYS)) {
return key;
}

const platformModifiers = CONST.PLATFORM_SPECIFIC_KEYS[key as keyof typeof CONST.PLATFORM_SPECIFIC_KEYS];
const platformModifiers = CONST.PLATFORM_SPECIFIC_KEYS[key];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice one 👍

return platformModifiers?.[operatingSystem as keyof typeof platformModifiers] ?? platformModifiers.DEFAULT ?? key;
});
}
Expand All @@ -130,12 +132,12 @@ function subscribe(
key: string,
callback: () => void,
descriptionKey: string,
modifiers: string[] = ['shift'],
modifiers: ShortcutModifiers = ['CTRL'],
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@kubabutkiewicz Why did you change the default value?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@VickyStash Because modifiers are declared here and there is no shortcut with only shift key modifier so it doesn't make sense to default it to that key

captureOnInputs = false,
shouldBubble = false,
priority = 0,
shouldPreventDefault = true,
excludedNodes = [],
excludedNodes: string[] | readonly never[] = [],
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@kubabutkiewicz Could you please clarify why readonly never[] is necessary?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@VickyStash It because of that
image
we are setting default value which comes from CONST and its basically an empty array but kt comes from CONST thats why its need to be typed as readonly never[]

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
excludedNodes: string[] | readonly never[] = [],
excludedNodes: string[] = [],

shouldStopPropagation = false,
) {
const platformAdjustedModifiers = getPlatformEquivalentForKeys(modifiers);
Expand All @@ -151,7 +153,7 @@ function subscribe(
captureOnInputs,
shouldPreventDefault,
shouldBubble,
excludedNodes,
excludedNodes: [...excludedNodes],
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
excludedNodes: [...excludedNodes],
excludedNodes,

shouldStopPropagation,
});

Expand Down
Loading