Add extra Input types to the UI (#2324)
* fix typo * WIP introduce float input * remove si dropdown for float input * assume floats to have si 1 billion * use correct SI name and parse decimal * fix linting issues * remove VecDeque from Vector UI component mapping
This commit is contained in:
parent
30d25e69a8
commit
7a159976f4
76
packages/react-components/src/InputFloat.tsx
Normal file
76
packages/react-components/src/InputFloat.tsx
Normal file
@ -0,0 +1,76 @@
|
||||
// Copyright 2017-2020 @polkadot/react-components authors & contributors
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import { BareProps, BitLength } from './types';
|
||||
|
||||
import BN from 'bn.js';
|
||||
import React from 'react';
|
||||
import styled from 'styled-components';
|
||||
import { BitLengthOption } from '@polkadot/react-components/constants';
|
||||
import { InputNumber } from '@polkadot/react-components';
|
||||
import { formatBalance } from '@polkadot/util';
|
||||
|
||||
interface Props extends BareProps {
|
||||
autoFocus?: boolean;
|
||||
defaultValue?: BN | string;
|
||||
help?: React.ReactNode;
|
||||
isDisabled?: boolean;
|
||||
isError?: boolean;
|
||||
isFull?: boolean;
|
||||
isZeroable?: boolean;
|
||||
label?: React.ReactNode;
|
||||
labelExtra?: React.ReactNode;
|
||||
maxValue?: BN;
|
||||
onChange?: (value?: BN) => void;
|
||||
onEnter?: () => void;
|
||||
onEscape?: () => void;
|
||||
placeholder?: string;
|
||||
value?: BN | string;
|
||||
withEllipsis?: boolean;
|
||||
withLabel?: boolean;
|
||||
withMax?: boolean;
|
||||
}
|
||||
|
||||
const DEFAULT_BITLENGTH = BitLengthOption.CHAIN_SPEC as BitLength;
|
||||
|
||||
function InputFloat ({ autoFocus, className, defaultValue: inDefault, help, isDisabled, isError, isFull, isZeroable = true, label, labelExtra, maxValue, onChange, onEnter, onEscape, placeholder, style, value, withEllipsis, withLabel, withMax }: Props): React.ReactElement<Props> {
|
||||
const defaultValue = inDefault;
|
||||
|
||||
const multiplier = formatBalance.findSi('G');
|
||||
|
||||
return (
|
||||
<InputNumber
|
||||
autoFocus={autoFocus}
|
||||
className={`ui--InputFloat ${className}`}
|
||||
bitLength={DEFAULT_BITLENGTH}
|
||||
defaultValue={defaultValue}
|
||||
defaultSi={multiplier}
|
||||
help={help}
|
||||
isDecimal={true}
|
||||
isDisabled={isDisabled}
|
||||
isError={isError}
|
||||
isFull={isFull}
|
||||
isZeroable={isZeroable}
|
||||
isSi={false}
|
||||
label={label}
|
||||
labelExtra={labelExtra}
|
||||
maxValue={maxValue}
|
||||
onChange={onChange}
|
||||
onEnter={onEnter}
|
||||
onEscape={onEscape}
|
||||
placeholder={placeholder}
|
||||
style={style}
|
||||
value={value}
|
||||
withEllipsis={withEllipsis}
|
||||
withLabel={withLabel}
|
||||
withMax={withMax}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export default styled(InputFloat)`
|
||||
&&:not(.label-small) .labelExtra {
|
||||
right: 6.5rem;
|
||||
}
|
||||
`;
|
||||
@ -22,6 +22,7 @@ interface Props extends BareProps {
|
||||
autoFocus?: boolean;
|
||||
bitLength?: BitLength;
|
||||
defaultValue?: BN | string;
|
||||
defaultSi?: SiDef;
|
||||
help?: React.ReactNode;
|
||||
isDisabled?: boolean;
|
||||
isError?: boolean;
|
||||
@ -110,22 +111,26 @@ function inputToBn (input: string, si: SiDef | null, props: Props): [BN, boolean
|
||||
const [siPower, basePower, siUnitPower] = getSiPowers(si);
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/prefer-regexp-exec
|
||||
const isDecimalValue = input.match(/^(\d+)\.(\d+)$/);
|
||||
const decimalMatch = input.match(/^(\d+)[.,](\d+)$/);
|
||||
|
||||
let result;
|
||||
|
||||
if (isDecimalValue) {
|
||||
if (siUnitPower - isDecimalValue[2].length < -basePower) {
|
||||
if (decimalMatch) {
|
||||
const intString = decimalMatch[1];
|
||||
const fractionString = decimalMatch[2];
|
||||
if (siUnitPower - fractionString.length < -basePower) {
|
||||
result = new BN(-1);
|
||||
}
|
||||
|
||||
const div = new BN(input.replace(/\.\d*$/, ''));
|
||||
const modString = input.replace(/^\d+\./, '');
|
||||
const mod = new BN(modString);
|
||||
const div = new BN(intString);
|
||||
const mod = new BN(fractionString);
|
||||
|
||||
result = div
|
||||
.mul(TEN.pow(siPower))
|
||||
.add(mod.mul(TEN.pow(new BN(basePower + siUnitPower - modString.length))));
|
||||
// multiply whole number part with 10^SI
|
||||
const divMul = div.mul(TEN.pow(new BN(siUnitPower)));
|
||||
// multiply the fractional part with 10^(SI-length)
|
||||
const modMul = mod.mul(TEN.pow(new BN(siUnitPower - fractionString.length)));
|
||||
|
||||
result = divMul.add(modMul);
|
||||
} else {
|
||||
result = new BN(input.replace(/[^\d]/g, ''))
|
||||
.mul(TEN.pow(siPower));
|
||||
@ -195,7 +200,7 @@ function isNewPropsValue (propsValue: BN | string, value: string, valueBn: BN):
|
||||
|
||||
function InputNumber (props: Props): React.ReactElement<Props> {
|
||||
const { t } = useTranslation();
|
||||
const { bitLength = DEFAULT_BITLENGTH, className, defaultValue = ZERO, help, isDecimal, isFull, isSi, isDisabled, isError = false, maxLength, maxValue, onChange, onEnter, onEscape, placeholder, style, value: propsValue } = props;
|
||||
const { bitLength = DEFAULT_BITLENGTH, className, defaultValue = ZERO, defaultSi, help, isDecimal, isFull, isSi, isDisabled, isError = false, maxLength, maxValue, onChange, onEnter, onEscape, placeholder, style, value: propsValue } = props;
|
||||
|
||||
const [si, setSi] = useState<SiDef | null>(isSi ? formatBalance.findSi('-') : null);
|
||||
const [isPreKeyDown, setIsPreKeyDown] = useState(false);
|
||||
@ -254,6 +259,11 @@ function InputNumber (props: Props): React.ReactElement<Props> {
|
||||
}
|
||||
};
|
||||
|
||||
useEffect((): void => {
|
||||
if (defaultSi) {
|
||||
setSi(defaultSi);
|
||||
}
|
||||
}, [defaultSi]);
|
||||
const _onSelectSiUnit = (siUnit: string): void => {
|
||||
setSi(formatBalance.findSi(siUnit));
|
||||
};
|
||||
@ -294,7 +304,7 @@ function InputNumber (props: Props): React.ReactElement<Props> {
|
||||
{t('Max')}
|
||||
</Button>
|
||||
) */}
|
||||
{!!si && (
|
||||
{isSi && !!si && (
|
||||
<Dropdown
|
||||
dropdownClassName='ui--SiDropdown'
|
||||
isButton
|
||||
|
||||
@ -51,6 +51,7 @@ export { default as InputConsts } from './InputConsts';
|
||||
export { default as InputError } from './InputError';
|
||||
export { default as InputExtrinsic } from './InputExtrinsic';
|
||||
export { default as InputFile } from './InputFile';
|
||||
export { default as InputFloat } from './InputFloat';
|
||||
export { default as InputNumber } from './InputNumber';
|
||||
export { default as InputRpc } from './InputRpc';
|
||||
export { default as InputStorage } from './InputStorage';
|
||||
|
||||
44
packages/react-params/src/Param/Float.tsx
Normal file
44
packages/react-params/src/Param/Float.tsx
Normal file
@ -0,0 +1,44 @@
|
||||
// Copyright 2017-2020 @polkadot/react-components authors & contributors
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import { Props } from '../types';
|
||||
|
||||
import BN from 'bn.js';
|
||||
import React from 'react';
|
||||
import { InputFloat } from '@polkadot/react-components';
|
||||
|
||||
import Bare from './Bare';
|
||||
|
||||
export default function Float ({ className, defaultValue: { value }, isDisabled, isError, label, onChange, onEnter, onEscape, style, withLabel }: Props): React.ReactElement<Props> {
|
||||
const defaultValue = new BN((value as BN || '0').toString()).toString(10);
|
||||
const _onChange = (value?: BN): void =>
|
||||
onChange && onChange({
|
||||
isValid: !isError && !!value,
|
||||
value
|
||||
});
|
||||
|
||||
return (
|
||||
<Bare
|
||||
className={className}
|
||||
style={style}
|
||||
>
|
||||
<InputFloat
|
||||
className='full'
|
||||
defaultValue={defaultValue}
|
||||
isDisabled={isDisabled}
|
||||
isError={isError}
|
||||
label={label}
|
||||
onChange={_onChange}
|
||||
withEllipsis
|
||||
onEnter={onEnter}
|
||||
onEscape={onEscape}
|
||||
withLabel={withLabel}
|
||||
/>
|
||||
</Bare>
|
||||
);
|
||||
}
|
||||
|
||||
export {
|
||||
Float
|
||||
};
|
||||
@ -18,6 +18,7 @@ import Call from './Call';
|
||||
import Code from './Code';
|
||||
import DispatchError from './DispatchError';
|
||||
import Enum from './Enum';
|
||||
import Float from './Float';
|
||||
import Hash256 from './Hash256';
|
||||
import Hash512 from './Hash512';
|
||||
import KeyValue from './KeyValue';
|
||||
@ -50,6 +51,7 @@ const componentDef: TypeToComponent[] = [
|
||||
{ c: DispatchError, t: ['DispatchError'] },
|
||||
{ c: Raw, t: ['Raw', 'Keys'] },
|
||||
{ c: Enum, t: ['Enum'] },
|
||||
{ c: Float, t: ['Perbill', 'Fixed64'] },
|
||||
{ c: Hash256, t: ['BlockHash', 'CodeHash', 'Hash', 'H256', 'SeedOf'] },
|
||||
{ c: Hash512, t: ['H512', 'Signature'] },
|
||||
{ c: KeyValue, t: ['KeyValue'] },
|
||||
@ -137,7 +139,7 @@ export default function findComponent (def: TypeDef, overrides: ComponentMap = {
|
||||
// console.error(error.message);
|
||||
}
|
||||
|
||||
// we only want to want once, not spam
|
||||
// we only want to warn once, not spam
|
||||
if (!warnList.includes(type)) {
|
||||
warnList.push(type);
|
||||
console.info(`params: No pre-defined component for type ${type} from ${JSON.stringify(def)}, using defaults`);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user