ghost-portal/packages/react-params/src/index.tsx
Jaco Greeff f5b0247270
Additional func. component conversions (#1659)
* Additional func. component conversions

* rendering function cleanups (pre-convert)

* Additional

* Forks to functional
2019-09-26 16:16:07 +02:00

246 lines
6.7 KiB
TypeScript

// Copyright 2017-2019 @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 { I18nProps } from '@polkadot/react-components/types';
import { ComponentMap, ParamDef, RawParam, RawParams, RawParamOnChange, RawParamOnChangeValue } from './types';
import './Params.css';
import React from 'react';
import styled from 'styled-components';
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 Props extends I18nProps {
isDisabled?: boolean;
onChange?: (value: RawParams) => void;
onEnter?: () => void;
overrides?: ComponentMap;
params: ParamDef[];
values?: RawParams | null;
}
interface State {
handlersChange?: RawParamOnChange[];
handlersOption?: ((value: boolean) => void)[];
onChangeOption: (at: number, value: boolean) => void;
onChangeParam: (at: number, next: RawParamOnChangeValue) => void;
optional?: boolean[];
params?: ParamDef[];
values?: RawParams;
}
const DISABLED_OPTIONAL = { isValid: true, value: undefined };
class Params extends React.PureComponent<Props, State> {
public state: State;
public constructor (props: Props) {
super(props);
this.state = {
onChangeOption: this.onChangeOption,
onChangeParam: this.onChangeParam
};
}
public static getDerivedStateFromProps ({ isDisabled, params, values }: Props, prevState: State): Pick<State, never> | null {
const isSame = JSON.stringify(prevState.params) === JSON.stringify(params);
if (isDisabled || isSame) {
return null;
}
const newValues = params.reduce(
(result: RawParams, param, index): RawParams => [
...result,
values && values[index]
? values[index]
: createValue(param)
],
[]
);
const handlersChange = newValues.map(
(_, index): RawParamOnChange =>
(value: RawParamOnChangeValue): void =>
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 {
handlersChange,
handlersOption,
optional,
params,
values: newValues
};
}
// Fire the intial onChange (we did update) when the component is loaded
public componentDidMount (): void {
this.componentDidUpdate({} as unknown as Props, {} as unknown as State);
}
// 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 { isDisabled } = this.props;
const { values } = this.state;
if (!isDisabled && prevState.values !== values) {
this.triggerUpdate();
}
}
public render (): React.ReactNode {
const { className, isDisabled, onEnter, overrides, params, style } = this.props;
const { handlersChange = [], handlersOption = [], optional = [], values = this.props.values } = this.state;
if (!params || params.length === 0) {
return null;
}
return (
<div
className={classes('ui--Params', className)}
style={style}
>
<div className='ui--Params-Content'>
{params.map(({ isOptional = false, name, type }: ParamDef, index: number): React.ReactNode => {
if (!values || values.length === 0) {
return null;
}
const isDisabledOptional = isOptional && !optional[index];
const key = `${name}:${type}:${index}`;
return (
<div
className='ui--Param-composite'
key={key}
>
<Param
defaultValue={
isDisabledOptional
? DISABLED_OPTIONAL
: values[index]
}
isDisabled={isDisabled || isDisabledOptional}
isOptional={isOptional}
key={`input:${key}`}
name={name}
onChange={handlersChange[index]}
onEnter={onEnter}
overrides={overrides}
type={type}
/>
{isOptional && (
<Toggle
className='ui--Param-overlay'
key={`option:${key}`}
label={
optional[index]
? 'include option'
: 'exclude option'
}
onChange={handlersOption[index]}
/>
)}
</div>
);
})}
</div>
</div>
);
}
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<State, never> => ({
optional: (prevState.optional || []).map((prev, index): boolean =>
index !== at
? prev
: value
)
}),
this.triggerUpdate
);
}
private onChangeParam = (at: number, newValue: RawParamOnChangeValue): void => {
const { isDisabled } = this.props;
if (isDisabled) {
return;
}
const { isValid = false, value } = newValue;
this.setState(
(prevState: State): Pick<State, never> => ({
values: (prevState.values || []).map((prev, index): RawParam =>
index !== at
? prev
: { isValid, value }
)
}),
this.triggerUpdate
);
}
private triggerUpdate = (): void => {
const { optional, values } = this.state;
const { onChange, isDisabled } = this.props;
if (isDisabled || !optional || !values) {
return;
}
onChange && onChange(this.extractValues());
}
}
export default translate(
styled(Params as React.ComponentClass<Props>)`
.ui--Param-composite {
position: relative;
.ui--Param-overlay {
position: absolute;
top: 0.5rem;
right: 3.5rem;
}
}
`
);