UNPKG

@wordpress/editor

Version:
306 lines (258 loc) 8.93 kB
"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