mirror of
https://github.com/actualbudget/actual.git
synced 2026-04-28 18:40:34 -05:00
⌨️ Enhanced keyboard shortcuts modal (#5340)
* start to an enhanced keyboard shortcuts modal * shortcut start * poc * still rough but getting closer * refactor a bit * types * bits * renaming * clarifying comment * release notes * suggestions * adding initial focus state on the search * fix lint
This commit is contained in:
@@ -1,8 +1,12 @@
|
||||
import { type CSSProperties } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useLocation } from 'react-router';
|
||||
import { useState, type CSSProperties, useMemo, type ReactNode } from 'react';
|
||||
import { Trans, useTranslation } from 'react-i18next';
|
||||
|
||||
import { Button } from '@actual-app/components/button';
|
||||
import { SvgArrowLeft } from '@actual-app/components/icons/v1';
|
||||
import { InitialFocus } from '@actual-app/components/initial-focus';
|
||||
import { styles as baseStyles } from '@actual-app/components/styles';
|
||||
import { Text } from '@actual-app/components/text';
|
||||
import { theme } from '@actual-app/components/theme';
|
||||
import { View } from '@actual-app/components/view';
|
||||
|
||||
import * as Platform from 'loot-core/shared/platform';
|
||||
@@ -12,17 +16,14 @@ import {
|
||||
ModalCloseButton,
|
||||
ModalHeader,
|
||||
} from '@desktop-client/components/common/Modal';
|
||||
import { Search } from '@desktop-client/components/common/Search';
|
||||
|
||||
type KeyIconProps = {
|
||||
shortcut: string;
|
||||
style?: CSSProperties;
|
||||
};
|
||||
|
||||
type GroupHeadingProps = {
|
||||
group: string;
|
||||
};
|
||||
|
||||
type ShortcutProps = {
|
||||
type ShortcutListItemProps = {
|
||||
shortcut: string;
|
||||
description: string;
|
||||
meta?: string;
|
||||
@@ -54,313 +55,490 @@ function KeyIcon({ shortcut, style }: KeyIconProps) {
|
||||
);
|
||||
}
|
||||
|
||||
function GroupHeading({ group }: GroupHeadingProps) {
|
||||
function ListItem({
|
||||
children,
|
||||
style,
|
||||
onClick,
|
||||
}: {
|
||||
children: ReactNode;
|
||||
style?: CSSProperties;
|
||||
onClick?: () => void;
|
||||
}) {
|
||||
const clickStyles = onClick && {
|
||||
cursor: 'pointer',
|
||||
};
|
||||
|
||||
return (
|
||||
<Text
|
||||
<View
|
||||
style={{
|
||||
fontWeight: 'bold',
|
||||
fontSize: 16,
|
||||
marginTop: 20,
|
||||
marginBottom: 10,
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'space-between',
|
||||
width: '100%',
|
||||
gap: 5,
|
||||
padding: 12,
|
||||
backgroundColor: theme.tableBackground,
|
||||
borderBottomWidth: 1,
|
||||
borderColor: theme.tableBorder,
|
||||
height: 45,
|
||||
flexShrink: 0,
|
||||
':hover': {
|
||||
backgroundColor: theme.tableRowBackgroundHover,
|
||||
color: theme.tableText,
|
||||
},
|
||||
...clickStyles,
|
||||
...style,
|
||||
}}
|
||||
onClick={onClick}
|
||||
>
|
||||
{group}:
|
||||
</Text>
|
||||
{children}
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
function Shortcut({
|
||||
function ShortcutListItem({
|
||||
shortcut,
|
||||
description,
|
||||
meta,
|
||||
shift,
|
||||
style,
|
||||
}: ShortcutProps) {
|
||||
}: ShortcutListItemProps) {
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
marginBottom: 5,
|
||||
marginLeft: 20,
|
||||
}}
|
||||
>
|
||||
<div
|
||||
<ListItem>
|
||||
<Text>{description}</Text>
|
||||
|
||||
<View
|
||||
style={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
marginRight: 10,
|
||||
}}
|
||||
>
|
||||
{shift && (
|
||||
<>
|
||||
<KeyIcon shortcut="Shift" />
|
||||
<Text
|
||||
style={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
textAlign: 'center',
|
||||
fontSize: 16,
|
||||
paddingLeft: 2,
|
||||
paddingRight: 2,
|
||||
}}
|
||||
>
|
||||
+
|
||||
</Text>
|
||||
</>
|
||||
)}
|
||||
{meta && (
|
||||
<>
|
||||
<KeyIcon shortcut={meta} />
|
||||
<Text
|
||||
style={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
textAlign: 'center',
|
||||
fontSize: 16,
|
||||
paddingLeft: 2,
|
||||
paddingRight: 2,
|
||||
}}
|
||||
>
|
||||
+
|
||||
</Text>
|
||||
</>
|
||||
)}
|
||||
<KeyIcon shortcut={shortcut} style={style} />
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
flex: 1,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
gap: 4,
|
||||
alignItems: 'center',
|
||||
maxWidth: 300,
|
||||
flexShrink: 0,
|
||||
}}
|
||||
>
|
||||
{description}
|
||||
</div>
|
||||
</div>
|
||||
{shift && (
|
||||
<>
|
||||
<KeyIcon shortcut="Shift" />
|
||||
<Text>+</Text>
|
||||
</>
|
||||
)}
|
||||
{meta && (
|
||||
<>
|
||||
<KeyIcon shortcut={meta} />
|
||||
<Text>+</Text>
|
||||
</>
|
||||
)}
|
||||
<KeyIcon shortcut={shortcut} style={style} />
|
||||
</View>
|
||||
</ListItem>
|
||||
);
|
||||
}
|
||||
|
||||
type Shortcut = {
|
||||
id: string;
|
||||
shortcut: string;
|
||||
description: string;
|
||||
meta?: string;
|
||||
shift?: boolean;
|
||||
style?: CSSProperties;
|
||||
};
|
||||
|
||||
type ShortcutCategories = {
|
||||
id: string;
|
||||
name: string;
|
||||
items: Shortcut[];
|
||||
};
|
||||
|
||||
export function KeyboardShortcutModal() {
|
||||
const location = useLocation();
|
||||
const { t } = useTranslation();
|
||||
const onBudget = location.pathname.startsWith('/budget');
|
||||
const onAccounts = location.pathname.startsWith('/accounts');
|
||||
const ctrl = Platform.OS === 'mac' ? '⌘' : 'Ctrl';
|
||||
const [searchText, setSearchText] = useState('');
|
||||
const [selectedCategoryId, setSelectedCategoryId] = useState<string | null>(
|
||||
null,
|
||||
);
|
||||
|
||||
// In future, we may move this to state and pull overrides from config/db
|
||||
// This would allow us to drive our shortcuts from state instead of hardcoding them
|
||||
const defaultShortcuts: ShortcutCategories[] = useMemo(
|
||||
() => [
|
||||
{
|
||||
name: t('General'),
|
||||
id: 'general',
|
||||
items: [
|
||||
{
|
||||
id: 'help',
|
||||
shortcut: '?',
|
||||
description: t('Open the help menu'),
|
||||
},
|
||||
{
|
||||
id: 'command-palette',
|
||||
shortcut: 'K',
|
||||
description: t('Open the Command Palette'),
|
||||
meta: ctrl,
|
||||
},
|
||||
{
|
||||
id: 'close-budget',
|
||||
shortcut: 'O',
|
||||
description: t('Close the current budget and open another'),
|
||||
meta: ctrl,
|
||||
},
|
||||
{
|
||||
id: 'toggle-privacy-filter',
|
||||
shortcut: 'P',
|
||||
description: t('Toggle the privacy filter'),
|
||||
meta: ctrl,
|
||||
shift: true,
|
||||
},
|
||||
{
|
||||
id: 'undo-last-change',
|
||||
shortcut: 'Z',
|
||||
description: t('Undo the last change'),
|
||||
meta: ctrl,
|
||||
},
|
||||
{
|
||||
id: 'redo-last-change',
|
||||
shortcut: 'Z',
|
||||
description: t('Redo the last undone change'),
|
||||
shift: true,
|
||||
meta: ctrl,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'budget-page',
|
||||
name: t('Budget page'),
|
||||
items: [
|
||||
{
|
||||
id: 'current-month',
|
||||
shortcut: '0',
|
||||
style: {
|
||||
fontVariantNumeric: 'slashed-zero',
|
||||
},
|
||||
description: t('View current month'),
|
||||
},
|
||||
{
|
||||
id: 'view-previous-month',
|
||||
shortcut: '←',
|
||||
description: t('View previous month'),
|
||||
},
|
||||
{
|
||||
id: 'view-next-month',
|
||||
shortcut: '→',
|
||||
description: t('View next month'),
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'account-page',
|
||||
name: t('Account page'),
|
||||
items: [
|
||||
{
|
||||
id: 'move-down',
|
||||
shortcut: 'Enter',
|
||||
description: t('Move down when editing'),
|
||||
},
|
||||
{
|
||||
id: 'move-up',
|
||||
shortcut: 'Enter',
|
||||
shift: true,
|
||||
description: t('Move up when editing'),
|
||||
},
|
||||
{
|
||||
id: 'import-transactions',
|
||||
shortcut: 'I',
|
||||
meta: ctrl,
|
||||
description: t('Import transactions'),
|
||||
},
|
||||
{
|
||||
id: 'bank-sync',
|
||||
shortcut: 'B',
|
||||
meta: ctrl,
|
||||
description: t('Bank sync'),
|
||||
},
|
||||
{
|
||||
id: 'filter-to-selected-transactions',
|
||||
shortcut: 'F',
|
||||
description: t('Filter to the selected transactions'),
|
||||
},
|
||||
{
|
||||
id: 'delete-selected-transactions',
|
||||
shortcut: 'D',
|
||||
description: t('Delete the selected transactions'),
|
||||
},
|
||||
{
|
||||
id: 'set-account-for-selected-transactions',
|
||||
shortcut: 'A',
|
||||
description: t('Set account for selected transactions'),
|
||||
},
|
||||
{
|
||||
id: 'set-payee-for-selected-transactions',
|
||||
shortcut: 'P',
|
||||
description: t('Set payee for selected transactions'),
|
||||
},
|
||||
{
|
||||
id: 'set-notes-for-selected-transactions',
|
||||
shortcut: 'N',
|
||||
description: t('Set notes for selected transactions'),
|
||||
},
|
||||
{
|
||||
id: 'set-category-for-selected-transactions',
|
||||
shortcut: 'C',
|
||||
description: t('Set category for selected transactions'),
|
||||
},
|
||||
{
|
||||
id: 'toggle-cleared-for-selected-transactions',
|
||||
shortcut: 'L',
|
||||
description: t('Toggle cleared for selected transactions'),
|
||||
},
|
||||
{
|
||||
id: 'link-or-view-schedule-for-selected-transactions',
|
||||
shortcut: 'S',
|
||||
description: t('Link or view schedule for selected transactions'),
|
||||
},
|
||||
{
|
||||
id: 'select-all-transactions',
|
||||
shortcut: 'A',
|
||||
description: t('Select all transactions'),
|
||||
meta: ctrl,
|
||||
},
|
||||
{
|
||||
id: 'move-left-when-editing',
|
||||
shortcut: 'Tab',
|
||||
description: t('Move left when editing'),
|
||||
shift: true,
|
||||
},
|
||||
{
|
||||
id: 'move-right-when-editing',
|
||||
shortcut: 'Tab',
|
||||
description: t('Move right when editing'),
|
||||
},
|
||||
{
|
||||
id: 'add-new-transaction',
|
||||
shortcut: 'T',
|
||||
description: t('Add a new transaction'),
|
||||
},
|
||||
{
|
||||
id: 'filter-transactions',
|
||||
shortcut: 'F',
|
||||
description: t('Filter transactions'),
|
||||
},
|
||||
{
|
||||
id: 'move-next-transaction',
|
||||
shortcut: 'J',
|
||||
description: t('Move to the next transaction down'),
|
||||
},
|
||||
{
|
||||
id: 'move-previous-transaction',
|
||||
shortcut: 'K',
|
||||
description: t('Move to the next transaction up'),
|
||||
},
|
||||
{
|
||||
id: 'move-previous-transaction-scroll',
|
||||
shortcut: '↑',
|
||||
description: t('Move to the previous transaction and scroll'),
|
||||
},
|
||||
{
|
||||
id: 'move-next-transaction-scroll',
|
||||
shortcut: '↓',
|
||||
description: t('Move to the next transaction and scroll'),
|
||||
},
|
||||
{
|
||||
id: 'toggle-selection-current-transaction',
|
||||
shortcut: 'Space',
|
||||
description: t('Toggle selection of current transaction'),
|
||||
},
|
||||
{
|
||||
id: 'toggle-selection-all-transactions',
|
||||
shortcut: 'Space',
|
||||
description: t(
|
||||
'Toggle transactions between current and most recently selected transaction',
|
||||
),
|
||||
shift: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
[t, ctrl],
|
||||
);
|
||||
|
||||
const { isSearching, isInCategory, currentCategory, itemsToShow } =
|
||||
useMemo(() => {
|
||||
const isSearching = Boolean(searchText);
|
||||
const isInCategory = Boolean(selectedCategoryId);
|
||||
|
||||
if (isSearching) {
|
||||
// Show all matching shortcuts across all categories
|
||||
const allMatches = defaultShortcuts.flatMap(category =>
|
||||
category.items.filter(item =>
|
||||
item.description.toLowerCase().includes(searchText.toLowerCase()),
|
||||
),
|
||||
);
|
||||
return {
|
||||
isSearching,
|
||||
isInCategory: false,
|
||||
currentCategory: null,
|
||||
itemsToShow: allMatches,
|
||||
};
|
||||
}
|
||||
|
||||
if (isInCategory) {
|
||||
// Show shortcuts for selected category
|
||||
const category = defaultShortcuts.find(
|
||||
s => s.id === selectedCategoryId,
|
||||
);
|
||||
return {
|
||||
isSearching: false,
|
||||
isInCategory: true,
|
||||
currentCategory: category || null,
|
||||
itemsToShow: category?.items || [],
|
||||
};
|
||||
}
|
||||
|
||||
// Show category list
|
||||
return {
|
||||
isSearching: false,
|
||||
isInCategory: false,
|
||||
currentCategory: null,
|
||||
itemsToShow: defaultShortcuts,
|
||||
};
|
||||
}, [searchText, selectedCategoryId, defaultShortcuts]);
|
||||
|
||||
const showingShortcuts = isSearching || isInCategory;
|
||||
|
||||
return (
|
||||
<Modal name="keyboard-shortcuts">
|
||||
<Modal name="keyboard-shortcuts" containerProps={{ style: { width: 700 } }}>
|
||||
{({ state: { close } }) => (
|
||||
<>
|
||||
<ModalHeader
|
||||
title={t('Keyboard shortcuts')}
|
||||
title={
|
||||
isSearching
|
||||
? t('Search results')
|
||||
: currentCategory
|
||||
? t('{{categoryName}} shortcuts', {
|
||||
categoryName: currentCategory.name,
|
||||
})
|
||||
: t('Keyboard shortcuts')
|
||||
}
|
||||
leftContent={
|
||||
showingShortcuts ? (
|
||||
<Button
|
||||
variant="bare"
|
||||
onClick={() => {
|
||||
setSearchText('');
|
||||
setSelectedCategoryId(null);
|
||||
}}
|
||||
style={{ marginRight: 10, marginLeft: 15, zIndex: 3000 }}
|
||||
>
|
||||
<SvgArrowLeft
|
||||
width={10}
|
||||
height={10}
|
||||
style={{ marginRight: 5, color: 'currentColor' }}
|
||||
/>
|
||||
<Trans>Back</Trans>
|
||||
</Button>
|
||||
) : null
|
||||
}
|
||||
rightContent={<ModalCloseButton onPress={close} />}
|
||||
/>
|
||||
<View
|
||||
style={{
|
||||
flexDirection: 'row',
|
||||
flexDirection: 'column',
|
||||
fontSize: 13,
|
||||
padding: '0 16px 16px 16px',
|
||||
}}
|
||||
>
|
||||
<View>
|
||||
<Shortcut shortcut="?" description={t('Open the help menu')} />
|
||||
<Shortcut
|
||||
shortcut="K"
|
||||
description={t('Open the Command Palette')}
|
||||
meta={ctrl}
|
||||
/>
|
||||
<Shortcut
|
||||
shortcut="O"
|
||||
description={t('Close the current budget and open another')}
|
||||
meta={ctrl}
|
||||
/>
|
||||
<Shortcut
|
||||
shortcut="P"
|
||||
description={t('Toggle the privacy filter')}
|
||||
meta={ctrl}
|
||||
shift={true}
|
||||
/>
|
||||
{onBudget && (
|
||||
<Shortcut
|
||||
shortcut="0"
|
||||
description={t('View current month')}
|
||||
<InitialFocus<HTMLInputElement>>
|
||||
{ref => (
|
||||
<Search
|
||||
inputRef={ref}
|
||||
value={searchText}
|
||||
isInModal
|
||||
onChange={text => {
|
||||
setSearchText(text);
|
||||
// Clear category selection when searching to search all shortcuts
|
||||
if (text && selectedCategoryId) {
|
||||
setSelectedCategoryId(null);
|
||||
}
|
||||
}}
|
||||
placeholder={t('Search shortcuts')}
|
||||
width="100%"
|
||||
style={{
|
||||
fontVariantNumeric: 'slashed-zero',
|
||||
backgroundColor: theme.tableBackground,
|
||||
borderColor: theme.formInputBorder,
|
||||
marginBottom: 10,
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{onAccounts && (
|
||||
<>
|
||||
<Shortcut
|
||||
shortcut="Enter"
|
||||
description={t('Move down when editing')}
|
||||
/>
|
||||
<Shortcut
|
||||
shortcut="Enter"
|
||||
description={t('Move up when editing')}
|
||||
shift={true}
|
||||
/>
|
||||
<Shortcut
|
||||
shortcut="I"
|
||||
description={t('Import transactions')}
|
||||
meta={ctrl}
|
||||
/>
|
||||
<Shortcut
|
||||
shortcut="B"
|
||||
description={t('Bank sync')}
|
||||
meta={ctrl}
|
||||
/>
|
||||
<GroupHeading group={t('With transactions selected')} />
|
||||
<Shortcut
|
||||
shortcut="F"
|
||||
description={t('Filter to the selected transactions')}
|
||||
/>
|
||||
<Shortcut
|
||||
shortcut="D"
|
||||
description={t('Delete selected transactions')}
|
||||
/>
|
||||
<Shortcut
|
||||
shortcut="A"
|
||||
description={t('Set account for selected transactions')}
|
||||
/>
|
||||
<Shortcut
|
||||
shortcut="P"
|
||||
description={t('Set payee for selected transactions')}
|
||||
/>
|
||||
<Shortcut
|
||||
shortcut="N"
|
||||
description={t('Set notes for selected transactions')}
|
||||
/>
|
||||
<Shortcut
|
||||
shortcut="C"
|
||||
description={t('Set category for selected transactions')}
|
||||
/>
|
||||
<Shortcut
|
||||
shortcut="L"
|
||||
description={t('Toggle cleared for selected transactions')}
|
||||
/>
|
||||
<Shortcut
|
||||
shortcut="S"
|
||||
description={t(
|
||||
'Link or view schedule for selected transactions',
|
||||
)}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</View>
|
||||
</InitialFocus>
|
||||
<View
|
||||
style={{
|
||||
marginRight: 15,
|
||||
flexDirection: 'column',
|
||||
overflowY: 'auto',
|
||||
maxHeight: '40vh',
|
||||
height: 400,
|
||||
backgroundColor: theme.tableBackground,
|
||||
border: `1px solid ${theme.tableBorder}`,
|
||||
borderRadius: baseStyles.menuBorderRadius,
|
||||
}}
|
||||
>
|
||||
<Shortcut
|
||||
shortcut="Z"
|
||||
description={t('Undo the last change')}
|
||||
meta={ctrl}
|
||||
/>
|
||||
<Shortcut
|
||||
shortcut="Z"
|
||||
description={t('Redo the last undone change')}
|
||||
shift={true}
|
||||
meta={ctrl}
|
||||
/>
|
||||
{onAccounts && (
|
||||
<>
|
||||
<Shortcut
|
||||
shortcut="Enter"
|
||||
description={t('Move up when editing')}
|
||||
shift={true}
|
||||
{itemsToShow.length === 0 ? (
|
||||
<View
|
||||
style={{
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
padding: 20,
|
||||
}}
|
||||
>
|
||||
<Text style={{ fontSize: 15 }}>
|
||||
<Trans>
|
||||
{isSearching
|
||||
? 'No matching shortcuts'
|
||||
: isInCategory
|
||||
? 'No shortcuts in this category'
|
||||
: 'No matching shortcuts'}
|
||||
</Trans>
|
||||
</Text>
|
||||
</View>
|
||||
) : showingShortcuts ? (
|
||||
(itemsToShow as Shortcut[]).map(shortcut => (
|
||||
<ShortcutListItem
|
||||
key={shortcut.id}
|
||||
shortcut={shortcut.shortcut}
|
||||
description={shortcut.description}
|
||||
meta={shortcut.meta}
|
||||
shift={shortcut.shift}
|
||||
style={shortcut.style}
|
||||
/>
|
||||
<Shortcut
|
||||
shortcut="Tab"
|
||||
description={t('Move left when editing')}
|
||||
shift={true}
|
||||
/>
|
||||
{onBudget && (
|
||||
<>
|
||||
<Shortcut
|
||||
shortcut="←"
|
||||
description={t('View previous month')}
|
||||
/>
|
||||
<Shortcut
|
||||
shortcut="→"
|
||||
description={t('View next month')}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
{onAccounts && (
|
||||
<>
|
||||
<Shortcut
|
||||
shortcut="A"
|
||||
description={t('Select all transactions')}
|
||||
meta={ctrl}
|
||||
/>
|
||||
<Shortcut
|
||||
shortcut="Tab"
|
||||
description={t('Move right when editing')}
|
||||
/>
|
||||
<Shortcut
|
||||
shortcut="Tab"
|
||||
description={t('Move left when editing')}
|
||||
shift={true}
|
||||
/>
|
||||
<Shortcut
|
||||
shortcut="T"
|
||||
description={t('Add a new transaction')}
|
||||
/>
|
||||
<Shortcut
|
||||
shortcut="F"
|
||||
description={t('Filter transactions')}
|
||||
/>
|
||||
<GroupHeading group={t('Select a transaction, then')} />
|
||||
<Shortcut
|
||||
shortcut="J"
|
||||
description={t('Move to the next transaction down')}
|
||||
/>
|
||||
<Shortcut
|
||||
shortcut="K"
|
||||
description={t('Move to the next transaction up')}
|
||||
/>
|
||||
<Shortcut
|
||||
shortcut="↑"
|
||||
description={t(
|
||||
'Move to the next transaction down and scroll',
|
||||
)}
|
||||
/>
|
||||
<Shortcut
|
||||
shortcut="↓"
|
||||
description={t(
|
||||
'Move to the next transaction up and scroll',
|
||||
)}
|
||||
/>
|
||||
<Shortcut
|
||||
shortcut="Space"
|
||||
description={t(
|
||||
'Toggle selection of current transaction',
|
||||
)}
|
||||
/>
|
||||
<Shortcut
|
||||
shortcut="Space"
|
||||
description={t(
|
||||
'Toggle all transactions between current and most recently selected transaction',
|
||||
)}
|
||||
shift={true}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
))
|
||||
) : (
|
||||
(itemsToShow as ShortcutCategories[]).map(category => (
|
||||
<ListItem
|
||||
key={category.id}
|
||||
onClick={() => {
|
||||
if (category.items.length > 0) {
|
||||
setSelectedCategoryId(category.id);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<View
|
||||
style={{
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'space-between',
|
||||
width: '100%',
|
||||
}}
|
||||
>
|
||||
<Text style={{ fontWeight: 'bold' }}>
|
||||
{category.name}
|
||||
</Text>
|
||||
<Text style={{ color: theme.pageTextLight }}>
|
||||
{category.items.length}{' '}
|
||||
{category.items.length === 1
|
||||
? t('shortcut')
|
||||
: t('shortcuts')}{' '}
|
||||
›
|
||||
</Text>
|
||||
</View>
|
||||
</ListItem>
|
||||
))
|
||||
)}
|
||||
</View>
|
||||
</View>
|
||||
|
||||
6
upcoming-release-notes/5340.md
Normal file
6
upcoming-release-notes/5340.md
Normal file
@@ -0,0 +1,6 @@
|
||||
---
|
||||
category: Enhancements
|
||||
authors: [MikesGlitch]
|
||||
---
|
||||
|
||||
Enhanced Keyboard Shortcuts modal to be searchable
|
||||
Reference in New Issue
Block a user