mirror of
https://github.com/lidarr/lidarr.git
synced 2025-07-11 15:47:09 -07:00
New: Custom Filtering for UI (#234)
This commit is contained in:
parent
c6873014c7
commit
7354e02bff
154 changed files with 3498 additions and 1370 deletions
|
@ -31,7 +31,6 @@
|
|||
.isDisabled {
|
||||
opacity: 0.7;
|
||||
cursor: not-allowed;
|
||||
pointer-events: all !important;
|
||||
}
|
||||
|
||||
.dropdownArrowContainer {
|
||||
|
|
|
@ -58,7 +58,7 @@
|
|||
}
|
||||
|
||||
.pathHighlighted {
|
||||
background-color: $menuItemHoverColor;
|
||||
background-color: $menuItemHoverBackgroundColor;
|
||||
}
|
||||
|
||||
.fileBrowserButton {
|
||||
|
|
|
@ -1,97 +1,77 @@
|
|||
.container {
|
||||
.inputContainer {
|
||||
composes: input from 'Components/Form/Input.css';
|
||||
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
position: relative;
|
||||
padding: 0;
|
||||
min-height: 35px;
|
||||
height: auto;
|
||||
|
||||
&.isFocused {
|
||||
outline: 0;
|
||||
border-color: $inputFocusBorderColor;
|
||||
box-shadow: inset 0 1px 1px $inputBoxShadowColor, 0 0 8px $inputFocusBoxShadowColor;
|
||||
}
|
||||
}
|
||||
|
||||
.containerFocused {
|
||||
outline: 0;
|
||||
border-color: $inputFocusBorderColor;
|
||||
box-shadow: inset 0 1px 1px $inputBoxShadowColor, 0 0 8px $inputFocusBoxShadowColor;
|
||||
.hasError {
|
||||
composes: hasError from 'Components/Form/Input.css';
|
||||
}
|
||||
|
||||
.selectedTagContainer {
|
||||
.hasWarning {
|
||||
composes: hasWarning from 'Components/Form/Input.css';
|
||||
}
|
||||
|
||||
.tags {
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
.selectedTag {
|
||||
composes: label from 'Components/Label.css';
|
||||
|
||||
border-style: none;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
/* Selected Tag Kinds */
|
||||
|
||||
.info {
|
||||
composes: info from 'Components/Label.css';
|
||||
}
|
||||
|
||||
.success {
|
||||
composes: success from 'Components/Label.css';
|
||||
}
|
||||
|
||||
.warning {
|
||||
composes: warning from 'Components/Label.css';
|
||||
}
|
||||
|
||||
.danger {
|
||||
composes: danger from 'Components/Label.css';
|
||||
}
|
||||
|
||||
.searchInputContainer {
|
||||
position: relative;
|
||||
flex: 1 0 100px;
|
||||
margin-top: 1px;
|
||||
padding-left: 5px;
|
||||
}
|
||||
|
||||
.searchInput {
|
||||
max-width: 100%;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
input {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
max-width: 100%;
|
||||
outline: none;
|
||||
border: 0;
|
||||
.input {
|
||||
flex: 1 1 0%;
|
||||
margin-left: 3px;
|
||||
min-width: 20%;
|
||||
max-width: 100%;
|
||||
width: 0%;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.suggestionsContainer {
|
||||
@add-mixin scrollbar;
|
||||
@add-mixin scrollbarTrack;
|
||||
@add-mixin scrollbarThumb;
|
||||
}
|
||||
|
||||
.containerOpen {
|
||||
.suggestionsContainer {
|
||||
position: absolute;
|
||||
right: -1px;
|
||||
left: -1px;
|
||||
z-index: 1;
|
||||
overflow-y: auto;
|
||||
margin-top: 1px;
|
||||
max-height: 110px;
|
||||
border: 1px solid $inputBorderColor;
|
||||
border-radius: 4px;
|
||||
background-color: $white;
|
||||
box-shadow: inset 0 1px 1px $inputBoxShadowColor;
|
||||
}
|
||||
}
|
||||
|
||||
.suggestions {
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
overflow-y: auto;
|
||||
max-height: 200px;
|
||||
width: 100%;
|
||||
border: 1px solid $inputBorderColor;
|
||||
border-radius: 4px;
|
||||
background-color: $white;
|
||||
box-shadow: inset 0 1px 1px $inputBoxShadowColor;
|
||||
.suggestionsList {
|
||||
margin: 5px 0;
|
||||
padding-left: 0;
|
||||
list-style-type: none;
|
||||
}
|
||||
|
||||
ul {
|
||||
margin: 5px 0;
|
||||
padding-left: 0;
|
||||
list-style-type: none;
|
||||
}
|
||||
.suggestion {
|
||||
padding: 0 16px;
|
||||
cursor: default;
|
||||
|
||||
li {
|
||||
padding: 0 16px;
|
||||
}
|
||||
|
||||
li mark {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
li:hover {
|
||||
background-color: $menuItemHoverColor;
|
||||
&:hover {
|
||||
background-color: $menuItemHoverBackgroundColor;
|
||||
}
|
||||
}
|
||||
|
||||
.suggestionActive {
|
||||
background-color: $menuItemHoverColor;
|
||||
.suggestionHighlighted {
|
||||
background-color: $menuItemHoverBackgroundColor;
|
||||
}
|
||||
|
|
|
@ -1,11 +1,27 @@
|
|||
import _ from 'lodash';
|
||||
import PropTypes from 'prop-types';
|
||||
import React, { Component } from 'react';
|
||||
import ReactTags from 'react-tag-autocomplete';
|
||||
import Autosuggest from 'react-autosuggest';
|
||||
import classNames from 'classnames';
|
||||
import { kinds } from 'Helpers/Props';
|
||||
import TagInputInput from './TagInputInput';
|
||||
import TagInputTag from './TagInputTag';
|
||||
import styles from './TagInput.css';
|
||||
|
||||
function getTag(value, selectedIndex, suggestions, allowNew) {
|
||||
if (selectedIndex == null && value) {
|
||||
const existingTag = _.find(suggestions, { name: value });
|
||||
|
||||
if (existingTag) {
|
||||
return existingTag;
|
||||
} else if (allowNew) {
|
||||
return { name: value };
|
||||
}
|
||||
} else if (selectedIndex != null) {
|
||||
return suggestions[selectedIndex];
|
||||
}
|
||||
}
|
||||
|
||||
class TagInput extends Component {
|
||||
|
||||
//
|
||||
|
@ -14,97 +30,240 @@ class TagInput extends Component {
|
|||
constructor(props, context) {
|
||||
super(props, context);
|
||||
|
||||
this._tagsRef = null;
|
||||
this._inputRef = null;
|
||||
this.state = {
|
||||
value: '',
|
||||
suggestions: [],
|
||||
isFocused: false
|
||||
};
|
||||
|
||||
this._autosuggestRef = null;
|
||||
}
|
||||
|
||||
//
|
||||
// Control
|
||||
|
||||
_setTagsRef = (ref) => {
|
||||
this._tagsRef = ref;
|
||||
_setAutosuggestRef = (ref) => {
|
||||
this._autosuggestRef = ref;
|
||||
}
|
||||
|
||||
if (ref) {
|
||||
this._inputRef = this._tagsRef.input.input;
|
||||
getSuggestionValue({ name }) {
|
||||
return name;
|
||||
}
|
||||
|
||||
this._inputRef.addEventListener('blur', this.onInputBlur);
|
||||
} else if (this._inputRef) {
|
||||
this._inputRef.removeEventListener('blur', this.onInputBlur);
|
||||
}
|
||||
shouldRenderSuggestions = (value) => {
|
||||
return value.length >= this.props.minQueryLength;
|
||||
}
|
||||
|
||||
renderSuggestion({ name }, { query }) {
|
||||
return name;
|
||||
}
|
||||
|
||||
//
|
||||
// Listeners
|
||||
|
||||
onInputContainerPress = () => {
|
||||
this._autosuggestRef.input.focus();
|
||||
}
|
||||
|
||||
onTagAdd(tag) {
|
||||
this.props.onTagAdd(tag);
|
||||
|
||||
this.setState({
|
||||
value: '',
|
||||
suggestions: []
|
||||
});
|
||||
}
|
||||
|
||||
onInputChange = (event, { newValue, method }) => {
|
||||
const value = _.isObject(newValue) ? newValue.name : newValue;
|
||||
|
||||
if (method === 'type') {
|
||||
this.setState({ value });
|
||||
}
|
||||
}
|
||||
|
||||
onInputKeyDown = (event) => {
|
||||
const {
|
||||
tags,
|
||||
allowNew,
|
||||
delimiters,
|
||||
onTagDelete
|
||||
} = this.props;
|
||||
|
||||
const {
|
||||
value,
|
||||
suggestions
|
||||
} = this.state;
|
||||
|
||||
const keyCode = event.keyCode;
|
||||
|
||||
if (keyCode === 8 && !value.length) {
|
||||
const index = tags.length - 1;
|
||||
|
||||
if (index >= 0) {
|
||||
onTagDelete({ index, id: tags[index].id });
|
||||
}
|
||||
|
||||
setTimeout(() => {
|
||||
this.onSuggestionsFetchRequested({ value: '' });
|
||||
});
|
||||
|
||||
event.preventDefault();
|
||||
}
|
||||
|
||||
if (delimiters.includes(keyCode)) {
|
||||
const selectedIndex = this._autosuggestRef.highlightedSuggestionIndex;
|
||||
const tag = getTag(value, selectedIndex, suggestions, allowNew);
|
||||
|
||||
if (tag) {
|
||||
this.onTagAdd(tag);
|
||||
}
|
||||
|
||||
event.preventDefault();
|
||||
}
|
||||
}
|
||||
|
||||
onInputFocus = () => {
|
||||
this.setState({ isFocused: true });
|
||||
}
|
||||
|
||||
onInputBlur = () => {
|
||||
if (!this._tagsRef) {
|
||||
this.setState({ isFocused: false });
|
||||
|
||||
if (!this._autosuggestRef) {
|
||||
return;
|
||||
}
|
||||
|
||||
const {
|
||||
tagList,
|
||||
allowNew
|
||||
} = this.props;
|
||||
|
||||
const query = this._tagsRef.state.query.trim();
|
||||
const {
|
||||
value,
|
||||
suggestions
|
||||
} = this.state;
|
||||
|
||||
if (query) {
|
||||
const existingTag = _.find(tagList, { name: query });
|
||||
const selectedIndex = this._autosuggestRef.highlightedSuggestionIndex;
|
||||
const tag = getTag(value, selectedIndex, suggestions, allowNew);
|
||||
|
||||
if (existingTag) {
|
||||
this._tagsRef.addTag(existingTag);
|
||||
} else if (allowNew) {
|
||||
this._tagsRef.addTag({ name: query });
|
||||
}
|
||||
if (tag) {
|
||||
this.onTagAdd(tag);
|
||||
}
|
||||
}
|
||||
|
||||
onSuggestionsFetchRequested = ({ value }) => {
|
||||
const lowerCaseValue = value.toLowerCase();
|
||||
|
||||
const {
|
||||
tags,
|
||||
tagList
|
||||
} = this.props;
|
||||
|
||||
const suggestions = tagList.filter((tag) => {
|
||||
return (
|
||||
tag.name.toLowerCase().includes(lowerCaseValue) &&
|
||||
!tags.some((t) => t.id === tag.id));
|
||||
});
|
||||
|
||||
this.setState({ suggestions });
|
||||
}
|
||||
|
||||
onSuggestionsClearRequested = () => {
|
||||
// Required because props aren't always rendered, but no-op
|
||||
// because we don't want to reset the paths after a path is selected.
|
||||
}
|
||||
|
||||
onSuggestionSelected = (event, { suggestion }) => {
|
||||
this.onTagAdd(suggestion);
|
||||
}
|
||||
|
||||
//
|
||||
// Render
|
||||
|
||||
render() {
|
||||
renderInputComponent = (inputProps) => {
|
||||
const {
|
||||
tags,
|
||||
tagList,
|
||||
allowNew,
|
||||
kind,
|
||||
placeholder,
|
||||
onTagAdd,
|
||||
tagComponent,
|
||||
onTagDelete
|
||||
} = this.props;
|
||||
|
||||
const tagInputClassNames = {
|
||||
root: styles.container,
|
||||
rootFocused: styles.containerFocused,
|
||||
selected: styles.selectedTagContainer,
|
||||
selectedTag: classNames(styles.selectedTag, styles[kind]),
|
||||
search: styles.searchInputContainer,
|
||||
searchInput: styles.searchInput,
|
||||
suggestions: styles.suggestions,
|
||||
suggestionActive: styles.suggestionActive,
|
||||
suggestionDisabled: styles.suggestionDisabled
|
||||
return (
|
||||
<TagInputInput
|
||||
tags={tags}
|
||||
kind={kind}
|
||||
inputProps={inputProps}
|
||||
isFocused={this.state.isFocused}
|
||||
tagComponent={tagComponent}
|
||||
onTagDelete={onTagDelete}
|
||||
onInputContainerPress={this.onInputContainerPress}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
placeholder,
|
||||
hasError,
|
||||
hasWarning
|
||||
} = this.props;
|
||||
|
||||
const {
|
||||
value,
|
||||
suggestions,
|
||||
isFocused
|
||||
} = this.state;
|
||||
|
||||
const inputProps = {
|
||||
className: styles.input,
|
||||
name,
|
||||
value,
|
||||
placeholder,
|
||||
autoComplete: 'off',
|
||||
spellCheck: false,
|
||||
onChange: this.onInputChange,
|
||||
onKeyDown: this.onInputKeyDown,
|
||||
onFocus: this.onInputFocus,
|
||||
onBlur: this.onInputBlur
|
||||
};
|
||||
|
||||
const theme = {
|
||||
container: classNames(
|
||||
styles.inputContainer,
|
||||
isFocused && styles.isFocused,
|
||||
hasError && styles.hasError,
|
||||
hasWarning && styles.hasWarning,
|
||||
),
|
||||
containerOpen: styles.containerOpen,
|
||||
suggestionsContainer: styles.suggestionsContainer,
|
||||
suggestionsList: styles.suggestionsList,
|
||||
suggestion: styles.suggestion,
|
||||
suggestionHighlighted: styles.suggestionHighlighted
|
||||
};
|
||||
|
||||
return (
|
||||
<ReactTags
|
||||
ref={this._setTagsRef}
|
||||
classNames={tagInputClassNames}
|
||||
tags={tags}
|
||||
suggestions={tagList}
|
||||
allowNew={allowNew}
|
||||
minQueryLength={1}
|
||||
placeholder={placeholder}
|
||||
delimiters={[9, 13, 32, 188]}
|
||||
handleAddition={onTagAdd}
|
||||
handleDelete={onTagDelete}
|
||||
<Autosuggest
|
||||
ref={this._setAutosuggestRef}
|
||||
id={name}
|
||||
inputProps={inputProps}
|
||||
theme={theme}
|
||||
suggestions={suggestions}
|
||||
getSuggestionValue={this.getSuggestionValue}
|
||||
shouldRenderSuggestions={this.shouldRenderSuggestions}
|
||||
focusInputOnSuggestionClick={false}
|
||||
renderSuggestion={this.renderSuggestion}
|
||||
renderInputComponent={this.renderInputComponent}
|
||||
onSuggestionSelected={this.onSuggestionSelected}
|
||||
onSuggestionsFetchRequested={this.onSuggestionsFetchRequested}
|
||||
onSuggestionsClearRequested={this.onSuggestionsClearRequested}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const tagShape = {
|
||||
id: PropTypes.number.isRequired,
|
||||
name: PropTypes.string.isRequired
|
||||
export const tagShape = {
|
||||
id: PropTypes.oneOfType([PropTypes.number, PropTypes.string]).isRequired,
|
||||
name: PropTypes.oneOfType([PropTypes.number, PropTypes.string]).isRequired
|
||||
};
|
||||
|
||||
TagInput.propTypes = {
|
||||
|
@ -113,6 +272,11 @@ TagInput.propTypes = {
|
|||
allowNew: PropTypes.bool.isRequired,
|
||||
kind: PropTypes.oneOf(kinds.all).isRequired,
|
||||
placeholder: PropTypes.string.isRequired,
|
||||
delimiters: PropTypes.arrayOf(PropTypes.number).isRequired,
|
||||
minQueryLength: PropTypes.number.isRequired,
|
||||
hasError: PropTypes.bool,
|
||||
hasWarning: PropTypes.bool,
|
||||
tagComponent: PropTypes.func.isRequired,
|
||||
onTagAdd: PropTypes.func.isRequired,
|
||||
onTagDelete: PropTypes.func.isRequired
|
||||
};
|
||||
|
@ -120,7 +284,11 @@ TagInput.propTypes = {
|
|||
TagInput.defaultProps = {
|
||||
allowNew: true,
|
||||
kind: kinds.INFO,
|
||||
placeholder: ''
|
||||
placeholder: '',
|
||||
// Tab, enter, space and comma
|
||||
delimiters: [9, 13, 32, 188],
|
||||
minQueryLength: 1,
|
||||
tagComponent: TagInputTag
|
||||
};
|
||||
|
||||
export default TagInput;
|
||||
|
|
|
@ -103,7 +103,7 @@ class TagInputConnector extends Component {
|
|||
this.props.onChange({ name, value: newValue });
|
||||
}
|
||||
|
||||
onTagDelete = (index) => {
|
||||
onTagDelete = ({ index }) => {
|
||||
const {
|
||||
name,
|
||||
value
|
||||
|
|
6
frontend/src/Components/Form/TagInputInput.css
Normal file
6
frontend/src/Components/Form/TagInputInput.css
Normal file
|
@ -0,0 +1,6 @@
|
|||
.inputContainer {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
padding: 6px 16px;
|
||||
cursor: default;
|
||||
}
|
76
frontend/src/Components/Form/TagInputInput.js
Normal file
76
frontend/src/Components/Form/TagInputInput.js
Normal file
|
@ -0,0 +1,76 @@
|
|||
import PropTypes from 'prop-types';
|
||||
import React, { Component } from 'react';
|
||||
import { kinds } from 'Helpers/Props';
|
||||
import { tagShape } from './TagInput';
|
||||
import styles from './TagInputInput.css';
|
||||
|
||||
class TagInputInput extends Component {
|
||||
|
||||
onMouseDown = (event) => {
|
||||
event.preventDefault();
|
||||
|
||||
const {
|
||||
isFocused,
|
||||
onInputContainerPress
|
||||
} = this.props;
|
||||
|
||||
if (isFocused) {
|
||||
return;
|
||||
}
|
||||
|
||||
onInputContainerPress();
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
className,
|
||||
tags,
|
||||
inputProps,
|
||||
kind,
|
||||
tagComponent: TagComponent,
|
||||
onTagDelete
|
||||
} = this.props;
|
||||
|
||||
return (
|
||||
<div
|
||||
className={className}
|
||||
component="div"
|
||||
onMouseDown={this.onMouseDown}
|
||||
>
|
||||
{
|
||||
tags.map((tag, index) => {
|
||||
return (
|
||||
<TagComponent
|
||||
key={tag.id}
|
||||
index={index}
|
||||
tag={tag}
|
||||
kind={kind}
|
||||
isLastTag={index === tags.length - 1}
|
||||
onDelete={onTagDelete}
|
||||
/>
|
||||
);
|
||||
})
|
||||
}
|
||||
|
||||
<input {...inputProps} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
TagInputInput.propTypes = {
|
||||
className: PropTypes.string.isRequired,
|
||||
tags: PropTypes.arrayOf(PropTypes.shape(tagShape)).isRequired,
|
||||
inputProps: PropTypes.object.isRequired,
|
||||
kind: PropTypes.oneOf(kinds.all).isRequired,
|
||||
isFocused: PropTypes.bool.isRequired,
|
||||
tagComponent: PropTypes.func.isRequired,
|
||||
onTagDelete: PropTypes.func.isRequired,
|
||||
onInputContainerPress: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
TagInputInput.defaultProps = {
|
||||
className: styles.inputContainer
|
||||
};
|
||||
|
||||
export default TagInputInput;
|
52
frontend/src/Components/Form/TagInputTag.js
Normal file
52
frontend/src/Components/Form/TagInputTag.js
Normal file
|
@ -0,0 +1,52 @@
|
|||
import PropTypes from 'prop-types';
|
||||
import React, { Component } from 'react';
|
||||
import { kinds } from 'Helpers/Props';
|
||||
import Label from 'Components/Label';
|
||||
import Link from 'Components/Link/Link';
|
||||
import { tagShape } from './TagInput';
|
||||
|
||||
class TagInputTag extends Component {
|
||||
|
||||
//
|
||||
// Listeners
|
||||
|
||||
onDelete = () => {
|
||||
const {
|
||||
index,
|
||||
tag,
|
||||
onDelete
|
||||
} = this.props;
|
||||
|
||||
onDelete({
|
||||
index,
|
||||
id: tag.id
|
||||
});
|
||||
}
|
||||
|
||||
//
|
||||
// Render
|
||||
|
||||
render() {
|
||||
const {
|
||||
tag,
|
||||
kind
|
||||
} = this.props;
|
||||
|
||||
return (
|
||||
<Link onPress={this.onDelete}>
|
||||
<Label kind={kind}>
|
||||
{tag.name}
|
||||
</Label>
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
TagInputTag.propTypes = {
|
||||
index: PropTypes.number.isRequired,
|
||||
tag: PropTypes.shape(tagShape),
|
||||
kind: PropTypes.oneOf(kinds.all).isRequired,
|
||||
onDelete: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
export default TagInputTag;
|
|
@ -1,68 +0,0 @@
|
|||
import PropTypes from 'prop-types';
|
||||
import React, { Component } from 'react';
|
||||
import ReactTags from 'react-tag-autocomplete';
|
||||
import classNames from 'classnames';
|
||||
import { kinds } from 'Helpers/Props';
|
||||
import styles from './TagInput.css';
|
||||
|
||||
class TextTagInput extends Component {
|
||||
|
||||
//
|
||||
// Render
|
||||
|
||||
render() {
|
||||
const {
|
||||
tags,
|
||||
allowNew,
|
||||
kind,
|
||||
placeholder,
|
||||
onTagAdd,
|
||||
onTagDelete
|
||||
} = this.props;
|
||||
|
||||
const tagInputClassNames = {
|
||||
root: styles.container,
|
||||
rootFocused: styles.containerFocused,
|
||||
selected: styles.selectedTagContainer,
|
||||
selectedTag: classNames(styles.selectedTag, styles[kind]),
|
||||
search: styles.searchInputContainer,
|
||||
searchInput: styles.searchInput,
|
||||
suggestions: styles.suggestions,
|
||||
suggestionActive: styles.suggestionActive,
|
||||
suggestionDisabled: styles.suggestionDisabled
|
||||
};
|
||||
|
||||
return (
|
||||
<ReactTags
|
||||
classNames={tagInputClassNames}
|
||||
tags={tags}
|
||||
allowNew={allowNew}
|
||||
minQueryLength={1}
|
||||
placeholder={placeholder}
|
||||
handleAddition={onTagAdd}
|
||||
handleDelete={onTagDelete}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const tagShape = {
|
||||
id: PropTypes.string.isRequired,
|
||||
name: PropTypes.string.isRequired
|
||||
};
|
||||
|
||||
TextTagInput.propTypes = {
|
||||
tags: PropTypes.arrayOf(PropTypes.shape(tagShape)).isRequired,
|
||||
allowNew: PropTypes.bool.isRequired,
|
||||
kind: PropTypes.string.isRequired,
|
||||
placeholder: PropTypes.string,
|
||||
onTagAdd: PropTypes.func.isRequired,
|
||||
onTagDelete: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
TextTagInput.defaultProps = {
|
||||
allowNew: true,
|
||||
kind: kinds.INFO
|
||||
};
|
||||
|
||||
export default TextTagInput;
|
|
@ -4,7 +4,7 @@ import React, { Component } from 'react';
|
|||
import { connect } from 'react-redux';
|
||||
import { createSelector } from 'reselect';
|
||||
import split from 'Utilities/String/split';
|
||||
import TextTagInput from './TextTagInput';
|
||||
import TagInput from './TagInput';
|
||||
|
||||
function createMapStateToProps() {
|
||||
return createSelector(
|
||||
|
@ -34,25 +34,27 @@ class TextTagInputConnector extends Component {
|
|||
onTagAdd = (tag) => {
|
||||
const {
|
||||
name,
|
||||
value
|
||||
value,
|
||||
onChange
|
||||
} = this.props;
|
||||
|
||||
const newValue = split(value);
|
||||
newValue.push(tag.name);
|
||||
|
||||
this.props.onChange({ name, value: newValue.join(',') });
|
||||
onChange({ name, value: newValue.join(',') });
|
||||
}
|
||||
|
||||
onTagDelete = (index) => {
|
||||
onTagDelete = ({ index }) => {
|
||||
const {
|
||||
name,
|
||||
value
|
||||
value,
|
||||
onChange
|
||||
} = this.props;
|
||||
|
||||
const newValue = split(value);
|
||||
newValue.splice(index, 1);
|
||||
|
||||
this.props.onChange({
|
||||
onChange({
|
||||
name,
|
||||
value: newValue.join(',')
|
||||
});
|
||||
|
@ -63,7 +65,8 @@ class TextTagInputConnector extends Component {
|
|||
|
||||
render() {
|
||||
return (
|
||||
<TextTagInput
|
||||
<TagInput
|
||||
tagList={[]}
|
||||
onTagAdd={this.onTagAdd}
|
||||
onTagDelete={this.onTagDelete}
|
||||
{...this.props}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue