diff --git a/packages/app-democracy/src/Overview/Referendum.tsx b/packages/app-democracy/src/Overview/Referendum.tsx index ae2ab6f915..c8b525d24c 100644 --- a/packages/app-democracy/src/Overview/Referendum.tsx +++ b/packages/app-democracy/src/Overview/Referendum.tsx @@ -139,6 +139,7 @@ class Referendum extends React.PureComponent { { public render (): React.ReactNode { const { t } = this.props; const { isValid, rpc } = this.state; - const params = rpc.params.map(({ name, type }): { name: string; type: TypeDef } => ({ + const params = rpc.params.map(({ isOptional, name, type }): ParamDef => ({ + isOptional, name, type: getTypeDef(type) })); @@ -76,9 +76,11 @@ class Selection extends TxComponent { this.setState( (prevState: State): State => { const { rpc = prevState.rpc, accountId = prevState.accountId, values = prevState.values } = newState; + const reqCount = rpc.params.reduce((count, { isOptional }): number => count + (isOptional ? 0 : 1), 0); const isValid = values.reduce((isValid, value): boolean => { return isValid && value.isValid === true; - }, rpc.params.length === values.length); + }, reqCount <= values.length); + return { isValid, rpc, diff --git a/packages/react-components/src/Toggle.tsx b/packages/react-components/src/Toggle.tsx index 77d82b366b..813a4a32f4 100644 --- a/packages/react-components/src/Toggle.tsx +++ b/packages/react-components/src/Toggle.tsx @@ -19,7 +19,7 @@ interface Props extends BareProps { class Toggle extends React.PureComponent { public render (): React.ReactNode { - const { className, asSwitch, defaultValue, isDisabled, value, label } = this.props; + const { className, asSwitch = true, defaultValue, isDisabled, value, label } = this.props; return (
diff --git a/packages/react-params/src/Param/Amount.tsx b/packages/react-params/src/Param/Amount.tsx index 693762494b..18d436a77e 100644 --- a/packages/react-params/src/Param/Amount.tsx +++ b/packages/react-params/src/Param/Amount.tsx @@ -29,7 +29,7 @@ export default class Amount extends React.PureComponent { style={style} > { style={style} > { public render (): React.ReactNode { - const { children, className, isDisabled, label, size = 'medium', style, withLabel } = this.props; + const { children, className, label, size = 'full', style, withLabel } = this.props; return ( { style={style} > { style={style} > { } private renderInput (): React.ReactNode { - const { className, defaultValue, isDisabled, isError, label, name, onChange, onEnter, style, type, withLabel } = this.props; + const { className, defaultValue, isDisabled, isOptional, isError, label, name, onChange, onEnter, style, type, withLabel } = this.props; return ( { defaultValue={defaultValue} isDisabled={isDisabled} isError={isError} + isOptional={isOptional} label={label} length={-1} name={name} onChange={onChange} onEnter={onEnter} - size='full' style={style} type={type} withLabel={withLabel} diff --git a/packages/react-params/src/Param/Code.tsx b/packages/react-params/src/Param/Code.tsx index bfef84ebbe..0abde0de24 100644 --- a/packages/react-params/src/Param/Code.tsx +++ b/packages/react-params/src/Param/Code.tsx @@ -31,13 +31,14 @@ export default class Code extends React.PureComponent { } private renderDisabled (): React.ReactNode { - const { className, defaultValue, isError, label, onEnter, style, type, withLabel } = this.props; + const { className, defaultValue, isError, isOptional, label, onEnter, style, type, withLabel } = this.props; return ( { style={style} > { public render (): React.ReactNode { - const { className, defaultValue, isDisabled, isError, label, name, onChange, onEnter, style, type, withLabel } = this.props; + const { className, defaultValue, isDisabled, isError, isOptional, label, name, onChange, onEnter, style, type, withLabel } = this.props; return ( { defaultValue={defaultValue} isDisabled={isDisabled} isError={isError} + isOptional={isOptional} label={label} length={32} name={name} onChange={onChange} onEnter={onEnter} - size={isDisabled ? 'full' : 'large'} style={style} type={type} withLabel={withLabel} diff --git a/packages/react-params/src/Param/KeyValueArray.tsx b/packages/react-params/src/Param/KeyValueArray.tsx index 329dd74d7b..d15d3e5e07 100644 --- a/packages/react-params/src/Param/KeyValueArray.tsx +++ b/packages/react-params/src/Param/KeyValueArray.tsx @@ -75,7 +75,6 @@ class KeyValueArray extends React.PureComponent {
@@ -88,6 +87,7 @@ class KeyValueArray extends React.PureComponent { { className={className} defaultValue={defaultValue} isDisabled={isDisabled} + isOptional={false} isError={isError} label={label} onChange={this.onChange} diff --git a/packages/react-params/src/Param/Static.tsx b/packages/react-params/src/Param/Static.tsx index d86bca4212..e91f63c20c 100644 --- a/packages/react-params/src/Param/Static.tsx +++ b/packages/react-params/src/Param/Static.tsx @@ -11,11 +11,11 @@ import translate from '@polkadot/react-components/translate'; import Bare from './Bare'; -type Props = BareProps & WithTranslation & { +interface Props extends BareProps, WithTranslation { asHex?: boolean; defaultValue: RawParam; withLabel?: boolean; -}; +} class StaticParam extends React.PureComponent { public render (): React.ReactNode { diff --git a/packages/react-params/src/Param/Struct.tsx b/packages/react-params/src/Param/Struct.tsx index 6c51ed5d47..a339173702 100644 --- a/packages/react-params/src/Param/Struct.tsx +++ b/packages/react-params/src/Param/Struct.tsx @@ -53,7 +53,6 @@ export default class StructParam extends React.PureComponent { diff --git a/packages/react-params/src/Param/Tuple.tsx b/packages/react-params/src/Param/Tuple.tsx index e72029cdcc..227f2d2fc9 100644 --- a/packages/react-params/src/Param/Tuple.tsx +++ b/packages/react-params/src/Param/Tuple.tsx @@ -66,6 +66,7 @@ export default class Tuple extends React.PureComponent { { public render (): React.ReactNode { - const { className, defaultValue, isDisabled, isError, label, name, onChange, onEnter, style, type } = this.props; + const { className, defaultValue, isDisabled, isError, isOptional, label, name, onChange, onEnter, style, type } = this.props; if (isDisabled) { return ; @@ -29,12 +29,12 @@ export default class Unknown extends React.PureComponent { defaultValue={defaultValue} isDisabled={isDisabled} isError={isError} + isOptional={isOptional} label={label} length={-1} name={name} onChange={onChange} onEnter={onEnter} - size='full' style={style} type={type} withLength={false} diff --git a/packages/react-params/src/Param/Vector.tsx b/packages/react-params/src/Param/Vector.tsx index f4a7c807a4..bd51906172 100644 --- a/packages/react-params/src/Param/Vector.tsx +++ b/packages/react-params/src/Param/Vector.tsx @@ -73,6 +73,7 @@ class Vector extends React.PureComponent { { style={style} > { style={style} > | null; @@ -41,7 +41,7 @@ class ParamComponent extends React.PureComponent { return null; } - const { className, defaultValue, isDisabled, name, onChange, onEnter, style, type } = this.props; + const { className, defaultValue, isDisabled, isOptional, name, onChange, onEnter, style, type } = this.props; return ( { defaultValue={defaultValue} key={`${name}:${type}`} isDisabled={isDisabled} + isOptional={isOptional} label={ isUndefined(name) ? type.type diff --git a/packages/react-params/src/Params.css b/packages/react-params/src/Params.css index aa8d2c2436..de461f5ce5 100644 --- a/packages/react-params/src/Params.css +++ b/packages/react-params/src/Params.css @@ -5,7 +5,7 @@ .ui--Params { border-left: 0.25rem solid #f2f2f2; - label { + .ui--Param label { text-transform: none !important; font-family: monospace; } @@ -13,12 +13,28 @@ .ui--row { flex-wrap: wrap; } + + .ui--Param-overlay { + margin-bottom: -100%; + z-index: 1; + text-align: right; + } } .ui--Param-Address { font-family: monospace; } +.ui--Param-composite { + position: relative; + + .ui--Param-overlay { + position: absolute; + top: 0.5rem; + right: 4.5rem; + } +} + .ui--Params-Content { box-sizing: border-box; padding: 0 0 0 1.75rem; diff --git a/packages/react-params/src/index.tsx b/packages/react-params/src/index.tsx index 8664127453..9fd02a9204 100644 --- a/packages/react-params/src/index.tsx +++ b/packages/react-params/src/index.tsx @@ -2,37 +2,35 @@ // This software may be modified and distributed under the terms // of the Apache-2.0 license. See the LICENSE file for details. -import { TypeDef } from '@polkadot/types/types'; import { I18nProps } from '@polkadot/react-components/types'; -import { ComponentMap, RawParam, RawParams, RawParamOnChange, RawParamOnChangeValue } from './types'; +import { ComponentMap, ParamDef, RawParam, RawParams, RawParamOnChange, RawParamOnChangeValue } from './types'; import './Params.css'; import React from 'react'; +import { Toggle } from '@polkadot/react-components'; import { classes } from '@polkadot/react-components/util'; import translate from '@polkadot/react-components/translate'; import Param from './Param'; import { createValue } from './values'; -interface Param { - name?: string; - type: TypeDef; -} - interface Props extends I18nProps { isDisabled?: boolean; onChange?: (value: RawParams) => void; onEnter?: () => void; overrides?: ComponentMap; - params: Param[]; + params: ParamDef[]; values?: RawParams | null; } interface State { - handlers?: RawParamOnChange[]; + handlersChange?: RawParamOnChange[]; + handlersOption?: ((value: boolean) => void)[]; + onChangeOption: (at: number, value: boolean) => void; onChangeParam: (at: number, next: RawParamOnChangeValue) => void; - params?: Param[]; + optional?: boolean[]; + params?: ParamDef[]; values?: RawParams; } @@ -43,38 +41,48 @@ class Params extends React.PureComponent { super(props); this.state = { + onChangeOption: this.onChangeOption, onChangeParam: this.onChangeParam }; } - public static getDerivedStateFromProps (props: Props, { params, onChangeParam }: State): State | null { - const isSame = JSON.stringify(params) === JSON.stringify(props.params); + public static getDerivedStateFromProps ({ isDisabled, params, values }: Props, prevState: State): Pick | null { + const isSame = JSON.stringify(prevState.params) === JSON.stringify(params); - if (props.isDisabled || isSame) { + if (isDisabled || isSame) { return null; } - const values = props.params.reduce( + const newValues = params.reduce( (result: RawParams, param, index): RawParams => [ ...result, - props.values && props.values[index] - ? props.values[index] + values && values[index] + ? values[index] : createValue(param) ], [] ); - const handlers = values.map( + const handlersChange = newValues.map( (_, index): RawParamOnChange => (value: RawParamOnChangeValue): void => - onChangeParam(index, value) + prevState.onChangeParam(index, value) + ); + + const optional = params.map(({ isOptional }): boolean => !isOptional); + + const handlersOption = params.map( + (_, index): (value: boolean) => void => + (value: boolean): void => + prevState.onChangeOption(index, value) ); return { - handlers, - onChangeParam, - params: props.params, - values + handlersChange, + handlersOption, + optional, + params, + values: newValues }; } @@ -86,19 +94,18 @@ class Params extends React.PureComponent { // This is needed in the case where the item changes, i.e. the values get // initialised and we need to alert the parent that we have new values public componentDidUpdate (_: Props, prevState: State): void { - const { onChange, isDisabled } = this.props; + const { isDisabled } = this.props; const { values } = this.state; if (!isDisabled && prevState.values !== values) { - onChange && onChange(values || []); + this.triggerUpdate(); } } public render (): React.ReactNode { - const { className, isDisabled, onEnter, overrides, params, style } = this.props; - const { handlers = [], values = this.props.values } = this.state; + const { className, params, style } = this.props; - if (!params || params.length === 0 || !values || values.length === 0) { + if (!params || params.length === 0) { return null; } @@ -108,23 +115,93 @@ class Params extends React.PureComponent { style={style} >
- {params.map(({ name, type }, index): React.ReactNode => ( - - ))} + {params.map(this.renderComponent)}
); } + private renderComponent = ({ isOptional = false, name, type }: ParamDef, index: number): React.ReactNode => { + const { isDisabled, onEnter, overrides } = this.props; + const { handlersChange = [], handlersOption = [], optional = [], values = this.props.values } = this.state; + const key = `${name}:${type}:${index}`; + + if (!values || values.length === 0) { + return null; + } + + const isDisabledOptional = isOptional && !optional[index]; + + return ( +
+ + { + isOptional + ? ( + + ) + : null + } +
+ ); + } + + private extractValues (): RawParam[] { + const { optional, values } = this.state; + const { isDisabled } = this.props; + + if (isDisabled || !optional || !values) { + return []; + } + + return values + .map((value, index): RawParam | null => + optional[index] + ? value + : null + ) + .filter((value): boolean => !!value) as RawParam[]; + } + + private onChangeOption = (at: number, value: boolean): void => { + this.setState( + (prevState: State): Pick => ({ + optional: (prevState.optional || []).map((prev, index): boolean => + index !== at + ? prev + : value + ) + }), + this.triggerUpdate + ); + } + private onChangeParam = (at: number, newValue: RawParamOnChangeValue): void => { const { isDisabled } = this.props; @@ -139,10 +216,7 @@ class Params extends React.PureComponent { values: (prevState.values || []).map((prev, index): RawParam => index !== at ? prev - : { - isValid, - value - } + : { isValid, value } ) }), this.triggerUpdate @@ -150,14 +224,14 @@ class Params extends React.PureComponent { } private triggerUpdate = (): void => { - const { values } = this.state; + const { optional, values } = this.state; const { onChange, isDisabled } = this.props; - if (isDisabled || !values) { + if (isDisabled || !optional || !values) { return; } - onChange && onChange(values); + onChange && onChange(this.extractValues()); } } diff --git a/packages/react-params/src/types.ts b/packages/react-params/src/types.ts index 08615b4f1c..8513d75a09 100644 --- a/packages/react-params/src/types.ts +++ b/packages/react-params/src/types.ts @@ -29,6 +29,7 @@ export type RawParams = RawParam[]; export interface BaseProps extends BareProps { defaultValue: RawParam; + isOptional: boolean; name?: string; onChange?: RawParamOnChange; onEnter?: RawParamOnEnter; @@ -46,3 +47,9 @@ export interface Props extends BaseProps { export type Size = 'full' | 'large' | 'medium' | 'small'; export type ComponentMap = Record>; + +export interface ParamDef { + isOptional?: boolean; + name?: string; + type: TypeDef; +}