forked from actualbudget/actual
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Break apart budget/misc.js (actualbudget#1584)
* Break apart budget/misc.js * Release notes * Rename release notes * UI bolder "No server" text
- Loading branch information
1 parent
256d368
commit 5a58b30
Showing
22 changed files
with
1,496 additions
and
1,375 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Binary file modified
BIN
+7 Bytes
(100%)
...tion-available-funds-overspent-budgeted-and-for-next-month-1-chromium-linux.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified
BIN
+7 Bytes
(100%)
...est.js-snapshots/Budget-transfer-funds-to-another-category-1-chromium-linux.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
316 changes: 316 additions & 0 deletions
316
packages/desktop-client/src/components/budget/BudgetCategories.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,316 @@ | ||
import React, { memo, useState, useMemo } from 'react'; | ||
|
||
import { colors } from '../../style'; | ||
import View from '../common/View'; | ||
import { DropHighlightPosContext } from '../sort'; | ||
import { Row } from '../table'; | ||
|
||
import { INCOME_HEADER_HEIGHT, MONTH_BOX_SHADOW } from './constants'; | ||
import ExpenseCategory from './ExpenseCategory'; | ||
import ExpenseGroup from './ExpenseGroup'; | ||
import IncomeCategory from './IncomeCategory'; | ||
import IncomeGroup from './IncomeGroup'; | ||
import IncomeHeader from './IncomeHeader'; | ||
import SidebarCategory from './SidebarCategory'; | ||
import SidebarGroup from './SidebarGroup'; | ||
import { separateGroups } from './util'; | ||
|
||
const BudgetCategories = memo( | ||
({ | ||
categoryGroups, | ||
newCategoryForGroup, | ||
showHiddenCategories, | ||
isAddingGroup, | ||
editingCell, | ||
collapsed, | ||
setCollapsed, | ||
dataComponents, | ||
onBudgetAction, | ||
onShowActivity, | ||
onEditName, | ||
onEditMonth, | ||
onSaveCategory, | ||
onSaveGroup, | ||
onDeleteCategory, | ||
onDeleteGroup, | ||
onReorderCategory, | ||
onReorderGroup, | ||
onShowNewCategory, | ||
onHideNewCategory, | ||
onShowNewGroup, | ||
onHideNewGroup, | ||
}) => { | ||
let items = useMemo(() => { | ||
let [expenseGroups, incomeGroup] = separateGroups(categoryGroups); | ||
|
||
let items = Array.prototype.concat.apply( | ||
[], | ||
expenseGroups.map(group => { | ||
if (group.hidden && !showHiddenCategories) { | ||
return []; | ||
} | ||
|
||
const groupCategories = group.categories.filter( | ||
cat => showHiddenCategories || !cat.hidden, | ||
); | ||
|
||
let items = [{ type: 'expense-group', value: { ...group } }]; | ||
|
||
if (newCategoryForGroup === group.id) { | ||
items.push({ type: 'new-category' }); | ||
} | ||
|
||
return [ | ||
...items, | ||
...(collapsed.includes(group.id) ? [] : groupCategories).map( | ||
cat => ({ | ||
type: 'expense-category', | ||
value: cat, | ||
}), | ||
), | ||
]; | ||
}), | ||
); | ||
|
||
if (isAddingGroup) { | ||
items.push({ type: 'new-group' }); | ||
} | ||
|
||
if (incomeGroup) { | ||
items = items.concat( | ||
[ | ||
{ type: 'income-separator' }, | ||
{ type: 'income-group', value: incomeGroup }, | ||
newCategoryForGroup === incomeGroup.id && { type: 'new-category' }, | ||
...(collapsed.includes(incomeGroup.id) | ||
? [] | ||
: incomeGroup.categories.filter( | ||
cat => showHiddenCategories || !cat.hidden, | ||
) | ||
).map(cat => ({ | ||
type: 'income-category', | ||
value: cat, | ||
})), | ||
].filter(x => x), | ||
); | ||
} | ||
|
||
return items; | ||
}, [ | ||
categoryGroups, | ||
collapsed, | ||
newCategoryForGroup, | ||
isAddingGroup, | ||
showHiddenCategories, | ||
]); | ||
|
||
let [dragState, setDragState] = useState(null); | ||
let [savedCollapsed, setSavedCollapsed] = useState(null); | ||
|
||
// TODO: If we turn this into a reducer, we could probably memoize | ||
// each item in the list for better perf | ||
function onDragChange(newDragState) { | ||
let { state } = newDragState; | ||
|
||
if (state === 'start-preview') { | ||
setDragState({ | ||
type: newDragState.type, | ||
item: newDragState.item, | ||
preview: true, | ||
}); | ||
} else if (state === 'start') { | ||
if (dragState) { | ||
setDragState({ | ||
...dragState, | ||
preview: false, | ||
}); | ||
setSavedCollapsed(collapsed); | ||
} | ||
} else if (state === 'hover') { | ||
setDragState({ | ||
...dragState, | ||
hoveredId: newDragState.id, | ||
hoveredPos: newDragState.pos, | ||
}); | ||
} else if (state === 'end') { | ||
setDragState(null); | ||
setCollapsed(savedCollapsed || []); | ||
} | ||
} | ||
|
||
function onToggleCollapse(id) { | ||
if (collapsed.includes(id)) { | ||
setCollapsed(collapsed.filter(id_ => id_ !== id)); | ||
} else { | ||
setCollapsed([...collapsed, id]); | ||
} | ||
} | ||
|
||
return ( | ||
<View | ||
style={{ | ||
marginBottom: 10, | ||
backgroundColor: 'white', | ||
overflow: 'hidden', | ||
boxShadow: MONTH_BOX_SHADOW, | ||
borderRadius: '0 0 4px 4px', | ||
flex: 1, | ||
}} | ||
> | ||
{items.map((item, idx) => { | ||
let content; | ||
switch (item.type) { | ||
case 'new-group': | ||
content = ( | ||
<Row style={{ backgroundColor: colors.n11 }}> | ||
<SidebarGroup | ||
group={{ id: 'new', name: '' }} | ||
editing={true} | ||
onSave={onSaveGroup} | ||
onHideNewGroup={onHideNewGroup} | ||
onEdit={onEditName} | ||
/> | ||
</Row> | ||
); | ||
break; | ||
case 'new-category': | ||
content = ( | ||
<Row> | ||
<SidebarCategory | ||
category={{ | ||
name: '', | ||
cat_group: newCategoryForGroup, | ||
is_income: | ||
newCategoryForGroup === | ||
categoryGroups.find(g => g.is_income).id, | ||
id: 'new', | ||
}} | ||
editing={true} | ||
onSave={onSaveCategory} | ||
onHideNewCategory={onHideNewCategory} | ||
onEditName={onEditName} | ||
/> | ||
</Row> | ||
); | ||
break; | ||
|
||
case 'expense-group': | ||
content = ( | ||
<ExpenseGroup | ||
group={item.value} | ||
editingCell={editingCell} | ||
collapsed={collapsed.includes(item.value.id)} | ||
MonthComponent={dataComponents.ExpenseGroupComponent} | ||
dragState={dragState} | ||
onEditName={onEditName} | ||
onSave={onSaveGroup} | ||
onDelete={onDeleteGroup} | ||
onDragChange={onDragChange} | ||
onReorderGroup={onReorderGroup} | ||
onReorderCategory={onReorderCategory} | ||
onToggleCollapse={onToggleCollapse} | ||
onShowNewCategory={onShowNewCategory} | ||
/> | ||
); | ||
break; | ||
case 'expense-category': | ||
content = ( | ||
<ExpenseCategory | ||
cat={item.value} | ||
editingCell={editingCell} | ||
MonthComponent={dataComponents.ExpenseCategoryComponent} | ||
dragState={dragState} | ||
onEditName={onEditName} | ||
onEditMonth={onEditMonth} | ||
onSave={onSaveCategory} | ||
onDelete={onDeleteCategory} | ||
onDragChange={onDragChange} | ||
onReorder={onReorderCategory} | ||
onBudgetAction={onBudgetAction} | ||
onShowActivity={onShowActivity} | ||
/> | ||
); | ||
break; | ||
case 'income-separator': | ||
content = ( | ||
<View | ||
style={{ | ||
height: INCOME_HEADER_HEIGHT, | ||
backgroundColor: 'white', | ||
}} | ||
> | ||
<IncomeHeader | ||
MonthComponent={dataComponents.IncomeHeaderComponent} | ||
onShowNewGroup={onShowNewGroup} | ||
/> | ||
</View> | ||
); | ||
break; | ||
case 'income-group': | ||
content = ( | ||
<IncomeGroup | ||
group={item.value} | ||
editingCell={editingCell} | ||
MonthComponent={dataComponents.IncomeGroupComponent} | ||
collapsed={collapsed.includes(item.value.id)} | ||
onEditName={onEditName} | ||
onSave={onSaveGroup} | ||
onToggleCollapse={onToggleCollapse} | ||
onShowNewCategory={onShowNewCategory} | ||
/> | ||
); | ||
break; | ||
case 'income-category': | ||
content = ( | ||
<IncomeCategory | ||
cat={item.value} | ||
editingCell={editingCell} | ||
isLast={idx === items.length - 1} | ||
MonthComponent={dataComponents.IncomeCategoryComponent} | ||
onEditName={onEditName} | ||
onEditMonth={onEditMonth} | ||
onSave={onSaveCategory} | ||
onDelete={onDeleteCategory} | ||
onDragChange={onDragChange} | ||
onReorder={onReorderCategory} | ||
onBudgetAction={onBudgetAction} | ||
onShowActivity={onShowActivity} | ||
/> | ||
); | ||
break; | ||
default: | ||
throw new Error('Unknown item type: ' + item.type); | ||
} | ||
|
||
let pos = | ||
idx === 0 ? 'first' : idx === items.length - 1 ? 'last' : null; | ||
|
||
return ( | ||
<DropHighlightPosContext.Provider | ||
key={ | ||
item.value | ||
? item.value.id | ||
: item.type === 'income-separator' | ||
? 'separator' | ||
: idx | ||
} | ||
value={pos} | ||
> | ||
<View | ||
style={ | ||
!dragState && { | ||
':hover': { backgroundColor: '#fcfcfc' }, | ||
} | ||
} | ||
> | ||
{content} | ||
</View> | ||
</DropHighlightPosContext.Provider> | ||
); | ||
})} | ||
</View> | ||
); | ||
}, | ||
); | ||
|
||
export default BudgetCategories; |
40 changes: 40 additions & 0 deletions
40
packages/desktop-client/src/components/budget/BudgetPageHeader.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
import React, { memo } from 'react'; | ||
|
||
import * as monthUtils from 'loot-core/src/shared/months'; | ||
|
||
import View from '../common/View'; | ||
|
||
import { MonthPicker } from './MonthPicker'; | ||
import { getScrollbarWidth } from './util'; | ||
|
||
const BudgetPageHeader = memo( | ||
({ startMonth, onMonthSelect, numMonths, monthBounds, style }) => { | ||
function getValidMonth(month) { | ||
let start = monthBounds.start; | ||
let end = monthUtils.subMonths(monthBounds.end, numMonths - 1); | ||
|
||
if (month < start) { | ||
return start; | ||
} else if (month > end) { | ||
return end; | ||
} | ||
return month; | ||
} | ||
|
||
return ( | ||
<View style={{ marginLeft: 200 + 5, flexShrink: 0 }}> | ||
<View style={{ marginRight: 5 + getScrollbarWidth() }}> | ||
<MonthPicker | ||
startMonth={startMonth} | ||
numDisplayed={numMonths} | ||
monthBounds={monthBounds} | ||
style={{ paddingTop: 5 }} | ||
onSelect={month => onMonthSelect(getValidMonth(month))} | ||
/> | ||
</View> | ||
</View> | ||
); | ||
}, | ||
); | ||
|
||
export default BudgetPageHeader; |
Oops, something went wrong.