Skip to content

Instantly share code, notes, and snippets.

@igorbenic
Last active February 16, 2022 09:58
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save igorbenic/1df80a183b99e0d434cdfd80b8ee589f to your computer and use it in GitHub Desktop.
Save igorbenic/1df80a183b99e0d434cdfd80b8ee589f to your computer and use it in GitHub Desktop.
Gutenberg Components: Form Token Field (Tags Field) | https://www.ibenic.com/gutenberg-components-form-token-field
import MyFormTokenField from './components/FormTokenField';
class App extends Component {
render() {
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<p>
Edit <code>src/App.js</code> and save to reload.
</p>
<MyFormTokenField />
</header>
</div>
);
}
}
fetchTerms( params = {} ) {
const { taxonomy } = this.props;
const query = { ...DEFAULT_QUERY, ...params };
const request = apiFetch( {
path: addQueryArgs( `/wp/v2/${ taxonomy.rest_base }`, query ),
} );
request.then( unescapeTerms ).then( ( terms ) => {
this.setState( ( state ) => ( {
availableTerms: state.availableTerms.concat(
terms.filter( ( term ) => ! find( state.availableTerms, ( availableTerm ) => availableTerm.id === term.id ) )
),
} ) );
this.updateSelectedTerms( this.props.terms );
} );
return request;
}
// ... other code
class SearchPosts extends React.Component {
constructor( props ) {
super( props );
this.state = { tokens: [], availablePosts: [] }
}
render(){
return 'Nothing yet';
}
}
// ... other code
class SearchPosts extends React.Component {
constructor( props ) {
super( props );
this.searchPosts = this.searchPosts.bind(this);
this.state = { tokens: [], availablePosts: [] }
}
searchPosts( value ) {
apiFetch( { path: 'https://www.ibenic.com/wp-json/wp/v2/posts?search=' + value } ).then( posts => {
this.setState( ( state ) => ( {
availablePosts: state.availablePosts.concat(
posts.filter( ( post ) => ! find( state.availablePosts, ( availablePost ) => availablePost.id === post.id ) )
),
} ) );
} );
}
// ..other code
}
// ... other code
class SearchPosts extends React.Component {
constructor( props ) {
super( props );
this.savePosts = this.savePosts.bind(this);
this.state = { tokens: [], availablePosts: [] }
}
savePosts( posts ) {
if ( typeof( Storage ) !== "undefined" ) {
sessionStorage.posts = posts;
}
this.setState( { tokens: posts } );
}
// ..other code
}
// ... other code
class SearchPosts extends React.Component {
constructor( props ) {
super( props );
this.savePosts = this.savePosts.bind(this);
this.state = { tokens: [], availablePosts: [] }
}
// Retrieving the saved posts titles.
componentDidMount() {
if ( typeof( Storage ) !== "undefined" ) {
if ( sessionStorage.posts ) {
let tokens = sessionStorage.posts.split(',');
this.setState( { tokens } );
}
}
}
// Saving the post in the local session
savePosts( posts ) {
if ( typeof( Storage ) !== "undefined" ) {
sessionStorage.posts = posts;
}
this.setState( { tokens: posts } );
}
// ..other code
}
// ... other code
class SearchPosts extends React.Component {
// .. other code
render() {
const { tokens, availablePosts } = this.state;
let suggestions = [];
if ( availablePosts.length ) {
suggestions = availablePosts.map( (post) => post.title.rendered );
}
return <FormTokenField
value={ tokens }
suggestions={ suggestions }
onChange={ tokens => this.savePosts( tokens ) }
onInputChange={ (value) => this.searchPosts( value ) }
placeholder="Type a post title"
/>;
}
}
export default SearchPosts;
import React from 'react';
import { FormTokenField } from '@wordpress/components';
import apiFetch from '@wordpress/api-fetch';
import { find } from 'lodash';
// function passed to onChange property
onChange( termNames ) {
const uniqueTerms = uniqBy( termNames, ( term ) => term.toLowerCase() );
this.setState( { selectedTerms: uniqueTerms } );
const newTermNames = uniqueTerms.filter( ( termName ) =>
! find( this.state.availableTerms, ( term ) => isSameTermName( term.name, termName ) )
);
const termNamesToIds = ( names, availableTerms ) => {
return names
.map( ( termName ) =>
find( availableTerms, ( term ) => isSameTermName( term.name, termName ) ).id
);
};
// Updating the terms with term IDs if there are no new ones.
if ( newTermNames.length === 0 ) {
return this.props.onUpdateTerms(
termNamesToIds( uniqueTerms, this.state.availableTerms ),
this.props.taxonomy.rest_base
);
}
// Adding new term names (IDs).
Promise
.all( newTermNames.map( this.findOrCreateTerm ) )
.then( ( newTerms ) => {
const newAvailableTerms = this.state.availableTerms.concat( newTerms );
this.setState( { availableTerms: newAvailableTerms } );
return this.props.onUpdateTerms(
termNamesToIds( uniqueTerms, newAvailableTerms ),
this.props.taxonomy.rest_base
);
} );
}
{
"name": "gutenberg-components-series",
"version": "0.1.0",
"private": true,
"dependencies": {
"@wordpress/api-fetch": "^3.1.0",
"@wordpress/components": "^7.0.6",
"lodash": "^4.17.11",
"node-sass": "^4.11.0",
"react": "^16.7.0",
"react-dom": "^16.7.0",
"react-scripts": "2.1.3"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
"eslintConfig": {
"extends": "react-app"
},
"browserslist": [
">0.2%",
"not dead",
"not ie <= 11",
"not op_mini all"
]
}
// return of the render() method in FlatTermSelector
return (
<FormTokenField
value={ selectedTerms }
suggestions={ termNames }
onChange={ this.onChange }
onInputChange={ this.searchTerms }
maxSuggestions={ MAX_TERMS_SUGGESTIONS }
disabled={ loading }
label={ newTermLabel }
messages={ {
added: termAddedLabel,
removed: termRemovedLabel,
remove: removeTermLabel,
} }
/>
);
// passed to onInputChange
searchTerms( search = '' ) {
invoke( this.searchRequest, [ 'abort' ] );
this.searchRequest = this.fetchTerms( { search } );
}
render() {
// ... code
const { loading, availableTerms, selectedTerms } = this.state;
const termNames = availableTerms.map( ( term ) => term.name );
// ... other code
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment