186 lines
3.3 KiB
React
186 lines
3.3 KiB
React
/**
|
|
* External dependencies
|
|
*/
|
|
import ReactSelect from 'react-select';
|
|
import { debounce } from 'throttle-debounce';
|
|
|
|
/**
|
|
* WordPress dependencies
|
|
*/
|
|
const {
|
|
Component,
|
|
} = wp.element;
|
|
|
|
const {
|
|
withSelect,
|
|
} = wp.data;
|
|
|
|
const {
|
|
BaseControl,
|
|
} = wp.components;
|
|
|
|
const cachedPosts = {};
|
|
|
|
function maybeCache( postType, newPosts ) {
|
|
if ( ! newPosts || ! newPosts.length ) {
|
|
return;
|
|
}
|
|
|
|
if ( ! cachedPosts[ postType ] ) {
|
|
cachedPosts[ postType ] = {};
|
|
}
|
|
|
|
newPosts.forEach( ( postData ) => {
|
|
if ( ! cachedPosts[ postType ][ postData.id ] ) {
|
|
cachedPosts[ postType ][ postData.id ] = postData;
|
|
}
|
|
} );
|
|
}
|
|
|
|
/**
|
|
* Component
|
|
*/
|
|
class ComponentPostsSelectorControl extends Component {
|
|
constructor() {
|
|
super( ...arguments );
|
|
|
|
this.state = {
|
|
searchTerm: '',
|
|
};
|
|
|
|
this.updateSearchTerm = debounce( 300, this.updateSearchTerm.bind( this ) );
|
|
}
|
|
|
|
/**
|
|
* Update search term with debounce.
|
|
*
|
|
* @param {String} search - search term.
|
|
*/
|
|
updateSearchTerm( search ) {
|
|
this.setState( {
|
|
searchTerm: search,
|
|
} );
|
|
}
|
|
|
|
render() {
|
|
const {
|
|
value,
|
|
label,
|
|
help,
|
|
isMulti,
|
|
onChange,
|
|
allPosts,
|
|
findPosts,
|
|
} = this.props;
|
|
|
|
const {
|
|
searchTerm,
|
|
} = this.state;
|
|
|
|
const foundPosts = findPosts( searchTerm ) || [];
|
|
|
|
return (
|
|
<BaseControl
|
|
label={ label }
|
|
help={ help }
|
|
>
|
|
<ReactSelect
|
|
options={
|
|
foundPosts.map( ( postData ) => {
|
|
return {
|
|
value: postData.id,
|
|
label: postData.title.raw,
|
|
};
|
|
} )
|
|
}
|
|
value={ ( () => {
|
|
let result = value ? value.split(',') : [];
|
|
if ( result && result.length ) {
|
|
result = result.map( ( id ) => {
|
|
let thisData = {
|
|
value: id,
|
|
label: id,
|
|
};
|
|
|
|
// get label from categories list
|
|
if ( allPosts[ id ] ) {
|
|
thisData.label = allPosts[ id ].title.raw;
|
|
}
|
|
|
|
return thisData;
|
|
} );
|
|
return result;
|
|
}
|
|
return [];
|
|
} )() }
|
|
isMulti={ isMulti }
|
|
name="filter-by-categories"
|
|
className="basic-multi-select"
|
|
classNamePrefix="select"
|
|
components={ {
|
|
IndicatorSeparator: () => null,
|
|
ClearIndicator: () => null,
|
|
} }
|
|
onInputChange={ ( val ) => {
|
|
this.updateSearchTerm( val );
|
|
} }
|
|
onChange={ ( val ) => {
|
|
let result = '';
|
|
let slug = '';
|
|
|
|
if ( val ) {
|
|
val.forEach( ( catData ) => {
|
|
result += slug + catData.value;
|
|
slug = ',';
|
|
} );
|
|
}
|
|
|
|
onChange( result );
|
|
} }
|
|
/>
|
|
</BaseControl>
|
|
);
|
|
}
|
|
}
|
|
|
|
export default withSelect( ( select, props, a, b, c ) => {
|
|
const {
|
|
getEntityRecords,
|
|
} = select( 'core' );
|
|
|
|
const {
|
|
value,
|
|
postType = 'post',
|
|
} = props;
|
|
|
|
let ids = value ? value.split(',') : [];
|
|
|
|
// find non-cached posts and try to retrieve.
|
|
ids = ids.filter( ( id ) => {
|
|
return ! cachedPosts[ postType ] || ! cachedPosts[ postType ][ id ];
|
|
} );
|
|
|
|
if ( ids && ids.length ) {
|
|
const newPosts = getEntityRecords( 'postType', postType, {
|
|
include: ids,
|
|
per_page: 100,
|
|
} );
|
|
|
|
maybeCache( postType, newPosts );
|
|
}
|
|
|
|
return {
|
|
findPosts( search = '' ) {
|
|
const searchPosts = getEntityRecords( 'postType', postType, {
|
|
search,
|
|
per_page: 20,
|
|
} );
|
|
|
|
maybeCache( postType, searchPosts );
|
|
|
|
return searchPosts;
|
|
},
|
|
allPosts: cachedPosts[ postType ] || {},
|
|
};
|
|
})( ComponentPostsSelectorControl );
|