diff --git a/packages/edit-post/src/components/sidebar/post-taxonomies/index.js b/packages/edit-post/src/components/sidebar/post-taxonomies/index.js deleted file mode 100644 index d44815ee98b71b..00000000000000 --- a/packages/edit-post/src/components/sidebar/post-taxonomies/index.js +++ /dev/null @@ -1,30 +0,0 @@ -/** - * WordPress dependencies - */ -import { - PostTaxonomies as PostTaxonomiesForm, - PostTaxonomiesCheck, -} from '@wordpress/editor'; - -/** - * Internal dependencies - */ -import TaxonomyPanel from './taxonomy-panel'; - -function PostTaxonomies() { - return ( - - { - return ( - - { content } - - ); - } } - /> - - ); -} - -export default PostTaxonomies; diff --git a/packages/edit-post/src/components/sidebar/settings-sidebar/index.js b/packages/edit-post/src/components/sidebar/settings-sidebar/index.js index 9b413d1858b589..3aac4d59df23b4 100644 --- a/packages/edit-post/src/components/sidebar/settings-sidebar/index.js +++ b/packages/edit-post/src/components/sidebar/settings-sidebar/index.js @@ -11,14 +11,17 @@ import { isRTL, __ } from '@wordpress/i18n'; import { drawerLeft, drawerRight } from '@wordpress/icons'; import { store as interfaceStore } from '@wordpress/interface'; import { store as keyboardShortcutsStore } from '@wordpress/keyboard-shortcuts'; -import { store as editorStore, PostLastRevisionPanel } from '@wordpress/editor'; +import { + store as editorStore, + PostLastRevisionPanel, + PostTaxonomiesPanel, +} from '@wordpress/editor'; /** * Internal dependencies */ import SettingsHeader from '../settings-header'; import PostStatus from '../post-status'; -import PostTaxonomies from '../post-taxonomies'; import FeaturedImage from '../featured-image'; import PostExcerpt from '../post-excerpt'; import DiscussionPanel from '../discussion-panel'; @@ -79,7 +82,7 @@ const SidebarContent = ( { - + diff --git a/packages/edit-site/src/components/sidebar-edit-mode/page-panels/index.js b/packages/edit-site/src/components/sidebar-edit-mode/page-panels/index.js index d23dc87f42543c..28741f4ed6db7c 100644 --- a/packages/edit-site/src/components/sidebar-edit-mode/page-panels/index.js +++ b/packages/edit-site/src/components/sidebar-edit-mode/page-panels/index.js @@ -12,7 +12,11 @@ import { humanTimeDiff } from '@wordpress/date'; import { useSelect } from '@wordpress/data'; import { store as coreStore } from '@wordpress/core-data'; import { decodeEntities } from '@wordpress/html-entities'; -import { PostLastRevisionPanel, store as editorStore } from '@wordpress/editor'; +import { + PostLastRevisionPanel, + PostTaxonomiesPanel, + store as editorStore, +} from '@wordpress/editor'; /** * Internal dependencies @@ -95,6 +99,7 @@ export default function PagePanels() { ) } + ); } diff --git a/packages/edit-site/src/components/sidebar-edit-mode/template-panel/index.js b/packages/edit-site/src/components/sidebar-edit-mode/template-panel/index.js index 2364053c834d71..97a8ba4db448e8 100644 --- a/packages/edit-site/src/components/sidebar-edit-mode/template-panel/index.js +++ b/packages/edit-site/src/components/sidebar-edit-mode/template-panel/index.js @@ -3,7 +3,11 @@ */ import { useSelect } from '@wordpress/data'; import { PanelBody } from '@wordpress/components'; -import { PostLastRevisionPanel, store as editorStore } from '@wordpress/editor'; +import { + PostTaxonomiesPanel, + PostLastRevisionPanel, + store as editorStore, +} from '@wordpress/editor'; import { store as coreStore } from '@wordpress/core-data'; import { decodeEntities } from '@wordpress/html-entities'; import { navigation, symbol } from '@wordpress/icons'; @@ -15,8 +19,6 @@ import { store as editSiteStore } from '../../../store'; import TemplateActions from './template-actions'; import TemplateAreas from './template-areas'; import SidebarCard from '../sidebar-card'; -import PatternCategories from './pattern-categories'; -import { PATTERN_TYPES } from '../../../utils/constants'; const CARD_ICONS = { wp_block: symbol, @@ -24,29 +26,24 @@ const CARD_ICONS = { }; export default function TemplatePanel() { - const { title, description, icon, record, postType } = useSelect( - ( select ) => { - const { getEditedPostType, getEditedPostId } = - select( editSiteStore ); - const { getEditedEntityRecord } = select( coreStore ); - const { __experimentalGetTemplateInfo: getTemplateInfo } = - select( editorStore ); + const { title, description, icon, record } = useSelect( ( select ) => { + const { getEditedPostType, getEditedPostId } = select( editSiteStore ); + const { getEditedEntityRecord } = select( coreStore ); + const { __experimentalGetTemplateInfo: getTemplateInfo } = + select( editorStore ); - const type = getEditedPostType(); - const postId = getEditedPostId(); - const _record = getEditedEntityRecord( 'postType', type, postId ); - const info = getTemplateInfo( _record ); + const type = getEditedPostType(); + const postId = getEditedPostId(); + const _record = getEditedEntityRecord( 'postType', type, postId ); + const info = getTemplateInfo( _record ); - return { - title: info.title, - description: info.description, - icon: info.icon, - record: _record, - postType: type, - }; - }, - [] - ); + return { + title: info.title, + description: info.description, + icon: info.icon, + record: _record, + }; + }, [] ); if ( ! title && ! description ) { return null; @@ -66,11 +63,7 @@ export default function TemplatePanel() { - - { postType === PATTERN_TYPES.user && ( - - ) } - + ); } diff --git a/packages/edit-site/src/components/sidebar-edit-mode/template-panel/pattern-categories.js b/packages/edit-site/src/components/sidebar-edit-mode/template-panel/pattern-categories.js deleted file mode 100644 index 3740b622361ff5..00000000000000 --- a/packages/edit-site/src/components/sidebar-edit-mode/template-panel/pattern-categories.js +++ /dev/null @@ -1,279 +0,0 @@ -/** - * WordPress dependencies - */ -import { __, _x, sprintf } from '@wordpress/i18n'; -import { useEffect, useMemo, useState } from '@wordpress/element'; -import { FormTokenField, FlexBlock, PanelRow } from '@wordpress/components'; -import { useSelect, useDispatch } from '@wordpress/data'; -import { store as coreStore } from '@wordpress/core-data'; -import { useDebounce } from '@wordpress/compose'; -import { store as noticesStore } from '@wordpress/notices'; -import { decodeEntities } from '@wordpress/html-entities'; - -/** - * Internal dependencies - */ -import { PATTERN_TYPES } from '../../../utils/constants'; - -export const unescapeString = ( arg ) => { - return decodeEntities( arg ); -}; - -/** - * Returns a term object with name unescaped. - * - * @param {Object} term The term object to unescape. - * - * @return {Object} Term object with name property unescaped. - */ -export const unescapeTerm = ( term ) => { - return { - ...term, - name: unescapeString( term.name ), - }; -}; - -/** - * Shared reference to an empty array for cases where it is important to avoid - * returning a new array reference on every invocation. - * - * @type {Array} - */ -const EMPTY_ARRAY = []; - -/** - * Module constants - */ -const MAX_TERMS_SUGGESTIONS = 20; -const DEFAULT_QUERY = { - per_page: MAX_TERMS_SUGGESTIONS, - _fields: 'id,name', - context: 'view', -}; - -const isSameTermName = ( termA, termB ) => - unescapeString( termA ).toLowerCase() === - unescapeString( termB ).toLowerCase(); - -const termNamesToIds = ( names, terms ) => { - return names.map( - ( termName ) => - terms.find( ( term ) => isSameTermName( term.name, termName ) ).id - ); -}; - -export default function PatternCategories( { post } ) { - const slug = 'wp_pattern_category'; - const [ values, setValues ] = useState( [] ); - const [ search, setSearch ] = useState( '' ); - const debouncedSearch = useDebounce( setSearch, 500 ); - - const { - terms, - taxonomy, - hasAssignAction, - hasCreateAction, - hasResolvedTerms, - } = useSelect( - ( select ) => { - const { getEntityRecords, getTaxonomy, hasFinishedResolution } = - select( coreStore ); - const _taxonomy = getTaxonomy( slug ); - const _termIds = - post?.wp_pattern_category?.length > 0 - ? post?.wp_pattern_category - : EMPTY_ARRAY; - const query = { - ...DEFAULT_QUERY, - include: _termIds?.join( ',' ), - per_page: -1, - }; - - return { - hasCreateAction: _taxonomy - ? post._links?.[ - 'wp:action-create-' + _taxonomy.rest_base - ] ?? false - : false, - hasAssignAction: _taxonomy - ? post._links?.[ - 'wp:action-assign-' + _taxonomy.rest_base - ] ?? false - : false, - taxonomy: _taxonomy, - termIds: _termIds, - terms: _termIds?.length - ? getEntityRecords( 'taxonomy', slug, query ) - : EMPTY_ARRAY, - hasResolvedTerms: hasFinishedResolution( 'getEntityRecords', [ - 'taxonomy', - slug, - query, - ] ), - }; - }, - [ slug, post ] - ); - - const { searchResults } = useSelect( - ( select ) => { - const { getEntityRecords } = select( coreStore ); - - return { - searchResults: !! search - ? getEntityRecords( 'taxonomy', slug, { - ...DEFAULT_QUERY, - search, - } ) - : EMPTY_ARRAY, - }; - }, - [ search, slug ] - ); - - // Update terms state only after the selectors are resolved. - // We're using this to avoid terms temporarily disappearing on slow networks - // while core data makes REST API requests. - useEffect( () => { - if ( hasResolvedTerms ) { - const newValues = ( terms ?? [] ).map( ( term ) => - unescapeString( term.name ) - ); - - setValues( newValues ); - } - }, [ terms, hasResolvedTerms ] ); - - const suggestions = useMemo( () => { - return ( searchResults ?? [] ).map( ( term ) => - unescapeString( term.name ) - ); - }, [ searchResults ] ); - - const { saveEntityRecord, editEntityRecord, invalidateResolution } = - useDispatch( coreStore ); - const { createErrorNotice } = useDispatch( noticesStore ); - - if ( ! hasAssignAction ) { - return null; - } - - async function findOrCreateTerm( term ) { - try { - const newTerm = await saveEntityRecord( 'taxonomy', slug, term, { - throwOnError: true, - } ); - invalidateResolution( 'getUserPatternCategories' ); - return unescapeTerm( newTerm ); - } catch ( error ) { - if ( error.code !== 'term_exists' ) { - throw error; - } - - return { - id: error.data.term_id, - name: term.name, - }; - } - } - - function onUpdateTerms( newTermIds ) { - editEntityRecord( 'postType', PATTERN_TYPES.user, post.id, { - wp_pattern_category: newTermIds, - } ); - } - - function onChange( termNames ) { - const availableTerms = [ - ...( terms ?? [] ), - ...( searchResults ?? [] ), - ]; - const uniqueTerms = termNames.reduce( ( acc, name ) => { - if ( - ! acc.some( ( n ) => n.toLowerCase() === name.toLowerCase() ) - ) { - acc.push( name ); - } - return acc; - }, [] ); - - const newTermNames = uniqueTerms.filter( - ( termName ) => - ! availableTerms.find( ( term ) => - isSameTermName( term.name, termName ) - ) - ); - - // Optimistically update term values. - // The selector will always re-fetch terms later. - setValues( uniqueTerms ); - - if ( newTermNames.length === 0 ) { - return onUpdateTerms( - termNamesToIds( uniqueTerms, availableTerms ) - ); - } - - if ( ! hasCreateAction ) { - return; - } - - Promise.all( - newTermNames.map( ( termName ) => - findOrCreateTerm( { name: termName } ) - ) - ) - .then( ( newTerms ) => { - const newAvailableTerms = availableTerms.concat( newTerms ); - return onUpdateTerms( - termNamesToIds( uniqueTerms, newAvailableTerms ) - ); - } ) - .catch( ( error ) => { - createErrorNotice( error.message, { - type: 'snackbar', - } ); - } ); - } - - const singularName = - taxonomy?.labels?.singular_name ?? - ( slug === 'post_tag' ? __( 'Tag' ) : __( 'Term' ) ); - const termAddedLabel = sprintf( - /* translators: %s: term name. */ - _x( '%s added', 'term' ), - singularName - ); - const termRemovedLabel = sprintf( - /* translators: %s: term name. */ - _x( '%s removed', 'term' ), - singularName - ); - const removeTermLabel = sprintf( - /* translators: %s: term name. */ - _x( 'Remove %s', 'term' ), - singularName - ); - - return ( - - - - - - ); -} diff --git a/packages/editor/src/components/index.js b/packages/editor/src/components/index.js index a16c01c8c166ad..4a0dfac03dd5ac 100644 --- a/packages/editor/src/components/index.js +++ b/packages/editor/src/components/index.js @@ -68,6 +68,7 @@ export { default as PostTaxonomies } from './post-taxonomies'; export { FlatTermSelector as PostTaxonomiesFlatTermSelector } from './post-taxonomies/flat-term-selector'; export { HierarchicalTermSelector as PostTaxonomiesHierarchicalTermSelector } from './post-taxonomies/hierarchical-term-selector'; export { default as PostTaxonomiesCheck } from './post-taxonomies/check'; +export { default as PostTaxonomiesPanel } from './post-taxonomies/panel'; export { default as PostTextEditor } from './post-text-editor'; export { default as PostTitle } from './post-title'; export { default as PostTitleRaw } from './post-title/post-title-raw'; diff --git a/packages/edit-post/src/components/sidebar/post-taxonomies/taxonomy-panel.js b/packages/editor/src/components/post-taxonomies/panel.js similarity index 66% rename from packages/edit-post/src/components/sidebar/post-taxonomies/taxonomy-panel.js rename to packages/editor/src/components/post-taxonomies/panel.js index 4b3bab1fcf3442..a2c2d175246403 100644 --- a/packages/edit-post/src/components/sidebar/post-taxonomies/taxonomy-panel.js +++ b/packages/editor/src/components/post-taxonomies/panel.js @@ -3,7 +3,13 @@ */ import { PanelBody } from '@wordpress/components'; import { useSelect, useDispatch } from '@wordpress/data'; -import { store as editorStore } from '@wordpress/editor'; + +/** + * Internal dependencies + */ +import { store as editorStore } from '../../store'; +import PostTaxonomiesForm from './index'; +import PostTaxonomiesCheck from './check'; function TaxonomyPanel( { taxonomy, children } ) { const slug = taxonomy?.slug; @@ -41,4 +47,20 @@ function TaxonomyPanel( { taxonomy, children } ) { ); } -export default TaxonomyPanel; +function PostTaxonomies() { + return ( + + { + return ( + + { content } + + ); + } } + /> + + ); +} + +export default PostTaxonomies;