UNPKG

@wordpress/editor

Version:
375 lines (373 loc) 14 kB
"use strict"; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // packages/editor/src/components/post-taxonomies/hierarchical-term-selector.js var hierarchical_term_selector_exports = {}; __export(hierarchical_term_selector_exports, { HierarchicalTermSelector: () => HierarchicalTermSelector, default: () => hierarchical_term_selector_default, findTerm: () => findTerm, getFilterMatcher: () => getFilterMatcher, sortBySelected: () => sortBySelected }); module.exports = __toCommonJS(hierarchical_term_selector_exports); var import_i18n = require("@wordpress/i18n"); var import_element = require("@wordpress/element"); var import_notices = require("@wordpress/notices"); var import_components = require("@wordpress/components"); var import_data = require("@wordpress/data"); var import_compose = require("@wordpress/compose"); var import_core_data = require("@wordpress/core-data"); var import_a11y = require("@wordpress/a11y"); var import_html_entities = require("@wordpress/html-entities"); var import_terms = require("../../utils/terms.cjs"); var import_store = require("../../store/index.cjs"); var import_lock_unlock = require("../../lock-unlock.cjs"); var import_jsx_runtime = require("react/jsx-runtime"); var { normalizeTextString } = (0, import_lock_unlock.unlock)(import_components.privateApis); var { RECEIVE_INTERMEDIATE_RESULTS } = (0, import_lock_unlock.unlock)(import_core_data.privateApis); var DEFAULT_QUERY = { per_page: -1, orderby: "name", order: "asc", _fields: "id,name,parent", context: "view", [RECEIVE_INTERMEDIATE_RESULTS]: true }; var MIN_TERMS_COUNT_FOR_FILTER = 8; var EMPTY_ARRAY = []; function sortBySelected(termsTree, terms) { const treeHasSelection = (termTree) => { if (terms.indexOf(termTree.id) !== -1) { return true; } if (void 0 === termTree.children) { return false; } return termTree.children.map(treeHasSelection).filter((child) => child).length > 0; }; const termOrChildIsSelected = (termA, termB) => { const termASelected = treeHasSelection(termA); const termBSelected = treeHasSelection(termB); if (termASelected === termBSelected) { return 0; } if (termASelected && !termBSelected) { return -1; } if (!termASelected && termBSelected) { return 1; } return 0; }; const newTermTree = [...termsTree]; newTermTree.sort(termOrChildIsSelected); return newTermTree; } function findTerm(terms, parent, name) { return terms.find((term) => { return (!term.parent && !parent || parseInt(term.parent) === parseInt(parent)) && term.name.toLowerCase() === name.toLowerCase(); }); } function getFilterMatcher(filterValue) { const matchTermsForFilter = (originalTerm) => { if ("" === filterValue) { return originalTerm; } const term = { ...originalTerm }; if (term.children.length > 0) { term.children = term.children.map(matchTermsForFilter).filter((child) => child); } if (-1 !== normalizeTextString(term.name).indexOf( normalizeTextString(filterValue) ) || term.children.length > 0) { return term; } return false; }; return matchTermsForFilter; } function HierarchicalTermSelector({ slug }) { const [adding, setAdding] = (0, import_element.useState)(false); const [formName, setFormName] = (0, import_element.useState)(""); const [formParent, setFormParent] = (0, import_element.useState)(""); const [showForm, setShowForm] = (0, import_element.useState)(false); const [filterValue, setFilterValue] = (0, import_element.useState)(""); const [filteredTermsTree, setFilteredTermsTree] = (0, import_element.useState)([]); const debouncedSpeak = (0, import_compose.useDebounce)(import_a11y.speak, 500); const { hasCreateAction, hasAssignAction, terms, loading, availableTerms, taxonomy } = (0, import_data.useSelect)( (select) => { const { getCurrentPost, getEditedPostAttribute } = select(import_store.store); const { getEntityRecord, getEntityRecords, isResolving } = select(import_core_data.store); const _taxonomy = getEntityRecord("root", "taxonomy", slug); const post = getCurrentPost(); return { hasCreateAction: _taxonomy ? !!post._links?.["wp:action-create-" + _taxonomy.rest_base] : false, hasAssignAction: _taxonomy ? !!post._links?.["wp:action-assign-" + _taxonomy.rest_base] : false, terms: _taxonomy ? getEditedPostAttribute(_taxonomy.rest_base) : EMPTY_ARRAY, loading: isResolving("getEntityRecords", [ "taxonomy", slug, DEFAULT_QUERY ]), availableTerms: getEntityRecords("taxonomy", slug, DEFAULT_QUERY) || EMPTY_ARRAY, taxonomy: _taxonomy }; }, [slug] ); const { editPost } = (0, import_data.useDispatch)(import_store.store); const { saveEntityRecord } = (0, import_data.useDispatch)(import_core_data.store); const availableTermsTree = (0, import_element.useMemo)( () => sortBySelected((0, import_terms.buildTermsTree)(availableTerms), terms), // Remove `terms` from the dependency list to avoid reordering every time // checking or unchecking a term. [availableTerms] ); const { createErrorNotice } = (0, import_data.useDispatch)(import_notices.store); if (!hasAssignAction) { return null; } const addTerm = (term) => { return saveEntityRecord("taxonomy", slug, term, { throwOnError: true }); }; const onUpdateTerms = (termIds) => { editPost({ [taxonomy.rest_base]: termIds }); }; const onChange = (termId) => { const hasTerm = terms.includes(termId); const newTerms = hasTerm ? terms.filter((id) => id !== termId) : [...terms, termId]; onUpdateTerms(newTerms); }; const onChangeFormName = (value) => { setFormName(value); }; const onChangeFormParent = (parentId) => { setFormParent(parentId); }; const onToggleForm = () => { setShowForm(!showForm); }; const onAddTerm = async (event) => { event.preventDefault(); if (formName === "" || adding) { return; } const existingTerm = findTerm(availableTerms, formParent, formName); if (existingTerm) { if (!terms.some((term) => term === existingTerm.id)) { onUpdateTerms([...terms, existingTerm.id]); } setFormName(""); setFormParent(""); return; } setAdding(true); let newTerm; try { newTerm = await addTerm({ name: formName, parent: formParent ? formParent : void 0 }); } catch (error) { createErrorNotice(error.message, { type: "snackbar" }); return; } const defaultName = slug === "category" ? (0, import_i18n.__)("Category") : (0, import_i18n.__)("Term"); const termAddedMessage = (0, import_i18n.sprintf)( /* translators: %s: term name. */ (0, import_i18n._x)("%s added", "term"), taxonomy?.labels?.singular_name ?? defaultName ); (0, import_a11y.speak)(termAddedMessage, "assertive"); setAdding(false); setFormName(""); setFormParent(""); onUpdateTerms([...terms, newTerm.id]); }; const setFilter = (value) => { const newFilteredTermsTree = availableTermsTree.map(getFilterMatcher(value)).filter((term) => term); const getResultCount = (termsTree) => { let count = 0; for (let i = 0; i < termsTree.length; i++) { count++; if (void 0 !== termsTree[i].children) { count += getResultCount(termsTree[i].children); } } return count; }; setFilterValue(value); setFilteredTermsTree(newFilteredTermsTree); const resultCount = getResultCount(newFilteredTermsTree); const resultsFoundMessage = (0, import_i18n.sprintf)( /* translators: %d: number of results. */ (0, import_i18n._n)("%d result found.", "%d results found.", resultCount), resultCount ); debouncedSpeak(resultsFoundMessage, "assertive"); }; const renderTerms = (renderedTerms) => { return renderedTerms.map((term) => { return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)( "div", { className: "editor-post-taxonomies__hierarchical-terms-choice", children: [ /* @__PURE__ */ (0, import_jsx_runtime.jsx)( import_components.CheckboxControl, { checked: terms.indexOf(term.id) !== -1, onChange: () => { const termId = parseInt(term.id, 10); onChange(termId); }, label: (0, import_html_entities.decodeEntities)(term.name) } ), !!term.children.length && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "editor-post-taxonomies__hierarchical-terms-subchoices", children: renderTerms(term.children) }) ] }, term.id ); }); }; const labelWithFallback = (labelProperty, fallbackIsCategory, fallbackIsNotCategory) => taxonomy?.labels?.[labelProperty] ?? (slug === "category" ? fallbackIsCategory : fallbackIsNotCategory); const newTermButtonLabel = labelWithFallback( "add_new_item", (0, import_i18n.__)("Add Category"), (0, import_i18n.__)("Add Term") ); const newTermLabel = labelWithFallback( "new_item_name", (0, import_i18n.__)("Add Category"), (0, import_i18n.__)("Add Term") ); const parentSelectLabel = labelWithFallback( "parent_item", (0, import_i18n.__)("Parent Category"), (0, import_i18n.__)("Parent Term") ); const noParentOption = `\u2014 ${parentSelectLabel} \u2014`; const newTermSubmitLabel = newTermButtonLabel; const filterLabel = taxonomy?.labels?.search_items ?? (0, import_i18n.__)("Search Terms"); const groupLabel = taxonomy?.name ?? (0, import_i18n.__)("Terms"); const showFilter = availableTerms.length >= MIN_TERMS_COUNT_FOR_FILTER; return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_components.Flex, { direction: "column", gap: "4", children: [ showFilter && !loading && /* @__PURE__ */ (0, import_jsx_runtime.jsx)( import_components.SearchControl, { __next40pxDefaultSize: true, label: filterLabel, placeholder: filterLabel, value: filterValue, onChange: setFilter } ), loading && /* @__PURE__ */ (0, import_jsx_runtime.jsx)( import_components.Flex, { justify: "center", style: { // Match SearchControl height to prevent layout shift. height: "40px" }, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_components.Spinner, {}) } ), /* @__PURE__ */ (0, import_jsx_runtime.jsx)( "div", { className: "editor-post-taxonomies__hierarchical-terms-list", tabIndex: "0", role: "group", "aria-label": groupLabel, children: renderTerms( "" !== filterValue ? filteredTermsTree : availableTermsTree ) } ), !loading && hasCreateAction && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_components.FlexItem, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)( import_components.Button, { __next40pxDefaultSize: true, onClick: onToggleForm, className: "editor-post-taxonomies__hierarchical-terms-add", "aria-expanded": showForm, variant: "link", children: newTermButtonLabel } ) }), showForm && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("form", { onSubmit: onAddTerm, children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_components.Flex, { direction: "column", gap: "4", children: [ /* @__PURE__ */ (0, import_jsx_runtime.jsx)( import_components.TextControl, { __next40pxDefaultSize: true, className: "editor-post-taxonomies__hierarchical-terms-input", label: newTermLabel, value: formName, onChange: onChangeFormName, required: true } ), !!availableTerms.length && /* @__PURE__ */ (0, import_jsx_runtime.jsx)( import_components.TreeSelect, { __next40pxDefaultSize: true, label: parentSelectLabel, noOptionLabel: noParentOption, onChange: onChangeFormParent, selectedId: formParent, tree: availableTermsTree } ), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_components.FlexItem, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)( import_components.Button, { __next40pxDefaultSize: true, variant: "secondary", type: "submit", className: "editor-post-taxonomies__hierarchical-terms-submit", children: newTermSubmitLabel } ) }) ] }) }) ] }); } var hierarchical_term_selector_default = (0, import_components.withFilters)("editor.PostTaxonomyType")( HierarchicalTermSelector ); // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { HierarchicalTermSelector, findTerm, getFilterMatcher, sortBySelected }); //# sourceMappingURL=hierarchical-term-selector.cjs.map