diff --git a/packages/desktop-client/src/components/sidebar/Account.tsx b/packages/desktop-client/src/components/sidebar/Account.tsx index 62985660a4f..77cecc6cf7a 100644 --- a/packages/desktop-client/src/components/sidebar/Account.tsx +++ b/packages/desktop-client/src/components/sidebar/Account.tsx @@ -1,15 +1,24 @@ // @ts-strict-ignore -import React, { type CSSProperties } from 'react'; +import React, { type CSSProperties, useEffect, useRef, useState } from 'react'; +import { useDispatch } from 'react-redux'; import { css, cx } from '@emotion/css'; +import { + openAccountCloseModal, + reopenAccount, + updateAccount, +} from 'loot-core/client/actions'; import * as Platform from 'loot-core/client/platform'; import { type AccountEntity } from 'loot-core/src/types/models'; +import { useContextMenu } from '../../hooks/useContextMenu'; import { useNotes } from '../../hooks/useNotes'; import { styles, theme } from '../../style'; import { AlignedText } from '../common/AlignedText'; import { Link } from '../common/Link'; +import { Menu } from '../common/Menu'; +import { Popover } from '../common/Popover'; import { Text } from '../common/Text'; import { Tooltip } from '../common/Tooltip'; import { View } from '../common/View'; @@ -74,6 +83,10 @@ export function Account>({ : 'account-onbudget' : 'title'; + const triggerRef = useRef(null); + const { setMenuOpen, menuOpen, handleContextMenu, position } = + useContextMenu(); + const { dragRef } = useDraggable({ type, onDragChange, @@ -87,12 +100,45 @@ export function Account>({ onDrop, }); + const dispatch = useDispatch(); + + const editingRef = useRef(null); + const [isEditing, setIsEditing] = useState(false); + useEffect(() => { + if (!editingRef.current) return; + if (isEditing) { + editingRef.current.focus(); + window.getSelection().selectAllChildren(editingRef.current); + } else { + editingRef.current.textContent = name; + } + }, [name, isEditing]); + + const updateName = () => { + if (account && isEditing) { + setIsEditing(false); + const newName = editingRef.current.textContent; + if (newName !== account.name && newName.trim()) { + dispatch( + updateAccount({ + ...account, + name: newName, + }), + ); + } + } + }; + const accountNote = useNotes(`account-${account?.id}`); const needsTooltip = !!account?.id; const accountRow = ( - - + + >({ paddingBottom: '3px', } } - left={name} + left={ + { + if (e.key === 'Enter') { + e.preventDefault(); + updateName(); + } else if (e.key === 'Escape') { + setIsEditing(false); + } + }} + > + {name} + + } right={} /> + {account && ( + setMenuOpen(false)} + style={{ width: 200, margin: 1 }} + isNonModal + {...position} + > + { + switch (type) { + case 'close': { + dispatch(openAccountCloseModal(account.id)); + break; + } + case 'reopen': { + dispatch(reopenAccount(account.id)); + break; + } + case 'rename': { + setIsEditing(true); + break; + } + } + setMenuOpen(false); + }} + items={[ + { name: 'rename', text: 'Rename' }, + account.closed + ? { name: 'reopen', text: 'Reopen' } + : { name: 'close', text: 'Close' }, + ]} + /> + + )} @@ -202,6 +302,7 @@ export function Account>({ placement="right top" triggerProps={{ delay: 1000, + isDisabled: menuOpen, }} > {accountRow} diff --git a/packages/desktop-client/src/components/sidebar/BudgetName.tsx b/packages/desktop-client/src/components/sidebar/BudgetName.tsx index d7b97e4365b..9499bc20caa 100644 --- a/packages/desktop-client/src/components/sidebar/BudgetName.tsx +++ b/packages/desktop-client/src/components/sidebar/BudgetName.tsx @@ -5,6 +5,7 @@ import { useDispatch } from 'react-redux'; import { closeBudget } from 'loot-core/src/client/actions'; import * as Platform from 'loot-core/src/client/platform'; +import { useContextMenu } from '../../hooks/useContextMenu'; import { useMetadataPref } from '../../hooks/useMetadataPref'; import { useNavigate } from '../../hooks/useNavigate'; import { SvgExpandArrow } from '../../icons/v0'; @@ -54,9 +55,10 @@ function EditableBudgetName() { const [budgetName, setBudgetNamePref] = useMetadataPref('budgetName'); const dispatch = useDispatch(); const navigate = useNavigate(); - const [menuOpen, setMenuOpen] = useState(false); const triggerRef = useRef(null); const [editing, setEditing] = useState(false); + const { setMenuOpen, menuOpen, handleContextMenu, resetPosition, position } = + useContextMenu(); function onMenuSelect(type: string) { setMenuOpen(false); @@ -106,7 +108,7 @@ function EditableBudgetName() { } return ( - <> +