diff --git a/docs/reference-guides/core-blocks.md b/docs/reference-guides/core-blocks.md
index aaa0a9e56eff3e..d14c1d4cca9e92 100644
--- a/docs/reference-guides/core-blocks.md
+++ b/docs/reference-guides/core-blocks.md
@@ -1010,7 +1010,7 @@ An advanced block that allows displaying taxonomy terms based on different query
- **Category:** theme
- **Allowed Blocks:** core/term-template
- **Supports:** align (full, wide), interactivity, ~~html~~
-- **Attributes:** tagName, termQuery, termsToShow
+- **Attributes:** tagName, termQuery
## Text Columns (deprecated)
diff --git a/packages/block-library/src/term-template/block.json b/packages/block-library/src/term-template/block.json
index 80f64e5e4fe357..dcec56e316e885 100644
--- a/packages/block-library/src/term-template/block.json
+++ b/packages/block-library/src/term-template/block.json
@@ -8,7 +8,7 @@
"ancestor": [ "core/terms-query" ],
"description": "Contains the block elements used to render a taxonomy term, like the name, description, and more.",
"textdomain": "default",
- "usesContext": [ "termQuery", "termsToShow" ],
+ "usesContext": [ "termQuery" ],
"supports": {
"reusable": false,
"html": false,
diff --git a/packages/block-library/src/term-template/edit.js b/packages/block-library/src/term-template/edit.js
index 70332605923998..071f39b9b59d59 100644
--- a/packages/block-library/src/term-template/edit.js
+++ b/packages/block-library/src/term-template/edit.js
@@ -125,79 +125,17 @@ function TermTemplateBlockPreview( {
// Prevent re-rendering of the block preview when the terms data changes.
const MemoizedTermTemplateBlockPreview = memo( TermTemplateBlockPreview );
-/**
- * Builds a hierarchical tree structure from flat terms array.
- *
- * @param {Array} terms Array of term objects.
- * @return {Array} Tree structure with parent/child relationships.
- */
-function buildTermsTree( terms ) {
- const termsById = {};
- const rootTerms = [];
-
- terms.forEach( ( term ) => {
- termsById[ term.id ] = {
- term,
- children: [],
- };
- } );
-
- terms.forEach( ( term ) => {
- if ( term.parent && termsById[ term.parent ] ) {
- termsById[ term.parent ].children.push( termsById[ term.id ] );
- } else {
- rootTerms.push( termsById[ term.id ] );
- }
- } );
-
- return rootTerms;
-}
-
-/**
- * Renders a single term node and its children recursively.
- *
- * @param {Object} termNode Term node with term object and children.
- * @param {Function} renderTerm Function to render individual terms.
- * @return {JSX.Element} Rendered term node with children.
- */
-function renderTermNode( termNode, renderTerm ) {
- return (
-
- { renderTerm( termNode.term ) }
- { termNode.children.length > 0 && (
-
- { termNode.children.map( ( child ) =>
- renderTermNode( child, renderTerm )
- ) }
-
- ) }
-
- );
-}
-
-/**
- * Checks if a term is the currently active term.
- *
- * @param {number} termId The term ID to check.
- * @param {number} activeBlockContextId The currently active block context ID.
- * @param {Array} blockContexts Array of block contexts.
- * @return {boolean} True if the term is active, false otherwise.
- */
-function isActiveTerm( termId, activeBlockContextId, blockContexts ) {
- return termId === ( activeBlockContextId || blockContexts[ 0 ]?.termId );
-}
-
export default function TermTemplateEdit( {
clientId,
setAttributes,
context: {
- termsToShow,
termQuery: {
taxonomy,
order,
orderBy,
hideEmpty,
- hierarchical,
+ hierarchical = false,
+ parent = 0,
perPage = 10,
} = {},
},
@@ -207,15 +145,21 @@ export default function TermTemplateEdit( {
const { replaceInnerBlocks } = useDispatch( blockEditorStore );
const queryArgs = {
+ hide_empty: hideEmpty,
order,
orderby: orderBy,
- hide_empty: hideEmpty,
// To preview the data the closest to the frontend, we fetch the largest number of terms
// and limit them during rendering. This is because WP_Term_Query fetches data in hierarchical manner,
// while in editor we build the hierarchy manually. It also allows us to avoid re-fetching data when max terms changes.
per_page: 100,
};
+ // Nested terms are returned by default from REST API as long as parent is not set.
+ // If we want to show nested terms, we must not set parent at all.
+ if ( parent || ! hierarchical ) {
+ queryArgs.parent = parent || 0;
+ }
+
const { records: terms, isResolving } = useEntityRecords(
'taxonomy',
taxonomy,
@@ -226,14 +170,9 @@ export default function TermTemplateEdit( {
if ( ! terms ) {
return [];
}
- if ( termsToShow === 'top-level' ) {
- return terms.filter( ( term ) => ! term.parent );
- }
- if ( termsToShow === 'subterms' ) {
- return terms.filter( ( term ) => term.parent );
- }
- return terms;
- }, [ terms, termsToShow ] );
+ // Limit to the number of terms defined by perPage.
+ return perPage === 0 ? terms : terms.slice( 0, perPage );
+ }, [ terms, perPage ] );
const { blocks, variations, defaultVariation } = useSelect(
( select ) => {
@@ -319,77 +258,37 @@ export default function TermTemplateEdit( {
return { __( 'No terms found.' ) }
;
}
- const renderTerm = ( term ) => {
- const blockContext = {
- taxonomy,
- termId: term.id,
- classList: `term-${ term.id }`,
- termData: term,
- };
-
- return (
-
- { isActiveTerm(
- term.id,
- activeBlockContextId,
- blockContexts
- ) ? (
-
- ) : null }
-
-
- );
- };
-
return (
<>
- { hierarchical
- ? buildTermsTree( filteredTerms ).map( ( termNode ) =>
- renderTermNode( termNode, renderTerm )
- )
- : blockContexts &&
- blockContexts
- .slice( 0, perPage )
- .map( ( blockContext ) => (
-
- { blockContext.termId ===
+ { blockContexts &&
+ blockContexts.map( ( blockContext ) => (
+
+ { blockContext.termId ===
+ ( activeBlockContextId ||
+ blockContexts[ 0 ]?.termId ) ? (
+
+ ) : null }
+
- ) : null }
-
-
- ) ) }
+ blockContexts[ 0 ]?.termId )
+ }
+ />
+
+ ) ) }
>
);
diff --git a/packages/block-library/src/term-template/index.php b/packages/block-library/src/term-template/index.php
index 86d21e54defc19..8e9db6f972ff55 100644
--- a/packages/block-library/src/term-template/index.php
+++ b/packages/block-library/src/term-template/index.php
@@ -27,8 +27,7 @@ function render_block_core_term_template( $attributes, $content, $block ) {
return '';
}
- $query = $query_block_context['termQuery'];
- $terms_to_show = $query_block_context['termsToShow'] ?? 'all';
+ $query = $query_block_context['termQuery'];
$query_args = array(
'taxonomy' => $query['taxonomy'] ?? 'category',
@@ -38,18 +37,23 @@ function render_block_core_term_template( $attributes, $content, $block ) {
'hide_empty' => $query['hideEmpty'] ?? true,
);
- // We set parent to 0 only if we show all terms as hierarchical or we show top-level terms.
- if ( ( 'all' === $terms_to_show && ! empty( $query['hierarchical'] ) ) || 'top-level' === $terms_to_show ) {
+ // We set parent only when inheriting from the taxonomy archive context or not
+ // showing nested terms, otherwise nested terms are not displayed.
+ if (
+ isset( $query['inherit'] )
+ && $query['inherit']
+ && (
+ is_tax( $query_args['taxonomy'] )
+ // is_tax() does not detect built-in category or tag archives, only custom taxonomies.
+ || ( 'category' === $query_args['taxonomy'] && is_category() )
+ || ( 'post_tag' === $query_args['taxonomy'] && is_tag() )
+ )
+ ) {
+ // Get the current term ID from the queried object.
+ $current_term_id = get_queried_object_id();
+ $query_args['parent'] = $current_term_id;
+ } elseif ( empty( $query['hierarchical'] ) ) {
$query_args['parent'] = 0;
- } elseif ( 'subterms' === $terms_to_show ) {
- // Check if we're in a taxonomy archive context.
- if ( is_tax( $query_args['taxonomy'] ) ) {
- // Get the current term ID from the queried object.
- $current_term_id = get_queried_object_id();
- if ( $current_term_id && $current_term_id > 0 ) {
- $query_args['parent'] = $current_term_id;
- }
- }
}
$terms_query = new WP_Term_Query( $query_args );
@@ -59,130 +63,15 @@ function render_block_core_term_template( $attributes, $content, $block ) {
return '';
}
- // Handle hierarchical list.
- $is_hierarchical = ! empty( $query['hierarchical'] );
-
- if ( $is_hierarchical ) {
- $content = render_block_core_term_template_hierarchical( $terms, $block, $query_args );
- } else {
- $content = render_block_core_term_template_flat( $terms, $block );
- }
-
- $classnames = 'wp-block-term-template';
-
- if ( isset( $attributes['style']['elements']['link']['color']['text'] ) ) {
- $classnames .= ' has-link-color';
- }
-
- $wrapper_attributes = get_block_wrapper_attributes( array( 'class' => trim( $classnames ) ) );
-
- // Default list layout.
- return sprintf(
- '',
- $wrapper_attributes,
- $content
- );
-}
-
-/**
- * Renders terms in a flat list structure.
- *
- * @since 6.9.0
- *
- * @param array $terms Array of WP_Term objects.
- * @param WP_Block $block Block instance.
- *
- * @return string HTML content for flat terms list.
- */
-function render_block_core_term_template_flat( $terms, $block ) {
- $content = '';
- foreach ( $terms as $term ) {
- $content .= render_block_core_term_template_single( $term, $block );
- }
- return $content;
-}
-
-/**
- * Renders terms in a hierarchical structure.
- *
- * @since 6.9.0
- *
- * @param array $terms Array of WP_Term objects.
- * @param WP_Block $block Block instance.
- * @param array $base_query_args Base query arguments.
- *
- * @return string HTML content for hierarchical terms list.
- */
-function render_block_core_term_template_hierarchical( $terms, $block, $base_query_args ) {
$content = '';
-
foreach ( $terms as $term ) {
- $term_content = render_block_core_term_template_single( $term, $block );
- $children_content = render_block_core_term_template_get_children( $term->term_id, $block, $base_query_args );
+ // Get an instance of the current Term Template block.
+ $block_instance = $block->parsed_block;
- if ( ! empty( $children_content ) ) {
- $term_content = str_replace( '', '' . $children_content . '
', $term_content );
- }
+ // Set the block name to one that does not correspond to an existing registered block.
+ // This ensures that for the inner instances of the Term Template block, we do not render any block supports.
+ $block_instance['blockName'] = 'core/null';
- $content .= $term_content;
- }
-
- return $content;
-}
-
-/**
- * Gets and renders children of a specific term.
- *
- * @since 6.9.0
- *
- * @param int $parent_term_id Parent term ID.
- * @param WP_Block $block Block instance.
- * @param array $base_query_args Base query arguments.
- *
- * @return string HTML content for children terms.
- */
-function render_block_core_term_template_get_children( $parent_term_id, $block, $base_query_args ) {
- $child_query_args = $base_query_args;
- $child_query_args['parent'] = $parent_term_id;
-
- $child_terms_query = new WP_Term_Query( $child_query_args );
- $child_terms = $child_terms_query->get_terms();
-
- if ( ! $child_terms || is_wp_error( $child_terms ) ) {
- return '';
- }
-
- $content = '';
-
- foreach ( $child_terms as $child_term ) {
- $term_content = render_block_core_term_template_single( $child_term, $block );
- $children_content = render_block_core_term_template_get_children( $child_term->term_id, $block, $base_query_args );
-
- if ( ! empty( $children_content ) ) {
- $term_content = str_replace( '', '' . $children_content . '
', $term_content );
- }
-
- $content .= $term_content;
- }
-
- return $content;
-}
-
-/**
- * Renders a single term with its inner blocks.
- *
- * @since 6.9.0
- *
- * @param WP_Term $term Term object.
- * @param WP_Block $block Block instance.
- *
- * @return string HTML content for a single term.
- */
-function render_block_core_term_template_single( $term, $block ) {
- $inner_blocks = $block->inner_blocks;
- $block_content = '';
-
- if ( ! empty( $inner_blocks ) ) {
$term_id = $term->term_id;
$taxonomy = $term->taxonomy;
@@ -192,24 +81,36 @@ function render_block_core_term_template_single( $term, $block ) {
return $context;
};
+ $block_content = '';
+
+ // Use an early priority to so that other 'render_block_context' filters have access to the values.
add_filter( 'render_block_context', $filter_block_context, 1 );
- foreach ( $inner_blocks as $inner_block ) {
- if ( method_exists( $inner_block, 'refresh_context_dependents' ) ) {
- // WP_Block::refresh_context_dependents() was introduced in WordPress 6.8.
- $inner_block->refresh_context_dependents();
- $block_content .= $inner_block->render( array( 'dynamic' => true ) );
- } else {
- $block_content = ( new WP_Block( $inner_block->parsed_block ) )->render( array( 'dynamic' => false ) );
- }
- }
+ // Render the inner blocks of the Term Template block with `dynamic` set to `false` to prevent calling
+ // `render_callback` and ensure that no wrapper markup is included.
+ $block_content .= ( new WP_Block( $block_instance ) )->render( array( 'dynamic' => false ) );
+
remove_filter( 'render_block_context', $filter_block_context, 1 );
+
+ // Wrap the render inner blocks in a `li` element with the appropriate term classes.
+ $term_classes = implode( ' ', array( 'wp-block-term', "term-{$term->term_id}", $term->taxonomy, "taxonomy-{$term->taxonomy}" ) );
+
+ $content .= '' . $block_content . '';
+ }
+
+ $classnames = 'wp-block-term-template';
+
+ if ( isset( $attributes['style']['elements']['link']['color']['text'] ) ) {
+ $classnames .= ' has-link-color';
}
- $term_classes = implode( ' ', array( 'wp-block-term', 'term-' . $term->term_id ) );
+ $wrapper_attributes = get_block_wrapper_attributes( array( 'class' => trim( $classnames ) ) );
- // Default list layout
- return '' . $block_content . '';
+ return sprintf(
+ '',
+ $wrapper_attributes,
+ $content
+ );
}
/**
diff --git a/packages/block-library/src/terms-query/block.json b/packages/block-library/src/terms-query/block.json
index 8061475e661dae..85b06d0a115792 100644
--- a/packages/block-library/src/terms-query/block.json
+++ b/packages/block-library/src/terms-query/block.json
@@ -18,22 +18,17 @@
"orderBy": "name",
"hideEmpty": true,
"parent": false,
- "hierarchical": false
+ "hierarchical": false,
+ "inherit": false
}
},
- "termsToShow": {
- "type": "string",
- "default": "all",
- "enum": [ "all", "top-level", "subterms" ]
- },
"tagName": {
"type": "string",
"default": "div"
}
},
"providesContext": {
- "termQuery": "termQuery",
- "termsToShow": "termsToShow"
+ "termQuery": "termQuery"
},
"supports": {
"align": [ "wide", "full" ],
diff --git a/packages/block-library/src/terms-query/edit.js b/packages/block-library/src/terms-query/edit/index.js
similarity index 100%
rename from packages/block-library/src/terms-query/edit.js
rename to packages/block-library/src/terms-query/edit/index.js
diff --git a/packages/block-library/src/terms-query/inspector-controls/advanced-controls.js b/packages/block-library/src/terms-query/edit/inspector-controls/advanced-controls.js
similarity index 94%
rename from packages/block-library/src/terms-query/inspector-controls/advanced-controls.js
rename to packages/block-library/src/terms-query/edit/inspector-controls/advanced-controls.js
index 0e0e5c48a6b43e..4c8e0c40a0b515 100644
--- a/packages/block-library/src/terms-query/inspector-controls/advanced-controls.js
+++ b/packages/block-library/src/terms-query/edit/inspector-controls/advanced-controls.js
@@ -10,7 +10,7 @@ import {
/**
* Internal dependencies
*/
-import { unlock } from '../../lock-unlock';
+import { unlock } from '../../../lock-unlock';
const { HTMLElementControl } = unlock( blockEditorPrivateApis );
diff --git a/packages/block-library/src/terms-query/edit/inspector-controls/empty-terms-control.js b/packages/block-library/src/terms-query/edit/inspector-controls/empty-terms-control.js
new file mode 100644
index 00000000000000..2a499674ccb870
--- /dev/null
+++ b/packages/block-library/src/terms-query/edit/inspector-controls/empty-terms-control.js
@@ -0,0 +1,15 @@
+/**
+ * WordPress dependencies
+ */
+import { ToggleControl } from '@wordpress/components';
+
+export default function EmptyTermsControl( { value, onChange, ...props } ) {
+ return (
+ onChange( ! showEmpty ) }
+ { ...props }
+ />
+ );
+}
diff --git a/packages/block-library/src/terms-query/edit/inspector-controls/index.js b/packages/block-library/src/terms-query/edit/inspector-controls/index.js
new file mode 100644
index 00000000000000..c3d44dc54ba548
--- /dev/null
+++ b/packages/block-library/src/terms-query/edit/inspector-controls/index.js
@@ -0,0 +1,206 @@
+/**
+ * WordPress dependencies
+ */
+import { __ } from '@wordpress/i18n';
+import {
+ __experimentalToolsPanel as ToolsPanel,
+ __experimentalToolsPanelItem as ToolsPanelItem,
+} from '@wordpress/components';
+import { InspectorControls } from '@wordpress/block-editor';
+import { useSelect } from '@wordpress/data';
+
+/**
+ * Internal dependencies
+ */
+import { useToolsPanelDropdownMenuProps } from '../../../utils/hooks';
+import { usePublicTaxonomies } from '../../utils';
+import TaxonomyControl from './taxonomy-control';
+import OrderControl from './order-control';
+import EmptyTermsControl from './empty-terms-control';
+import NestedTermsControl from './nested-terms-control';
+import InheritControl from './inherit-control';
+import MaxTermsControl from './max-terms-control';
+import AdvancedControls from './advanced-controls';
+
+export default function TermsQueryInspectorControls( {
+ attributes,
+ setQuery,
+ setAttributes,
+ TagName,
+ clientId,
+} ) {
+ const { termQuery } = attributes;
+ const {
+ taxonomy,
+ orderBy,
+ order,
+ hideEmpty,
+ inherit,
+ hierarchical,
+ perPage,
+ } = termQuery;
+ const dropdownMenuProps = useToolsPanelDropdownMenuProps();
+
+ const { templateSlug } = useSelect( ( select ) => {
+ // @wordpress/block-library should not depend on @wordpress/editor.
+ // Blocks can be loaded into a *non-post* block editor, so to avoid
+ // declaring @wordpress/editor as a dependency, we must access its
+ // store by string.
+ // The solution here is to split WP specific blocks from generic blocks.
+ // eslint-disable-next-line @wordpress/data-no-store-string-literals
+ const { getEditedPostSlug } = select( 'core/editor' );
+ return {
+ templateSlug: getEditedPostSlug(),
+ };
+ }, [] );
+
+ const taxonomies = usePublicTaxonomies();
+
+ const isTaxonomyHierarchical = taxonomies.find(
+ ( _taxonomy ) => _taxonomy.slug === taxonomy
+ )?.hierarchical;
+
+ const isTaxonomyMatchingTemplate =
+ typeof templateSlug === 'string' && templateSlug.includes( taxonomy );
+
+ // Only display the inherit control if the taxonomy is hierarchical and matches the current template.
+ const displayInheritControl =
+ isTaxonomyHierarchical && isTaxonomyMatchingTemplate;
+
+ // Only display the hierarchical control if the taxonomy is hierarchical and not inheriting.
+ const displayHierarchicalControl =
+ isTaxonomyHierarchical && ! termQuery.inherit;
+
+ // Labels shared between ToolsPanelItem and its child control.
+ const taxonomyControlLabel = __( 'Taxonomy' );
+ const orderByControlLabel = __( 'Order by' );
+ const emptyTermsControlLabel = __( 'Show empty terms' );
+ const inheritControlLabel = __( 'Inherit parent term from archive' );
+ const nestedTermsControlLabel = __( 'Show nested terms' );
+ const maxTermsControlLabel = __( 'Max terms' );
+
+ return (
+ <>
+
+ {
+ setAttributes( {
+ termQuery: {
+ taxonomy: 'category',
+ order: 'asc',
+ orderBy: 'name',
+ hideEmpty: true,
+ hierarchical: false,
+ parent: false,
+ perPage: 10,
+ },
+ } );
+ } }
+ dropdownMenuProps={ dropdownMenuProps }
+ >
+ taxonomy !== 'category' }
+ label={ taxonomyControlLabel }
+ onDeselect={ () => {
+ setQuery( { taxonomy: 'category' } );
+ } }
+ isShownByDefault
+ >
+
+ setQuery( { taxonomy: value } )
+ }
+ />
+
+ orderBy !== 'name' || order !== 'asc' }
+ label={ orderByControlLabel }
+ onDeselect={ () =>
+ setQuery( { orderBy: 'name', order: 'asc' } )
+ }
+ isShownByDefault
+ >
+ {
+ setQuery( {
+ orderBy: newOrderBy,
+ order: newOrder,
+ } );
+ } }
+ />
+
+ hideEmpty !== true }
+ label={ emptyTermsControlLabel }
+ onDeselect={ () => setQuery( { hideEmpty: true } ) }
+ isShownByDefault
+ >
+
+ setQuery( { hideEmpty: value } )
+ }
+ />
+
+ { displayInheritControl && (
+ inherit !== false }
+ label={ inheritControlLabel }
+ onDeselect={ () => setQuery( { inherit: false } ) }
+ isShownByDefault
+ >
+
+
+ ) }
+ { displayHierarchicalControl && (
+ hierarchical !== false }
+ label={ nestedTermsControlLabel }
+ onDeselect={ () =>
+ setQuery( { hierarchical: false } )
+ }
+ isShownByDefault
+ >
+
+ setQuery( { hierarchical: value } )
+ }
+ />
+
+ ) }
+ perPage !== 10 }
+ label={ maxTermsControlLabel }
+ onDeselect={ () => setQuery( { perPage: 10 } ) }
+ isShownByDefault
+ >
+
+ setQuery( { perPage: value } )
+ }
+ />
+
+
+
+
+ >
+ );
+}
diff --git a/packages/block-library/src/terms-query/edit/inspector-controls/inherit-control.js b/packages/block-library/src/terms-query/edit/inspector-controls/inherit-control.js
new file mode 100644
index 00000000000000..4cc8a7a51576c5
--- /dev/null
+++ b/packages/block-library/src/terms-query/edit/inspector-controls/inherit-control.js
@@ -0,0 +1,21 @@
+/**
+ * WordPress dependencies
+ */
+import { ToggleControl } from '@wordpress/components';
+
+export default function InheritControl( { value, onChange, ...props } ) {
+ return (
+
+ onChange( {
+ inherit,
+ // When enabling inherit, hierarchical is not supported.
+ ...( inherit ? { hierarchical: false } : {} ),
+ } )
+ }
+ { ...props }
+ />
+ );
+}
diff --git a/packages/block-library/src/terms-query/edit/inspector-controls/max-terms-control.js b/packages/block-library/src/terms-query/edit/inspector-controls/max-terms-control.js
new file mode 100644
index 00000000000000..81f9246d2d25bf
--- /dev/null
+++ b/packages/block-library/src/terms-query/edit/inspector-controls/max-terms-control.js
@@ -0,0 +1,22 @@
+/**
+ * WordPress dependencies
+ */
+import { __ } from '@wordpress/i18n';
+import { RangeControl } from '@wordpress/components';
+
+export default function MaxTermsControl( { value, onChange, ...props } ) {
+ return (
+
+ );
+}
diff --git a/packages/block-library/src/terms-query/edit/inspector-controls/nested-terms-control.js b/packages/block-library/src/terms-query/edit/inspector-controls/nested-terms-control.js
new file mode 100644
index 00000000000000..f8778cdbcdb1f4
--- /dev/null
+++ b/packages/block-library/src/terms-query/edit/inspector-controls/nested-terms-control.js
@@ -0,0 +1,15 @@
+/**
+ * WordPress dependencies
+ */
+import { ToggleControl } from '@wordpress/components';
+
+export default function NestedTermsControl( { value, onChange, ...props } ) {
+ return (
+
+ );
+}
diff --git a/packages/block-library/src/terms-query/edit/inspector-controls/order-control.js b/packages/block-library/src/terms-query/edit/inspector-controls/order-control.js
new file mode 100644
index 00000000000000..b429422bcdaf6a
--- /dev/null
+++ b/packages/block-library/src/terms-query/edit/inspector-controls/order-control.js
@@ -0,0 +1,38 @@
+/**
+ * WordPress dependencies
+ */
+import { __ } from '@wordpress/i18n';
+import { SelectControl } from '@wordpress/components';
+
+export default function OrderControl( { orderBy, order, onChange, ...props } ) {
+ return (
+ {
+ const [ newOrderBy, newOrder ] = value.split( '/' );
+ onChange( newOrderBy, newOrder );
+ } }
+ { ...props }
+ />
+ );
+}
diff --git a/packages/block-library/src/terms-query/edit/inspector-controls/taxonomy-control.js b/packages/block-library/src/terms-query/edit/inspector-controls/taxonomy-control.js
new file mode 100644
index 00000000000000..cfdc61a7c884a5
--- /dev/null
+++ b/packages/block-library/src/terms-query/edit/inspector-controls/taxonomy-control.js
@@ -0,0 +1,28 @@
+/**
+ * WordPress dependencies
+ */
+import { SelectControl } from '@wordpress/components';
+
+/**
+ * Internal dependencies
+ */
+import { usePublicTaxonomies } from '../../utils';
+
+export default function TaxonomyControl( { value, onChange, ...props } ) {
+ const taxonomies = usePublicTaxonomies();
+ const taxonomyOptions = taxonomies.map( ( taxonomy ) => ( {
+ label: taxonomy.name,
+ value: taxonomy.slug,
+ } ) );
+
+ return (
+
+ );
+}
diff --git a/packages/block-library/src/terms-query/inspector-controls/display-options.js b/packages/block-library/src/terms-query/inspector-controls/display-options.js
deleted file mode 100644
index 031cd6c50fe951..00000000000000
--- a/packages/block-library/src/terms-query/inspector-controls/display-options.js
+++ /dev/null
@@ -1,79 +0,0 @@
-/**
- * WordPress dependencies
- */
-import { __ } from '@wordpress/i18n';
-import {
- __experimentalToolsPanelItem as ToolsPanelItem,
- RadioControl,
-} from '@wordpress/components';
-
-const getOptions = ( displayTopLevelControl, displaySubtermsControl ) => {
- const options = [ { label: __( 'Show all' ), value: 'all' } ];
-
- if ( displayTopLevelControl ) {
- options.push( {
- label: __( 'Show only top level terms' ),
- value: 'top-level',
- } );
- }
-
- if ( displaySubtermsControl ) {
- options.push( {
- label: __( 'Show subterms only' ),
- value: 'subterms',
- description: __(
- 'Display subterms of the current term. E.g. subcategories of current category.'
- ),
- } );
- }
-
- return options;
-};
-
-const getQueryAttributes = ( value ) => {
- if ( value === 'top-level' ) {
- return {
- parent: 0,
- hierarchical: false,
- };
- }
-
- // For 'all' and 'subterms', we fetch all terms and then filter them as the tree is built in Term Template.
- return {
- parent: false,
- };
-};
-
-export default function DisplayOptions( {
- attributes,
- displayTopLevelControl,
- displaySubtermsControl,
- setAttributes,
-} ) {
- const { termQuery, termsToShow } = attributes;
-
- return (
- termsToShow !== 'all' }
- label={ __( 'Terms to show' ) }
- onDeselect={ () => setAttributes( { termsToShow: 'all' } ) }
- isShownByDefault
- >
- {
- const queryAttributes = getQueryAttributes( value );
- setAttributes( {
- termsToShow: value,
- termQuery: { ...termQuery, ...queryAttributes },
- } );
- } }
- />
-
- );
-}
diff --git a/packages/block-library/src/terms-query/inspector-controls/empty-terms-control.js b/packages/block-library/src/terms-query/inspector-controls/empty-terms-control.js
deleted file mode 100644
index d5589d73bab936..00000000000000
--- a/packages/block-library/src/terms-query/inspector-controls/empty-terms-control.js
+++ /dev/null
@@ -1,30 +0,0 @@
-/**
- * WordPress dependencies
- */
-import { __ } from '@wordpress/i18n';
-import {
- __experimentalToolsPanelItem as ToolsPanelItem,
- ToggleControl,
-} from '@wordpress/components';
-
-export default function EmptyTermsControl( { attributes, setQuery } ) {
- const { termQuery } = attributes;
-
- return (
- termQuery.hideEmpty !== true }
- label={ __( 'Show empty terms' ) }
- onDeselect={ () => setQuery( { hideEmpty: true } ) }
- isShownByDefault
- >
-
- setQuery( { hideEmpty: ! showEmpty } )
- }
- />
-
- );
-}
diff --git a/packages/block-library/src/terms-query/inspector-controls/hierarchy-control.js b/packages/block-library/src/terms-query/inspector-controls/hierarchy-control.js
deleted file mode 100644
index a81114c534b307..00000000000000
--- a/packages/block-library/src/terms-query/inspector-controls/hierarchy-control.js
+++ /dev/null
@@ -1,30 +0,0 @@
-/**
- * WordPress dependencies
- */
-import { __ } from '@wordpress/i18n';
-import {
- __experimentalToolsPanelItem as ToolsPanelItem,
- ToggleControl,
-} from '@wordpress/components';
-
-export default function HierarchyControl( { attributes, setQuery } ) {
- const { termQuery } = attributes;
-
- return (
- termQuery.hierarchical !== false }
- label={ __( 'Show hierarchy' ) }
- onDeselect={ () => setQuery( { hierarchical: false } ) }
- isShownByDefault
- >
- {
- setQuery( { hierarchical } );
- } }
- />
-
- );
-}
diff --git a/packages/block-library/src/terms-query/inspector-controls/index.js b/packages/block-library/src/terms-query/inspector-controls/index.js
deleted file mode 100644
index 4c8e6b43c75f30..00000000000000
--- a/packages/block-library/src/terms-query/inspector-controls/index.js
+++ /dev/null
@@ -1,137 +0,0 @@
-/**
- * WordPress dependencies
- */
-import { __ } from '@wordpress/i18n';
-import { __experimentalToolsPanel as ToolsPanel } from '@wordpress/components';
-import { InspectorControls } from '@wordpress/block-editor';
-import { store as coreStore } from '@wordpress/core-data';
-import { useSelect } from '@wordpress/data';
-import { useMemo } from '@wordpress/element';
-
-/**
- * Internal dependencies
- */
-import { useToolsPanelDropdownMenuProps } from '../../utils/hooks';
-import TaxonomyControl from './taxonomy-control';
-import OrderingControls from './ordering-controls';
-import DisplayOptions from './display-options';
-import HierarchyControl from './hierarchy-control';
-import EmptyTermsControl from './empty-terms-control';
-import MaxTermsControl from './max-terms-control';
-import AdvancedControls from './advanced-controls';
-
-const usePublicTaxonomies = () => {
- const taxonomies = useSelect(
- ( select ) => select( coreStore ).getTaxonomies( { per_page: -1 } ),
- []
- );
- return useMemo( () => {
- return (
- taxonomies?.filter(
- ( { visibility } ) => visibility?.publicly_queryable
- ) || []
- );
- }, [ taxonomies ] );
-};
-
-export default function TermsQueryInspectorControls( {
- attributes,
- setQuery,
- setAttributes,
- TagName,
- clientId,
-} ) {
- const { termQuery, termsToShow } = attributes;
- const dropdownMenuProps = useToolsPanelDropdownMenuProps();
- const taxonomies = usePublicTaxonomies();
-
- const { templateSlug } = useSelect( ( select ) => {
- // @wordpress/block-library should not depend on @wordpress/editor.
- // Blocks can be loaded into a *non-post* block editor, so to avoid
- // declaring @wordpress/editor as a dependency, we must access its
- // store by string.
- // The solution here is to split WP specific blocks from generic blocks.
- // eslint-disable-next-line @wordpress/data-no-store-string-literals
- const { getEditedPostSlug } = select( 'core/editor' );
- return {
- templateSlug: getEditedPostSlug(),
- };
- }, [] );
-
- const taxonomyOptions = taxonomies.map( ( taxonomy ) => ( {
- label: taxonomy.name,
- value: taxonomy.slug,
- } ) );
-
- const isTaxonomyHierarchical = taxonomies.find(
- ( taxonomy ) => taxonomy.slug === termQuery.taxonomy
- )?.hierarchical;
-
- const isTaxonomyMatchingTemplate =
- typeof templateSlug === 'string' &&
- templateSlug.includes( termQuery.taxonomy );
-
- const displaySubtermsControl =
- isTaxonomyHierarchical && isTaxonomyMatchingTemplate;
-
- return (
- <>
-
- {
- setAttributes( {
- termQuery: {
- taxonomy: 'category',
- order: 'asc',
- orderBy: 'name',
- hideEmpty: true,
- hierarchical: false,
- parent: false,
- perPage: 10,
- },
- termsToShow: 'all',
- } );
- } }
- dropdownMenuProps={ dropdownMenuProps }
- >
-
-
-
-
-
- { isTaxonomyHierarchical && termsToShow === 'all' && (
-
- ) }
-
-
-
- >
- );
-}
diff --git a/packages/block-library/src/terms-query/inspector-controls/max-terms-control.js b/packages/block-library/src/terms-query/inspector-controls/max-terms-control.js
deleted file mode 100644
index d1328870b1e22c..00000000000000
--- a/packages/block-library/src/terms-query/inspector-controls/max-terms-control.js
+++ /dev/null
@@ -1,44 +0,0 @@
-/**
- * WordPress dependencies
- */
-import { __ } from '@wordpress/i18n';
-import {
- __experimentalToolsPanelItem as ToolsPanelItem,
- RangeControl,
-} from '@wordpress/components';
-
-export default function MaxTermsControl( { attributes, setQuery } ) {
- const { termQuery } = attributes;
-
- // Only show pagination control when not hierarchical.
- if ( termQuery.hierarchical ) {
- return null;
- }
-
- return (
- termQuery.perPage !== 10 }
- label={ __( 'Max terms' ) }
- onDeselect={ () => setQuery( { perPage: 10 } ) }
- isShownByDefault
- >
- {
- // Show all terms (-1) when 0 is selected.
- setQuery( {
- perPage: perPage === 0 ? -1 : perPage,
- } );
- } }
- help={ __(
- 'Limit the number of terms you want to show. To show all terms, use 0 (zero).'
- ) }
- />
-
- );
-}
diff --git a/packages/block-library/src/terms-query/inspector-controls/ordering-controls.js b/packages/block-library/src/terms-query/inspector-controls/ordering-controls.js
deleted file mode 100644
index 260c07d9d42946..00000000000000
--- a/packages/block-library/src/terms-query/inspector-controls/ordering-controls.js
+++ /dev/null
@@ -1,55 +0,0 @@
-/**
- * WordPress dependencies
- */
-import { __ } from '@wordpress/i18n';
-import {
- __experimentalToolsPanelItem as ToolsPanelItem,
- SelectControl,
-} from '@wordpress/components';
-
-export default function OrderingControls( { attributes, setQuery } ) {
- const { termQuery } = attributes;
-
- return (
-
- termQuery.orderBy !== 'name' || termQuery.order !== 'asc'
- }
- label={ __( 'Order by' ) }
- onDeselect={ () => setQuery( { orderBy: 'name', order: 'asc' } ) }
- isShownByDefault
- >
- {
- const [ newOrderBy, newOrder ] = orderBy.split( '/' );
- setQuery( {
- orderBy: newOrderBy,
- order: newOrder,
- } );
- } }
- />
-
- );
-}
diff --git a/packages/block-library/src/terms-query/inspector-controls/taxonomy-control.js b/packages/block-library/src/terms-query/inspector-controls/taxonomy-control.js
deleted file mode 100644
index 0847b2ff7821fd..00000000000000
--- a/packages/block-library/src/terms-query/inspector-controls/taxonomy-control.js
+++ /dev/null
@@ -1,41 +0,0 @@
-/**
- * WordPress dependencies
- */
-import { __ } from '@wordpress/i18n';
-import {
- __experimentalToolsPanelItem as ToolsPanelItem,
- SelectControl,
-} from '@wordpress/components';
-
-export default function TaxonomyControl( {
- attributes,
- setQuery,
- setAttributes,
- taxonomyOptions,
-} ) {
- const { termQuery } = attributes;
-
- return (
- termQuery.taxonomy !== 'category' }
- label={ __( 'Taxonomy' ) }
- onDeselect={ () => {
- setQuery( { taxonomy: 'category' } );
- setAttributes( { termsToShow: 'all' } );
- } }
- isShownByDefault
- >
- {
- setQuery( { taxonomy: selectedTaxonomy } );
- setAttributes( { termsToShow: 'all' } );
- } }
- />
-
- );
-}
diff --git a/packages/block-library/src/terms-query/utils.js b/packages/block-library/src/terms-query/utils.js
new file mode 100644
index 00000000000000..f18c129b7b2ce7
--- /dev/null
+++ b/packages/block-library/src/terms-query/utils.js
@@ -0,0 +1,25 @@
+/**
+ * WordPress dependencies
+ */
+import { store as coreStore } from '@wordpress/core-data';
+import { useSelect } from '@wordpress/data';
+import { useMemo } from '@wordpress/element';
+
+/**
+ * Retrieve publicly-queryable taxonomies.
+ *
+ * @return {Object[]} Array of public taxonomy objects.
+ */
+export function usePublicTaxonomies() {
+ const taxonomies = useSelect(
+ ( select ) => select( coreStore ).getTaxonomies( { per_page: -1 } ),
+ []
+ );
+ return useMemo( () => {
+ return (
+ taxonomies?.filter(
+ ( { visibility } ) => visibility?.publicly_queryable
+ ) || []
+ );
+ }, [ taxonomies ] );
+}