Added controlled vocab dropdown to standard tag widget

This commit is contained in:
Rainer Simon 2020-07-09 15:59:55 +02:00
parent e6e33158ab
commit 1c2a66f5fd
7 changed files with 131 additions and 110 deletions

View File

@ -1,7 +1,7 @@
import React, { useState, useEffect, useRef } from 'react' import React, { useState, useEffect, useRef } from 'react'
import { useCombobox } from 'downshift' import { useCombobox } from 'downshift'
const RelationAutocomplete = props => { const Autocomplete = props => {
const element = useRef(); const element = useRef();
@ -33,7 +33,7 @@ const RelationAutocomplete = props => {
} }
return ( return (
<div ref={element}> <div ref={element} className="r6o-autocomplete">
<div {...getComboboxProps()}> <div {...getComboboxProps()}>
<input <input
{...getInputProps({ onKeyDown })} {...getInputProps({ onKeyDown })}
@ -58,4 +58,4 @@ const RelationAutocomplete = props => {
} }
export default RelationAutocomplete; export default Autocomplete;

View File

@ -3,6 +3,7 @@ import { useState } from 'preact/hooks';
import { CSSTransition } from 'react-transition-group'; import { CSSTransition } from 'react-transition-group';
import { CloseIcon } from '../../../Icons'; import { CloseIcon } from '../../../Icons';
import i18n from '../../../i18n'; import i18n from '../../../i18n';
import Autocomplete from '../Autocomplete';
/** The basic freetext tag control from original Recogito **/ /** The basic freetext tag control from original Recogito **/
const TagWidget = props => { const TagWidget = props => {
@ -34,9 +35,9 @@ const TagWidget = props => {
} }
return ( return (
<div className="tags"> <div className="r6o-widget tag">
{ tagBodies.length > 0 && { tagBodies.length > 0 &&
<ul> <ul className="r6o-taglist">
{ tagBodies.map(tag => { tagBodies.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>
@ -56,12 +57,12 @@ const TagWidget = props => {
} }
{ !props.readOnly && { !props.readOnly &&
<input <Autocomplete
type="text" content={newTag}
value={newTag} placeholder={i18n.t('Add tag...')}
onChange={evt => setNewTag(evt.target.value)} onChange={setNewTag}
onKeyDown={onKeyDown} onKeyDown={onKeyDown}
placeholder={i18n.t('Add tag...')} /> vocabulary={props.vocabulary || []} />
} }
</div> </div>
) )

View File

@ -1,6 +1,6 @@
import React, { Component } from 'preact/compat'; import React, { Component } from 'preact/compat';
import { TrashIcon, CheckIcon } from '../../Icons'; import { TrashIcon, CheckIcon } from '../../Icons';
import RelationAutocomplete from './RelationAutocomplete'; import Autocomplete from '../../editor/widgets/Autocomplete';
/** /**
* Shorthand to get the label (= first tag body value) from the * Shorthand to get the label (= first tag body value) from the
@ -101,7 +101,7 @@ export default class RelationEditor extends Component {
return( return(
<div className="r6o-relation-editor" ref={this.element}> <div className="r6o-relation-editor" ref={this.element}>
<div className="input-wrapper"> <div className="input-wrapper">
<RelationAutocomplete <Autocomplete
content={this.state.content} content={this.state.content}
placeholder="Tag..." placeholder="Tag..."
onChange={this.onChange} onChange={this.onChange}

View File

@ -1,3 +1,4 @@
@import "widgets/autocomplete";
@import "widgets/textentry"; @import "widgets/textentry";
@import "widgets/comment/comment"; @import "widgets/comment/comment";
@import "widgets/tag/tag"; @import "widgets/tag/tag";

View File

@ -0,0 +1,46 @@
.r6o-autocomplete {
display:inline;
position:relative;
div[role=combobox] {
display:inline;
}
input {
outline:none;
border:none;
width:80px;
height:100%;
line-height:14px;
white-space:pre;
box-sizing:border-box;
background-color:transparent;
font-size:14px;
color:$standard-type;
}
ul {
position:absolute;
margin:0;
padding:0;
list-style-type:none;
background-color:#fff;
min-width:100%;
border-radius:3px;
border:1px solid #d6d7d9;
box-sizing:border-box;
box-shadow:0 0 20px rgba(0,0,0,0.25);
}
ul:empty {
display:none;
}
li {
box-sizing:border-box;
padding:2px 12px;
width:100%;
cursor:pointer;
}
}

View File

@ -2,81 +2,97 @@
display:none; display:none;
} }
.tags { .r6o-widget.tag {
background-color:$blueish-white; background-color:$blueish-white;
border-bottom:1px solid $lightgrey-border; border-bottom:1px solid $lightgrey-border;
padding:1px 3px; padding:1px 3px;
display:flex;
ul, li { ul {
padding:0;
margin:0; margin:0;
display:inline; padding:0;
list-style-type:none;
} }
li { ul.r6o-taglist {
display:inline-block; flex:1;
margin:1px 1px 1px 0; white-space:nowrap;
padding:0;
vertical-align:middle;
overflow:hidden;
font-size:12px;
background-color:#fff;
border:1px solid $lightgrey-border-darker;
cursor:pointer;
position:relative;
line-height:180%;
@include noselect();
@include rounded-corners(2px);
@include box-shadow(0, 0, 4px, 0.1);
.label { li {
padding:2px 8px; margin:0;
display:inline-block; display:inline-block;
} margin:1px 1px 1px 0;
padding:0;
vertical-align:middle;
overflow:hidden;
font-size:12px;
background-color:#fff;
border:1px solid $lightgrey-border-darker;
cursor:pointer;
position:relative;
line-height:180%;
@include noselect();
@include rounded-corners(2px);
@include box-shadow(0, 0, 4px, 0.1);
.delete-wrapper { .label {
display:inline-block; padding:2px 8px;
padding:2px 0; display:inline-block;
color:#fff; }
width:0;
height:100%;
background-color:$ocean;
@include rounded-corners-right(2px);
.delete { .delete-wrapper {
padding:2px 6px; display:inline-block;
padding:2px 0;
color:#fff;
width:0;
height:100%;
background-color:$ocean;
@include rounded-corners-right(2px);
.delete {
padding:2px 6px;
.icon {
height:11px;
padding-bottom:1px;
}
.icon {
height:11px;
padding-bottom:1px;
} }
svg {
vertical-align:text-top;
}
} }
svg { .delete-enter-active {
vertical-align:text-top; width:24px;
transition:width 200ms;
}
.delete-enter-done {
width:24px;
}
.delete-exit {
width:24px;
}
.delete-exit-active {
width:0;
transition:width 200ms;
} }
} }
.delete-enter-active { }
width:24px;
transition:width 200ms;
}
.delete-enter-done { .r6o-autocomplete {
width:24px; flex:2;
}
.delete-exit { li {
width:24px; font-size:14px;
} }
.delete-exit-active {
width:0;
transition:width 200ms;
}
} }
input { input {

View File

@ -24,50 +24,7 @@
font-size:14px; font-size:14px;
background-color:$blueish-white; background-color:$blueish-white;
cursor:text; cursor:text;
@include rounded-corners-left(3px); @include rounded-corners-left(3px);
div[role=combobox] {
height:34px;
}
input {
outline:none;
border:none;
width:80px;
height:100%;
line-height:14px;
white-space:pre;
box-sizing:border-box;
background-color:transparent;
font-size:14px;
color:$standard-type;
}
ul {
position:relative;
left:-6px;
margin:0;
padding:0;
list-style-type:none;
background-color:#fff;
min-width:100%;
border-radius:3px;
border:1px solid #d6d7d9;
box-sizing:border-box;
box-shadow:0 0 20px rgba(0,0,0,0.25);
}
ul:empty {
display:none;
}
li {
box-sizing:border-box;
padding:2px 12px;
width:100%;
cursor:pointer;
}
} }
.buttons { .buttons {