diff --git a/packages/desktop-client/e2e/accounts.test.js-snapshots/Accounts-closes-an-account-1-chromium-linux.png b/packages/desktop-client/e2e/accounts.test.js-snapshots/Accounts-closes-an-account-1-chromium-linux.png index a2e2fc3f1b967b26480683520384b0c83aa98999..db20f97c9684d79de15ae77daf85cae9ff409547 100644 Binary files a/packages/desktop-client/e2e/accounts.test.js-snapshots/Accounts-closes-an-account-1-chromium-linux.png and b/packages/desktop-client/e2e/accounts.test.js-snapshots/Accounts-closes-an-account-1-chromium-linux.png differ diff --git a/packages/desktop-client/e2e/accounts.test.js-snapshots/Accounts-closes-an-account-2-chromium-linux.png b/packages/desktop-client/e2e/accounts.test.js-snapshots/Accounts-closes-an-account-2-chromium-linux.png index 6240ae33bc16d6a11b29a30885b9f498615b3459..dd98f0c1392566992385d5ef250c5c1a8cb355ec 100644 Binary files a/packages/desktop-client/e2e/accounts.test.js-snapshots/Accounts-closes-an-account-2-chromium-linux.png and b/packages/desktop-client/e2e/accounts.test.js-snapshots/Accounts-closes-an-account-2-chromium-linux.png differ diff --git a/packages/desktop-client/e2e/mobile.test.js-snapshots/Mobile-loads-the-budget-page-with-budgeted-amounts-1-chromium-linux.png b/packages/desktop-client/e2e/mobile.test.js-snapshots/Mobile-loads-the-budget-page-with-budgeted-amounts-1-chromium-linux.png index 343c6cedfb66da0ce8a28f49a2b50bbea2399539..58229d4ae3e4a0aa362e4882381a67302bf34f95 100644 Binary files a/packages/desktop-client/e2e/mobile.test.js-snapshots/Mobile-loads-the-budget-page-with-budgeted-amounts-1-chromium-linux.png and b/packages/desktop-client/e2e/mobile.test.js-snapshots/Mobile-loads-the-budget-page-with-budgeted-amounts-1-chromium-linux.png differ diff --git a/packages/desktop-client/e2e/mobile.test.js-snapshots/Mobile-loads-the-budget-page-with-budgeted-amounts-2-chromium-linux.png b/packages/desktop-client/e2e/mobile.test.js-snapshots/Mobile-loads-the-budget-page-with-budgeted-amounts-2-chromium-linux.png index dd9aab12b5f932ae5f6e467aca50e551088c1a3f..278d72136022b255b899859161b5f9763f4d3731 100644 Binary files a/packages/desktop-client/e2e/mobile.test.js-snapshots/Mobile-loads-the-budget-page-with-budgeted-amounts-2-chromium-linux.png and b/packages/desktop-client/e2e/mobile.test.js-snapshots/Mobile-loads-the-budget-page-with-budgeted-amounts-2-chromium-linux.png differ diff --git a/packages/desktop-client/src/components/Modals.tsx b/packages/desktop-client/src/components/Modals.tsx index bdd7cc587c733b56ef18348f80ef283428469e16..2953f58e4a03cf21dba2ead2a7e161c9623dace5 100644 --- a/packages/desktop-client/src/components/Modals.tsx +++ b/packages/desktop-client/src/components/Modals.tsx @@ -11,8 +11,10 @@ import * as monthUtils from 'loot-core/src/shared/months'; import { useActions } from '../hooks/useActions'; import { useSyncServerStatus } from '../hooks/useSyncServerStatus'; +import { ModalTitle } from './common/Modal'; import { AccountAutocompleteModal } from './modals/AccountAutocompleteModal'; import { AccountMenuModal } from './modals/AccountMenuModal'; +import { BudgetMenuModal } from './modals/BudgetMenuModal'; import { CategoryAutocompleteModal } from './modals/CategoryAutocompleteModal'; import { CategoryGroupMenuModal } from './modals/CategoryGroupMenuModal'; import { CategoryMenuModal } from './modals/CategoryMenuModal'; @@ -46,7 +48,7 @@ import { ScheduledTransactionMenuModal } from './modals/ScheduledTransactionMenu import { SelectLinkedAccounts } from './modals/SelectLinkedAccounts'; import { SimpleFinInitialise } from './modals/SimpleFinInitialise'; import { SingleInputModal } from './modals/SingleInputModal'; -import { SwitchBudgetType } from './modals/SwitchBudgetType'; +import { SwitchBudgetTypeModal } from './modals/SwitchBudgetTypeModal'; import { TransferModal } from './modals/TransferModal'; import { DiscoverSchedules } from './schedules/DiscoverSchedules'; import { PostsOfflineNotification } from './schedules/PostsOfflineNotification'; @@ -313,7 +315,7 @@ export function Modals() { return ( <SingleInputModal modalProps={modalProps} - title="New Category" + title={<ModalTitle title="New Category" shrinkOnOverflow />} inputPlaceholder="Category name" buttonText="Add" onValidate={options.onValidate} @@ -325,7 +327,7 @@ export function Modals() { return ( <SingleInputModal modalProps={modalProps} - title="New Category Group" + title={<ModalTitle title="New Category Group" shrinkOnOverflow />} inputPlaceholder="Category group name" buttonText="Add" onValidate={options.onValidate} @@ -400,10 +402,10 @@ export function Modals() { case 'switch-budget-type': return ( - <SwitchBudgetType + <SwitchBudgetTypeModal key={name} modalProps={modalProps} - onSwitch={options?.onSwitch} + onSwitch={options.onSwitch} /> ); @@ -549,6 +551,21 @@ export function Modals() { /> ); + case 'budget-menu': + return ( + <NamespaceContext.Provider + key={name} + value={monthUtils.sheetForMonth(options.month)} + > + <BudgetMenuModal + modalProps={modalProps} + month={options.month} + onToggleHiddenCategories={options.onToggleHiddenCategories} + onSwitchBudgetType={options.onSwitchBudgetType} + /> + </NamespaceContext.Provider> + ); + default: console.error('Unknown modal:', name); return null; diff --git a/packages/desktop-client/src/components/common/Menu.tsx b/packages/desktop-client/src/components/common/Menu.tsx index 38d0b9a9c3a106cf9b2dfd39557ea997974a7451..105f703aefdb59c162e95f900ff5a7b2120d12c9 100644 --- a/packages/desktop-client/src/components/common/Menu.tsx +++ b/packages/desktop-client/src/components/common/Menu.tsx @@ -144,7 +144,6 @@ export function Menu<T extends MenuItem>({ ); } - const lastItem = items[idx - 1]; const Icon = item.icon; return ( @@ -153,14 +152,9 @@ export function Menu<T extends MenuItem>({ key={item.name} style={{ cursor: 'default', - padding: '9px 10px', - marginTop: - idx === 0 || - lastItem === Menu.line || - lastItem.type === Menu.label - ? 0 - : -3, + padding: 10, flexDirection: 'row', + justifyContent: 'center', alignItems: 'center', color: theme.menuItemText, ...(item.disabled && { color: theme.buttonBareDisabledText }), diff --git a/packages/desktop-client/src/components/common/Modal.tsx b/packages/desktop-client/src/components/common/Modal.tsx index 9c07af48ff0f988864199c645326372c73309ae2..8435bdbedb47ac361f7b62b761a9dac461182d92 100644 --- a/packages/desktop-client/src/components/common/Modal.tsx +++ b/packages/desktop-client/src/components/common/Modal.tsx @@ -10,7 +10,9 @@ import { useHotkeysContext } from 'react-hotkeys-hook'; import ReactModal from 'react-modal'; import { AnimatedLoading } from '../../icons/AnimatedLoading'; +import { SvgLogo } from '../../icons/logo'; import { SvgDelete } from '../../icons/v0'; +import { useResponsive } from '../../ResponsiveProvider'; import { type CSSProperties, styles, theme } from '../../style'; import { tokens } from '../../tokens'; @@ -19,21 +21,16 @@ import { Input } from './Input'; import { Text } from './Text'; import { View } from './View'; -type ModalChildrenProps = { - isEditingTitle: boolean; -}; - export type ModalProps = { - title?: string; + title?: ReactNode; isCurrent?: boolean; isHidden?: boolean; - children: ReactNode | ((props: ModalChildrenProps) => ReactNode); + children: ReactNode | (() => ReactNode); size?: { width?: CSSProperties['width']; height?: CSSProperties['height'] }; padding?: CSSProperties['padding']; showHeader?: boolean; leftHeaderContent?: ReactNode; showTitle?: boolean; - editableTitle?: boolean; showClose?: boolean; showOverlay?: boolean; loading?: boolean; @@ -42,11 +39,9 @@ export type ModalProps = { stackIndex?: number; parent?: HTMLElement; style?: CSSProperties; - titleStyle?: CSSProperties; contentStyle?: CSSProperties; overlayStyle?: CSSProperties; onClose?: () => void; - onTitleUpdate?: (title: string) => void; }; export const Modal = ({ @@ -58,7 +53,6 @@ export const Modal = ({ showHeader = true, leftHeaderContent, showTitle = true, - editableTitle = false, showClose = true, showOverlay = true, loading = false, @@ -67,13 +61,12 @@ export const Modal = ({ stackIndex, parent, style, - titleStyle, contentStyle, overlayStyle, children, onClose, - onTitleUpdate, }: ModalProps) => { + const { isNarrowWidth } = useResponsive(); const { enableScope, disableScope } = useHotkeysContext(); // This deactivates any key handlers in the "app" scope @@ -83,20 +76,6 @@ export const Modal = ({ return () => disableScope(scopeId); }, [enableScope, disableScope, scopeId]); - const [isEditingTitle, setIsEditingTitle] = useState(false); - const [_title, setTitle] = useState(title); - - const onTitleClick = () => { - setIsEditingTitle(true); - }; - - const _onTitleUpdate = newTitle => { - if (newTitle !== title) { - onTitleUpdate?.(newTitle); - } - setIsEditingTitle(false); - }; - return ( <ReactModal isOpen={true} @@ -171,38 +150,27 @@ export const Modal = ({ {showHeader && ( <View style={{ + justifyContent: 'center', + alignItems: 'center', padding: 20, position: 'relative', - flexShrink: 0, + height: 80, }} > <View style={{ position: 'absolute', left: 0, - top: 0, - bottom: 0, - justifyContent: 'center', - alignItems: 'center', + marginRight: !isNarrowWidth ? 5 : undefined, + marginLeft: !isNarrowWidth ? 15 : undefined, }} > - <View - style={{ - flexDirection: 'row', - marginLeft: 15, - }} - > - {leftHeaderContent && !isEditingTitle - ? leftHeaderContent - : null} - </View> + {leftHeaderContent} </View> {showTitle && ( <View style={{ - flex: 1, - alignSelf: 'center', textAlign: 'center', // We need to force a width for the text-overflow // ellipses to work because we are aligning center. @@ -210,37 +178,16 @@ export const Modal = ({ width: 'calc(100% - 40px)', }} > - {isEditingTitle ? ( - <Input - style={{ - fontSize: 25, - fontWeight: 700, - textAlign: 'center', - }} - value={_title} - onChange={e => setTitle(e.target.value)} - onKeyDown={e => { - if (e.key === 'Enter') { - e.preventDefault(); - _onTitleUpdate(e.currentTarget.value); - } - }} - onBlur={e => _onTitleUpdate(e.target.value)} + {!title ? ( + <SvgLogo + width={30} + height={30} + style={{ justifyContent: 'center', alignSelf: 'center' }} /> + ) : typeof title === 'string' || typeof title === 'number' ? ( + <ModalTitle title={`${title}`} /> ) : ( - <Text - style={{ - fontSize: 25, - fontWeight: 700, - whiteSpace: 'nowrap', - overflow: 'hidden', - textOverflow: 'ellipsis', - ...titleStyle, - }} - {...(editableTitle && { onPointerUp: onTitleClick })} - > - {_title} - </Text> + title )} </View> )} @@ -249,36 +196,25 @@ export const Modal = ({ style={{ position: 'absolute', right: 0, - top: 0, - bottom: 0, - justifyContent: 'center', - alignItems: 'center', + marginRight: !isNarrowWidth ? 15 : undefined, + marginLeft: !isNarrowWidth ? 5 : undefined, }} > - <View - style={{ - flexDirection: 'row', - marginRight: 15, - }} - > - {showClose && !isEditingTitle && ( - <Button - type="bare" - onClick={onClose} - style={{ padding: '10px 10px' }} - aria-label="Close" - > - <SvgDelete width={10} style={{ color: 'inherit' }} /> - </Button> - )} - </View> + {showClose && ( + <Button + type="bare" + onClick={onClose} + style={{ padding: 10 }} + aria-label="Close" + > + <SvgDelete width={10} style={{ color: 'inherit' }} /> + </Button> + )} </View> </View> )} <View style={{ padding, paddingTop: 0, flex: 1 }}> - {typeof children === 'function' - ? children({ isEditingTitle }) - : children} + {typeof children === 'function' ? children() : children} </View> {loading && ( <View @@ -427,3 +363,102 @@ export const ModalButtons = ({ </View> ); }; + +type ModalTitleProps = { + title: string; + isEditable?: boolean; + getStyle?: (isEditing: boolean) => CSSProperties; + onEdit?: (isEditing: boolean) => void; + onTitleUpdate?: (newName: string) => void; + shrinkOnOverflow?: boolean; +}; + +export function ModalTitle({ + title, + isEditable, + getStyle, + onTitleUpdate, + shrinkOnOverflow = false, +}: ModalTitleProps) { + const [isEditing, setIsEditing] = useState(false); + const style = getStyle?.(isEditing); + + const _onEdit = () => { + if (!isEditable) { + return; + } + + setIsEditing(true); + }; + + const _onTitleUpdate = newTitle => { + if (newTitle !== title) { + onTitleUpdate?.(newTitle); + } + setIsEditing(false); + }; + + const inputRef = useRef<HTMLInputElement>(); + useEffect(() => { + if (isEditing) { + if (inputRef.current) { + inputRef.current.scrollLeft = 0; + } + } + }, [isEditing]); + + // Dynamic font size to avoid ellipsis. + const textRef = useRef<HTMLSpanElement>(); + const [textFontSize, setTextFontSize] = useState(25); + useEffect(() => { + if (shrinkOnOverflow) { + const containerWidth = textRef.current.offsetWidth; + const textWidth = textRef.current.scrollWidth; + + if (textWidth > containerWidth) { + const newFontSize = Math.floor( + (containerWidth / textWidth) * textFontSize, + ); + setTextFontSize(newFontSize); + } + } + }, [textFontSize, shrinkOnOverflow]); + + return isEditing ? ( + <Input + inputRef={inputRef} + style={{ + fontSize: 25, + fontWeight: 700, + textAlign: 'center', + ...style, + }} + focused={isEditing} + defaultValue={title} + onUpdate={_onTitleUpdate} + onKeyDown={e => { + if (e.key === 'Enter') { + e.preventDefault(); + _onTitleUpdate?.(e.currentTarget.value); + } + }} + /> + ) : ( + <Text + innerRef={textRef} + style={{ + fontSize: textFontSize, + fontWeight: 700, + textAlign: 'center', + whiteSpace: 'nowrap', + overflow: 'hidden', + textOverflow: 'ellipsis', + ...(isEditable && styles.underlinedText), + ...style, + }} + onClick={_onEdit} + > + {title} + </Text> + ); +} diff --git a/packages/desktop-client/src/components/mobile/budget/BudgetTable.jsx b/packages/desktop-client/src/components/mobile/budget/BudgetTable.jsx index e004f887e4bcf90ebe04d365bed1adb28643501c..9e1f43d21bf08e0890a66d11243e7dc977a5b487 100644 --- a/packages/desktop-client/src/components/mobile/budget/BudgetTable.jsx +++ b/packages/desktop-client/src/components/mobile/budget/BudgetTable.jsx @@ -7,17 +7,13 @@ import { collapseModals, pushModal } from 'loot-core/client/actions'; import { rolloverBudget, reportBudget } from 'loot-core/src/client/queries'; import * as monthUtils from 'loot-core/src/shared/months'; -import { useFeatureFlag } from '../../../hooks/useFeatureFlag'; import { useLocalPref } from '../../../hooks/useLocalPref'; import { SingleActiveEditFormProvider, useSingleActiveEditForm, } from '../../../hooks/useSingleActiveEditForm'; -import { - SvgArrowThinLeft, - SvgArrowThinRight, - SvgDotsHorizontalTriple, -} from '../../../icons/v1'; +import { SvgLogo } from '../../../icons/logo'; +import { SvgAdd, SvgArrowThinLeft, SvgArrowThinRight } from '../../../icons/v1'; import { useResponsive } from '../../../ResponsiveProvider'; import { theme, styles } from '../../../style'; import { BalanceWithCarryover } from '../../budget/BalanceWithCarryover'; @@ -25,14 +21,12 @@ import { makeAmountGrey } from '../../budget/util'; import { Button } from '../../common/Button'; import { Card } from '../../common/Card'; import { Label } from '../../common/Label'; -import { Menu } from '../../common/Menu'; import { Text } from '../../common/Text'; import { View } from '../../common/View'; import { Page } from '../../Page'; import { CellValue } from '../../spreadsheet/CellValue'; import { useFormat } from '../../spreadsheet/useFormat'; import { useSheetValue } from '../../spreadsheet/useSheetValue'; -import { Tooltip, useTooltip } from '../../tooltips'; import { AmountInput } from '../../util/AmountInput'; import { MOBILE_NAV_HEIGHT } from '../MobileNavTabs'; import { PullToRefresh } from '../PullToRefresh'; @@ -1033,7 +1027,6 @@ function BudgetGroups({ onSaveCategory, onDeleteCategory, onAddCategory, - onAddGroup, onReorderCategory, onReorderGroup, onBudgetAction, @@ -1082,17 +1075,6 @@ function BudgetGroups({ ); })} - <View - style={{ - alignItems: 'flex-start', - justifyContent: 'flex-start', - }} - > - <Button onClick={onAddGroup} style={{ fontSize: 12, margin: 10 }}> - Add Group - </Button> - </View> - {incomeGroup && ( <IncomeGroup type={type} @@ -1118,7 +1100,7 @@ export function BudgetTable({ categoryGroups, month, monthBounds, - editMode, + // editMode, // refreshControl, onPrevMonth, onNextMonth, @@ -1128,30 +1110,28 @@ export function BudgetTable({ onAddCategory, onSaveCategory, onDeleteCategory, - onEditMode, onReorderCategory, onReorderGroup, onShowBudgetSummary, - onOpenMonthActionMenu, onBudgetAction, onRefresh, - onSwitchBudgetType, onEditGroup, onEditCategory, + onOpenBudgetActionMenu, }) { const { width } = useResponsive(); const show3Cols = width >= 360; // let editMode = false; // neuter editMode -- sorry, not rewriting drag-n-drop right now const format = useFormat(); - const dispatch = useDispatch(); const [showSpentColumn = false, setShowSpentColumnPref] = useLocalPref( 'mobile.showSpentColumn', ); - const [showHiddenCategories = false, setShowHiddenCategoriesPref] = - useLocalPref('budget.showHiddenCategories'); + const [showHiddenCategories = false] = useLocalPref( + 'budget.showHiddenCategories', + ); function toggleDisplay() { setShowSpentColumnPref(!showSpentColumn); @@ -1163,16 +1143,9 @@ export function BudgetTable({ borderRadius: 'unset', }; - const _onSwitchBudgetType = () => { - dispatch( - pushModal('switch-budget-type', { - onSwitch: onSwitchBudgetType, - }), - ); - }; - - const onToggleHiddenCategories = () => { - setShowHiddenCategoriesPref(!showHiddenCategories); + const noBackgroundColorStyle = { + backgroundColor: 'transparent', + color: 'white', }; return ( @@ -1186,31 +1159,33 @@ export function BudgetTable({ onNextMonth={onNextMonth} /> } + headerLeftContent={ + <Button + type="bare" + style={{ + color: theme.mobileHeaderText, + margin: 10, + }} + hoveredStyle={noBackgroundColorStyle} + activeStyle={noBackgroundColorStyle} + onClick={() => onOpenBudgetActionMenu?.(month)} + > + <SvgLogo width="20" height="20" /> + </Button> + } headerRightContent={ - !editMode ? ( - <BudgetPageMenu - onEditMode={onEditMode} - onToggleHiddenCategories={onToggleHiddenCategories} - onSwitchBudgetType={_onSwitchBudgetType} - /> - ) : ( - <Button - type="bare" - hoveredStyle={{ - color: theme.mobileHeaderText, - background: theme.mobileHeaderTextHover, - }} - style={{ - ...styles.noTapHighlight, - ...styles.text, - backgroundColor: 'transparent', - color: theme.mobileHeaderText, - }} - onClick={() => onEditMode?.(false)} - > - Done - </Button> - ) + <Button + type="bare" + style={{ + color: theme.mobileHeaderText, + margin: 10, + }} + hoveredStyle={noBackgroundColorStyle} + activeStyle={noBackgroundColorStyle} + onClick={onAddGroup} + > + <SvgAdd width="20" height="20" /> + </Button> } style={{ flex: 1 }} > @@ -1347,154 +1322,35 @@ export function BudgetTable({ </View> </View> <PullToRefresh onRefresh={onRefresh}> - {!editMode ? ( - // <ScrollView - // ref={el => (this.list = el)} - // keyboardShouldPersistTaps="always" - // refreshControl={refreshControl} - // style={{ backgroundColor: colors.n10 }} - // automaticallyAdjustContentInsets={false} - // > - <View - data-testid="budget-table" - style={{ - paddingBottom: MOBILE_NAV_HEIGHT, - }} - > - <BudgetGroups - type={type} - categoryGroups={categoryGroups} - showBudgetedCol={!showSpentColumn} - show3Cols={show3Cols} - showHiddenCategories={showHiddenCategories} - // gestures={gestures} - month={month} - editMode={editMode} - onEditGroup={onEditGroup} - onEditCategory={onEditCategory} - onSaveCategory={onSaveCategory} - onDeleteCategory={onDeleteCategory} - onAddCategory={onAddCategory} - onAddGroup={onAddGroup} - onSaveGroup={onSaveGroup} - onDeleteGroup={onDeleteGroup} - onReorderCategory={onReorderCategory} - onReorderGroup={onReorderGroup} - onOpenMonthActionMenu={onOpenMonthActionMenu} - onBudgetAction={onBudgetAction} - /> - </View> - ) : ( - // </ScrollView> - // <DragDrop> - // {({ - // dragging, - // onGestureEvent, - // onHandlerStateChange, - // scrollRef, - // onScroll - // }) => ( - <View data-testid="budget-table"> - <BudgetGroups - type={type} - categoryGroups={categoryGroups} - showBudgetedCol={!showSpentColumn} - show3Cols={show3Cols} - showHiddenCategories={showHiddenCategories} - // gestures={gestures} - editMode={editMode} - onEditGroup={onEditGroup} - onEditCategory={onEditCategory} - onSaveCategory={onSaveCategory} - onDeleteCategory={onDeleteCategory} - onAddCategory={onAddCategory} - onAddGroup={onAddGroup} - onSaveGroup={onSaveGroup} - onDeleteGroup={onDeleteGroup} - onReorderCategory={onReorderCategory} - onReorderGroup={onReorderGroup} - onOpenMonthActionMenu={onOpenMonthActionMenu} - onBudgetAction={onBudgetAction} - /> - </View> - - // <DragDropHighlight /> - // </DragDrop> - )} - </PullToRefresh> - </Page> - ); -} - -function BudgetPageMenu({ - onEditMode, - onToggleHiddenCategories, - onSwitchBudgetType, -}) { - const tooltip = useTooltip(); - const isReportBudgetEnabled = useFeatureFlag('reportBudget'); - - const onMenuSelect = name => { - tooltip.close(); - switch (name) { - case 'edit-mode': - onEditMode?.(true); - break; - case 'toggle-hidden-categories': - onToggleHiddenCategories?.(); - break; - case 'switch-budget-type': - onSwitchBudgetType?.(); - break; - default: - throw new Error(`Unrecognized menu option: ${name}`); - } - }; - - return ( - <> - <Button - type="bare" - style={{ - ...styles.noTapHighlight, - }} - hoveredStyle={{ - color: theme.mobileHeaderText, - background: theme.mobileHeaderTextHover, - }} - {...tooltip.getOpenEvents()} - > - <SvgDotsHorizontalTriple - width="20" - height="20" - style={{ color: theme.mobileHeaderText }} - /> - </Button> - {tooltip.isOpen && ( - <Tooltip - position="bottom-right" - width={250} - style={{ padding: 0 }} - onClose={tooltip.close} + <View + data-testid="budget-table" + style={{ + paddingBottom: MOBILE_NAV_HEIGHT, + }} > - <Menu - onMenuSelect={onMenuSelect} - items={[ - // Removing for now until we work on mobile category drag and drop. - // { name: 'edit-mode', text: 'Edit mode' }, - { - name: 'toggle-hidden-categories', - text: 'Toggle hidden categories', - }, - isReportBudgetEnabled && { - name: 'switch-budget-type', - text: 'Switch budget type', - }, - ]} + <BudgetGroups + type={type} + categoryGroups={categoryGroups} + showBudgetedCol={!showSpentColumn} + show3Cols={show3Cols} + showHiddenCategories={showHiddenCategories} + month={month} + // gestures={gestures} + // editMode={editMode} + onEditGroup={onEditGroup} + onEditCategory={onEditCategory} + onSaveCategory={onSaveCategory} + onDeleteCategory={onDeleteCategory} + onAddCategory={onAddCategory} + onSaveGroup={onSaveGroup} + onDeleteGroup={onDeleteGroup} + onReorderCategory={onReorderCategory} + onReorderGroup={onReorderGroup} + onBudgetAction={onBudgetAction} /> - </Tooltip> - )} - </> + </View> + </PullToRefresh> + </Page> ); } diff --git a/packages/desktop-client/src/components/mobile/budget/index.tsx b/packages/desktop-client/src/components/mobile/budget/index.tsx index eb5e5a7fd377efc4fb5ec11c9422370e5098aafa..4374b7246332173b91e65772fa692585b330151e 100644 --- a/packages/desktop-client/src/components/mobile/budget/index.tsx +++ b/packages/desktop-client/src/components/mobile/budget/index.tsx @@ -53,7 +53,7 @@ function BudgetInner(props: BudgetInnerProps) { const [bounds, setBounds] = useState({ start: currMonth, end: currMonth }); const [currentMonth, setCurrentMonth] = useState(currMonth); const [initialized, setInitialized] = useState(false); - const [editMode, setEditMode] = useState(false); + // const [editMode, setEditMode] = useState(false); const [_numberFormat] = useLocalPref('numberFormat'); const numberFormat = _numberFormat || 'comma-dot'; @@ -353,6 +353,36 @@ function BudgetInner(props: BudgetInnerProps) { ); }; + const _onSwitchBudgetType = () => { + dispatch( + pushModal('switch-budget-type', { + onSwitch: () => { + onSwitchBudgetType?.(); + dispatch(collapseModals('budget-menu')); + }, + }), + ); + }; + + const [showHiddenCategories, setShowHiddenCategoriesPref] = useLocalPref( + 'budget.showHiddenCategories', + ); + + const onToggleHiddenCategories = () => { + setShowHiddenCategoriesPref(!showHiddenCategories); + dispatch(collapseModals('budget-menu')); + }; + + const onOpenBudgetActionMenu = month => { + dispatch( + pushModal('budget-menu', { + month, + onToggleHiddenCategories, + onSwitchBudgetType: _onSwitchBudgetType, + }), + ); + }; + if (!categoryGroups || !initialized) { return ( <View @@ -385,8 +415,7 @@ function BudgetInner(props: BudgetInnerProps) { type={budgetType} month={currentMonth} monthBounds={bounds} - editMode={editMode} - onEditMode={flag => setEditMode(flag)} + // editMode={editMode} onShowBudgetSummary={onShowBudgetSummary} onPrevMonth={onPrevMonth} onNextMonth={onNextMonth} @@ -398,12 +427,11 @@ function BudgetInner(props: BudgetInnerProps) { onDeleteCategory={onDeleteCategory} onReorderCategory={onReorderCategory} onReorderGroup={onReorderGroup} - onOpenMonthActionMenu={() => {}} //onOpenMonthActionMenu} onBudgetAction={onBudgetAction} onRefresh={onRefresh} - onSwitchBudgetType={onSwitchBudgetType} onEditGroup={onEditGroup} onEditCategory={onEditCategory} + onOpenBudgetActionMenu={onOpenBudgetActionMenu} /> )} </SyncRefresh> diff --git a/packages/desktop-client/src/components/modals/AccountMenuModal.tsx b/packages/desktop-client/src/components/modals/AccountMenuModal.tsx index c67599891926ce5b51d272c449ba6fc24e061087..33c456f873954c372473c641514afd018f4d0fcb 100644 --- a/packages/desktop-client/src/components/modals/AccountMenuModal.tsx +++ b/packages/desktop-client/src/components/modals/AccountMenuModal.tsx @@ -10,7 +10,7 @@ import { SvgNotesPaper } from '../../icons/v2'; import { type CSSProperties, styles, theme } from '../../style'; import { Button } from '../common/Button'; import { Menu } from '../common/Menu'; -import { Modal } from '../common/Modal'; +import { Modal, ModalTitle } from '../common/Modal'; import { View } from '../common/View'; import { type CommonModalProps } from '../Modals'; import { Notes } from '../Notes'; @@ -88,8 +88,9 @@ export function AccountMenuModal({ return ( <Modal - title={account.name} - titleStyle={styles.underlinedText} + title={ + <ModalTitle isEditable title={account.name} onTitleUpdate={onRename} /> + } showHeader focusAfterClose={false} {...modalProps} @@ -101,8 +102,6 @@ export function AccountMenuModal({ padding: '0 10px', borderRadius: '6px', }} - editableTitle={true} - onTitleUpdate={onRename} leftHeaderContent={ <AdditionalAccountMenu account={account} @@ -111,63 +110,51 @@ export function AccountMenuModal({ /> } > - {({ isEditingTitle }) => ( + <View + style={{ + flex: 1, + flexDirection: 'column', + }} + > <View style={{ + overflowY: 'auto', flex: 1, - flexDirection: 'column', }} > - <View - style={{ - overflowY: 'auto', - flex: 1, - }} - > - <Notes - notes={ - originalNotes && originalNotes.length > 0 - ? originalNotes - : 'No notes' - } - editable={false} - focused={false} - getStyle={() => ({ - borderRadius: 6, - ...((!originalNotes || originalNotes.length === 0) && { - justifySelf: 'center', - alignSelf: 'center', - color: theme.pageTextSubdued, - }), - })} - /> - </View> - <View - style={{ - flexDirection: 'row', - flexWrap: 'wrap', - justifyContent: 'space-between', - alignContent: 'space-between', - margin: '10px 0', - }} - > - <Button - style={{ - ...buttonStyle, - display: isEditingTitle ? 'none' : undefined, - }} - onClick={_onEditNotes} - > - <SvgNotesPaper - width={20} - height={20} - style={{ paddingRight: 5 }} - /> - Edit notes - </Button> - </View> + <Notes + notes={ + originalNotes && originalNotes.length > 0 + ? originalNotes + : 'No notes' + } + editable={false} + focused={false} + getStyle={() => ({ + borderRadius: 6, + ...((!originalNotes || originalNotes.length === 0) && { + justifySelf: 'center', + alignSelf: 'center', + color: theme.pageTextSubdued, + }), + })} + /> + </View> + <View + style={{ + flexDirection: 'row', + flexWrap: 'wrap', + justifyContent: 'space-between', + alignContent: 'space-between', + margin: '10px 0', + }} + > + <Button style={buttonStyle} onClick={_onEditNotes}> + <SvgNotesPaper width={20} height={20} style={{ paddingRight: 5 }} /> + Edit notes + </Button> </View> - )} + </View> </Modal> ); } diff --git a/packages/desktop-client/src/components/modals/BudgetMenuModal.tsx b/packages/desktop-client/src/components/modals/BudgetMenuModal.tsx new file mode 100644 index 0000000000000000000000000000000000000000..59a7d867c7e3b2fd5a5ab384da04b939ed236813 --- /dev/null +++ b/packages/desktop-client/src/components/modals/BudgetMenuModal.tsx @@ -0,0 +1,105 @@ +import React, { type ComponentPropsWithoutRef } from 'react'; + +import { useFeatureFlag } from '../../hooks/useFeatureFlag'; +import { type CSSProperties, theme, styles } from '../../style'; +import { Menu } from '../common/Menu'; +import { Modal } from '../common/Modal'; +import { type CommonModalProps } from '../Modals'; + +type BudgetMenuModalProps = ComponentPropsWithoutRef<typeof BudgetMenu> & { + modalProps: CommonModalProps; +}; + +export function BudgetMenuModal({ + modalProps, + month, + onToggleHiddenCategories, + onSwitchBudgetType, +}: BudgetMenuModalProps) { + const defaultMenuItemStyle: CSSProperties = { + ...styles.mobileMenuItem, + color: theme.menuItemText, + borderRadius: 0, + borderTop: `1px solid ${theme.pillBorder}`, + }; + + return ( + <Modal + showHeader + focusAfterClose={false} + {...modalProps} + padding={0} + style={{ + flex: 1, + padding: '0 10px', + paddingBottom: 10, + borderRadius: '6px', + }} + > + <BudgetMenu + getItemStyle={() => defaultMenuItemStyle} + month={month} + onToggleHiddenCategories={onToggleHiddenCategories} + onSwitchBudgetType={onSwitchBudgetType} + /> + </Modal> + ); +} + +type BudgetMenuProps = Omit< + ComponentPropsWithoutRef<typeof Menu>, + 'onMenuSelect' | 'items' +> & { + month: string; + onToggleHiddenCategories: () => void; + onSwitchBudgetType: () => void; +}; + +function BudgetMenu({ + // onEditMode, + month, + onToggleHiddenCategories, + onSwitchBudgetType, + ...props +}: BudgetMenuProps) { + const isReportBudgetEnabled = useFeatureFlag('reportBudget'); + + const onMenuSelect = (name: string) => { + switch (name) { + // case 'edit-mode': + // onEditMode?.(true); + // break; + case 'toggle-hidden-categories': + onToggleHiddenCategories?.(); + break; + case 'switch-budget-type': + onSwitchBudgetType?.(); + break; + default: + throw new Error(`Unrecognized menu option: ${name}`); + } + }; + + return ( + <Menu + {...props} + onMenuSelect={onMenuSelect} + items={[ + // Removing for now until we work on mobile category drag and drop. + // { name: 'edit-mode', text: 'Edit mode' }, + { + name: 'toggle-hidden-categories', + text: 'Toggle hidden categories', + }, + ...(isReportBudgetEnabled + ? [ + { + name: 'switch-budget-type', + text: 'Switch budget type', + }, + ] + : []), + ]} + /> + ); +} diff --git a/packages/desktop-client/src/components/modals/CategoryGroupMenuModal.tsx b/packages/desktop-client/src/components/modals/CategoryGroupMenuModal.tsx index daf67d511ecdbd842e671ddd2c3b4526b1d96372..3f99d8e2bfd6c24e2f51a641e57f9392fd84310b 100644 --- a/packages/desktop-client/src/components/modals/CategoryGroupMenuModal.tsx +++ b/packages/desktop-client/src/components/modals/CategoryGroupMenuModal.tsx @@ -14,7 +14,7 @@ import { SvgNotesPaper, SvgViewHide, SvgViewShow } from '../../icons/v2'; import { type CSSProperties, styles, theme } from '../../style'; import { Button } from '../common/Button'; import { Menu } from '../common/Menu'; -import { Modal } from '../common/Modal'; +import { Modal, ModalTitle } from '../common/Modal'; import { View } from '../common/View'; import { type CommonModalProps } from '../Modals'; import { Notes } from '../Notes'; @@ -94,7 +94,9 @@ export function CategoryGroupMenuModal({ return ( <Modal - title={group.name} + title={ + <ModalTitle isEditable title={group.name} onTitleUpdate={onRename} /> + } showHeader focusAfterClose={false} {...modalProps} @@ -106,9 +108,6 @@ export function CategoryGroupMenuModal({ padding: '0 10px', borderRadius: '6px', }} - editableTitle={true} - titleStyle={styles.underlinedText} - onTitleUpdate={onRename} leftHeaderContent={ <AdditionalCategoryGroupMenu group={group} @@ -117,72 +116,53 @@ export function CategoryGroupMenuModal({ /> } > - {({ isEditingTitle }) => ( + <View + style={{ + flex: 1, + flexDirection: 'column', + }} + > <View style={{ + overflowY: 'auto', flex: 1, - flexDirection: 'column', }} > - <View - style={{ - overflowY: 'auto', - flex: 1, - }} - > - <Notes - notes={notes?.length > 0 ? notes : 'No notes'} - editable={false} - focused={false} - getStyle={() => ({ - ...styles.mediumText, - borderRadius: 6, - ...((!notes || notes.length === 0) && { - justifySelf: 'center', - alignSelf: 'center', - color: theme.pageTextSubdued, - }), - })} - /> - </View> - <View - style={{ - flexDirection: 'row', - flexWrap: 'wrap', - justifyContent: 'space-between', - alignContent: 'space-between', - paddingTop: 10, - paddingBottom: 10, - }} - > - <Button - disabled={isEditingTitle} - style={{ - ...buttonStyle, - display: isEditingTitle ? 'none' : undefined, - }} - onClick={_onAddCategory} - > - <SvgAdd width={17} height={17} style={{ paddingRight: 5 }} /> - Add category - </Button> - <Button - style={{ - ...buttonStyle, - display: isEditingTitle ? 'none' : undefined, - }} - onClick={_onEditNotes} - > - <SvgNotesPaper - width={20} - height={20} - style={{ paddingRight: 5 }} - /> - Edit notes - </Button> - </View> + <Notes + notes={notes?.length > 0 ? notes : 'No notes'} + editable={false} + focused={false} + getStyle={() => ({ + ...styles.mediumText, + borderRadius: 6, + ...((!notes || notes.length === 0) && { + justifySelf: 'center', + alignSelf: 'center', + color: theme.pageTextSubdued, + }), + })} + /> </View> - )} + <View + style={{ + flexDirection: 'row', + flexWrap: 'wrap', + justifyContent: 'space-between', + alignContent: 'space-between', + paddingTop: 10, + paddingBottom: 10, + }} + > + <Button style={buttonStyle} onClick={_onAddCategory}> + <SvgAdd width={17} height={17} style={{ paddingRight: 5 }} /> + Add category + </Button> + <Button style={buttonStyle} onClick={_onEditNotes}> + <SvgNotesPaper width={20} height={20} style={{ paddingRight: 5 }} /> + Edit notes + </Button> + </View> + </View> </Modal> ); } diff --git a/packages/desktop-client/src/components/modals/CategoryMenuModal.tsx b/packages/desktop-client/src/components/modals/CategoryMenuModal.tsx index 449c97fa2ab158d24bcd279774ca45f6cd960a93..b88d52a0abf28a1370a3890f3cdde49ea3792992 100644 --- a/packages/desktop-client/src/components/modals/CategoryMenuModal.tsx +++ b/packages/desktop-client/src/components/modals/CategoryMenuModal.tsx @@ -14,7 +14,7 @@ import { SvgNotesPaper, SvgViewHide, SvgViewShow } from '../../icons/v2'; import { type CSSProperties, styles, theme } from '../../style'; import { Button } from '../common/Button'; import { Menu } from '../common/Menu'; -import { Modal } from '../common/Modal'; +import { Modal, ModalTitle } from '../common/Modal'; import { View } from '../common/View'; import { type CommonModalProps } from '../Modals'; import { Notes } from '../Notes'; @@ -85,8 +85,9 @@ export function CategoryMenuModal({ return ( <Modal - title={category.name} - titleStyle={styles.underlinedText} + title={ + <ModalTitle isEditable title={category.name} onTitleUpdate={onRename} /> + } showHeader focusAfterClose={false} {...modalProps} @@ -98,8 +99,6 @@ export function CategoryMenuModal({ padding: '0 10px', borderRadius: '6px', }} - editableTitle={true} - onTitleUpdate={onRename} leftHeaderContent={ <AdditionalCategoryMenu category={category} @@ -108,59 +107,47 @@ export function CategoryMenuModal({ /> } > - {({ isEditingTitle }) => ( + <View + style={{ + flex: 1, + flexDirection: 'column', + }} + > <View style={{ + overflowY: 'auto', flex: 1, - flexDirection: 'column', }} > - <View - style={{ - overflowY: 'auto', - flex: 1, - }} - > - <Notes - notes={originalNotes?.length > 0 ? originalNotes : 'No notes'} - editable={false} - focused={false} - getStyle={() => ({ - borderRadius: 6, - ...((!originalNotes || originalNotes.length === 0) && { - justifySelf: 'center', - alignSelf: 'center', - color: theme.pageTextSubdued, - }), - })} - /> - </View> - <View - style={{ - flexDirection: 'row', - flexWrap: 'wrap', - justifyContent: 'space-between', - alignContent: 'space-between', - margin: '10px 0', - }} - > - <Button - style={{ - ...buttonStyle, - display: isEditingTitle ? 'none' : undefined, - }} - onClick={_onEditNotes} - > - <SvgNotesPaper - width={20} - height={20} - style={{ paddingRight: 5 }} - /> - Edit notes - </Button> - </View> + <Notes + notes={originalNotes?.length > 0 ? originalNotes : 'No notes'} + editable={false} + focused={false} + getStyle={() => ({ + borderRadius: 6, + ...((!originalNotes || originalNotes.length === 0) && { + justifySelf: 'center', + alignSelf: 'center', + color: theme.pageTextSubdued, + }), + })} + /> + </View> + <View + style={{ + flexDirection: 'row', + flexWrap: 'wrap', + justifyContent: 'space-between', + alignContent: 'space-between', + margin: '10px 0', + }} + > + <Button style={buttonStyle} onClick={_onEditNotes}> + <SvgNotesPaper width={20} height={20} style={{ paddingRight: 5 }} /> + Edit notes + </Button> </View> - )} + </View> </Modal> ); } diff --git a/packages/desktop-client/src/components/modals/ReportBalanceMenuModal.tsx b/packages/desktop-client/src/components/modals/ReportBalanceMenuModal.tsx index 6c4d5e6e1eada91c9fba9df240502a0e0632b1b2..788d9e19d588aca0cb2e1b28cd860348c9de2227 100644 --- a/packages/desktop-client/src/components/modals/ReportBalanceMenuModal.tsx +++ b/packages/desktop-client/src/components/modals/ReportBalanceMenuModal.tsx @@ -25,7 +25,6 @@ export function ReportBalanceMenuModal({ return ( <Modal - title="Actions" showHeader focusAfterClose={false} {...modalProps} @@ -37,13 +36,11 @@ export function ReportBalanceMenuModal({ borderRadius: '6px', }} > - {() => ( - <BalanceMenu - categoryId={categoryId} - getItemStyle={() => defaultMenuItemStyle} - onCarryover={onCarryover} - /> - )} + <BalanceMenu + categoryId={categoryId} + getItemStyle={() => defaultMenuItemStyle} + onCarryover={onCarryover} + /> </Modal> ); } diff --git a/packages/desktop-client/src/components/modals/RolloverBalanceMenuModal.tsx b/packages/desktop-client/src/components/modals/RolloverBalanceMenuModal.tsx index 75c4159103f0ef8903f06c6bbe1b569e10ae6090..247fb0526f44c54fcd275000114907adae9190e0 100644 --- a/packages/desktop-client/src/components/modals/RolloverBalanceMenuModal.tsx +++ b/packages/desktop-client/src/components/modals/RolloverBalanceMenuModal.tsx @@ -27,7 +27,6 @@ export function RolloverBalanceMenuModal({ return ( <Modal - title="Actions" showHeader focusAfterClose={false} {...modalProps} @@ -39,15 +38,13 @@ export function RolloverBalanceMenuModal({ borderRadius: '6px', }} > - {() => ( - <BalanceMenu - categoryId={categoryId} - getItemStyle={() => defaultMenuItemStyle} - onCarryover={onCarryover} - onTransfer={onTransfer} - onCover={onCover} - /> - )} + <BalanceMenu + categoryId={categoryId} + getItemStyle={() => defaultMenuItemStyle} + onCarryover={onCarryover} + onTransfer={onTransfer} + onCover={onCover} + /> </Modal> ); } diff --git a/packages/desktop-client/src/components/modals/RolloverToBudgetMenuModal.tsx b/packages/desktop-client/src/components/modals/RolloverToBudgetMenuModal.tsx index da62d53d08f696d4e9a5844517d68bcc9f0a3a2c..9daa6ac60b1d12ca16b0149728e974dce2370baf 100644 --- a/packages/desktop-client/src/components/modals/RolloverToBudgetMenuModal.tsx +++ b/packages/desktop-client/src/components/modals/RolloverToBudgetMenuModal.tsx @@ -26,7 +26,6 @@ export function RolloverToBudgetMenuModal({ return ( <Modal - title="Actions" showHeader focusAfterClose={false} {...modalProps} @@ -38,14 +37,12 @@ export function RolloverToBudgetMenuModal({ borderRadius: '6px', }} > - {() => ( - <ToBudgetMenu - getItemStyle={() => defaultMenuItemStyle} - onTransfer={onTransfer} - onHoldBuffer={onHoldBuffer} - onResetHoldBuffer={onResetHoldBuffer} - /> - )} + <ToBudgetMenu + getItemStyle={() => defaultMenuItemStyle} + onTransfer={onTransfer} + onHoldBuffer={onHoldBuffer} + onResetHoldBuffer={onResetHoldBuffer} + /> </Modal> ); } diff --git a/packages/desktop-client/src/components/modals/ScheduledTransactionMenuModal.tsx b/packages/desktop-client/src/components/modals/ScheduledTransactionMenuModal.tsx index cfadec05dab4e74ebec8f14455330a925129ea52..8a70ceb15723efc3a9b9224e91a5f24d60e0ec05 100644 --- a/packages/desktop-client/src/components/modals/ScheduledTransactionMenuModal.tsx +++ b/packages/desktop-client/src/components/modals/ScheduledTransactionMenuModal.tsx @@ -24,7 +24,6 @@ export function ScheduledTransactionMenuModal({ return ( <Modal - title="Actions" showHeader focusAfterClose={false} {...modalProps} @@ -36,14 +35,12 @@ export function ScheduledTransactionMenuModal({ borderRadius: '6px', }} > - {() => ( - <ScheduledTransactionMenu - transactionId={transactionId} - onPost={onPost} - onSkip={onSkip} - getItemStyle={() => defaultMenuItemStyle} - /> - )} + <ScheduledTransactionMenu + transactionId={transactionId} + onPost={onPost} + onSkip={onSkip} + getItemStyle={() => defaultMenuItemStyle} + /> </Modal> ); } diff --git a/packages/desktop-client/src/components/modals/SingleInputModal.tsx b/packages/desktop-client/src/components/modals/SingleInputModal.tsx index c95d820fabe9ffceab892d0615babf0c46c6dc37..7ea44494095c490f557d70a6377c5abdafd6b244 100644 --- a/packages/desktop-client/src/components/modals/SingleInputModal.tsx +++ b/packages/desktop-client/src/components/modals/SingleInputModal.tsx @@ -1,5 +1,5 @@ // @ts-strict-ignore -import React, { useState } from 'react'; +import React, { type ComponentProps, useState } from 'react'; import { styles } from '../../style'; import { Button } from '../common/Button'; @@ -12,7 +12,7 @@ import { type CommonModalProps } from '../Modals'; type SingleInputModalProps = { modalProps: Partial<CommonModalProps>; - title: string; + title: ComponentProps<typeof Modal>['title']; buttonText: string; onSubmit: (value: string) => void; onValidate?: (value: string) => string[]; diff --git a/packages/desktop-client/src/components/modals/SwitchBudgetType.tsx b/packages/desktop-client/src/components/modals/SwitchBudgetTypeModal.tsx similarity index 78% rename from packages/desktop-client/src/components/modals/SwitchBudgetType.tsx rename to packages/desktop-client/src/components/modals/SwitchBudgetTypeModal.tsx index 2c9c66ef2c2a94fdfb8b45ebc48e1e860f2e37b2..27b92df6cb56195fb64827643487e0bf1ab4a547 100644 --- a/packages/desktop-client/src/components/modals/SwitchBudgetType.tsx +++ b/packages/desktop-client/src/components/modals/SwitchBudgetTypeModal.tsx @@ -2,6 +2,8 @@ import React from 'react'; import { useLocalPref } from '../../hooks/useLocalPref'; +import { useResponsive } from '../../ResponsiveProvider'; +import { styles } from '../../style'; import { Button } from '../common/Button'; import { Link } from '../common/Link'; import { Modal } from '../common/Modal'; @@ -9,16 +11,22 @@ import { Paragraph } from '../common/Paragraph'; import { Text } from '../common/Text'; import { type CommonModalProps } from '../Modals'; -type SwitchBudgetTypeProps = { +type SwitchBudgetTypeModalProps = { modalProps: CommonModalProps; onSwitch: () => void; }; -export function SwitchBudgetType({ +export function SwitchBudgetTypeModal({ modalProps, onSwitch, -}: SwitchBudgetTypeProps) { +}: SwitchBudgetTypeModalProps) { const [budgetType] = useLocalPref('budgetType'); + const { isNarrowWidth } = useResponsive(); + const narrowStyle = isNarrowWidth + ? { + height: styles.mobileMinHeight, + } + : {}; return ( <Modal title="Switch budget type?" {...modalProps}> {() => ( @@ -32,8 +40,11 @@ export function SwitchBudgetType({ </Paragraph> <Button type="primary" + style={{ + ...narrowStyle, + }} onClick={() => { - onSwitch(); + onSwitch?.(); modalProps.onClose?.(); }} > diff --git a/packages/loot-core/src/client/state-types/modals.d.ts b/packages/loot-core/src/client/state-types/modals.d.ts index f780f445e7f4c66c7072bb20b4beabdc15c4e868..4d4f45f8de2e887679d22fb04aa8ff1c29da1d72 100644 --- a/packages/loot-core/src/client/state-types/modals.d.ts +++ b/packages/loot-core/src/client/state-types/modals.d.ts @@ -215,6 +215,11 @@ type FinanceModals = { onPost: (transactionId: string) => void; onSkip: (transactionId: string) => void; }; + 'budget-menu': { + month: string; + onToggleHiddenCategories: () => void; + onSwitchBudgetType: () => void; + }; }; export type PushModalAction = { diff --git a/upcoming-release-notes/2491.md b/upcoming-release-notes/2491.md new file mode 100644 index 0000000000000000000000000000000000000000..ae89be45b967c9116ee5f52d1a1b03df635bba41 --- /dev/null +++ b/upcoming-release-notes/2491.md @@ -0,0 +1,6 @@ +--- +category: Enhancements +authors: [joel-jeremy] +--- + +Add + button to add a group on mobile budget page and a budget related menu.