diff --git a/app/src/frontend/building/data-components/multi-data-entry/multi-data-entry.tsx b/app/src/frontend/building/data-components/multi-data-entry/multi-data-entry.tsx index c55f1026..3614e642 100644 --- a/app/src/frontend/building/data-components/multi-data-entry/multi-data-entry.tsx +++ b/app/src/frontend/building/data-components/multi-data-entry/multi-data-entry.tsx @@ -1,4 +1,4 @@ -import React, { Component, Fragment } from 'react'; +import React, { useCallback, useMemo, useState } from 'react'; import './multi-data-entry.css'; @@ -6,94 +6,66 @@ import { BaseDataEntryProps } from '../data-entry'; import { DataEntryInput, TextDataEntryInputProps } from '../data-entry-input'; import { DataTitleCopyable } from '../data-title'; - interface MultiDataEntryProps extends BaseDataEntryProps, TextDataEntryInputProps { value: string[]; - copyable: boolean; - editableEntries: boolean; - confirmOnEnter: boolean; - - addOnAutofillSelect: boolean; - acceptAutofillValuesOnly: boolean; + copyable?: boolean; + editableEntries?: boolean; + confirmOnEnter?: boolean; } -interface MultiDataEntryState { - newValue: string; -} +export const MultiDataEntry: React.FC = ({ + editableEntries = false, + copyable = false, + confirmOnEnter = true, + ...props +}) => { + const [newValue, setNewValue] = useState(); -class MultiDataEntry extends Component { + const values = useMemo(() => props.value ?? [], [props.value]); - static defaultProps = { - editableEntries: false, - copyable: false, - confirmOnEnter: true, - addOnAutofillSelect: false, - acceptAutofillValuesOnly: false - }; + const edit = useCallback((index: number, value: string) => { + const editedValues = values.slice(); + editedValues.splice(index, 1, value); + props.onChange(props.slug, editedValues); + }, [values, props.onChange, props.slug]); - constructor(props) { - super(props); - this.state = { - newValue: null - }; - - this.setNewValue = this.setNewValue.bind(this); - this.edit = this.edit.bind(this); - this.addNew = this.addNew.bind(this); - this.remove = this.remove.bind(this); - } + /* 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 addNew = useCallback((newValueArg?: string) => { + const val = newValueArg ?? newValue; + if(val == undefined) return; - getValues() { - return this.props.value == undefined ? [] : this.props.value; - } + const editedValues = values.slice().concat(val); - cloneValues() { - return this.getValues().slice(); - } + setNewValue(null); + props.onChange(props.slug, editedValues); + }, [newValue, values, props.onChange, props.slug]); - 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; - const values = this.cloneValues().concat(val); - this.setState({newValue: null}); - this.props.onChange(this.props.slug, values); - } + const remove = useCallback((index: number) => { + const editedValues = values.slice(); + editedValues.splice(index, 1); - remove(index: number){ - const values = this.cloneValues(); - values.splice(index, 1); - this.props.onChange(this.props.slug, values); - } + 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 isDisabled = !isEditing || props.disabled; - const slugWithModifier = props.slug + (props.slugModifier ?? ''); - return - -
-
    + const isEditing = props.mode === 'edit'; + const isDisabled = !isEditing || props.disabled; + const slugWithModifier = props.slug + (props.slugModifier ?? ''); + + return (<> + +
    +
      { values.length === 0 && !isEditing &&
      @@ -108,8 +80,8 @@ class MultiDataEntry extends Component name={`${slugWithModifier}-${i}`} id={`${slugWithModifier}-${i}`} value={val} - disabled={!props.editableEntries || isDisabled} - onChange={(key, val) => this.edit(i, val)} + disabled={!editableEntries || isDisabled} + onChange={(_key, val) => edit(i, val)} maxLength={props.maxLength} isUrl={props.isUrl} @@ -121,7 +93,7 @@ class MultiDataEntry extends Component { !isDisabled &&
      -
      @@ -129,25 +101,25 @@ class MultiDataEntry extends Component )) } -
    - { - !isDisabled && +
+ { + !isDisabled &&
this.setNewValue(val)} - onConfirm={(key, val) => this.addNew(val)} + onChange={(_key, val) => setNewValue(val)} + onConfirm={(_key, val) => addNew(val)} maxLength={props.maxLength} placeholder={props.placeholder} isUrl={props.isUrl} valueTransform={props.valueTransform} - confirmOnEnter={props.confirmOnEnter} + confirmOnEnter={confirmOnEnter} autofill={props.autofill} showAllOptionsOnEmpty={props.showAllOptionsOnEmpty} @@ -157,15 +129,12 @@ class MultiDataEntry extends Component
- } - -
; - } -} - -export default MultiDataEntry; + } + + ) +}; diff --git a/app/src/frontend/building/data-containers/age.tsx b/app/src/frontend/building/data-containers/age.tsx index 2ac2f4a0..7175d54d 100644 --- a/app/src/frontend/building/data-containers/age.tsx +++ b/app/src/frontend/building/data-containers/age.tsx @@ -1,7 +1,7 @@ import React, { Fragment } from 'react'; 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 SelectDataEntry from '../data-components/select-data-entry'; import TextboxDataEntry from '../data-components/textbox-data-entry'; diff --git a/app/src/frontend/building/data-containers/dynamics/dynamics-data-entry.tsx b/app/src/frontend/building/data-containers/dynamics/dynamics-data-entry.tsx index 3bcaa6a3..193ee940 100644 --- a/app/src/frontend/building/data-containers/dynamics/dynamics-data-entry.tsx +++ b/app/src/frontend/building/data-containers/dynamics/dynamics-data-entry.tsx @@ -6,7 +6,7 @@ import { FieldRow } from '../../data-components/field-row'; import DataEntry, { BaseDataEntryProps } from '../../data-components/data-entry'; import { dataFields } from '../../../config/data-fields-config'; 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 './dynamics-data-entry.css'; diff --git a/app/src/frontend/building/data-containers/use.tsx b/app/src/frontend/building/data-containers/use.tsx index 545b569a..26ff6e11 100644 --- a/app/src/frontend/building/data-containers/use.tsx +++ b/app/src/frontend/building/data-containers/use.tsx @@ -3,7 +3,7 @@ import React, { Fragment } from 'react'; import InfoBox from '../../components/info-box'; import { dataFields } from '../../config/data-fields-config'; 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 { CategoryViewProps } from './category-view-props'; @@ -28,7 +28,6 @@ const UseView: React.FunctionComponent = (props) => ( copyable={true} autofill={true} showAllOptionsOnEmpty={true} - addOnAutofillSelect={true} />