Skip to content

Commit b488881

Browse files
authored
feat: improve TokenInput and use-form (#93)
1 parent 619b6ca commit b488881

15 files changed

Lines changed: 400 additions & 182 deletions

.changeset/six-weeks-hammer.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
"@interlay/ui": patch
3+
"@interlay/hooks": patch
4+
---
5+
6+
Fix/token input decimal undefined

packages/components/src/TokenInput/BaseTokenInput.tsx

Lines changed: 20 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { useCurrencyFormatter, useDOMRef } from '@interlay/hooks';
22
import { Spacing, TokenInputSize } from '@interlay/theme';
33
import { AriaTextFieldOptions, useTextField } from '@react-aria/textfield';
44
import { mergeProps } from '@react-aria/utils';
5-
import { ChangeEventHandler, FocusEvent, forwardRef, ReactNode, useCallback, useEffect, useState } from 'react';
5+
import { ChangeEventHandler, FocusEvent, forwardRef, ReactNode, useCallback } from 'react';
66

77
import { HelperTextProps } from '../HelperText';
88
import { LabelProps } from '../Label';
@@ -42,7 +42,7 @@ type Props = {
4242
value?: string;
4343
defaultValue?: string;
4444
// TODO: use Currency from bob-ui
45-
currency: { decimals: number };
45+
currency?: { decimals: number };
4646
onValueChange?: (value: string | number) => void;
4747
onChange?: (e: React.ChangeEvent<HTMLInputElement>) => void;
4848
onFocus?: (e: FocusEvent<Element>) => void;
@@ -75,7 +75,7 @@ const BaseTokenInput = forwardRef<HTMLInputElement, BaseTokenInputProps>(
7575
size = 'md',
7676
defaultValue,
7777
inputMode,
78-
value: valueProp,
78+
value,
7979
endAdornment,
8080
currency,
8181
onChange,
@@ -84,49 +84,43 @@ const BaseTokenInput = forwardRef<HTMLInputElement, BaseTokenInputProps>(
8484
},
8585
ref
8686
): JSX.Element => {
87-
const [value, setValue] = useState<string | undefined>(defaultValue?.toString());
8887
const inputRef = useDOMRef(ref);
8988

9089
const format = useCurrencyFormatter();
9190

91+
const { inputProps, descriptionProps, errorMessageProps, labelProps } = useTextField(
92+
{
93+
...props,
94+
label,
95+
inputMode,
96+
isInvalid: isInvalid || !!props.errorMessage,
97+
value,
98+
onChange: () => {},
99+
defaultValue,
100+
placeholder,
101+
autoComplete: 'off'
102+
},
103+
inputRef
104+
);
105+
92106
const handleChange: ChangeEventHandler<HTMLInputElement> = useCallback(
93107
(e) => {
94108
const value = e.target.value;
95109

96110
const isEmpty = value === '';
97111
const hasValidDecimalFormat = RegExp(`^\\d*(?:\\\\[.])?\\d*$`).test(escapeRegExp(value));
98-
const hasValidDecimalsAmount = hasCorrectDecimals(value, currency.decimals);
112+
const hasValidDecimalsAmount = currency ? hasCorrectDecimals(value, currency.decimals) : true;
99113

100114
const isValid = hasValidDecimalFormat && hasValidDecimalsAmount;
101115

102116
if (isEmpty || isValid) {
103117
onChange?.(e);
104118
onValueChange?.(value);
105-
setValue(value);
106119
}
107120
},
108-
[onChange, onValueChange]
109-
);
110-
111-
const { inputProps, descriptionProps, errorMessageProps, labelProps } = useTextField(
112-
{
113-
...props,
114-
label,
115-
inputMode,
116-
isInvalid: isInvalid || !!props.errorMessage,
117-
value: value,
118-
placeholder,
119-
autoComplete: 'off'
120-
},
121-
inputRef
121+
[onChange, onValueChange, currency]
122122
);
123123

124-
useEffect(() => {
125-
if (valueProp === undefined) return;
126-
127-
setValue(valueProp.toString());
128-
}, [valueProp]);
129-
130124
const hasLabel = !!label || !!balance;
131125

132126
// FIXME: move this into Field

packages/components/src/TokenInput/FixedTokenInput.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,11 @@ import { BaseTokenInput, BaseTokenInputProps } from './BaseTokenInput';
55
import { TokenInputBalance } from './TokenInputBalance';
66

77
type Props = {
8+
currency: any;
89
balance?: string;
910
humanBalance?: string | number;
1011
balanceLabel?: ReactNode;
1112
onClickBalance?: (balance: string | number) => void;
12-
ticker: string;
1313
logoUrl: string;
1414
};
1515

@@ -24,11 +24,11 @@ const FixedTokenInput = forwardRef<HTMLInputElement, FixedTokenInputProps>(
2424
humanBalance,
2525
balanceLabel,
2626
onClickBalance,
27-
ticker: tickerProp,
2827
logoUrl,
2928
isDisabled,
3029
id,
3130
size = 'md',
31+
currency,
3232
...props
3333
},
3434
ref
@@ -49,7 +49,7 @@ const FixedTokenInput = forwardRef<HTMLInputElement, FixedTokenInputProps>(
4949
{...props}
5050
ref={ref}
5151
balance={balance}
52-
endAdornment={<TokenAdornment logoUrl={logoUrl} size={size} ticker={tickerProp} />}
52+
endAdornment={<TokenAdornment logoUrl={logoUrl} size={size} ticker={currency.symbol} />}
5353
id={id}
5454
isDisabled={isDisabled}
5555
size={size}

packages/components/src/TokenInput/SelectableTokenInput.tsx

Lines changed: 28 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,21 @@
11
import { chain, useId } from '@react-aria/utils';
2-
import { Key, ReactNode, forwardRef, useEffect, useState } from 'react';
2+
import { Key, ReactNode, forwardRef, useCallback, useEffect } from 'react';
33

44
import { HelperText } from '../HelperText';
55

66
import { BaseTokenInput, BaseTokenInputProps } from './BaseTokenInput';
77
import { TokenInputBalance } from './TokenInputBalance';
8-
import { TokenSelect, TokenSelectProps } from './TokenSelect';
8+
import { TokenData, TokenSelect, TokenSelectProps } from './TokenSelect';
99

1010
type Props = {
1111
balance?: string;
1212
humanBalance?: string | number;
1313
balanceLabel?: ReactNode;
14+
currency?: any;
15+
items?: TokenData[];
1416
onClickBalance?: (balance: string) => void;
15-
selectProps: Omit<TokenSelectProps, 'label' | 'helperTextId'>;
17+
onChangeCurrency?: (currency?: any) => void;
18+
selectProps?: Omit<TokenSelectProps, 'label' | 'helperTextId' | 'items'>;
1619
};
1720

1821
type InheritAttrs = Omit<BaseTokenInputProps, keyof Props | 'endAdornment'>;
@@ -31,25 +34,33 @@ const SelectableTokenInput = forwardRef<HTMLInputElement, SelectableTokenInputPr
3134
isDisabled,
3235
balanceLabel,
3336
humanBalance,
37+
currency,
38+
items,
3439
onClickBalance,
40+
onChangeCurrency,
3541
size,
3642
...props
3743
},
3844
ref
3945
): JSX.Element => {
4046
const selectHelperTextId = useId();
4147

42-
const [ticker, setTicker] = useState<string | undefined>(selectProps?.defaultValue?.toString());
43-
4448
useEffect(() => {
45-
const value = selectProps?.value;
49+
if (selectProps?.value === undefined) return;
50+
51+
const item = (items as TokenData[]).find((item) => item.currency.symbol === selectProps?.value);
4652

47-
if (value === undefined) return;
53+
onChangeCurrency?.(item?.currency);
54+
}, [selectProps?.value, onChangeCurrency]);
4855

49-
setTicker(value.toString());
50-
}, [selectProps?.value]);
56+
const handleSelectionChange = useCallback(
57+
(ticker: Key) => {
58+
const tokenData = (items as TokenData[]).find((item) => item.currency.symbol === ticker);
5159

52-
const handleTokenChange = (ticker: Key) => setTicker(ticker as string);
60+
onChangeCurrency?.(tokenData?.currency);
61+
},
62+
[selectProps]
63+
);
5364

5465
// Prioritise Number Input description and error message
5566
const hasNumberFieldMessages = !!(errorMessage || description);
@@ -58,7 +69,7 @@ const SelectableTokenInput = forwardRef<HTMLInputElement, SelectableTokenInputPr
5869

5970
const isInvalid = !hasNumberFieldMessages && !!selectProps?.errorMessage;
6071

61-
const { onSelectionChange, ...restSelectProps } = selectProps;
72+
const { onSelectionChange, ...restSelectProps } = selectProps || {};
6273

6374
const endAdornment = (
6475
<TokenSelect
@@ -67,18 +78,19 @@ const SelectableTokenInput = forwardRef<HTMLInputElement, SelectableTokenInputPr
6778
description={undefined}
6879
errorMessage={undefined}
6980
isInvalid={isInvalid}
81+
items={items}
7082
size={size}
71-
value={ticker}
72-
onSelectionChange={chain(onSelectionChange, handleTokenChange)}
83+
value={currency?.symbol}
84+
onSelectionChange={chain(onSelectionChange, handleSelectionChange)}
7385
/>
7486
);
7587

7688
const balance = balanceProp !== undefined && (
7789
<TokenInputBalance
78-
balance={ticker ? balanceProp : '0'}
90+
balance={currency ? balanceProp : '0'}
7991
balanceHuman={humanBalance}
8092
inputId={id}
81-
isDisabled={isDisabled || !ticker}
93+
isDisabled={isDisabled || !currency}
8294
label={balanceLabel}
8395
onClickBalance={onClickBalance}
8496
/>
@@ -88,6 +100,7 @@ const SelectableTokenInput = forwardRef<HTMLInputElement, SelectableTokenInputPr
88100
<BaseTokenInput
89101
ref={ref}
90102
balance={balance}
103+
currency={currency}
91104
description={description}
92105
endAdornment={endAdornment}
93106
errorMessage={errorMessage}

0 commit comments

Comments
 (0)