Enum, Struct & Tuple as func. components (#1727)
* Enum, Struct & Tuple as func. components * type.type * Fix linting * default export
This commit is contained in:
parent
42a7b98779
commit
171bd77909
@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user