Skip to content
Snippets Groups Projects
Unverified Commit 70726741 authored by Robert Dyer's avatar Robert Dyer Committed by GitHub
Browse files

Add help modal for keyboard shortcuts. (#3033)

* Add help modal for keyboard shortcuts.

* add release note

* fix linter

* fix typecheck

* fix linter

* use component syntax for GroupHeading

* use component syntax for Shortcut

* fix linter

* use component syntax for KeyIcon

* refactor to support different dialogs

* show different help based on current page

* fix linter

* reword help

* capitalize letters

* show cmd on mac

* stop event propagation

* dont show if a modal is already open

* remove unused import

* rename modal

* move where location check happens

* dont stop event

* allow typing '?' in inputs

* better filter

* extract function

* fix linter

* dont show if filter popover is visible

* fix linter

* fix wrong shortcut, support SHIFT

* fix linter

* fix conditional
parent 16e887c9
No related branches found
No related tags found
No related merge requests found
......@@ -135,6 +135,14 @@ global.Actual = {
},
};
function inputFocused(e) {
return (
e.target.tagName === 'INPUT' ||
e.target.tagName === 'TEXTAREA' ||
e.target.isContentEditable
);
}
document.addEventListener('keydown', e => {
if (e.metaKey || e.ctrlKey) {
// Cmd/Ctrl+o
......@@ -144,11 +152,7 @@ document.addEventListener('keydown', e => {
}
// Cmd/Ctrl+z
else if (e.key.toLowerCase() === 'z') {
if (
e.target.tagName === 'INPUT' ||
e.target.tagName === 'TEXTAREA' ||
e.target.isContentEditable
) {
if (inputFocused(e)) {
return;
}
e.preventDefault();
......@@ -160,5 +164,10 @@ document.addEventListener('keydown', e => {
window.__actionsForMenu.undo();
}
}
} else if (e.key === '?') {
if (inputFocused(e)) {
return;
}
window.__actionsForMenu.pushModal('keyboard-shortcuts');
}
});
......@@ -35,6 +35,7 @@ import { GoCardlessExternalMsg } from './modals/GoCardlessExternalMsg';
import { GoCardlessInitialise } from './modals/GoCardlessInitialise';
import { HoldBufferModal } from './modals/HoldBufferModal';
import { ImportTransactions } from './modals/ImportTransactions';
import { KeyboardShortcutModal } from './modals/KeyboardShortcutModal';
import { LoadBackup } from './modals/LoadBackup';
import { ManageRulesModal } from './modals/ManageRulesModal';
import { MergeUnusedPayees } from './modals/MergeUnusedPayees';
......@@ -95,6 +96,9 @@ export function Modals() {
};
switch (name) {
case 'keyboard-shortcuts':
return <KeyboardShortcutModal modalProps={modalProps} />;
case 'import-transactions':
return (
<ImportTransactions
......
import { useLocation } from 'react-router-dom';
import * as Platform from 'loot-core/src/client/platform';
import { Modal, type ModalProps } from '../common/Modal';
import { Text } from '../common/Text';
import { View } from '../common/View';
type KeyboardShortcutsModalProps = {
modalProps?: Partial<ModalProps>;
};
type KeyIconProps = {
shortcut: string;
};
type GroupHeadingProps = {
group: string;
};
type ShortcutProps = {
shortcut: string;
description: string;
meta?: string;
shift?: boolean;
};
function KeyIcon({ shortcut }: KeyIconProps) {
return (
<div
style={{
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
fontWeight: 'bold',
backgroundColor: '#fff',
color: '#000',
border: '1px solid #000',
borderRadius: 8,
minWidth: 35,
minHeight: 35,
filter: 'drop-shadow(1px 1px)',
padding: 5,
}}
>
{shortcut}
</div>
);
}
function GroupHeading({ group }: GroupHeadingProps) {
return (
<Text
style={{
fontWeight: 'bold',
fontSize: 16,
marginTop: 20,
marginBottom: 10,
}}
>
{group}:
</Text>
);
}
function Shortcut({ shortcut, description, meta, shift }: ShortcutProps) {
return (
<div
style={{
display: 'flex',
marginBottom: 10,
marginLeft: 20,
}}
>
<div
style={{
display: 'flex',
flexDirection: 'column',
}}
>
<div
style={{
display: 'flex',
flexDirection: 'row',
marginRight: 10,
}}
>
{meta && (
<>
<KeyIcon shortcut={meta} />
<Text
style={{
display: 'flex',
alignItems: 'center',
textAlign: 'center',
fontSize: 16,
paddingLeft: 2,
paddingRight: 2,
}}
>
+
</Text>
</>
)}
{shift && (
<>
<KeyIcon shortcut="Shift" />
<Text
style={{
display: 'flex',
alignItems: 'center',
textAlign: 'center',
fontSize: 16,
paddingLeft: 2,
paddingRight: 2,
}}
>
+
</Text>
</>
)}
<KeyIcon shortcut={shortcut} />
</div>
<div
style={{
display: 'flex',
flexDirection: 'row',
flex: 1,
}}
/>
</div>
<div
style={{
display: 'flex',
alignItems: 'center',
maxWidth: 300,
}}
>
{description}
</div>
</div>
);
}
export function KeyboardShortcutModal({
modalProps,
}: KeyboardShortcutsModalProps) {
const location = useLocation();
const onAccounts = location.pathname.startsWith('/accounts');
const ctrl = Platform.OS === 'mac' ? '' : 'Ctrl';
return (
<Modal title="Keyboard Shortcuts" {...modalProps}>
<View
style={{
flexDirection: 'row',
}}
>
<View>
<Shortcut
shortcut="O"
description="Close the current budget and open another"
meta={ctrl}
/>
<Shortcut shortcut="?" description="Show this help dialog" />
{onAccounts && (
<>
<Shortcut shortcut="Enter" description="Move down when editing" />
<Shortcut shortcut="Tab" description="Move right when editing" />
<GroupHeading group="Select a transaction, then" />
<Shortcut
shortcut="J"
description="Move to the next transaction down"
/>
<Shortcut
shortcut="K"
description="Move to the next transaction up"
/>
<Shortcut
shortcut="↑"
description="Move to the next transaction down and scroll"
/>
<Shortcut
shortcut="↓"
description="Move to the next transaction up and scroll"
/>
<Shortcut
shortcut="Space"
description="Toggle selection of current transaction"
/>
<Shortcut
shortcut="Space"
description="Toggle all transactions between current and most recently selected transaction"
shift={true}
/>
</>
)}
</View>
<View
style={{
marginLeft: 20,
marginRight: 20,
}}
>
<Shortcut
shortcut="Z"
description="Undo the last change"
meta={ctrl}
/>
<Shortcut
shortcut="Z"
description="Redo the last undone change"
shift={true}
meta={ctrl}
/>
{onAccounts && (
<>
<Shortcut
shortcut="Enter"
description="Move up when editing"
shift={true}
/>
<Shortcut
shortcut="Tab"
description="Move left when editing"
shift={true}
/>
<GroupHeading group="With transaction(s) selected" />
<Shortcut
shortcut="F"
description="Filter to the selected transactions"
/>
<Shortcut
shortcut="D"
description="Delete selected transactions"
/>
<Shortcut
shortcut="A"
description="Set account for selected transactions"
/>
<Shortcut
shortcut="P"
description="Set payee for selected transactions"
/>
<Shortcut
shortcut="N"
description="Set notes for selected transactions"
/>
<Shortcut
shortcut="C"
description="Set category for selected transactions"
/>
<Shortcut
shortcut="L"
description="Toggle cleared for current transaction"
/>
</>
)}
</View>
</View>
</Modal>
);
}
......@@ -10,6 +10,16 @@ const initialState: ModalsState = {
export function update(state = initialState, action: Action): ModalsState {
switch (action.type) {
case constants.PUSH_MODAL:
// special case: don't show the keyboard shortcuts modal if there's already a modal open
if (
action.modal.name.endsWith('keyboard-shortcuts') &&
(state.modalStack.length > 0 ||
window.document.querySelector(
'div[data-testid="filters-menu-tooltip"]',
) !== null)
) {
return state;
}
return {
...state,
modalStack: [...state.modalStack, action.modal],
......
---
category: Features
authors: [psybers]
---
Add help modal for keyboard shortcuts.
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment