diff --git a/packages/ui-params/src/Param/Enum.tsx b/packages/ui-params/src/Param/Enum.tsx new file mode 100644 index 0000000000..05867dfee0 --- /dev/null +++ b/packages/ui-params/src/Param/Enum.tsx @@ -0,0 +1,113 @@ +// Copyright 2017-2019 @polkadot/ui-app 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 as BaseProps, RawParam } from '../types'; + +import React from 'react'; +import { Enum, TypeDef, createType, getTypeDef } from '@polkadot/types'; +import { Dropdown } from '@polkadot/ui-app'; + +import Params from '../'; +import Bare from './Bare'; +import Static from './Static'; + +type Props = BaseProps; + +type State = { + def: TypeDef | null, + options: Array<{ text: string, value: string }>, + sub: Array, + type: string | null +}; + +export default class EnumParam extends React.PureComponent { + state: State = { + def: null, + options: [], + sub: [], + type: null + }; + + static getDerivedStateFromProps ({ type: { type } }: Props, prevState: State) { + if (prevState.type === type) { + return null; + } + + const sub = getTypeDef(createType(type).toRawType()).sub as Array; + const options = sub.map(({ name }) => ({ + text: name, + value: name + })); + const def = prevState.def || sub[0]; + + return { + def, + options, + sub, + type + } as State; + } + + render () { + const { className, defaultValue, isDisabled, isError, label, style, withLabel } = this.props; + + if (isDisabled) { + return ; + } + + const { def, options } = this.state; + const initialValue = defaultValue && defaultValue.value + ? defaultValue.value instanceof Enum + ? defaultValue.value.type + : Object.keys(defaultValue.value)[0] + : defaultValue; + + return ( + + + {def && ( + + )} + + ); + } + + private onChange = (value: string): void => { + const { sub } = this.state; + + this.setState({ + def: sub.find(({ name }) => name === value) || null + }); + } + + private onChangeParam = ([{ isValid, value }]: Array): void => { + const { onChange } = this.props; + const { def } = this.state; + + if (def) { + onChange && onChange({ + isValid, + value: { + [def.name as string]: value + } + }); + } + } +} diff --git a/packages/ui-params/src/Param/Null.tsx b/packages/ui-params/src/Param/Null.tsx new file mode 100644 index 0000000000..cffc214e31 --- /dev/null +++ b/packages/ui-params/src/Param/Null.tsx @@ -0,0 +1,22 @@ +// Copyright 2017-2019 @polkadot/ui-app 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 React from 'react'; + +export default class Null extends React.PureComponent { + componentDidMount () { + const { onChange } = this.props; + + onChange && onChange({ + isValid: true, + value: null + }); + } + + render () { + return null; + } +} diff --git a/packages/ui-params/src/Param/Static.tsx b/packages/ui-params/src/Param/Static.tsx new file mode 100644 index 0000000000..15c18c3949 --- /dev/null +++ b/packages/ui-params/src/Param/Static.tsx @@ -0,0 +1,39 @@ +// Copyright 2017-2019 @polkadot/ui-app 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 { WithTranslation } from 'react-i18next'; +import { Props as BareProps, RawParam } from '../types'; + +import React from 'react'; +import { Static } from '@polkadot/ui-app'; +import translate from '@polkadot/ui-app/translate'; + +import Bare from './Bare'; + +type Props = BareProps & WithTranslation & { + defaultValue: RawParam, + withLabel?: boolean +}; + +class StaticParam extends React.PureComponent { + render () { + const { className, defaultValue, label, style, t } = this.props; + const value = defaultValue && defaultValue.value && defaultValue.value.toString(); + + return ( + + + + ); + } +} + +export default translate(StaticParam); diff --git a/packages/ui-params/src/Param/Struct.tsx b/packages/ui-params/src/Param/Struct.tsx new file mode 100644 index 0000000000..e459fbafb9 --- /dev/null +++ b/packages/ui-params/src/Param/Struct.tsx @@ -0,0 +1,85 @@ +// Copyright 2017-2019 @polkadot/ui-app 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 as BaseProps, RawParam } from '../types'; + +import React from 'react'; +import { TypeDef, createType, getTypeDef } from '@polkadot/types'; + +import Params from '../'; +import Base from './Base'; +import Static from './Static'; + +type Props = BaseProps; + +type State = { + defs: Array, + type: string | null +}; + +export default class StructParam extends React.PureComponent { + state: State = { + defs: [], + type: null + }; + + static getDerivedStateFromProps ({ type: { type } }: Props, prevState: State) { + if (prevState.type === type) { + return null; + } + + const defs = getTypeDef(createType(type).toRawType()).sub as Array; + + return { + defs, + type + } as State; + } + + render () { + const { className, isDisabled, label, style, withLabel } = this.props; + + if (isDisabled) { + return ; + } + + const { defs } = this.state; + const params = defs.map((type) => ({ name: type.name, type })); + + return ( +
+ +   + + +
+ ); + } + + private onChangeParams = (values: Array) => { + const { onChange } = this.props; + + if (onChange) { + const { defs } = this.state; + + onChange({ + isValid: values.reduce((result, { isValid }) => result && isValid, true as boolean), + value: defs.reduce((value, { name }, index) => { + value[name as string] = values[index].value; + + return value; + }, {} as { [index: string]: any }) + }); + } + } +} diff --git a/packages/ui-params/src/Param/Unknown.tsx b/packages/ui-params/src/Param/Unknown.tsx index 409c55d962..a3add02d01 100644 --- a/packages/ui-params/src/Param/Unknown.tsx +++ b/packages/ui-params/src/Param/Unknown.tsx @@ -2,40 +2,24 @@ // This software may be modified and distributed under the terms // of the Apache-2.0 license. See the LICENSE file for details. -import { WithTranslation } from 'react-i18next'; import { Props as BareProps, RawParam } from '../types'; import React from 'react'; -import { Static } from '@polkadot/ui-app'; -import translate from '@polkadot/ui-app/translate'; -import Bare from './Bare'; import BaseBytes from './BaseBytes'; +import Static from './Static'; -type Props = BareProps & WithTranslation & { +type Props = BareProps & { defaultValue: RawParam, withLabel?: boolean }; -class Unknown extends React.PureComponent { +export default class Unknown extends React.PureComponent { render () { - const { className, defaultValue, isDisabled, isError, label, name, onChange, onEnter, style, t, type } = this.props; + const { className, defaultValue, isDisabled, isError, label, name, onChange, onEnter, style, type } = this.props; if (isDisabled) { - const value = defaultValue && defaultValue.value && defaultValue.value.toString(); - - return ( - - - - ); + return ; } return ( @@ -57,5 +41,3 @@ class Unknown extends React.PureComponent { ); } } - -export default translate(Unknown); diff --git a/packages/ui-params/src/Param/findComponent.ts b/packages/ui-params/src/Param/findComponent.ts index 59b0c34ce2..5c93da9cf6 100644 --- a/packages/ui-params/src/Param/findComponent.ts +++ b/packages/ui-params/src/Param/findComponent.ts @@ -2,20 +2,25 @@ // This software may be modified and distributed under the terms // of the Apache-2.0 license. See the LICENSE file for details. -import { TypeDef, TypeDefInfo, UInt, createType } from '@polkadot/types'; import { Props, ComponentMap } from '../types'; +import BN from 'bn.js'; +import { TypeDef, TypeDefInfo, createType, getTypeDef } from '@polkadot/types'; + import Account from './Account'; import Amount from './Amount'; import Balance from './Balance'; import Bool from './Bool'; import Bytes from './Bytes'; import Code from './Code'; +import Enum from './Enum'; import Hash from './Hash'; import Moment from './Moment'; import Proposal from './Proposal'; import KeyValue from './KeyValue'; import KeyValueArray from './KeyValueArray'; +import Null from './Null'; +import Struct from './Struct'; import Text from './Text'; import Tuple from './Tuple'; import Unknown from './Unknown'; @@ -35,16 +40,20 @@ const components: ComponentMap = ([ { c: Bool, t: ['bool'] }, { c: Bytes, t: ['Bytes'] }, { c: Code, t: ['Code'] }, + { c: Enum, t: ['Enum'] }, { c: Hash, t: ['CodeHash', 'Hash', 'SeedOf', 'Signature'] }, { c: KeyValue, t: ['KeyValue'] }, { c: KeyValueArray, t: ['Vec'] }, { c: Moment, t: ['Moment', 'MomentOf'] }, + { c: Null, t: ['Null'] }, { c: Proposal, t: ['Proposal'] }, { c: Text, t: ['String', 'Text'] }, + { c: Struct, t: ['Struct'] }, { c: Tuple, t: ['Tuple'] }, { c: Vector, t: ['Vector'] }, { c: Vote, t: ['Vote'] }, - { c: VoteThreshold, t: ['VoteThreshold'] } + { c: VoteThreshold, t: ['VoteThreshold'] }, + { c: Unknown, t: ['Unknown'] } ] as Array).reduce((components, { c, t }) => { t.forEach((type) => { components[type] = c; @@ -57,8 +66,15 @@ export default function findComponent (def: TypeDef, overrides: ComponentMap = { const type = (({ info, sub, type }: TypeDef) => { switch (info) { case TypeDefInfo.Compact: + case TypeDefInfo.Option: return (sub as TypeDef).type; + case TypeDefInfo.Enum: + return 'Enum'; + + case TypeDefInfo.Struct: + return 'Struct'; + case TypeDefInfo.Tuple: return 'Tuple'; @@ -77,9 +93,12 @@ export default function findComponent (def: TypeDef, overrides: ComponentMap = { if (!Component) { try { const instance = createType(type); + const raw = getTypeDef(instance.toRawType()); - if (instance instanceof UInt) { + if (instance instanceof BN) { return Amount; + } else if ([TypeDefInfo.Enum, TypeDefInfo.Struct].includes(raw.info)) { + return findComponent(raw, overrides); } } catch (error) { // console.error(error.message); diff --git a/packages/ui-params/src/Params.css b/packages/ui-params/src/Params.css index 4fe161feea..158878423f 100644 --- a/packages/ui-params/src/Params.css +++ b/packages/ui-params/src/Params.css @@ -17,6 +17,10 @@ .ui--Params-Content { box-sizing: border-box; padding: 0 0 0 1.75rem; + + label .withEllipsis { + white-space: normal; + } } .ui--Param-text { diff --git a/packages/ui-params/src/initValue.ts b/packages/ui-params/src/initValue.ts index dac323e781..d53bdca222 100644 --- a/packages/ui-params/src/initValue.ts +++ b/packages/ui-params/src/initValue.ts @@ -5,7 +5,7 @@ import { RawParam$Value } from './types'; import BN from 'bn.js'; -import { Bytes, Hash, TypeDef, TypeDefInfo, U8a, UInt, createType } from '@polkadot/types'; +import { Bytes, Hash, TypeDef, TypeDefInfo, U8a, createType, getTypeDef } from '@polkadot/types'; export default function getInitValue (def: TypeDef): RawParam$Value | Array { if (def.info === TypeDefInfo.Vector) { @@ -15,11 +15,20 @@ export default function getInitValue (def: TypeDef): RawParam$Value | Array getInitValue(def)) : []; } else if (def.info === TypeDefInfo.Struct) { - console.error(`Unable to determine default type from Struct ${JSON.stringify(def)}`); - return void 0; + return Array.isArray(def.sub) + ? def.sub.reduce((result, def) => { + result[def.name as string] = getInitValue(def); + + return result; + }, {} as { [index: string]: RawParam$Value | Array }) + : {}; + } else if (def.info === TypeDefInfo.Enum) { + return Array.isArray(def.sub) + ? { [def.sub[0].name as string]: getInitValue(def.sub[0]) } + : {}; } - const type = def.info === TypeDefInfo.Compact + const type = [TypeDefInfo.Compact, TypeDefInfo.Option].includes(def.info) ? (def.sub as TypeDef).type : def.type; @@ -84,12 +93,18 @@ export default function getInitValue (def: TypeDef): RawParam$Value | Array