Pull shiftKey state directly from the initiating event (#1022)

Fixes #1021
This commit is contained in:
Jed Fox
2023-05-10 18:45:34 -04:00
committed by GitHub
parent 655b677961
commit e4e9267c08
9 changed files with 39 additions and 92 deletions

View File

@@ -329,8 +329,8 @@ let Rule = memo(
<SelectCell
exposed={hovered || selected || editing}
focused={focusedField === 'select'}
onSelect={() => {
dispatchSelected({ type: 'select', id: rule.id });
onSelect={e => {
dispatchSelected({ type: 'select', id: rule.id, event: e });
}}
onEdit={() => onEdit(rule.id, 'select')}
selected={selected}
@@ -478,7 +478,7 @@ function RulesHeader() {
exposed={true}
focused={false}
selected={selectedItems.size > 0}
onSelect={() => dispatchSelected({ type: 'select-all' })}
onSelect={e => dispatchSelected({ type: 'select-all', event: e })}
/>
<Cell value="Stage" width={50} />
<Cell value="Rule" width="flex" />

View File

@@ -51,8 +51,8 @@ const TransactionRow = memo(function TransactionRow({
<SelectCell
exposed={true}
focused={false}
onSelect={() => {
dispatchSelected({ type: 'select', id: transaction.id });
onSelect={e => {
dispatchSelected({ type: 'select', id: transaction.id, event: e });
}}
selected={selected}
/>
@@ -185,7 +185,7 @@ export default function SimpleTransactionsTable({
focused={false}
selected={selectedItems.size > 0}
width={20}
onSelect={() => dispatchSelected({ type: 'select-all' })}
onSelect={e => dispatchSelected({ type: 'select-all', event: e })}
/>
{fields.map((field, i) => {
switch (field) {

View File

@@ -255,7 +255,7 @@ export const TransactionHeader = memo(
focused={false}
selected={hasSelected}
width={20}
onSelect={() => dispatchSelected({ type: 'select-all' })}
onSelect={e => dispatchSelected({ type: 'select-all', event: e })}
/>
<Cell value="Date" width={110} />
{showAccount && <Cell value="Account" width="flex" />}
@@ -690,8 +690,8 @@ export const Transaction = memo(function Transaction(props) {
<SelectCell
exposed={hovered || selected || editing}
focused={focusedField === 'select'}
onSelect={() => {
dispatchSelected({ type: 'select', id: transaction.id });
onSelect={e => {
dispatchSelected({ type: 'select', id: transaction.id, event: e });
}}
onEdit={() => onEdit(id, 'select')}
selected={selected}

View File

@@ -138,8 +138,8 @@ let Payee = memo(
}
focused={focusedField === 'select'}
selected={selected}
onSelect={() => {
dispatchSelected({ type: 'select', id: payee.id });
onSelect={e => {
dispatchSelected({ type: 'select', id: payee.id, event: e });
}}
/>
<InputCell
@@ -247,7 +247,7 @@ function PayeeTableHeader() {
exposed={true}
focused={false}
selected={selectedItems.size > 0}
onSelect={() => dispatchSelected({ type: 'select-all' })}
onSelect={e => dispatchSelected({ type: 'select-all', event: e })}
/>
<Cell value="Name" width="flex" />
</TableHeader>

View File

@@ -35,8 +35,8 @@ function DiscoverSchedulesTable({ schedules, loading }) {
height={ROW_HEIGHT}
inset={15}
backgroundColor="transparent"
onClick={() => {
dispatchSelected({ type: 'select', id: item.id });
onClick={e => {
dispatchSelected({ type: 'select', id: item.id, event: e });
}}
borderColor={selected ? colors.b8 : colors.border}
style={{
@@ -51,8 +51,8 @@ function DiscoverSchedulesTable({ schedules, loading }) {
exposed={true}
focused={false}
selected={selected}
onSelect={() => {
dispatchSelected({ type: 'select', id: item.id });
onSelect={e => {
dispatchSelected({ type: 'select', id: item.id, event: e });
}}
/>
<Field width="flex">
@@ -76,7 +76,7 @@ function DiscoverSchedulesTable({ schedules, loading }) {
exposed={true}
focused={false}
selected={selectedItems.size > 0}
onSelect={() => dispatchSelected({ type: 'select-all' })}
onSelect={e => dispatchSelected({ type: 'select-all', event: e })}
/>
<Field width="flex">Payee</Field>
<Field width="flex">Account</Field>

View File

@@ -491,7 +491,7 @@ export const CellButton = forwardRef(
if (e.key === 'x' || e.key === ' ') {
e.preventDefault();
if (!disabled) {
onSelect && onSelect();
onSelect && onSelect(e);
}
}
}}
@@ -513,9 +513,9 @@ export const CellButton = forwardRef(
onClick={
clickBehavior === 'none'
? null
: () => {
: e => {
if (!disabled) {
onSelect && onSelect();
onSelect && onSelect(e);
onEdit && onEdit();
}
}
@@ -545,7 +545,7 @@ export function SelectCell({
style={[{ alignItems: 'center', userSelect: 'none' }, style]}
onClick={e => {
e.stopPropagation();
onSelect && onSelect();
onSelect && onSelect(e);
onEdit && onEdit();
}}
>

View File

@@ -10,8 +10,7 @@ import { useSelector } from 'react-redux';
import { listen } from 'loot-core/src/platform/client/fetch';
import * as undo from 'loot-core/src/platform/client/undo';
import { hasModifierKey } from '../util/keys';
import { isNonProductionEnvironment } from 'loot-core/src/shared/environment';
function iterateRange(range, func) {
let from = Math.min(range.start, range.end);
@@ -29,9 +28,9 @@ export default function useSelected(name, items, initialSelectedIds) {
case 'select': {
let { selectedRange } = state;
let selectedItems = new Set(state.selectedItems);
let { id } = action;
let { id, event } = action;
if (hasModifierKey('shift') && selectedRange) {
if (event.shiftKey && selectedRange) {
let idx = items.findIndex(p => p.id === id);
let startIdx = items.findIndex(p => p.id === selectedRange.start);
let endIdx = items.findIndex(p => p.id === selectedRange.end);
@@ -224,17 +223,24 @@ export function SelectedProvider({ instance, fetchAllIds, children }) {
let dispatch = useCallback(
async action => {
if (!action.event && isNonProductionEnvironment()) {
throw new Error('SelectedDispatch actions must have an event');
}
if (action.type === 'select-all') {
if (latestItems.current && latestItems.current.size > 0) {
return instance.dispatch({ type: 'select-none' });
return instance.dispatch({
type: 'select-none',
event: action.event,
});
} else {
if (fetchAllIds) {
return instance.dispatch({
type: 'select-all',
ids: await fetchAllIds(),
event: action.event,
});
}
return instance.dispatch({ type: 'select-all' });
return instance.dispatch({ type: 'select-all', event: action.event });
}
}
return instance.dispatch(action);

View File

@@ -1,65 +0,0 @@
// TODO: This is a barebones module for now, need to think about a
// generic way keys are handled across the app
let _keyHandlers = {};
let _modifierState = {
shift: false,
ctrl: false,
alt: false,
meta: false,
};
export function hasModifierKey(modifier) {
return !!_modifierState[modifier];
}
export function registerKeyHandler(key, func) {
if (!_keyHandlers[key]) {
_keyHandlers[key] = [];
}
_keyHandlers[key].push(func);
return () => {
_keyHandlers[key] = _keyHandlers[key].filter(f => f !== func);
};
}
document.addEventListener('keydown', e => {
if (e.key === 'Shift') {
_modifierState.shift = true;
}
if (e.key === 'Control') {
_modifierState.ctrl = true;
}
if (e.key === 'Alt') {
_modifierState.alt = true;
}
if (e.key === 'Meta') {
_modifierState.meta = true;
}
if (!(e.target && e.target.matches('input'))) {
let handlers = _keyHandlers[e.key.toUpperCase()];
if (handlers && handlers.length > 0) {
handlers[handlers.length - 1](_modifierState);
e.preventDefault();
e.stopPropagation();
}
}
});
document.addEventListener('keyup', e => {
if (e.key === 'Shift') {
_modifierState.shift = false;
}
if (e.key === 'Control') {
_modifierState.ctrl = false;
}
if (e.key === 'Alt') {
_modifierState.alt = false;
}
if (e.key === 'Meta') {
_modifierState.meta = false;
}
});

View File

@@ -0,0 +1,6 @@
---
category: Maintenance
authors: [j-f1]
---
Improve behavior of shift-clicking checkboxes to select multiple transactions.