Change MultiDataEntry to FC with hooks

This commit is contained in:
Maciej Ziarkowski 2021-03-15 19:32:10 +00:00
parent efb660db9e
commit 6ecd5b6819
4 changed files with 66 additions and 98 deletions

View File

@ -1,4 +1,4 @@
import React, { Component, Fragment } from 'react'; import React, { useCallback, useMemo, useState } from 'react';
import './multi-data-entry.css'; import './multi-data-entry.css';
@ -6,91 +6,63 @@ import { BaseDataEntryProps } from '../data-entry';
import { DataEntryInput, TextDataEntryInputProps } from '../data-entry-input'; import { DataEntryInput, TextDataEntryInputProps } from '../data-entry-input';
import { DataTitleCopyable } from '../data-title'; import { DataTitleCopyable } from '../data-title';
interface MultiDataEntryProps extends BaseDataEntryProps, TextDataEntryInputProps { interface MultiDataEntryProps extends BaseDataEntryProps, TextDataEntryInputProps {
value: string[]; value: string[];
copyable: boolean; copyable?: boolean;
editableEntries: boolean; editableEntries?: boolean;
confirmOnEnter: boolean; confirmOnEnter?: boolean;
addOnAutofillSelect: boolean;
acceptAutofillValuesOnly: boolean;
} }
interface MultiDataEntryState { export const MultiDataEntry: React.FC<MultiDataEntryProps> = ({
newValue: string; editableEntries = false,
} copyable = false,
confirmOnEnter = true,
...props
}) => {
const [newValue, setNewValue] = useState<string>();
class MultiDataEntry extends Component<MultiDataEntryProps, MultiDataEntryState> { const values = useMemo(() => props.value ?? [], [props.value]);
static defaultProps = { const edit = useCallback((index: number, value: string) => {
editableEntries: false, const editedValues = values.slice();
copyable: false, editedValues.splice(index, 1, value);
confirmOnEnter: true, props.onChange(props.slug, editedValues);
addOnAutofillSelect: false, }, [values, props.onChange, props.slug]);
acceptAutofillValuesOnly: false
};
constructor(props) { /* accept a newValue parameter to handle cases where the value is set and submitted at the same time
super(props); * (like with autofill select enabled) - but otherwise use the current newValue saved in state
this.state = { */
newValue: null const addNew = useCallback((newValueArg?: string) => {
}; const val = newValueArg ?? newValue;
this.setNewValue = this.setNewValue.bind(this);
this.edit = this.edit.bind(this);
this.addNew = this.addNew.bind(this);
this.remove = this.remove.bind(this);
}
getValues() {
return this.props.value == undefined ? [] : this.props.value;
}
cloneValues() {
return this.getValues().slice();
}
setNewValue(value: string) {
this.setState({newValue: value});
}
edit(index: number, value: string) {
let values = this.cloneValues();
values.splice(index, 1, value);
this.props.onChange(this.props.slug, values);
}
addNew(newValue?: string) {
// accept a newValue parameter to handle cases where the value is set and submitted at the same time
// (like with autofill select enabled) - but otherwise use the current newValue saved in state
const val = newValue ?? this.state.newValue;
if(val == undefined) return; if(val == undefined) return;
const values = this.cloneValues().concat(val);
this.setState({newValue: null});
this.props.onChange(this.props.slug, values);
}
remove(index: number){ const editedValues = values.slice().concat(val);
const values = this.cloneValues();
values.splice(index, 1); setNewValue(null);
this.props.onChange(this.props.slug, values); props.onChange(props.slug, editedValues);
} }, [newValue, values, props.onChange, props.slug]);
const remove = useCallback((index: number) => {
const editedValues = values.slice();
editedValues.splice(index, 1);
props.onChange(props.slug, editedValues);
}, [values, props.onChange, props.slug]);
render() {
const values = this.getValues();
const props = this.props;
const isEditing = props.mode === 'edit'; const isEditing = props.mode === 'edit';
const isDisabled = !isEditing || props.disabled; const isDisabled = !isEditing || props.disabled;
const slugWithModifier = props.slug + (props.slugModifier ?? ''); const slugWithModifier = props.slug + (props.slugModifier ?? '');
return <Fragment> return (<>
<DataTitleCopyable <DataTitleCopyable
slug={props.slug} slug={props.slug}
slugModifier={props.slugModifier} slugModifier={props.slugModifier}
title={props.title} title={props.title}
tooltip={props.tooltip} tooltip={props.tooltip}
disabled={props.disabled || props.value == undefined || props.value.length === 0} disabled={props.disabled || props.value == undefined || props.value.length === 0}
copy={props.copyable ? props.copy : undefined} copy={copyable ? props.copy : undefined}
/> />
<div id={`${props.slug}-wrapper`}> <div id={`${props.slug}-wrapper`}>
<ul className="data-link-list"> <ul className="data-link-list">
@ -108,8 +80,8 @@ class MultiDataEntry extends Component<MultiDataEntryProps, MultiDataEntryState>
name={`${slugWithModifier}-${i}`} name={`${slugWithModifier}-${i}`}
id={`${slugWithModifier}-${i}`} id={`${slugWithModifier}-${i}`}
value={val} value={val}
disabled={!props.editableEntries || isDisabled} disabled={!editableEntries || isDisabled}
onChange={(key, val) => this.edit(i, val)} onChange={(_key, val) => edit(i, val)}
maxLength={props.maxLength} maxLength={props.maxLength}
isUrl={props.isUrl} isUrl={props.isUrl}
@ -121,7 +93,7 @@ class MultiDataEntry extends Component<MultiDataEntryProps, MultiDataEntryState>
{ {
!isDisabled && !isDisabled &&
<div className="input-group-append"> <div className="input-group-append">
<button type="button" onClick={e => this.remove(i)} <button type="button" onClick={() => remove(i)}
title="Remove" title="Remove"
data-index={i} className="btn btn-outline-dark"></button> data-index={i} className="btn btn-outline-dark"></button>
</div> </div>
@ -137,17 +109,17 @@ class MultiDataEntry extends Component<MultiDataEntryProps, MultiDataEntryState>
slug={props.slug} slug={props.slug}
name={slugWithModifier} name={slugWithModifier}
id={slugWithModifier} id={slugWithModifier}
value={this.state.newValue} value={newValue}
disabled={props.disabled} disabled={props.disabled}
required={props.required && values.length < 1} required={props.required && values.length < 1}
onChange={(key, val) => this.setNewValue(val)} onChange={(_key, val) => setNewValue(val)}
onConfirm={(key, val) => this.addNew(val)} onConfirm={(_key, val) => addNew(val)}
maxLength={props.maxLength} maxLength={props.maxLength}
placeholder={props.placeholder} placeholder={props.placeholder}
isUrl={props.isUrl} isUrl={props.isUrl}
valueTransform={props.valueTransform} valueTransform={props.valueTransform}
confirmOnEnter={props.confirmOnEnter} confirmOnEnter={confirmOnEnter}
autofill={props.autofill} autofill={props.autofill}
showAllOptionsOnEmpty={props.showAllOptionsOnEmpty} showAllOptionsOnEmpty={props.showAllOptionsOnEmpty}
@ -157,15 +129,12 @@ class MultiDataEntry extends Component<MultiDataEntryProps, MultiDataEntryState>
<button type="button" <button type="button"
className="btn btn-outline-dark" className="btn btn-outline-dark"
title="Add to list" title="Add to list"
onClick={() => this.addNew()} onClick={() => addNew()}
disabled={this.state.newValue == undefined} disabled={newValue == undefined}
>+</button> >+</button>
</div> </div>
</div> </div>
} }
</div> </div>
</Fragment>; </>)
} };
}
export default MultiDataEntry;

View File

@ -1,7 +1,7 @@
import React, { Fragment } from 'react'; import React, { Fragment } from 'react';
import { dataFields } from '../../config/data-fields-config'; import { dataFields } from '../../config/data-fields-config';
import MultiDataEntry from '../data-components/multi-data-entry/multi-data-entry'; import { MultiDataEntry } from '../data-components/multi-data-entry/multi-data-entry';
import NumericDataEntry from '../data-components/numeric-data-entry'; import NumericDataEntry from '../data-components/numeric-data-entry';
import SelectDataEntry from '../data-components/select-data-entry'; import SelectDataEntry from '../data-components/select-data-entry';
import TextboxDataEntry from '../data-components/textbox-data-entry'; import TextboxDataEntry from '../data-components/textbox-data-entry';

View File

@ -6,7 +6,7 @@ import { FieldRow } from '../../data-components/field-row';
import DataEntry, { BaseDataEntryProps } from '../../data-components/data-entry'; import DataEntry, { BaseDataEntryProps } from '../../data-components/data-entry';
import { dataFields } from '../../../config/data-fields-config'; import { dataFields } from '../../../config/data-fields-config';
import SelectDataEntry from '../../data-components/select-data-entry'; import SelectDataEntry from '../../data-components/select-data-entry';
import MultiDataEntry from '../../data-components/multi-data-entry/multi-data-entry'; import { MultiDataEntry } from '../../data-components/multi-data-entry/multi-data-entry';
import { NumberRangeDataEntry } from './number-range-data-entry'; import { NumberRangeDataEntry } from './number-range-data-entry';
import './dynamics-data-entry.css'; import './dynamics-data-entry.css';

View File

@ -3,7 +3,7 @@ import React, { Fragment } from 'react';
import InfoBox from '../../components/info-box'; import InfoBox from '../../components/info-box';
import { dataFields } from '../../config/data-fields-config'; import { dataFields } from '../../config/data-fields-config';
import DataEntry from '../data-components/data-entry'; import DataEntry from '../data-components/data-entry';
import MultiDataEntry from '../data-components/multi-data-entry/multi-data-entry'; import { MultiDataEntry } from '../data-components/multi-data-entry/multi-data-entry';
import withCopyEdit from '../data-container'; import withCopyEdit from '../data-container';
import { CategoryViewProps } from './category-view-props'; import { CategoryViewProps } from './category-view-props';
@ -28,7 +28,6 @@ const UseView: React.FunctionComponent<CategoryViewProps> = (props) => (
copyable={true} copyable={true}
autofill={true} autofill={true}
showAllOptionsOnEmpty={true} showAllOptionsOnEmpty={true}
addOnAutofillSelect={true}
/> />
<Verification <Verification
slug="current_landuse_group" slug="current_landuse_group"