Enum, Struct & Tuple as func. components (#1727)

* Enum, Struct & Tuple as func. components

* type.type

* Fix linting

* default export
This commit is contained in:
Jaco Greeff 2019-10-16 12:42:58 +02:00 committed by GitHub
parent 42a7b98779
commit 171bd77909
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 152 additions and 238 deletions

View File

@ -3,9 +3,9 @@
// of the Apache-2.0 license. See the LICENSE file for details.
import { TypeDef } from '@polkadot/types/types';
import { Props, RawParam } from '../types';
import { ParamDef, Props, RawParam } from '../types';
import React from 'react';
import React, { useEffect, useState } from 'react';
import { Enum, createType, getTypeDef } from '@polkadot/types';
import { Dropdown } from '@polkadot/react-components';
@ -18,103 +18,80 @@ interface Option {
value?: string;
}
interface State {
def: TypeDef | null;
options: Option[];
sub: TypeDef[];
type: string | null;
}
export default function EnumParam (props: Props): React.ReactElement<Props> {
const { className, defaultValue, isDisabled, isError, label, onChange, style, type, withLabel } = props;
const [current, setCurrent] = useState<ParamDef[] | null>(null);
const [initialValue, setInitialValue] = useState<string | null>(null);
const [{ options, subTypes }, setOptions] = useState<{ options: Option[]; subTypes: TypeDef[] }>({ options: [], subTypes: [] });
export default class EnumParam extends React.PureComponent<Props, State> {
public state: State = {
def: null,
options: [],
sub: [],
type: null
};
public static getDerivedStateFromProps ({ type: { type } }: Props, prevState: State): State | null {
if (prevState.type === type) {
return null;
}
const rawType = createType(type as any).toRawType();
useEffect((): void => {
const rawType = createType(type.type as any).toRawType();
const typeDef = getTypeDef(rawType);
const subTypes = typeDef.sub as TypeDef[];
// HACK This is a quick hack to allow `Option<struct>` ... this is certainly not the right
// place for this, so we need to move it (even the detection just sucks)... also see struct
const sub = typeDef.type.startsWith('Option<')
? (typeDef.sub as TypeDef).sub as TypeDef[]
: typeDef.sub as TypeDef[];
return {
def: prevState.def || sub[0],
options: sub.map(({ name }): Option => ({
setOptions({
options: subTypes.map(({ name }): Option => ({
text: name,
value: name
})),
sub,
type
};
}
subTypes
});
setCurrent([{ name: subTypes[0].name, type: subTypes[0] }]);
}, [type]);
public render (): React.ReactNode {
const { className, defaultValue, isDisabled, isError, label, style, withLabel } = this.props;
if (isDisabled) {
return <Static {...this.props} />;
}
const { def, options } = this.state;
const initialValue = defaultValue && defaultValue.value
? defaultValue.value instanceof Enum
? defaultValue.value.type
: Object.keys(defaultValue.value)[0]
: defaultValue;
return (
<Bare
className={className}
style={style}
>
<Dropdown
className='full'
defaultValue={initialValue}
isDisabled={isDisabled}
isError={isError}
label={label}
options={options}
onChange={this.onChange}
withEllipsis
withLabel={withLabel}
/>
{def && (
<Params
onChange={this.onChangeParam}
params={[{ name: def.name, type: def }]}
/>
)}
</Bare>
useEffect((): void => {
setInitialValue(
defaultValue && defaultValue.value
? defaultValue.value instanceof Enum
? defaultValue.value.type
: Object.keys(defaultValue.value)[0]
: null
);
}, [defaultValue]);
if (isDisabled) {
return <Static {...props} />;
}
private onChange = (value: string): void => {
this.setState(({ sub }): Pick<State, never> => ({
def: sub.find(({ name }): boolean => name === value) || null
}));
}
const _onChange = (value: string): void => {
const newType = subTypes.find(({ name }): boolean => name === value) || null;
private onChangeParam = ([{ isValid, value }]: RawParam[]): void => {
const { onChange } = this.props;
const { def } = this.state;
setCurrent(
newType
? [{ name: newType.name, type: newType }]
: null
);
};
if (def) {
onChange && onChange({
isValid,
value: {
[def.name as string]: value
}
});
}
}
const _onChangeParam = ([{ isValid, value }]: RawParam[]): void => {
current && onChange && onChange({
isValid,
value: { [current[0].name as string]: value }
});
};
return (
<Bare
className={className}
style={style}
>
<Dropdown
className='full'
defaultValue={initialValue}
isDisabled={isDisabled}
isError={isError}
label={label}
options={options}
onChange={_onChange}
withEllipsis
withLabel={withLabel}
/>
{current && (
<Params
onChange={_onChangeParam}
params={current}
/>
)}
</Bare>
);
}

View File

@ -3,86 +3,53 @@
// of the Apache-2.0 license. See the LICENSE file for details.
import { TypeDef } from '@polkadot/types/types';
import { Props, RawParam } from '../types';
import { ParamDef, Props, RawParam } from '../types';
import React from 'react';
import React, { useEffect, useState } from 'react';
import { createType, getTypeDef } from '@polkadot/types';
import Params from '../';
import Base from './Base';
import Static from './Static';
interface State {
defs: TypeDef[];
type: string | null;
}
export default function StructParam (props: Props): React.ReactElement<Props> {
const { className, isDisabled, label, onChange, style, type, withLabel } = props;
const [params, setParams] = useState<ParamDef[]>([]);
export default class StructParam extends React.PureComponent<Props, State> {
public state: State = {
defs: [],
type: null
};
public static getDerivedStateFromProps ({ type: { type } }: Props, prevState: State): State | null {
if (prevState.type === type) {
return null;
}
const rawType = createType(type as any).toRawType();
useEffect((): void => {
const rawType = createType(type.type as any).toRawType();
const typeDef = getTypeDef(rawType);
// HACK This is a quick hack to allow `Option<struct>` ... this is certainly not the right
// place for this, so we need to move it (even the detection just sucks)... also see enum
const defs = typeDef.type.startsWith('Option<')
? (typeDef.sub as TypeDef).sub as TypeDef[]
: typeDef.sub as TypeDef[];
setParams((typeDef.sub as TypeDef[]).map((type): ParamDef => ({ name: type.name, type })));
}, [type]);
return {
defs,
type
} as unknown as State;
if (isDisabled) {
return <Static {...props} />;
}
public render (): React.ReactNode {
const { className, isDisabled, label, style, withLabel } = this.props;
const _onChangeParams = (values: RawParam[]): void => {
onChange && onChange({
isValid: values.reduce((result, { isValid }): boolean => result && isValid, true as boolean),
value: params.reduce((value: Record<string, any>, { name }, index): Record<string, any> => {
value[name as string] = values[index].value;
if (isDisabled) {
return <Static {...this.props} />;
}
return value;
}, {})
});
};
const { defs } = this.state;
const params = defs.map((type): { name?: string; type: TypeDef } => ({ name: type.name, type }));
return (
<div className='ui--Params-Struct'>
<Base
className={className}
label={label}
style={style}
withLabel={withLabel}
/>
<Params
onChange={this.onChangeParams}
params={params}
/>
</div>
);
}
private onChangeParams = (values: RawParam[]): void => {
const { onChange } = this.props;
if (onChange) {
const { defs } = this.state;
onChange({
isValid: values.reduce((result, { isValid }): boolean => result && isValid, true as boolean),
value: defs.reduce((value, { name }, index): Record<string, any> => {
value[name as string] = values[index].value;
return value;
}, {} as unknown as Record<string, any>)
});
}
}
return (
<div className='ui--Params-Struct'>
<Base
className={className}
label={label}
style={style}
withLabel={withLabel}
/>
<Params
onChange={_onChangeParams}
params={params}
/>
</div>
);
}

View File

@ -5,99 +5,69 @@
import { Codec, TypeDef } from '@polkadot/types/types';
import { Props, RawParam } from '../types';
import React from 'react';
import React, { useEffect, useState } from 'react';
import { isUndefined } from '@polkadot/util';
import Bare from './Bare';
import findComponent from './findComponent';
interface State {
Components: React.ComponentType<Props>[];
sub: string[];
subTypes: TypeDef[];
type?: string;
values: RawParam[];
}
export default function Tuple ({ className, defaultValue, isDisabled, onChange, onEnter, style, type, withLabel }: Props): React.ReactElement<Props> {
const [{ Components, subTypes }, setComponents] = useState<{ Components: React.ComponentType<Props>[]; subTypes: TypeDef[] }>({ Components: [], subTypes: [] });
const [values, setValues] = useState<RawParam[]>([]);
export default class Tuple extends React.PureComponent<Props, State> {
public state: State = {
Components: [],
sub: [],
subTypes: [],
values: []
};
public static getDerivedStateFromProps ({ defaultValue: { value }, type: { sub, type } }: Props, prevState: State): Partial<State> | null {
if (type === prevState.type) {
return null;
}
const subTypes = sub && Array.isArray(sub)
? sub
useEffect((): void => {
const subTypes: TypeDef[] = type.sub && Array.isArray(type.sub)
? type.sub
: [];
const values = (value as any[]).map((value): { isValid: boolean; value: Codec } =>
isUndefined(value) || isUndefined(value.isValid)
? {
isValid: !isUndefined(value),
value
}
: value
);
return {
setComponents({
Components: subTypes.map((type): React.ComponentType<Props> => findComponent(type)),
sub: subTypes.map(({ type }): string => type),
subTypes,
type,
values
};
}
subTypes
});
}, [type]);
public render (): React.ReactNode {
const { className, isDisabled, onEnter, style, withLabel } = this.props;
const { Components, sub, subTypes, values } = this.state;
return (
<Bare
className={className}
style={style}
>
{Components.map((Component, index): React.ReactNode => (
<Component
defaultValue={values[index] || {}}
isDisabled={isDisabled}
key={index}
label={sub[index]}
onChange={this.onChange(index)}
onEnter={onEnter}
type={subTypes[index]}
withLabel={withLabel}
/>
))}
</Bare>
useEffect((): void => {
setValues(
(((defaultValue && defaultValue.value) || []) as any[]).map((value): { isValid: boolean; value: Codec } =>
isUndefined(value) || isUndefined(value.isValid)
? { isValid: !isUndefined(value), value }
: value
)
);
}
}, [defaultValue]);
private onChange = (index: number): (value: RawParam) => void => {
return (value: RawParam): void => {
this.setState(
({ values }: State): State => ({
values: values.map((svalue, sindex): RawParam =>
(sindex === index)
? value
: svalue
)
} as unknown as State),
(): void => {
const { values } = this.state;
const { onChange } = this.props;
useEffect((): void => {
onChange && onChange({
isValid: values.reduce((result: boolean, { isValid }): boolean => result && isValid, true),
value: values.map(({ value }): any => value)
});
}, [values]);
onChange && onChange({
isValid: values.reduce((result: boolean, { isValid }): boolean => result && isValid, true),
value: values.map(({ value }): any => value)
});
}
);
};
}
const _onChange = (index: number): (value: RawParam) => void =>
(value: RawParam): void =>
setValues(values.map((prev, prevIndex): RawParam =>
(prevIndex === index)
? value
: prev
));
return (
<Bare
className={className}
style={style}
>
{Components.map((Component, index): React.ReactNode => (
<Component
defaultValue={values[index] || {}}
isDisabled={isDisabled}
key={index}
label={subTypes[index].type}
onChange={_onChange(index)}
onEnter={onEnter}
type={subTypes[index]}
withLabel={withLabel}
/>
))}
</Bare>
);
}