Introduces 'draft mode' tags, so user doesn't need to hit Enter to store

This commit is contained in:
Rainer Simon 2020-09-15 15:09:32 +02:00
parent 48d65ce520
commit 4373abf54b
2 changed files with 37 additions and 9 deletions

View File

@ -9,7 +9,6 @@ const Autocomplete = props => {
const onInputValueChange = ({ inputValue }) => { const onInputValueChange = ({ inputValue }) => {
if (inputValue.length > 0) { if (inputValue.length > 0) {
// Set suggestions to prefix matches...
const prefixMatches = props.vocabulary.filter(item => { const prefixMatches = props.vocabulary.filter(item => {
return item.toLowerCase().startsWith(inputValue.toLowerCase()); return item.toLowerCase().startsWith(inputValue.toLowerCase());
}); });
@ -61,6 +60,7 @@ const Autocomplete = props => {
<div {...getComboboxProps()}> <div {...getComboboxProps()}>
<input <input
{...getInputProps({ onKeyDown })} {...getInputProps({ onKeyDown })}
onChange={evt => props.onChange && props.onChange(evt)}
placeholder={props.placeholder} placeholder={props.placeholder}
defaultValue={props.initialValue} defaultValue={props.initialValue}
/> />

View File

@ -5,14 +5,25 @@ import { CloseIcon } from '../../../Icons';
import i18n from '../../../i18n'; import i18n from '../../../i18n';
import Autocomplete from '../Autocomplete'; import Autocomplete from '../Autocomplete';
const getDraftTag = existingDraft =>
existingDraft ? existingDraft : {
type: 'TextualBody', value: '', purpose: 'tagging', draft: true
};
/** The basic freetext tag control from original Recogito **/ /** The basic freetext tag control from original Recogito **/
const TagWidget = props => { const TagWidget = props => {
const [ showDelete, setShowDelete ] = useState(false); // All tags (draft + non-draft)
const all = props.annotation ?
props.annotation.bodies.filter(b => b.type === 'TextualBody' && b.purpose === 'tagging') : [];
// Every body with a 'tagging' purpose is considered a tag // Last draft tag goes into the input field
const tagBodies = props.annotation ? const draftTag = getDraftTag(all.slice().reverse().find(b => b.draft));
props.annotation.bodies.filter(b => b.purpose === 'tagging') : [];
// All except draft tag
const tags = all.filter(b => b != draftTag);
const [ showDelete, setShowDelete ] = useState(false);
const toggle = tag => _ => { const toggle = tag => _ => {
if (showDelete === tag) // Removes delete button if (showDelete === tag) // Removes delete button
@ -26,16 +37,31 @@ const TagWidget = props => {
props.onRemoveBody(tag); props.onRemoveBody(tag);
} }
const onDraftChange = evt => {
const prev = draftTag.value.trim();
const updated = evt.target.value.trim();
if (prev.length === 0 && updated.length > 0) {
props.onAppendBody({ ...draftTag, value: updated });
} else if (prev.length > 0 && updated.length === 0) {
props.onRemoveBody(draftTag);
} else {
props.onUpdateBody(draftTag, { ...draftTag, value: updated });
}
}
const onSubmit = tag => { const onSubmit = tag => {
props.onAppendBody({ type: 'TextualBody', purpose: 'tagging', value: tag.trim() }); // Just 'undraft' the current draft tag
const { draft, ...undrafted } = draftTag;
props.onUpdateBody(draftTag, undrafted);
} }
return ( return (
<div className="r6o-widget tag"> <div className="r6o-widget tag">
<div> <div>
{ tagBodies.length > 0 && { tags.length > 0 &&
<ul className="r6o-taglist"> <ul className="r6o-taglist">
{ tagBodies.map(tag => { tags.map(tag =>
<li key={tag.value} onClick={toggle(tag.value)}> <li key={tag.value} onClick={toggle(tag.value)}>
<span className="label">{tag.value}</span> <span className="label">{tag.value}</span>
@ -57,6 +83,8 @@ const TagWidget = props => {
{!props.readOnly && {!props.readOnly &&
<Autocomplete <Autocomplete
placeholder={i18n.t('Add tag...')} placeholder={i18n.t('Add tag...')}
initialValue={draftTag.value}
onChange={onDraftChange}
onSubmit={onSubmit} onSubmit={onSubmit}
vocabulary={props.vocabulary || []} /> vocabulary={props.vocabulary || []} />
} }