* fix: Combined Hotkeys for special characters * add record method to hotkey manager * fix record plugin * remove unused component * add record to modal props * rename record method * replace handlers to use hotkeyRecord * fix combined keys * change expected result count from 18 to 17 * autoformat * Remove duplicate test, that was testing the wrong things; fix label; update configs * Revert "Remove duplicate test, that was testing the wrong things; fix label; update configs" This reverts commit 4292f4fe67351962d61cae623b920dcbd87dd71d. * Fix the record plugin's registration * fix exposed record method usage * adding logging for info level items * Hotkey definitions don't need to be globally reactive; use localstorage/appconfig as sources of truth; not redux * Tidy up test * Remove unused code from UserPreferencesForm * Log info when we run a command * fix hotkey preference restore * use application configured hotkeys if there are no user preferred * Avoid logging circular ref * Fix callouts * Fix small issue with array * Fix langua issue after refactor and merge * Refactor on recordCurrentCombo as Rodrigo did before * Separating components in 2 files * WIP Refactor to simplify the user preferences and move into each form the save and controll functionalities * Remove context * Remove unused import * Initial work on Field treatment * Refactor General preferences * Small refactor removing type from HotkeyField * small update on style * Refactor and layout fixed * Make hotkeys preferences working with old hotkeys row * Move error handling out of hotkey row/input component * WIP custom form * Moving validation function to component * Exposing hotkeyRecord as it does not depend on HotkeyManager Class * Making hotkeyField as much detached possible from parent component * Small refactors * Refactor on user preferences * Clean up into the changes * Small fix to let save working * Style finish * move about docs into about folder * Fix double tap on single keys * Style refactor * Remove log * Fix log issues on unit tests * Fix unit test breaking on ohif/core index * Fixing hotkeys unpause unit test issue * Rename file to adopt lowercase * Rename file to adopt lowercase * Fixing callouts * Big refactor miving some of the components into viewer and creating small components into ohif/ui * Typo on folder name * Updating ohif ui docs * Remove comments * Fix binding of combo keys * Fix some cypress tests failures * Fixing onCancel button * Fixing e2e tests * Small style update * Fixing unit tests failing after fix issue * Remove some not used code * Remove left over after debug * Adding prevent default on hotkeys events * Fixinf existing hotkeys validator with 3 keys pressed * Exposing hotkeys as root level on ohif-core * Clean up * Exposing all availableLanguages with labels and fixing an issue on language switcher * Fixing e2e cypress tests * Preveinting some simple errors * Treating error once we try to set hotkey definitions * Adding ui notification on setHotkeys errors * Implementing a service queue request to hold until functions are implemented * Making sure toFixed is only called on Numbers Co-authored-by: Danny Brown <danny.ri.brown@gmail.com> Co-authored-by: Gustavo André Lelis <galelis@gmail.com>
202 lines
6.0 KiB
JavaScript
202 lines
6.0 KiB
JavaScript
import React, { useState } from 'react';
|
|
import PropTypes from 'prop-types';
|
|
import classnames from 'classnames';
|
|
|
|
import { useSnackbarContext, TabFooter, HotkeyField } from '@ohif/ui';
|
|
import { useTranslation } from 'react-i18next';
|
|
|
|
import { hotkeysValidators } from './hotkeysValidators';
|
|
import { MODIFIER_KEYS } from './hotkeysConfig';
|
|
|
|
import { hotkeysManager } from '../../App';
|
|
|
|
import './HotkeysPreferences.styl';
|
|
/**
|
|
* Take hotkeyDefenintions and build an initialState to be used into the component state
|
|
*
|
|
* @param {Object} hotkeyDefinitions
|
|
* @returns {Object} initialState
|
|
*/
|
|
const initialState = hotkeyDefinitions => ({
|
|
hotkeys: { ...hotkeyDefinitions },
|
|
errors: {},
|
|
});
|
|
/**
|
|
* Take the updated command and keys and validate the changes with all validators
|
|
*
|
|
* @param {Object} arguments
|
|
* @param {string} arguments.commandName command name string to be updated
|
|
* @param {array} arguments.pressedKeys new array of keys to be added for the commandName
|
|
* @param {array} arguments.hotkeys all hotkeys currently into the app
|
|
* @returns {Object} {errorMessage} errorMessage coming from any of the validator or undefined if none
|
|
*/
|
|
const validateCommandKey = ({ commandName, pressedKeys, hotkeys }) => {
|
|
for (const validator of hotkeysValidators) {
|
|
const validation = validator({
|
|
commandName,
|
|
pressedKeys,
|
|
hotkeys,
|
|
});
|
|
if (validation && validation.hasError) {
|
|
return validation;
|
|
}
|
|
}
|
|
|
|
return {
|
|
errorMessage: undefined,
|
|
};
|
|
};
|
|
|
|
/**
|
|
* Take all hotkeys and split the list into two lists
|
|
*
|
|
* @param {array} hotkeys list of all hotkeys
|
|
* @returns {array} array containing two arrays of keys
|
|
*/
|
|
const splitHotkeys = hotkeys => {
|
|
const splitedHotkeys = [];
|
|
const arrayHotkeys = Object.entries(hotkeys);
|
|
|
|
if (arrayHotkeys.length) {
|
|
const halfwayThrough = Math.ceil(arrayHotkeys.length / 2);
|
|
splitedHotkeys.push(arrayHotkeys.slice(0, halfwayThrough));
|
|
splitedHotkeys.push(
|
|
arrayHotkeys.slice(halfwayThrough, arrayHotkeys.length)
|
|
);
|
|
}
|
|
|
|
return splitedHotkeys;
|
|
};
|
|
|
|
/**
|
|
* HotkeysPreferences tab
|
|
* It renders all hotkeys displayed into columns/rows
|
|
*
|
|
* It stores current state and whenever it changes, component messages parent of new value (through function callback)
|
|
* @param {object} props component props
|
|
* @param {string} props.onClose
|
|
*/
|
|
function HotkeysPreferences({ onClose }) {
|
|
const { t } = useTranslation('UserPreferencesModal');
|
|
const { hotkeyDefaults, hotkeyDefinitions } = hotkeysManager;
|
|
|
|
const [state, setState] = useState(initialState(hotkeyDefinitions));
|
|
|
|
const snackbar = useSnackbarContext();
|
|
|
|
const onResetPreferences = () => {
|
|
const defaultHotKeyDefinitions = {};
|
|
|
|
hotkeyDefaults.map(item => {
|
|
const { commandName, ...values } = item;
|
|
defaultHotKeyDefinitions[commandName] = { ...values };
|
|
});
|
|
|
|
setState(initialState(defaultHotKeyDefinitions));
|
|
};
|
|
|
|
const onSave = () => {
|
|
const { hotkeys } = state;
|
|
|
|
hotkeysManager.setHotkeys(hotkeys);
|
|
|
|
localStorage.setItem('hotkey-definitions', JSON.stringify(hotkeys));
|
|
|
|
onClose();
|
|
|
|
snackbar.show({
|
|
message: t('SaveMessage'),
|
|
type: 'success',
|
|
});
|
|
};
|
|
|
|
const onHotkeyChanged = (commandName, hotkeyDefinition, keys) => {
|
|
const { errorMessage } = validateCommandKey({
|
|
commandName,
|
|
pressedKeys: keys,
|
|
hotkeys: state.hotkeys,
|
|
});
|
|
|
|
setState(prevState => ({
|
|
hotkeys: {
|
|
...prevState.hotkeys,
|
|
[commandName]: { ...hotkeyDefinition, keys },
|
|
},
|
|
errors: {
|
|
...prevState.errors,
|
|
[commandName]: errorMessage,
|
|
},
|
|
}));
|
|
};
|
|
|
|
const hasErrors = Object.keys(state.errors).some(key => !!state.errors[key]);
|
|
const hasHotkeys = Object.keys(state.hotkeys).length;
|
|
const splitedHotkeys = splitHotkeys(state.hotkeys);
|
|
|
|
return (
|
|
<React.Fragment>
|
|
<div className="HotkeysPreferences">
|
|
{hasHotkeys ? (
|
|
<div className="hotkeyTable">
|
|
{splitedHotkeys.map((hotkeys, index) => {
|
|
return (
|
|
<div className="hotkeyColumn" key={index}>
|
|
<div className="hotkeyHeader">
|
|
<div className="headerItemText text-right">Function</div>
|
|
<div className="headerItemText text-center">Shortcut</div>
|
|
</div>
|
|
{hotkeys.map(hotkey => {
|
|
const commandName = hotkey[0];
|
|
const hotkeyDefinition = hotkey[1];
|
|
const { keys, label } = hotkeyDefinition;
|
|
const errorMessage = state.errors[hotkey[0]];
|
|
const handleChange = keys => {
|
|
onHotkeyChanged(commandName, hotkeyDefinition, keys);
|
|
};
|
|
|
|
return (
|
|
<div key={commandName} className="hotkeyRow">
|
|
<div className="hotkeyLabel">{label}</div>
|
|
<div
|
|
data-key="defaultTool"
|
|
className={classnames(
|
|
'wrapperHotkeyInput',
|
|
errorMessage ? 'stateError' : ''
|
|
)}
|
|
>
|
|
<HotkeyField
|
|
keys={keys}
|
|
modifier_keys={MODIFIER_KEYS}
|
|
handleChange={handleChange}
|
|
classNames={'hotkeyInput'}
|
|
></HotkeyField>
|
|
<span className="errorMessage">{errorMessage}</span>
|
|
</div>
|
|
</div>
|
|
);
|
|
})}
|
|
</div>
|
|
);
|
|
})}
|
|
</div>
|
|
) : (
|
|
'Hotkeys definitions is empty'
|
|
)}
|
|
</div>
|
|
<TabFooter
|
|
onResetPreferences={onResetPreferences}
|
|
onSave={onSave}
|
|
onCancel={onClose}
|
|
hasErrors={hasErrors}
|
|
t={t}
|
|
/>
|
|
</React.Fragment>
|
|
);
|
|
}
|
|
|
|
HotkeysPreferences.propTypes = {
|
|
onClose: PropTypes.func,
|
|
};
|
|
|
|
export { HotkeysPreferences };
|