1 import { useState } from 'react';
3 import { c } from 'ttag';
5 import Icon from '@proton/components/components/icon/Icon';
6 import Checkbox from '@proton/components/components/input/Checkbox';
7 import Label from '@proton/components/components/label/Label';
8 import type { CountryOptions } from '@proton/components/helpers/countries';
9 import clsx from '@proton/utils/clsx';
11 import { CountryFlagAndName } from './CountryFlagAndName';
12 import type { GatewayLocation } from './GatewayLocation';
13 import { getLocationDisplayName } from './helpers';
15 export const ButtonNumberInput = ({
32 value: number | undefined;
34 location: GatewayLocation;
35 countryOptions: CountryOptions;
38 onChange?: (newValue: number) => void;
40 const [tmpValue, setTmpValue] = useState<number | undefined>(value);
42 const getIsValidValue = (newValue?: number) => {
43 return newValue !== undefined && newValue >= min && newValue <= max && newValue % step === 0;
46 const title = getLocationDisplayName(location, countryOptions);
48 const maxUsable = Math.min(max, ownedCount - usedCount);
49 const isDecDisabled = disabled || tmpValue === undefined || tmpValue <= min;
50 const isIncDisabled = disabled || tmpValue === undefined || tmpValue >= maxUsable;
52 const isValidTmpValue = getIsValidValue(tmpValue);
54 const setTmpNum = (newValue: number) => {
55 if (newValue >= min && newValue <= maxUsable) {
56 setTmpValue(newValue);
63 <Label className="flex-1" style={{ opacity: disabled ? 0.5 : 1 }}>
66 const newValue = e.target.checked ? 1 : undefined;
67 setTmpValue(newValue);
68 onChange?.(newValue || 0);
71 checked={(tmpValue || 0) > 0}
73 <CountryFlagAndName countryCode={location.Country} countryName={title} />
76 className="border rounded shrink-0 flex flex-nowrap"
77 style={{ visibility: tmpValue === undefined ? 'hidden' : 'visible', opacity: disabled ? 0.5 : 1 }}
81 title={c('Action').t`Decrease`}
82 className={clsx(['p-2 flex', isDecDisabled && 'color-disabled'])}
83 disabled={isDecDisabled}
85 if (!isValidTmpValue || tmpValue === undefined) {
88 const newValue = tmpValue - step;
89 setTmpNum?.(newValue);
93 <Icon name="minus" alt={c('Action').t`Decrease`} className="m-auto" />
95 <label htmlFor={id} className="my-2 flex">
102 className="w-custom border-left border-right text-center"
103 style={{ '--w-custom': '6em' }}
105 if (!isValidTmpValue) {
106 // Revert to the latest valid value upon blur
110 onChange={({ target: { value: newValue } }) => {
111 if (newValue === '') {
112 setTmpValue(undefined);
115 const newIntValue = parseInt(newValue, 10);
116 setTmpValue(newIntValue);
117 if (newIntValue >= min && newIntValue <= maxUsable) {
118 onChange?.(newIntValue);
125 title={c('Action').t`Increase`}
126 className={clsx(['p-2 flex', isIncDisabled && 'color-disabled'])}
127 disabled={isIncDisabled}
129 if (!isValidTmpValue || tmpValue === undefined) {
132 const newValue = tmpValue + step;
133 setTmpNum?.(newValue);
134 onChange?.(newValue);
137 <Icon name="plus" alt={c('Action').t`Increase`} className="m-auto" />