Skip to content

Commit

Permalink
[Maintenance] Split up large payee management components file (actual…
Browse files Browse the repository at this point in the history
  • Loading branch information
kymckay authored Nov 21, 2023
1 parent 6ed1b58 commit baa4748
Show file tree
Hide file tree
Showing 6 changed files with 277 additions and 254 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import React, {
import {
forwardRef,
memo,
useState,
useEffect,
useLayoutEffect,
Expand All @@ -20,207 +19,22 @@ import useSelected, {
useSelectedDispatch,
} from '../../hooks/useSelected';
import useStableCallback from '../../hooks/useStableCallback';
import Delete from '../../icons/v0/Delete';
import ExpandArrow from '../../icons/v0/ExpandArrow';
import Merge from '../../icons/v0/Merge';
import ArrowThinRight from '../../icons/v1/ArrowThinRight';
import { theme } from '../../style';
import Button from '../common/Button';
import Menu from '../common/Menu';
import Search from '../common/Search';
import Text from '../common/Text';
import View from '../common/View';
import {
Table,
TableHeader,
Row,
Cell,
InputCell,
SelectCell,
CellButton,
useTableNavigator,
} from '../table';
import { Tooltip } from '../tooltips';
import { TableHeader, Cell, SelectCell, useTableNavigator } from '../table';

import PayeeMenu from './PayeeMenu';
import PayeeTable from './PayeeTable';

let getPayeesById = memoizeOne(payees => groupById(payees));

function plural(count, singleText, pluralText) {
return count === 1 ? singleText : pluralText;
}

function RuleButton({ ruleCount, focused, onEdit, onClick }) {
return (
<Cell
name="rule-count"
width="auto"
focused={focused}
style={{ padding: '0 10px' }}
plain
>
<CellButton
style={{
borderRadius: 4,
padding: '3px 6px',
backgroundColor: theme.noticeBackground,
border: '1px solid ' + theme.noticeBackground,
color: theme.noticeTextDark,
fontSize: 12,
}}
onEdit={onEdit}
onSelect={onClick}
onFocus={onEdit}
>
<Text style={{ paddingRight: 5 }}>
{ruleCount > 0 ? (
<>
{ruleCount} associated {plural(ruleCount, 'rule', 'rules')}
</>
) : (
<>Create rule</>
)}
</Text>
<ArrowThinRight style={{ width: 8, height: 8 }} />
</CellButton>
</Cell>
);
}

let Payee = memo(
({
style,
payee,
ruleCount,
selected,
hovered,
editing,
focusedField,
onViewRules,
onCreateRule,
onHover,
onEdit,
onUpdate,
}) => {
let { id } = payee;
let dispatchSelected = useSelectedDispatch();
let borderColor = selected ? theme.tableBorderSelected : theme.tableBorder;
let backgroundFocus = hovered || focusedField === 'select';

return (
<Row
style={{
alignItems: 'stretch',
...style,
borderColor,
backgroundColor: hovered
? theme.tableRowBackgroundHover
: selected
? theme.tableRowBackgroundHighlight
: backgroundFocus
? theme.tableRowBackgroundHover
: theme.tableBackground,
...(selected && {
backgroundColor: theme.tableRowBackgroundHighlight,
zIndex: 100,
}),
}}
data-focus-key={payee.id}
onMouseEnter={() => onHover && onHover(payee.id)}
>
<SelectCell
exposed={
payee.transfer_acct == null && (hovered || selected || editing)
}
focused={focusedField === 'select'}
selected={selected}
onSelect={e => {
dispatchSelected({ type: 'select', id: payee.id, event: e });
}}
/>
<InputCell
value={(payee.transfer_acct ? 'Transfer: ' : '') + payee.name}
valueStyle={
!selected && payee.transfer_acct && { color: theme.pageTextSubdued }
}
exposed={focusedField === 'name'}
width="flex"
onUpdate={value =>
!payee.transfer_acct && onUpdate(id, 'name', value)
}
onExpose={() => onEdit(id, 'name')}
inputProps={{ readOnly: !!payee.transfer_acct }}
/>
<RuleButton
ruleCount={ruleCount}
focused={focusedField === 'rule-count'}
onEdit={() => onEdit(id, 'rule-count')}
onClick={() =>
ruleCount > 0 ? onViewRules(payee.id) : onCreateRule(payee.id)
}
/>
</Row>
);
},
);

const PayeeTable = forwardRef(
(
{
payees,
ruleCounts,
navigator,
categoryGroups,
highlightedRows,
ruleActions,
onUpdate,
onViewRules,
onCreateRule,
},
ref,
) => {
let [hovered, setHovered] = useState(null);
let selectedItems = useSelectedItems();

useLayoutEffect(() => {
let firstSelected = [...selectedItems][0];
ref.current.scrollTo(firstSelected, 'center');
navigator.onEdit(firstSelected, 'select');
}, []);

let onHover = useCallback(id => {
setHovered(id);
}, []);

return (
<View style={{ flex: 1 }} onMouseLeave={() => setHovered(null)}>
<Table
ref={ref}
items={payees}
navigator={navigator}
renderItem={({ item, editing, focusedField, onEdit }) => {
return (
<Payee
payee={item}
ruleCount={ruleCounts.get(item.id) || 0}
categoryGroups={categoryGroups}
selected={selectedItems.has(item.id)}
highlighted={highlightedRows && highlightedRows.has(item.id)}
editing={editing}
focusedField={focusedField}
hovered={hovered === item.id}
onHover={onHover}
onEdit={onEdit}
onUpdate={onUpdate}
onViewRules={onViewRules}
onCreateRule={onCreateRule}
/>
);
}}
/>
</View>
);
},
);

function PayeeTableHeader() {
let borderColor = theme.tableborder;
let dispatchSelected = useSelectedDispatch();
Expand Down Expand Up @@ -267,68 +81,6 @@ function EmptyMessage({ text, style }) {
);
}

function PayeeMenu({ payeesById, selectedPayees, onDelete, onMerge, onClose }) {
// Transfer accounts are never editable
let isDisabled = [...selectedPayees].some(
id => payeesById[id] == null || payeesById[id].transfer_acct,
);

return (
<Tooltip
position="bottom"
width={250}
style={{ padding: 0 }}
onClose={onClose}
>
<Menu
onMenuSelect={type => {
onClose();
switch (type) {
case 'delete':
onDelete();
break;
case 'merge':
onMerge();
break;
default:
}
}}
footer={
<View
style={{
padding: 3,
fontSize: 11,
fontStyle: 'italic',
color: theme.pageTextSubdued,
}}
>
{[...selectedPayees]
.slice(0, 4)
.map(id => payeesById[id].name)
.join(', ') + (selectedPayees.size > 4 ? ', and more' : '')}
</View>
}
items={[
{
icon: Delete,
name: 'delete',
text: 'Delete',
disabled: isDisabled,
},
{
icon: Merge,
iconSize: 9,
name: 'merge',
text: 'Merge',
disabled: isDisabled || selectedPayees.size < 2,
},
Menu.line,
]}
/>
</Tooltip>
);
}

export const ManagePayees = forwardRef(
(
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { applyChanges } from 'loot-core/src/shared/util';
import { useActions } from '../../hooks/useActions';
import useCategories from '../../hooks/useCategories';

import { ManagePayees } from '.';
import { ManagePayees } from './ManagePayees';

export default function ManagePayeesWithData({ initialSelectedIds }) {
let initialPayees = useSelector(state => state.queries.payees);
Expand Down
74 changes: 74 additions & 0 deletions packages/desktop-client/src/components/payees/PayeeMenu.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import Delete from '../../icons/v0/Delete';
import Merge from '../../icons/v0/Merge';
import { theme } from '../../style';
import Menu from '../common/Menu';
import View from '../common/View';
import { Tooltip } from '../tooltips';

export default function PayeeMenu({
payeesById,
selectedPayees,
onDelete,
onMerge,
onClose,
}) {
// Transfer accounts are never editable
let isDisabled = [...selectedPayees].some(
id => payeesById[id] == null || payeesById[id].transfer_acct,
);

return (
<Tooltip
position="bottom"
width={250}
style={{ padding: 0 }}
onClose={onClose}
>
<Menu
onMenuSelect={type => {
onClose();
switch (type) {
case 'delete':
onDelete();
break;
case 'merge':
onMerge();
break;
default:
}
}}
footer={
<View
style={{
padding: 3,
fontSize: 11,
fontStyle: 'italic',
color: theme.pageTextSubdued,
}}
>
{[...selectedPayees]
.slice(0, 4)
.map(id => payeesById[id].name)
.join(', ') + (selectedPayees.size > 4 ? ', and more' : '')}
</View>
}
items={[
{
icon: Delete,
name: 'delete',
text: 'Delete',
disabled: isDisabled,
},
{
icon: Merge,
iconSize: 9,
name: 'merge',
text: 'Merge',
disabled: isDisabled || selectedPayees.size < 2,
},
Menu.line,
]}
/>
</Tooltip>
);
}
Loading

0 comments on commit baa4748

Please sign in to comment.