diff --git a/packages/react-components/src/InputFloat.tsx b/packages/react-components/src/InputFloat.tsx new file mode 100644 index 0000000000..d6c07c1838 --- /dev/null +++ b/packages/react-components/src/InputFloat.tsx @@ -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 { + const defaultValue = inDefault; + + const multiplier = formatBalance.findSi('G'); + + return ( + + ); +} + +export default styled(InputFloat)` + &&:not(.label-small) .labelExtra { + right: 6.5rem; + } +`; diff --git a/packages/react-components/src/InputNumber.tsx b/packages/react-components/src/InputNumber.tsx index 8aec4e3222..dd7cfb75f7 100644 --- a/packages/react-components/src/InputNumber.tsx +++ b/packages/react-components/src/InputNumber.tsx @@ -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 { 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(isSi ? formatBalance.findSi('-') : null); const [isPreKeyDown, setIsPreKeyDown] = useState(false); @@ -254,6 +259,11 @@ function InputNumber (props: Props): React.ReactElement { } }; + 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 { {t('Max')} ) */} - {!!si && ( + {isSi && !!si && ( { + const defaultValue = new BN((value as BN || '0').toString()).toString(10); + const _onChange = (value?: BN): void => + onChange && onChange({ + isValid: !isError && !!value, + value + }); + + return ( + + + + ); +} + +export { + Float +}; diff --git a/packages/react-params/src/Param/findComponent.ts b/packages/react-params/src/Param/findComponent.ts index 40153429bb..3bddc6b828 100644 --- a/packages/react-params/src/Param/findComponent.ts +++ b/packages/react-params/src/Param/findComponent.ts @@ -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`);