diff --git a/.eslintrc.json b/.eslintrc.json
index 4fe46964516009..763603865944f8 100644
--- a/.eslintrc.json
+++ b/.eslintrc.json
@@ -8,6 +8,7 @@
],
"env": {
"browser": false,
+ "es6": true,
"node": true,
"mocha": true
},
diff --git a/components/form-token-field/style.scss b/components/form-token-field/style.scss
index 21dba507942310..29413b21e6b6cc 100644
--- a/components/form-token-field/style.scss
+++ b/components/form-token-field/style.scss
@@ -4,8 +4,9 @@
margin: 0;
padding: 0;
background-color: $white;
+ border-radius: 4px;
border: 1px solid $light-gray-500;
- color: $dark-gray-800;
+ color: $dark-gray-700;
cursor: text;
transition: all .15s ease-in-out;
@@ -28,7 +29,7 @@
display: flex;
flex-wrap: wrap;
align-items: flex-start;
- padding: 5px 14px 5px 0;
+ padding: 4px;
}
// Token input
@@ -37,13 +38,13 @@ input[type="text"].components-form-token-field__input {
width: auto;
max-width: 100%;
margin: 2px 0 2px 8px;
- padding: 0 0 0 6px;
+ padding: 0;
line-height: 24px;
background: inherit;
border: 0;
outline: none;
font-family: inherit;
- font-size: 14px;
+ font-size: $default-font-size;
color: $dark-gray-800;
box-shadow: none;
@@ -54,10 +55,10 @@ input[type="text"].components-form-token-field__input {
// Tokens
.components-form-token-field__token {
- font-size: 14px;
+ font-size: $default-font-size;
display: flex;
- margin: 2px 0 2px 8px;
- color: $white;
+ margin: 2px 4px 2px 0;
+ color: $dark-gray-700;
overflow: hidden;
&.is-success {
@@ -127,32 +128,31 @@ input[type="text"].components-form-token-field__input {
}
.components-form-token-field__token-text,
-.components-form-token-field__remove-token {
+.components-form-token-field__remove-token.components-icon-button {
display: inline-block;
line-height: 24px;
- background: $dark-gray-500;
+ background: $light-gray-500;
transition: all .2s cubic-bezier( .4, 1, .4, 1 );
}
.components-form-token-field__token-text {
- border-radius: 4px 0 0 4px;
- padding: 0 4px 0 6px;
+ border-radius: 12px 0 0 12px;
+ padding: 0 4px 0 8px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
-.components-form-token-field__remove-token {
+.components-form-token-field__remove-token.components-icon-button {
cursor: pointer;
- border-radius: 0 4px 4px 0;
+ border-radius: 0 12px 12px 0;
padding: 0 2px;
font-size: 10px;
- color: $light-gray-500;
+ color: $dark-gray-500;
line-height: 10px;
&:hover {
- color: white;
- background: $dark-gray-600;
+ color: $dark-gray-700;
}
}
diff --git a/components/form-token-field/token.js b/components/form-token-field/token.js
index f42d46cb0759d4..e16734d413435d 100644
--- a/components/form-token-field/token.js
+++ b/components/form-token-field/token.js
@@ -43,7 +43,7 @@ function Token( {
diff --git a/components/index.js b/components/index.js
index 1582340f6b380e..5a7e9876cf3f8f 100644
--- a/components/index.js
+++ b/components/index.js
@@ -1,6 +1,7 @@
export { default as Button } from './button';
export { default as Dashicon } from './dashicon';
export { default as FormToggle } from './form-toggle';
+export { default as FormTokenField } from './form-token-field';
export { default as HtmlEmbed } from './html-embed';
export { default as IconButton } from './icon-button';
export { default as Panel } from './panel';
diff --git a/editor/sidebar/post-taxonomies/tags-selector.js b/editor/sidebar/post-taxonomies/tags-selector.js
index f0918e3ce64451..9d1af1ac29cc76 100644
--- a/editor/sidebar/post-taxonomies/tags-selector.js
+++ b/editor/sidebar/post-taxonomies/tags-selector.js
@@ -1,36 +1,132 @@
+/**
+ * External dependencies
+ */
+import { connect } from 'react-redux';
+import { unescape, find } from 'lodash';
+
/**
* WordPress dependencies
*/
import { Component } from 'element';
-import FormTokenField from 'components/form-token-field';
+import { FormTokenField } from 'components';
+import { getEditedPostAttribute } from '../../selectors';
+import { editPost } from '../../actions';
+
+const DEFAULT_TAGS_QUERY = {
+ number: -1,
+ orderby: 'count',
+ order: 'DESC',
+};
+const MAX_TERMS_SUGGESTIONS = 20;
class TagsSelector extends Component {
constructor() {
super( ...arguments );
- this.onTokensChange = this.onTokensChange.bind( this );
+ this.onTagsChange = this.onTagsChange.bind( this );
this.state = {
- tokens: [ 'React', 'Vue' ],
+ loading: true,
+ availableTags: [],
+ selectedTags: [],
};
}
- onTokensChange( value ) {
- this.setState( { tokens: value } );
+ componentDidMount() {
+ this.fetchTagsRequest = new wp.api.collections.Tags().fetch( DEFAULT_TAGS_QUERY )
+ .done( ( tags ) => {
+ this.setState( {
+ loading: false,
+ availableTags: tags,
+ } );
+ this.updateSelectedTags( this.props.tags );
+ } )
+ .fail( ( xhr ) => {
+ if ( xhr.statusText === 'abort' ) {
+ return;
+ }
+ this.setState( {
+ loading: false,
+ } );
+ } );
+ }
+
+ componentWillUnmount() {
+ if ( this.fetchTagsRequest ) {
+ this.fetchTagsRequest.abort();
+ }
+ }
+
+ componentWillReceiveProps( newProps ) {
+ if ( newProps.tags !== this.props.tags ) {
+ this.updateSelectedTags( newProps.tags );
+ }
+ }
+
+ updateSelectedTags( tags = [] ) {
+ const selectedTags = tags.map( ( tagId ) => {
+ const tagObject = find( this.state.availableTags, ( tag ) => tag.id === tagId );
+ return tagObject ? tagObject.name : '';
+ } );
+ this.setState( {
+ selectedTags,
+ } );
+ }
+
+ onTagsChange( tagNames ) {
+ this.setState( { selectedTags: tagNames } );
+ const newTagNames = tagNames.filter( ( tagName ) =>
+ ! find( this.state.availableTags, ( tag ) => tag.name === tagName )
+ );
+ const tagNamesToIds = ( names, availableTags ) => {
+ return names
+ .map( ( tagName ) =>
+ find( availableTags, ( tag ) => tag.name === tagName ).id
+ );
+ };
+
+ if ( newTagNames.length === 0 ) {
+ return this.props.onUpdateTags( tagNamesToIds( tagNames, this.state.availableTags ) );
+ }
+ const createTag = ( tagName ) => new wp.api.models.Tag( { name: tagName } ).save();
+ Promise
+ .all( newTagNames.map( createTag ) )
+ .then( ( newTags ) => {
+ const newAvailableTags = this.state.availableTags.concat( newTags );
+ this.setState( { availableTags: newAvailableTags } );
+ return this.props.onUpdateTags( tagNamesToIds( tagNames, newAvailableTags ) );
+ } );
}
render() {
- const suggestions = [ 'React', 'Vue', 'Angular', 'Cycle', 'PReact', 'Inferno' ];
+ const { loading, availableTags, selectedTags } = this.state;
+ const tagNames = availableTags.map( ( tag ) => tag.name );
return (
);
}
}
-export default TagsSelector;
+export default connect(
+ ( state ) => {
+ return {
+ tags: getEditedPostAttribute( state, 'tags' ),
+ };
+ },
+ ( dispatch ) => {
+ return {
+ onUpdateTags( tags ) {
+ dispatch( editPost( { tags } ) );
+ },
+ };
+ }
+)( TagsSelector );