Improve multi entry + autofill UI
This commit is contained in:
parent
c3f757734a
commit
a5f29c6aad
@ -7,13 +7,17 @@ export interface TextDataEntryInputProps {
|
||||
name?: string;
|
||||
id?: string;
|
||||
onChange?: (key: string, val: any) => void;
|
||||
onConfirm?: (key: string, val: any) => void;
|
||||
|
||||
maxLength?: number;
|
||||
disabled?: boolean;
|
||||
placeholder?: string;
|
||||
valueTransform?: (val: string) => string;
|
||||
confirmOnEnter?: boolean;
|
||||
|
||||
autofill?: boolean;
|
||||
showAllOptionsOnEmpty?: boolean;
|
||||
confirmOnAutofillSelect?: boolean;
|
||||
}
|
||||
|
||||
export const DataEntryInput: React.FC<TextDataEntryInputProps & {value?: string}> = props => {
|
||||
@ -21,13 +25,29 @@ export const DataEntryInput: React.FC<TextDataEntryInputProps & {value?: string}
|
||||
const nameAttr = props.name || props.slug;
|
||||
const idAttr = props.id || props.slug;
|
||||
|
||||
const handleChange = (value: string) => {
|
||||
console.log(value);
|
||||
const transformValue = (value: string) => {
|
||||
const transform = props.valueTransform || (x => x);
|
||||
const transformedValue = value === '' ?
|
||||
null :
|
||||
transform(value);
|
||||
props.onChange(props.slug, transformedValue);
|
||||
return transformedValue;
|
||||
};
|
||||
|
||||
const handleChange = (value: string) => {
|
||||
props.onChange?.(props.slug, transformValue(value));
|
||||
};
|
||||
|
||||
const handleConfirm = () => {
|
||||
props.onConfirm?.(props.slug, props.value);
|
||||
};
|
||||
|
||||
const handleAutofillSelect = (value: string) => {
|
||||
const transformedValue = transformValue(value);
|
||||
if(props.confirmOnAutofillSelect) {
|
||||
props.onConfirm?.(props.slug, transformedValue);
|
||||
} else {
|
||||
props.onChange?.(props.slug, transformedValue);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
@ -39,6 +59,15 @@ export const DataEntryInput: React.FC<TextDataEntryInputProps & {value?: string}
|
||||
maxLength={props.maxLength}
|
||||
disabled={props.disabled}
|
||||
placeholder={props.placeholder}
|
||||
onKeyDown={e => {
|
||||
if(e.keyCode === 13) {
|
||||
// prevent form submit on enter
|
||||
e.preventDefault();
|
||||
if(props.confirmOnEnter) {
|
||||
handleConfirm();
|
||||
}
|
||||
}
|
||||
}}
|
||||
onChange={e => handleChange(e.target.value)}
|
||||
onInput={e => setEditing(true)}
|
||||
onFocus={e => setEditing(true)}
|
||||
@ -48,7 +77,7 @@ export const DataEntryInput: React.FC<TextDataEntryInputProps & {value?: string}
|
||||
props.autofill &&
|
||||
<AutofillDropdown
|
||||
editing={isEditing}
|
||||
onSelect={value => handleChange(value)}
|
||||
onSelect={handleAutofillSelect}
|
||||
onClose={() => setEditing(false)}
|
||||
fieldName={props.slug}
|
||||
fieldValue={props.value}
|
||||
|
@ -10,6 +10,10 @@ import { DataTitleCopyable } from '../data-title';
|
||||
interface MultiDataEntryProps extends BaseDataEntryProps, TextDataEntryInputProps {
|
||||
value: string[];
|
||||
editableEntries: boolean;
|
||||
confirmOnEnter: boolean;
|
||||
|
||||
addOnAutofillSelect: boolean;
|
||||
acceptAutofillValuesOnly: boolean;
|
||||
}
|
||||
|
||||
interface MultiDataEntryState {
|
||||
@ -19,7 +23,10 @@ interface MultiDataEntryState {
|
||||
class MultiDataEntry extends Component<MultiDataEntryProps, MultiDataEntryState> {
|
||||
|
||||
static defaultProps = {
|
||||
editableEntries: false
|
||||
editableEntries: false,
|
||||
confirmOnEnter: true,
|
||||
addOnAutofillSelect: false,
|
||||
acceptAutofillValuesOnly: false
|
||||
};
|
||||
|
||||
constructor(props) {
|
||||
@ -32,7 +39,6 @@ class MultiDataEntry extends Component<MultiDataEntryProps, MultiDataEntryState>
|
||||
this.edit = this.edit.bind(this);
|
||||
this.addNew = this.addNew.bind(this);
|
||||
this.remove = this.remove.bind(this);
|
||||
this.getValues = this.getValues.bind(this);
|
||||
}
|
||||
|
||||
getValues() {
|
||||
@ -52,10 +58,12 @@ class MultiDataEntry extends Component<MultiDataEntryProps, MultiDataEntryState>
|
||||
values.splice(index, 1, value);
|
||||
this.props.onChange(this.props.slug, values);
|
||||
}
|
||||
addNew(event) {
|
||||
event.preventDefault();
|
||||
if (this.state.newValue == undefined) return;
|
||||
const values = this.cloneValues().concat(this.state.newValue);
|
||||
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);
|
||||
}
|
||||
@ -69,7 +77,8 @@ class MultiDataEntry extends Component<MultiDataEntryProps, MultiDataEntryState>
|
||||
render() {
|
||||
const values = this.getValues();
|
||||
const props = this.props;
|
||||
const isDisabled = props.mode === 'view' || props.disabled;
|
||||
const isEditing = props.mode === 'edit';
|
||||
const isDisabled = !isEditing || props.disabled;
|
||||
return <Fragment>
|
||||
<DataTitleCopyable
|
||||
slug={props.slug}
|
||||
@ -80,7 +89,7 @@ class MultiDataEntry extends Component<MultiDataEntryProps, MultiDataEntryState>
|
||||
<div id={`${props.slug}-wrapper`}>
|
||||
<ul className="data-link-list">
|
||||
{
|
||||
values.length === 0 &&
|
||||
values.length === 0 && !isEditing &&
|
||||
<div className="input-group">
|
||||
<input className="form-control no-entries" type="text" value="No entries" disabled={true} />
|
||||
</div>
|
||||
@ -124,18 +133,22 @@ class MultiDataEntry extends Component<MultiDataEntryProps, MultiDataEntryState>
|
||||
value={this.state.newValue}
|
||||
disabled={props.disabled}
|
||||
onChange={(key, val) => this.setNewValue(val)}
|
||||
onConfirm={(key, val) => this.addNew(val)}
|
||||
|
||||
maxLength={props.maxLength}
|
||||
placeholder={props.placeholder}
|
||||
valueTransform={props.valueTransform}
|
||||
confirmOnEnter={props.confirmOnEnter}
|
||||
|
||||
autofill={props.autofill}
|
||||
showAllOptionsOnEmpty={props.showAllOptionsOnEmpty}
|
||||
confirmOnAutofillSelect={true}
|
||||
/>
|
||||
<div className="input-group-append">
|
||||
<button type="button"
|
||||
className="btn btn-outline-dark"
|
||||
title="Add to list"
|
||||
onClick={this.addNew}
|
||||
onClick={() => this.addNew()}
|
||||
disabled={this.state.newValue == undefined}
|
||||
>+</button>
|
||||
</div>
|
||||
|
@ -15,17 +15,22 @@ const UseView: React.FunctionComponent<CategoryViewProps> = (props) => (
|
||||
<Fragment>
|
||||
<MultiDataEntry
|
||||
title={dataFields.current_landuse_group.title}
|
||||
tooltip={dataFields.current_landuse_group.tooltip}
|
||||
slug="current_landuse_group"
|
||||
value={props.building.current_landuse_group}
|
||||
mode={props.mode}
|
||||
copy={props.copy}
|
||||
onChange={props.onChange}
|
||||
// tooltip={dataFields.current_landuse_class.tooltip}
|
||||
placeholder="New land use group..."
|
||||
confirmOnEnter={true}
|
||||
tooltip={dataFields.current_landuse_group.tooltip}
|
||||
placeholder="Type new land use group here"
|
||||
autofill={true}
|
||||
showAllOptionsOnEmpty={true}
|
||||
addOnAutofillSelect={true}
|
||||
/>
|
||||
{
|
||||
props.mode != 'view' &&
|
||||
<InfoBox msg="Land use order is automatically derived from the land use groups"></InfoBox>
|
||||
}
|
||||
<DataEntry
|
||||
title={dataFields.current_landuse_order.title}
|
||||
tooltip={dataFields.current_landuse_order.tooltip}
|
||||
@ -36,10 +41,6 @@ const UseView: React.FunctionComponent<CategoryViewProps> = (props) => (
|
||||
copy={props.copy}
|
||||
onChange={props.onChange}
|
||||
/>
|
||||
{
|
||||
props.mode != 'view' &&
|
||||
<InfoBox msg="Land use order is automatically derived from the land use groups"></InfoBox>
|
||||
}
|
||||
</Fragment>
|
||||
);
|
||||
const UseContainer = withCopyEdit(UseView);
|
||||
|
@ -21,3 +21,7 @@
|
||||
.tooltip .arrow {
|
||||
right: 7px;
|
||||
}
|
||||
|
||||
.tooltip a {
|
||||
color: rgb(255, 11, 245);
|
||||
}
|
@ -79,7 +79,7 @@ class Tooltip extends Component<TooltipProps, TooltipState> {
|
||||
<div className="tooltip bs-tooltip-bottom">
|
||||
<div className="arrow"></div>
|
||||
<div className="tooltip-inner">
|
||||
{...tooltipTextToComponents(this.props.text)}
|
||||
{tooltipTextToComponents(this.props.text)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
Loading…
Reference in New Issue
Block a user