diff --git a/packages/desktop-client/src/components/budget/BudgetTable.jsx b/packages/desktop-client/src/components/budget/BudgetTable.jsx
index db7826460c0..cc61c3d8b5e 100644
--- a/packages/desktop-client/src/components/budget/BudgetTable.jsx
+++ b/packages/desktop-client/src/components/budget/BudgetTable.jsx
@@ -1,9 +1,9 @@
-import React, { createRef, Component } from 'react';
-import { connect } from 'react-redux';
+import React, { useRef, useState } from 'react';
-import { savePrefs } from 'loot-core/src/client/actions';
import * as monthUtils from 'loot-core/src/shared/months';
+import { useCategories } from '../../hooks/useCategories';
+import { useLocalPref } from '../../hooks/useLocalPref';
import { theme, styles } from '../../style';
import { View } from '../common/View';
import { IntersectionBoundary } from '../tooltips';
@@ -14,28 +14,41 @@ import { BudgetTotals } from './BudgetTotals';
import { MonthsProvider } from './MonthsContext';
import { findSortDown, findSortUp, getScrollbarWidth } from './util';
-class BudgetTableInner extends Component {
- constructor(props) {
- super(props);
- this.budgetCategoriesRef = createRef();
-
- this.state = {
- editing: null,
- draggingState: null,
- };
- }
-
- onEditMonth = (id, monthIndex) => {
- this.setState({ editing: id ? { id, cell: monthIndex } : null });
+export function BudgetTable(props) {
+ const {
+ type,
+ prewarmStartMonth,
+ startMonth,
+ numMonths,
+ monthBounds,
+ dataComponents,
+ onSaveCategory,
+ onDeleteCategory,
+ onSaveGroup,
+ onDeleteGroup,
+ onReorderCategory,
+ onReorderGroup,
+ onShowActivity,
+ onBudgetAction,
+ } = props;
+
+ const budgetCategoriesRef = useRef();
+ const { grouped: categoryGroups } = useCategories();
+ const [collapsed = [], setCollapsedPref] = useLocalPref('budget.collapsed');
+ const [showHiddenCategories, setShowHiddenCategoriesPef] = useLocalPref(
+ 'budget.showHiddenCategories',
+ );
+ const [editing, setEditing] = useState(null);
+
+ const onEditMonth = (id, monthIndex) => {
+ setEditing(id ? { id, cell: monthIndex } : null);
};
- onEditName = id => {
- this.setState({ editing: id ? { id, cell: 'name' } : null });
+ const onEditName = id => {
+ setEditing(id ? { id, cell: 'name' } : null);
};
- onReorderCategory = (id, dropPos, targetId) => {
- const { categoryGroups } = this.props;
-
+ const _onReorderCategory = (id, dropPos, targetId) => {
const isGroup = !!categoryGroups.find(g => g.id === targetId);
if (isGroup) {
@@ -48,7 +61,7 @@ class BudgetTableInner extends Component {
if (group) {
const { categories } = group;
- this.props.onReorderCategory({
+ onReorderCategory({
id,
groupId: group.id,
targetId:
@@ -67,7 +80,7 @@ class BudgetTableInner extends Component {
}
}
- this.props.onReorderCategory({
+ onReorderCategory({
id,
groupId: targetGroup.id,
...findSortDown(targetGroup.categories, dropPos, targetId),
@@ -75,19 +88,14 @@ class BudgetTableInner extends Component {
}
};
- onReorderGroup = (id, dropPos, targetId) => {
- const { categoryGroups } = this.props;
-
- this.props.onReorderGroup({
+ const _onReorderGroup = (id, dropPos, targetId) => {
+ onReorderGroup({
id,
...findSortDown(categoryGroups, dropPos, targetId),
});
};
- moveVertically = dir => {
- const { editing } = this.state;
- const { type, categoryGroups, collapsed } = this.props;
-
+ const moveVertically = dir => {
const flattened = categoryGroups.reduce((all, group) => {
if (collapsed.includes(group.id)) {
return all.concat({ id: group.id, isGroup: true });
@@ -106,7 +114,7 @@ class BudgetTableInner extends Component {
nextIdx += dir;
continue;
} else if (type === 'report' || !next.is_income) {
- this.onEditMonth(next.id, editing.cell);
+ onEditMonth(next.id, editing.cell);
return;
} else {
break;
@@ -115,187 +123,136 @@ class BudgetTableInner extends Component {
}
};
- onKeyDown = e => {
- if (!this.state.editing) {
+ const onKeyDown = e => {
+ if (!editing) {
return null;
}
if (e.key === 'Enter' || e.key === 'Tab') {
e.preventDefault();
- this.moveVertically(e.shiftKey ? -1 : 1);
+ moveVertically(e.shiftKey ? -1 : 1);
}
};
- onShowActivity = (catId, monthIndex) => {
- this.props.onShowActivity(catId, this.resolveMonth(monthIndex));
+ const resolveMonth = monthIndex => {
+ return monthUtils.addMonths(startMonth, monthIndex);
};
- onBudgetAction = (monthIndex, type, args) => {
- this.props.onBudgetAction(this.resolveMonth(monthIndex), type, args);
+ const _onShowActivity = (catId, monthIndex) => {
+ onShowActivity(catId, resolveMonth(monthIndex));
};
- resolveMonth = monthIndex => {
- return monthUtils.addMonths(this.props.startMonth, monthIndex);
+ const _onBudgetAction = (monthIndex, type, args) => {
+ onBudgetAction(resolveMonth(monthIndex), type, args);
};
- // This is called via ref.
- clearEditing() {
- this.setState({ editing: null });
- }
+ const onCollapse = collapsedIds => {
+ setCollapsedPref(collapsedIds);
+ };
- toggleHiddenCategories = () => {
- this.props.onToggleHiddenCategories();
+ const onToggleHiddenCategories = () => {
+ setShowHiddenCategoriesPef(!showHiddenCategories);
};
- expandAllCategories = () => {
- this.props.onCollapse([]);
+ const toggleHiddenCategories = () => {
+ onToggleHiddenCategories();
};
- collapseAllCategories = () => {
- const { onCollapse, categoryGroups } = this.props;
- onCollapse(categoryGroups.map(g => g.id));
+ const expandAllCategories = () => {
+ onCollapse([]);
};
- render() {
- const {
- type,
- categoryGroups,
- prewarmStartMonth,
- startMonth,
- numMonths,
- monthBounds,
- dataComponents,
- onSaveCategory,
- onSaveGroup,
- onDeleteCategory,
- onDeleteGroup,
- } = this.props;
- const { editing, draggingState } = this.state;
+ const collapseAllCategories = () => {
+ onCollapse(categoryGroups.map(g => g.id));
+ };
- return (
+ return (
+
-
-
-
-
-
-
-
+
-
-
+
+
+
+
+
+
+
+
- (this.budgetDataNode = el)}
- >
-
-
+
-
-
-
- );
- }
+
+
+
+
+ );
}
-const mapStateToProps = state => {
- const { grouped: categoryGroups } = state.queries.categories;
- const collapsed = state.prefs.local?.['budget.collapsed'] || [];
- return {
- categoryGroups,
- collapsed,
- };
-};
-
-const mapDispatchToProps = dispatch => {
- const onCollapse = collapsedIds => {
- dispatch(savePrefs({ 'budget.collapsed': collapsedIds }));
- };
-
- const onToggleHiddenCategories = () =>
- dispatch((innerDispatch, getState) => {
- const { prefs } = getState();
- const showHiddenCategories = prefs.local['budget.showHiddenCategories'];
- innerDispatch(
- savePrefs({
- 'budget.showHiddenCategories': !showHiddenCategories,
- }),
- );
- });
- return {
- onCollapse,
- onToggleHiddenCategories,
- };
-};
-
-export const BudgetTable = connect(mapStateToProps, mapDispatchToProps, null, {
- forwardRef: true,
-})(BudgetTableInner);
+BudgetTable.displayName = 'BudgetTable';
diff --git a/packages/desktop-client/src/components/budget/DynamicBudgetTable.tsx b/packages/desktop-client/src/components/budget/DynamicBudgetTable.tsx
index 1422a021c97..60e97052b8e 100644
--- a/packages/desktop-client/src/components/budget/DynamicBudgetTable.tsx
+++ b/packages/desktop-client/src/components/budget/DynamicBudgetTable.tsx
@@ -1,8 +1,7 @@
// @ts-strict-ignore
-import React, { forwardRef, useEffect, type ComponentProps } from 'react';
+import React, { useEffect, type ComponentProps } from 'react';
import AutoSizer from 'react-virtualized-auto-sizer';
-import { useActions } from '../../hooks/useActions';
import { View } from '../common/View';
import { useBudgetMonthCount } from './BudgetMonthCountContext';
@@ -30,90 +29,72 @@ function getNumPossibleMonths(width: number) {
type DynamicBudgetTableInnerProps = {
width: number;
height: number;
-} & ComponentProps;
+} & DynamicBudgetTableProps;
-const DynamicBudgetTableInner = forwardRef<
- typeof BudgetTable,
- DynamicBudgetTableInnerProps
->(
- (
- {
- width,
- height,
- prewarmStartMonth,
- startMonth,
- maxMonths = 3,
- monthBounds,
- onMonthSelect: onMonthSelect_,
- onPreload,
- ...props
- },
- ref,
- ) => {
- const { setDisplayMax } = useBudgetMonthCount();
- const actions = useActions();
+const DynamicBudgetTableInner = ({
+ width,
+ height,
+ prewarmStartMonth,
+ startMonth,
+ maxMonths = 3,
+ monthBounds,
+ onMonthSelect,
+ ...props
+}: DynamicBudgetTableInnerProps) => {
+ const { setDisplayMax } = useBudgetMonthCount();
- const numPossible = getNumPossibleMonths(width);
- const numMonths = Math.min(numPossible, maxMonths);
- const maxWidth = 200 + 500 * numMonths;
+ const numPossible = getNumPossibleMonths(width);
+ const numMonths = Math.min(numPossible, maxMonths);
+ const maxWidth = 200 + 500 * numMonths;
- useEffect(() => {
- setDisplayMax(numPossible);
- }, [numPossible]);
+ useEffect(() => {
+ setDisplayMax(numPossible);
+ }, [numPossible]);
- function onMonthSelect(month) {
- onMonthSelect_(month, numMonths);
- }
+ function _onMonthSelect(month) {
+ onMonthSelect(month, numMonths);
+ }
- return (
-
-
-
-
-
+ return (
+
+
+
+
- );
- },
-);
+
+ );
+};
DynamicBudgetTableInner.displayName = 'DynamicBudgetTableInner';
-export const DynamicBudgetTable = forwardRef<
- typeof BudgetTable,
- DynamicBudgetTableInnerProps
->((props, ref) => {
+type DynamicBudgetTableProps = ComponentProps;
+
+export const DynamicBudgetTable = (props: DynamicBudgetTableProps) => {
return (
{({ width, height }) => (
-
+
)}
);
-});
+};
DynamicBudgetTable.displayName = 'DynamicBudgetTable';
diff --git a/packages/desktop-client/src/components/budget/index.tsx b/packages/desktop-client/src/components/budget/index.tsx
index ed36d976b25..2d339b755ad 100644
--- a/packages/desktop-client/src/components/budget/index.tsx
+++ b/packages/desktop-client/src/components/budget/index.tsx
@@ -1,12 +1,5 @@
// @ts-strict-ignore
-import React, {
- memo,
- useContext,
- useMemo,
- useState,
- useEffect,
- useRef,
-} from 'react';
+import React, { memo, useContext, useMemo, useState, useEffect } from 'react';
import { useDispatch } from 'react-redux';
import {
@@ -69,16 +62,15 @@ type RolloverComponents = {
IncomeHeaderComponent: typeof rollover.IncomeHeaderMonth;
};
-type BudgetProps = {
+type BudgetInnerProps = {
accountId?: string;
reportComponents: ReportComponents;
rolloverComponents: RolloverComponents;
titlebar: TitlebarContextValue;
};
-function BudgetInner(props: BudgetProps) {
+function BudgetInner(props: BudgetInnerProps) {
const currentMonth = monthUtils.currentMonth();
- const tableRef = useRef(null);
const spreadsheet = useSpreadsheet();
const dispatch = useDispatch();
const navigate = useNavigate();
@@ -138,17 +130,6 @@ function BudgetInner(props: BudgetProps) {
}),
listen('undo-event', ({ tables }) => {
- if (tableRef.current) {
- // g dammit
- // We need to clear the editing cell, otherwise when
- // the user navigates away from the page they will
- // accidentally clear the undo stack if they have pressed
- // undo, since the cell will save itself on blur (worst case:
- // undo takes you to another screen and then you can't redo
- // any of the budget changes)
- tableRef.current.clearEditing();
- }
-
if (tables.includes('categories')) {
loadCategories();
}
@@ -377,7 +358,6 @@ function BudgetInner(props: BudgetProps) {
onToggleSummaryCollapse={onToggleCollapse}
>