@wordpress/editor
Version:
Enhanced block editor for WordPress posts.
306 lines (258 loc) • 8.93 kB
JavaScript
"use strict";
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _element = require("@wordpress/element");
var _lodash = require("lodash");
var _i18n = require("@wordpress/i18n");
var _components = require("@wordpress/components");
var _data = require("@wordpress/data");
var _coreData = require("@wordpress/core-data");
var _compose = require("@wordpress/compose");
var _apiFetch = _interopRequireDefault(require("@wordpress/api-fetch"));
var _url = require("@wordpress/url");
var _store = require("../../store");
var _terms = require("../../utils/terms");
var _mostUsedTerms = _interopRequireDefault(require("./most-used-terms"));
/**
* External dependencies
*/
/**
* WordPress dependencies
*/
/**
* Internal dependencies
*/
/**
* Module constants
*/
const MAX_TERMS_SUGGESTIONS = 20;
const DEFAULT_QUERY = {
per_page: MAX_TERMS_SUGGESTIONS,
orderby: 'count',
order: 'desc',
_fields: 'id,name,count'
};
const isSameTermName = (termA, termB) => (0, _terms.unescapeString)(termA).toLowerCase() === (0, _terms.unescapeString)(termB).toLowerCase();
const termNamesToIds = (names, terms) => {
return names.map(termName => (0, _lodash.find)(terms, term => isSameTermName(term.name, termName)).id);
};
class FlatTermSelector extends _element.Component {
constructor() {
super(...arguments);
this.onChange = this.onChange.bind(this);
this.searchTerms = (0, _lodash.throttle)(this.searchTerms.bind(this), 500);
this.findOrCreateTerm = this.findOrCreateTerm.bind(this);
this.appendTerm = this.appendTerm.bind(this);
this.state = {
loading: !(0, _lodash.isEmpty)(this.props.terms),
availableTerms: [],
selectedTerms: []
};
}
componentDidMount() {
if (!(0, _lodash.isEmpty)(this.props.terms)) {
this.initRequest = this.fetchTerms({
include: this.props.terms.join(','),
per_page: -1
});
this.initRequest.then(() => {
this.setState({
loading: false
});
}, xhr => {
if (xhr.statusText === 'abort') {
return;
}
this.setState({
loading: false
});
});
}
}
componentWillUnmount() {
(0, _lodash.invoke)(this.initRequest, ['abort']);
(0, _lodash.invoke)(this.searchRequest, ['abort']);
}
componentDidUpdate(prevProps) {
if (prevProps.terms !== this.props.terms) {
this.updateSelectedTerms(this.props.terms);
}
}
fetchTerms(params = {}) {
const {
taxonomy
} = this.props;
const query = { ...DEFAULT_QUERY,
...params
};
const request = (0, _apiFetch.default)({
path: (0, _url.addQueryArgs)(`/wp/v2/${taxonomy.rest_base}`, query)
});
request.then(_terms.unescapeTerms).then(terms => {
this.setState(state => ({
availableTerms: state.availableTerms.concat(terms.filter(term => !(0, _lodash.find)(state.availableTerms, availableTerm => availableTerm.id === term.id)))
}));
this.updateSelectedTerms(this.props.terms);
});
return request;
}
updateSelectedTerms(terms = []) {
const selectedTerms = terms.reduce((accumulator, termId) => {
const termObject = (0, _lodash.find)(this.state.availableTerms, term => term.id === termId);
if (termObject) {
accumulator.push(termObject.name);
}
return accumulator;
}, []);
this.setState({
selectedTerms
});
}
findOrCreateTerm(termName) {
const {
taxonomy
} = this.props;
const termNameEscaped = (0, _lodash.escape)(termName); // Tries to create a term or fetch it if it already exists.
return (0, _apiFetch.default)({
path: `/wp/v2/${taxonomy.rest_base}`,
method: 'POST',
data: {
name: termNameEscaped
}
}).catch(error => {
const errorCode = error.code;
if (errorCode === 'term_exists') {
// If the terms exist, fetch it instead of creating a new one.
this.addRequest = (0, _apiFetch.default)({
path: (0, _url.addQueryArgs)(`/wp/v2/${taxonomy.rest_base}`, { ...DEFAULT_QUERY,
search: termNameEscaped
})
}).then(_terms.unescapeTerms);
return this.addRequest.then(searchResult => {
return (0, _lodash.find)(searchResult, result => isSameTermName(result.name, termName));
});
}
return Promise.reject(error);
}).then(_terms.unescapeTerm);
}
onChange(termNames) {
const uniqueTerms = (0, _lodash.uniqBy)(termNames, term => term.toLowerCase());
this.setState({
selectedTerms: uniqueTerms
});
const newTermNames = uniqueTerms.filter(termName => !(0, _lodash.find)(this.state.availableTerms, term => isSameTermName(term.name, termName)));
if (newTermNames.length === 0) {
return this.props.onUpdateTerms(termNamesToIds(uniqueTerms, this.state.availableTerms), this.props.taxonomy.rest_base);
}
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);
});
}
searchTerms(search = '') {
(0, _lodash.invoke)(this.searchRequest, ['abort']);
if (search.length >= 3) {
this.searchRequest = this.fetchTerms({
search
});
}
}
appendTerm(newTerm) {
const {
onUpdateTerms,
taxonomy,
terms = [],
slug,
speak
} = this.props;
if (terms.includes(newTerm.id)) {
return;
}
const newTerms = [...terms, newTerm.id];
const termAddedMessage = (0, _i18n.sprintf)(
/* translators: %s: term name. */
(0, _i18n._x)('%s added', 'term'), (0, _lodash.get)(taxonomy, ['labels', 'singular_name'], slug === 'post_tag' ? (0, _i18n.__)('Tag') : (0, _i18n.__)('Term')));
speak(termAddedMessage, 'assertive');
this.setState({
availableTerms: [...this.state.availableTerms, newTerm]
});
onUpdateTerms(newTerms, taxonomy.rest_base);
}
render() {
const {
slug,
taxonomy,
hasAssignAction
} = this.props;
if (!hasAssignAction) {
return null;
}
const {
loading,
availableTerms,
selectedTerms
} = this.state;
const termNames = availableTerms.map(term => term.name);
const newTermLabel = (0, _lodash.get)(taxonomy, ['labels', 'add_new_item'], slug === 'post_tag' ? (0, _i18n.__)('Add new tag') : (0, _i18n.__)('Add new Term'));
const singularName = (0, _lodash.get)(taxonomy, ['labels', 'singular_name'], slug === 'post_tag' ? (0, _i18n.__)('Tag') : (0, _i18n.__)('Term'));
const termAddedLabel = (0, _i18n.sprintf)(
/* translators: %s: term name. */
(0, _i18n._x)('%s added', 'term'), singularName);
const termRemovedLabel = (0, _i18n.sprintf)(
/* translators: %s: term name. */
(0, _i18n._x)('%s removed', 'term'), singularName);
const removeTermLabel = (0, _i18n.sprintf)(
/* translators: %s: term name. */
(0, _i18n._x)('Remove %s', 'term'), singularName);
return (0, _element.createElement)(_element.Fragment, null, (0, _element.createElement)(_components.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
}
}), (0, _element.createElement)(_mostUsedTerms.default, {
taxonomy: taxonomy,
onSelect: this.appendTerm
}));
}
}
var _default = (0, _compose.compose)((0, _data.withSelect)((select, {
slug
}) => {
const {
getCurrentPost
} = select(_store.store);
const {
getTaxonomy
} = select(_coreData.store);
const taxonomy = getTaxonomy(slug);
return {
hasCreateAction: taxonomy ? (0, _lodash.get)(getCurrentPost(), ['_links', 'wp:action-create-' + taxonomy.rest_base], false) : false,
hasAssignAction: taxonomy ? (0, _lodash.get)(getCurrentPost(), ['_links', 'wp:action-assign-' + taxonomy.rest_base], false) : false,
terms: taxonomy ? select(_store.store).getEditedPostAttribute(taxonomy.rest_base) : [],
taxonomy
};
}), (0, _data.withDispatch)(dispatch => {
return {
onUpdateTerms(terms, restBase) {
dispatch(_store.store).editPost({
[restBase]: terms
});
}
};
}), _components.withSpokenMessages, (0, _components.withFilters)('editor.PostTaxonomyType'))(FlatTermSelector);
exports.default = _default;
//# sourceMappingURL=flat-term-selector.js.map