@wordpress/block-library
Version:
Block library for the WordPress editor.
462 lines (445 loc) • 13.2 kB
JavaScript
/**
* WordPress dependencies
*/
import {
TextControl,
SelectControl,
Notice,
__experimentalVStack as VStack,
__experimentalToolsPanel as ToolsPanel,
__experimentalToolsPanelItem as ToolsPanelItem,
__experimentalToggleGroupControl as ToggleGroupControl,
__experimentalToggleGroupControlOption as ToggleGroupControlOption,
} from '@wordpress/components';
import { useSelect } from '@wordpress/data';
import { store as coreStore } from '@wordpress/core-data';
import { __ } from '@wordpress/i18n';
import { debounce } from '@wordpress/compose';
import { useState, useMemo } from '@wordpress/element';
/**
* Internal dependencies
*/
import OrderControl from './order-control';
import AuthorControl from './author-control';
import ParentControl from './parent-control';
import { TaxonomyControls } from './taxonomy-controls';
import FormatControls from './format-controls';
import StickyControl from './sticky-control';
import PerPageControl from './per-page-control';
import OffsetControl from './offset-controls';
import PagesControl from './pages-control';
import {
usePostTypes,
useIsPostTypeHierarchical,
useAllowedControls,
isControlAllowed,
useTaxonomies,
useOrderByOptions,
} from '../../utils';
import { useToolsPanelDropdownMenuProps } from '../../../utils/hooks';
export default function QueryInspectorControls( props ) {
const { attributes, setQuery, isSingular } = props;
const { query } = attributes;
const {
order,
orderBy,
author: authorIds,
pages,
postType,
perPage,
offset,
sticky,
inherit,
taxQuery,
parents,
format,
} = query;
const allowedControls = useAllowedControls( attributes );
const showSticky = postType === 'post';
const {
postTypesTaxonomiesMap,
postTypesSelectOptions,
postTypeFormatSupportMap,
} = usePostTypes();
const taxonomies = useTaxonomies( postType );
const isPostTypeHierarchical = useIsPostTypeHierarchical( postType );
const onPostTypeChange = ( newValue ) => {
const updateQuery = { postType: newValue };
// We need to dynamically update the `taxQuery` property,
// by removing any not supported taxonomy from the query.
const supportedTaxonomies = postTypesTaxonomiesMap[ newValue ];
const updatedTaxQuery = Object.entries( taxQuery || {} ).reduce(
( accumulator, [ taxonomySlug, terms ] ) => {
if ( supportedTaxonomies.includes( taxonomySlug ) ) {
accumulator[ taxonomySlug ] = terms;
}
return accumulator;
},
{}
);
updateQuery.taxQuery = !! Object.keys( updatedTaxQuery ).length
? updatedTaxQuery
: undefined;
if ( newValue !== 'post' ) {
updateQuery.sticky = '';
}
// We need to reset `parents` because they are tied to each post type.
updateQuery.parents = [];
// Post types can register post format support with `add_post_type_support`.
// But we need to reset the `format` property when switching to post types
// that do not support post formats.
const hasFormatSupport = postTypeFormatSupportMap[ newValue ];
if ( ! hasFormatSupport ) {
updateQuery.format = [];
}
setQuery( updateQuery );
};
const [ querySearch, setQuerySearch ] = useState( query.search );
const debouncedQuerySearch = useMemo( () => {
return debounce( ( newQuerySearch ) => {
setQuery( { search: newQuerySearch } );
}, 250 );
}, [ setQuery ] );
const orderByOptions = useOrderByOptions( postType );
const showInheritControl = isControlAllowed( allowedControls, 'inherit' );
const showPostTypeControl =
! inherit && isControlAllowed( allowedControls, 'postType' );
const postTypeControlLabel = __( 'Post type' );
const postTypeControlHelp = __(
'Select the type of content to display: posts, pages, or custom post types.'
);
const showOrderControl =
! inherit && isControlAllowed( allowedControls, 'order' );
const showStickyControl =
! inherit &&
showSticky &&
isControlAllowed( allowedControls, 'sticky' );
const showSettingsPanel =
showInheritControl ||
showPostTypeControl ||
showOrderControl ||
showStickyControl;
const showTaxControl =
!! taxonomies?.length &&
isControlAllowed( allowedControls, 'taxQuery' );
const showAuthorControl = isControlAllowed( allowedControls, 'author' );
const showSearchControl = isControlAllowed( allowedControls, 'search' );
const showParentControl =
isControlAllowed( allowedControls, 'parents' ) &&
isPostTypeHierarchical;
const postTypeHasFormatSupport = postTypeFormatSupportMap[ postType ];
const showFormatControl = useSelect(
( select ) => {
// Check if the post type supports post formats and if the control is allowed.
if (
! postTypeHasFormatSupport ||
! isControlAllowed( allowedControls, 'format' )
) {
return false;
}
const themeSupports = select( coreStore ).getThemeSupports();
// If there are no supported formats, getThemeSupports still includes the default 'standard' format,
// and in this case the control should not be shown since the user has no other formats to choose from.
return (
themeSupports.formats &&
themeSupports.formats.length > 0 &&
themeSupports.formats.some( ( type ) => type !== 'standard' )
);
},
[ allowedControls, postTypeHasFormatSupport ]
);
const showFiltersPanel =
showTaxControl ||
showAuthorControl ||
showSearchControl ||
showParentControl ||
showFormatControl;
const dropdownMenuProps = useToolsPanelDropdownMenuProps();
const showPostCountControl = isControlAllowed(
allowedControls,
'postCount'
);
const showOffSetControl = isControlAllowed( allowedControls, 'offset' );
const showPagesControl = isControlAllowed( allowedControls, 'pages' );
const showDisplayPanel =
showPostCountControl || showOffSetControl || showPagesControl;
// The block cannot inherit a default WordPress query in singular content (e.g., post, page, 404, blank).
// Warn users but still permit this type of query for exceptional cases in Classic and Hybrid themes.
const hasInheritanceWarning = isSingular && inherit;
return (
<>
{ showSettingsPanel && (
<ToolsPanel
label={ __( 'Settings' ) }
resetAll={ () => {
setQuery( {
postType: 'post',
order: 'desc',
orderBy: 'date',
sticky: '',
inherit: true,
} );
} }
dropdownMenuProps={ dropdownMenuProps }
>
{ showInheritControl && (
<ToolsPanelItem
hasValue={ () => ! inherit }
label={ __( 'Query type' ) }
onDeselect={ () => setQuery( { inherit: true } ) }
isShownByDefault
>
<VStack spacing={ 4 }>
<ToggleGroupControl
__next40pxDefaultSize
__nextHasNoMarginBottom
label={ __( 'Query type' ) }
isBlock
onChange={ ( value ) => {
setQuery( {
inherit: value === 'default',
} );
} }
help={
inherit
? __(
'Display a list of posts or custom post types based on the current template.'
)
: __(
'Display a list of posts or custom post types based on specific criteria.'
)
}
value={ !! inherit ? 'default' : 'custom' }
>
<ToggleGroupControlOption
value="default"
label={ __( 'Default' ) }
/>
<ToggleGroupControlOption
value="custom"
label={ __( 'Custom' ) }
/>
</ToggleGroupControl>
{ hasInheritanceWarning && (
<Notice
status="warning"
isDismissible={ false }
>
{ __(
'Cannot inherit the current template query when placed inside the singular content (e.g., post, page, 404, blank).'
) }
</Notice>
) }
</VStack>
</ToolsPanelItem>
) }
{ showPostTypeControl && (
<ToolsPanelItem
hasValue={ () => postType !== 'post' }
label={ postTypeControlLabel }
onDeselect={ () => onPostTypeChange( 'post' ) }
isShownByDefault
>
{ postTypesSelectOptions.length > 2 ? (
<SelectControl
__nextHasNoMarginBottom
__next40pxDefaultSize
options={ postTypesSelectOptions }
value={ postType }
label={ postTypeControlLabel }
onChange={ onPostTypeChange }
help={ postTypeControlHelp }
/>
) : (
<ToggleGroupControl
__nextHasNoMarginBottom
__next40pxDefaultSize
isBlock
value={ postType }
label={ postTypeControlLabel }
onChange={ onPostTypeChange }
help={ postTypeControlHelp }
>
{ postTypesSelectOptions.map(
( option ) => (
<ToggleGroupControlOption
key={ option.value }
value={ option.value }
label={ option.label }
/>
)
) }
</ToggleGroupControl>
) }
</ToolsPanelItem>
) }
{ showOrderControl && (
<ToolsPanelItem
hasValue={ () =>
order !== 'desc' || orderBy !== 'date'
}
label={ __( 'Order by' ) }
onDeselect={ () =>
setQuery( { order: 'desc', orderBy: 'date' } )
}
isShownByDefault
>
<OrderControl
{ ...{ order, orderBy, orderByOptions } }
onChange={ setQuery }
/>
</ToolsPanelItem>
) }
{ showStickyControl && (
<ToolsPanelItem
hasValue={ () => !! sticky }
label={ __( 'Sticky posts' ) }
onDeselect={ () => setQuery( { sticky: '' } ) }
isShownByDefault
>
<StickyControl
value={ sticky }
onChange={ ( value ) =>
setQuery( { sticky: value } )
}
/>
</ToolsPanelItem>
) }
</ToolsPanel>
) }
{ ! inherit && showDisplayPanel && (
<ToolsPanel
className="block-library-query-toolspanel__display"
label={ __( 'Display' ) }
resetAll={ () => {
setQuery( {
offset: 0,
pages: 0,
} );
} }
dropdownMenuProps={ dropdownMenuProps }
>
<ToolsPanelItem
label={ __( 'Items per page' ) }
hasValue={ () => perPage > 0 }
>
<PerPageControl
perPage={ perPage }
offset={ offset }
onChange={ setQuery }
/>
</ToolsPanelItem>
<ToolsPanelItem
label={ __( 'Offset' ) }
hasValue={ () => offset > 0 }
onDeselect={ () => setQuery( { offset: 0 } ) }
>
<OffsetControl
offset={ offset }
onChange={ setQuery }
/>
</ToolsPanelItem>
<ToolsPanelItem
label={ __( 'Max pages to show' ) }
hasValue={ () => pages > 0 }
onDeselect={ () => setQuery( { pages: 0 } ) }
>
<PagesControl pages={ pages } onChange={ setQuery } />
</ToolsPanelItem>
</ToolsPanel>
) }
{ ! inherit && showFiltersPanel && (
<ToolsPanel
className="block-library-query-toolspanel__filters" // unused but kept for backward compatibility
label={ __( 'Filters' ) }
resetAll={ () => {
setQuery( {
author: '',
parents: [],
search: '',
taxQuery: null,
format: [],
} );
setQuerySearch( '' );
} }
dropdownMenuProps={ dropdownMenuProps }
>
{ showTaxControl && (
<ToolsPanelItem
label={ __( 'Taxonomies' ) }
hasValue={ () =>
Object.values( taxQuery || {} ).some(
( terms ) => !! terms.length
)
}
onDeselect={ () => setQuery( { taxQuery: null } ) }
>
<TaxonomyControls
onChange={ setQuery }
query={ query }
/>
</ToolsPanelItem>
) }
{ showAuthorControl && (
<ToolsPanelItem
hasValue={ () => !! authorIds }
label={ __( 'Authors' ) }
onDeselect={ () => setQuery( { author: '' } ) }
>
<AuthorControl
value={ authorIds }
onChange={ setQuery }
/>
</ToolsPanelItem>
) }
{ showSearchControl && (
<ToolsPanelItem
hasValue={ () => !! querySearch }
label={ __( 'Keyword' ) }
onDeselect={ () => {
setQuery( { search: '' } );
setQuerySearch( '' );
} }
>
<TextControl
__nextHasNoMarginBottom
__next40pxDefaultSize
label={ __( 'Keyword' ) }
value={ querySearch }
onChange={ ( newQuerySearch ) => {
debouncedQuerySearch( newQuerySearch );
setQuerySearch( newQuerySearch );
} }
/>
</ToolsPanelItem>
) }
{ showParentControl && (
<ToolsPanelItem
hasValue={ () => !! parents?.length }
label={ __( 'Parents' ) }
onDeselect={ () => setQuery( { parents: [] } ) }
>
<ParentControl
parents={ parents }
postType={ postType }
onChange={ setQuery }
/>
</ToolsPanelItem>
) }
{ showFormatControl && (
<ToolsPanelItem
hasValue={ () => !! format?.length }
label={ __( 'Formats' ) }
onDeselect={ () => setQuery( { format: [] } ) }
>
<FormatControls
onChange={ setQuery }
query={ query }
/>
</ToolsPanelItem>
) }
</ToolsPanel>
) }
</>
);
}