mirror of
https://github.com/actualbudget/actual.git
synced 2026-04-30 03:23:51 -05:00
feat(currency): add currency display to rules (#5639)
* feat(currency): add to rules * doc: release notes * feat: remove keydown from Input * doc: release notes * fix: make onEnter optional * fix: ai remark * refactor: remove onKeyDown from Input.tsx * fix: handle Amount (inflow) and Amount (outflow) properly * [autofix.ci] apply automated fixes * fix: update AmountInput to sign and on outflow set + * refactor: onSubmit handling of input value * coderabbit suggestions --------- Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
parent
90ac8d8520
commit
99ca34458e
@@ -5,8 +5,9 @@ import React, {
|
||||
useState,
|
||||
useEffect,
|
||||
type FocusEventHandler,
|
||||
type KeyboardEventHandler,
|
||||
type KeyboardEvent,
|
||||
type CSSProperties,
|
||||
useCallback,
|
||||
} from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
@@ -17,22 +18,19 @@ import { theme } from '@actual-app/components/theme';
|
||||
import { View } from '@actual-app/components/view';
|
||||
import { css, cx } from '@emotion/css';
|
||||
|
||||
import { evalArithmetic } from 'loot-core/shared/arithmetic';
|
||||
import { amountToInteger, appendDecimals } from 'loot-core/shared/util';
|
||||
|
||||
import { useFormat } from '@desktop-client/hooks/useFormat';
|
||||
import { useMergedRefs } from '@desktop-client/hooks/useMergedRefs';
|
||||
import { useSyncedPref } from '@desktop-client/hooks/useSyncedPref';
|
||||
|
||||
type AmountInputProps = {
|
||||
id?: string;
|
||||
inputRef?: Ref<HTMLInputElement>;
|
||||
value: number;
|
||||
zeroSign?: '-' | '+';
|
||||
sign?: '-' | '+';
|
||||
onChangeValue?: (value: string) => void;
|
||||
onFocus?: FocusEventHandler<HTMLInputElement>;
|
||||
onBlur?: FocusEventHandler<HTMLInputElement>;
|
||||
onEnter?: KeyboardEventHandler<HTMLInputElement>;
|
||||
onEnter?: (event: KeyboardEvent<HTMLInputElement>, amount?: number) => void;
|
||||
onUpdate?: (amount: number) => void;
|
||||
style?: CSSProperties;
|
||||
inputStyle?: CSSProperties;
|
||||
@@ -47,6 +45,7 @@ export function AmountInput({
|
||||
inputRef,
|
||||
value: initialValue,
|
||||
zeroSign = '-', // + or -
|
||||
sign,
|
||||
onFocus,
|
||||
onBlur,
|
||||
onChangeValue,
|
||||
@@ -61,20 +60,32 @@ export function AmountInput({
|
||||
}: AmountInputProps) {
|
||||
const { t } = useTranslation();
|
||||
const format = useFormat();
|
||||
const [symbol, setSymbol] = useState<'+' | '-'>(
|
||||
initialValue === 0 ? zeroSign : initialValue > 0 ? '+' : '-',
|
||||
);
|
||||
const [symbol, setSymbol] = useState<'+' | '-'>(() => {
|
||||
if (sign) return sign;
|
||||
return initialValue === 0 ? zeroSign : initialValue > 0 ? '+' : '-';
|
||||
});
|
||||
|
||||
const [isFocused, setIsFocused] = useState(focused ?? false);
|
||||
|
||||
const initialValueAbsolute = format(Math.abs(initialValue || 0), 'financial');
|
||||
const [value, setValue] = useState(initialValueAbsolute);
|
||||
useEffect(() => setValue(initialValueAbsolute), [initialValueAbsolute]);
|
||||
const getDisplayValue = useCallback(
|
||||
(value: number, isEditing: boolean) => {
|
||||
const absoluteValue = Math.abs(value || 0);
|
||||
return isEditing
|
||||
? format.forEdit(absoluteValue)
|
||||
: format(absoluteValue, 'financial');
|
||||
},
|
||||
[format],
|
||||
);
|
||||
|
||||
const [value, setValue] = useState(getDisplayValue(initialValue, false));
|
||||
useEffect(
|
||||
() => setValue(getDisplayValue(initialValue, isFocused)),
|
||||
[initialValue, isFocused, getDisplayValue],
|
||||
);
|
||||
|
||||
const buttonRef = useRef(null);
|
||||
const ref = useRef<HTMLInputElement>(null);
|
||||
const mergedRef = useMergedRefs<HTMLInputElement>(inputRef, ref);
|
||||
const [hideFraction] = useSyncedPref('hideFraction');
|
||||
|
||||
useEffect(() => {
|
||||
if (focused) {
|
||||
@@ -82,7 +93,30 @@ export function AmountInput({
|
||||
}
|
||||
}, [focused]);
|
||||
|
||||
useEffect(() => {
|
||||
if (sign) {
|
||||
setSymbol(sign);
|
||||
}
|
||||
}, [sign]);
|
||||
|
||||
const getAmount = useCallback(() => {
|
||||
const signedValued = symbol === '-' ? symbol + value : value;
|
||||
return format.fromEdit(signedValued, 0);
|
||||
}, [symbol, value, format]);
|
||||
|
||||
useEffect(() => {
|
||||
if (ref.current) {
|
||||
(
|
||||
ref.current as HTMLInputElement & { getCurrentAmount?: () => number }
|
||||
).getCurrentAmount = () => getAmount();
|
||||
}
|
||||
}, [getAmount]);
|
||||
|
||||
function onSwitch() {
|
||||
if (sign) {
|
||||
return;
|
||||
}
|
||||
|
||||
const amount = getAmount();
|
||||
if (amount === 0) {
|
||||
setSymbol(symbol === '+' ? '-' : '+');
|
||||
@@ -90,26 +124,35 @@ export function AmountInput({
|
||||
fireUpdate(amount * -1);
|
||||
}
|
||||
|
||||
function getAmount() {
|
||||
const signedValued = symbol === '-' ? symbol + value : value;
|
||||
return amountToInteger(evalArithmetic(signedValued));
|
||||
}
|
||||
|
||||
function onInputTextChange(val) {
|
||||
val = autoDecimals
|
||||
? appendDecimals(val, String(hideFraction) === 'true')
|
||||
: val;
|
||||
setValue(val ? val : '');
|
||||
onChangeValue?.(val);
|
||||
let newText = val;
|
||||
if (autoDecimals) {
|
||||
const digits = val.replace(/\D/g, '');
|
||||
if (digits === '') {
|
||||
newText = '';
|
||||
} else {
|
||||
const intValue = parseInt(digits, 10);
|
||||
newText = format.forEdit(intValue);
|
||||
}
|
||||
}
|
||||
|
||||
setValue(newText || '');
|
||||
onChangeValue?.(newText);
|
||||
}
|
||||
|
||||
function fireUpdate(amount) {
|
||||
onUpdate?.(amount);
|
||||
if (amount > 0) {
|
||||
setSymbol('+');
|
||||
} else if (amount < 0) {
|
||||
setSymbol('-');
|
||||
|
||||
if (sign) {
|
||||
setSymbol(sign);
|
||||
} else {
|
||||
if (amount > 0) {
|
||||
setSymbol('+');
|
||||
} else if (amount < 0) {
|
||||
setSymbol('-');
|
||||
}
|
||||
}
|
||||
setValue(format(Math.abs(amount), 'financial'));
|
||||
}
|
||||
|
||||
function onInputAmountBlur(e) {
|
||||
@@ -136,7 +179,7 @@ export function AmountInput({
|
||||
>
|
||||
<Button
|
||||
variant="bare"
|
||||
isDisabled={disabled}
|
||||
isDisabled={disabled || !!sign}
|
||||
aria-label={symbol === '-' ? t('Make positive') : t('Make negative')}
|
||||
style={{ padding: '0 7px' }}
|
||||
onPress={onSwitch}
|
||||
@@ -172,6 +215,7 @@ export function AmountInput({
|
||||
)}
|
||||
onFocus={e => {
|
||||
setIsFocused(true);
|
||||
setValue(format.forEdit(Math.abs(initialValue ?? 0)));
|
||||
onFocus?.(e);
|
||||
}}
|
||||
onBlur={e => {
|
||||
@@ -179,9 +223,9 @@ export function AmountInput({
|
||||
onInputAmountBlur(e);
|
||||
}}
|
||||
onEnter={(_, e) => {
|
||||
onEnter?.(e);
|
||||
const amount = getAmount();
|
||||
fireUpdate(amount);
|
||||
onEnter?.(e, amount);
|
||||
}}
|
||||
onChangeValue={onInputTextChange}
|
||||
/>
|
||||
|
||||
Reference in New Issue
Block a user