IO-177 Added key modifier hooks.
This commit is contained in:
@@ -45,6 +45,9 @@ import ScheduleJobModalContainer from "../../components/schedule-job-modal/sched
|
||||
import { selectJobReadOnly } from "../../redux/application/application.selectors";
|
||||
import { setModalContext } from "../../redux/modals/modals.actions";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
import useKeyboardShortcut, {
|
||||
useKeyboardSaveShortcut,
|
||||
} from "../../utils/useKeyboardShortcut";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
@@ -77,6 +80,8 @@ export function JobsDetailPage({
|
||||
form.resetFields();
|
||||
}, [form, job]);
|
||||
|
||||
useKeyboardSaveShortcut(form.submit);
|
||||
|
||||
const handleFinish = async (values) => {
|
||||
setLoading(true);
|
||||
const result = await mutationUpdateJob({
|
||||
|
||||
129
client/src/utils/useKeyboardShortcut.jsx
Normal file
129
client/src/utils/useKeyboardShortcut.jsx
Normal file
@@ -0,0 +1,129 @@
|
||||
import { useEffect, useCallback, useReducer } from "react";
|
||||
//Based on https://www.fullstacklabs.co/blog/keyboard-shortcuts-with-react-hooks
|
||||
const blacklistedTargets = []; // ["INPUT", "TEXTAREA"];
|
||||
export const useKeyboardSaveShortcut = (callback) =>
|
||||
useKeyboardShortcut(["Control", "S"], callback, { overrideSystem: true });
|
||||
|
||||
const keysReducer = (state, action) => {
|
||||
switch (action.type) {
|
||||
case "set-key-down":
|
||||
const keydownState = { ...state, [action.key]: true };
|
||||
return keydownState;
|
||||
case "set-key-up":
|
||||
const keyUpState = { ...state, [action.key]: false };
|
||||
return keyUpState;
|
||||
case "reset-keys":
|
||||
const resetState = { ...action.data };
|
||||
return resetState;
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
};
|
||||
|
||||
const useKeyboardShortcut = (shortcutKeys, callback, options) => {
|
||||
if (!Array.isArray(shortcutKeys))
|
||||
throw new Error(
|
||||
"The first parameter to `useKeyboardShortcut` must be an ordered array of `KeyboardEvent.key` strings."
|
||||
);
|
||||
|
||||
if (!shortcutKeys.length)
|
||||
throw new Error(
|
||||
"The first parameter to `useKeyboardShortcut` must contain atleast one `KeyboardEvent.key` string."
|
||||
);
|
||||
|
||||
if (!callback || typeof callback !== "function")
|
||||
throw new Error(
|
||||
"The second parameter to `useKeyboardShortcut` must be a function that will be envoked when the keys are pressed."
|
||||
);
|
||||
|
||||
const { overrideSystem } = options || {};
|
||||
const initalKeyMapping = shortcutKeys.reduce((currentKeys, key) => {
|
||||
currentKeys[key.toLowerCase()] = false;
|
||||
return currentKeys;
|
||||
}, {});
|
||||
|
||||
const [keys, setKeys] = useReducer(keysReducer, initalKeyMapping);
|
||||
|
||||
const keydownListener = useCallback(
|
||||
(assignedKey) => (keydownEvent) => {
|
||||
const loweredKey = assignedKey.toLowerCase();
|
||||
|
||||
if (keydownEvent.repeat) return;
|
||||
if (blacklistedTargets.includes(keydownEvent.target.tagName)) return;
|
||||
if (loweredKey !== keydownEvent.key.toLowerCase()) return;
|
||||
if (keys[loweredKey] === undefined) return;
|
||||
|
||||
if (overrideSystem) {
|
||||
keydownEvent.preventDefault();
|
||||
disabledEventPropagation(keydownEvent);
|
||||
}
|
||||
|
||||
setKeys({ type: "set-key-down", key: loweredKey });
|
||||
return false;
|
||||
},
|
||||
[keys, overrideSystem]
|
||||
);
|
||||
|
||||
const keyupListener = useCallback(
|
||||
(assignedKey) => (keyupEvent) => {
|
||||
const raisedKey = assignedKey.toLowerCase();
|
||||
|
||||
if (blacklistedTargets.includes(keyupEvent.target.tagName)) return;
|
||||
if (keyupEvent.key.toLowerCase() !== raisedKey) return;
|
||||
if (keys[raisedKey] === undefined) return;
|
||||
|
||||
if (overrideSystem) {
|
||||
keyupEvent.preventDefault();
|
||||
disabledEventPropagation(keyupEvent);
|
||||
}
|
||||
|
||||
setKeys({ type: "set-key-up", key: raisedKey });
|
||||
return false;
|
||||
},
|
||||
[keys, overrideSystem]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (!Object.values(keys).filter((value) => !value).length) {
|
||||
callback(keys);
|
||||
setKeys({ type: "reset-keys", data: initalKeyMapping });
|
||||
} else {
|
||||
setKeys({ type: null });
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [callback, keys]);
|
||||
|
||||
useEffect(() => {
|
||||
shortcutKeys.forEach((k) =>
|
||||
window.addEventListener("keydown", keydownListener(k))
|
||||
);
|
||||
return () =>
|
||||
shortcutKeys.forEach((k) =>
|
||||
window.removeEventListener("keydown", keydownListener(k))
|
||||
);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
shortcutKeys.forEach((k) =>
|
||||
window.addEventListener("keyup", keyupListener(k))
|
||||
);
|
||||
return () =>
|
||||
shortcutKeys.forEach((k) =>
|
||||
window.removeEventListener("keyup", keyupListener(k))
|
||||
);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
};
|
||||
|
||||
export default useKeyboardShortcut;
|
||||
|
||||
function disabledEventPropagation(e) {
|
||||
if (e) {
|
||||
if (e.stopPropagation) {
|
||||
e.stopPropagation();
|
||||
} else if (window.event) {
|
||||
window.event.cancelBubble = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user