Add coexistence checks to all enqueue methods to prevent loading both React and Grid.js assets simultaneously. Changes: - ReactAdmin.php: Only enqueue React assets when ?react=1 - Init.php: Skip Grid.js when React active on admin pages - Form.php, Coupon.php, Access.php: Restore classic assets when ?react=0 - Customer.php, Product.php, License.php: Add coexistence checks Now the toggle between Classic and React versions works correctly. Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
201 lines
5.4 KiB
TypeScript
201 lines
5.4 KiB
TypeScript
/**
|
|
* WordPress dependencies
|
|
*/
|
|
import { __ } from '@wordpress/i18n';
|
|
|
|
/**
|
|
* Internal dependencies
|
|
*/
|
|
import AuthorSelect from './author-select';
|
|
import CategorySelect from './category-select';
|
|
import FormTokenField from '../form-token-field';
|
|
import RangeControl from '../range-control';
|
|
import SelectControl from '../select-control';
|
|
import { VStack } from '../v-stack';
|
|
import type {
|
|
QueryControlsProps,
|
|
QueryControlsWithMultipleCategorySelectionProps,
|
|
QueryControlsWithSingleCategorySelectionProps,
|
|
} from './types';
|
|
|
|
const DEFAULT_MIN_ITEMS = 1;
|
|
const DEFAULT_MAX_ITEMS = 100;
|
|
const MAX_CATEGORIES_SUGGESTIONS = 20;
|
|
|
|
function isSingleCategorySelection(
|
|
props: QueryControlsProps
|
|
): props is QueryControlsWithSingleCategorySelectionProps {
|
|
return 'categoriesList' in props;
|
|
}
|
|
|
|
function isMultipleCategorySelection(
|
|
props: QueryControlsProps
|
|
): props is QueryControlsWithMultipleCategorySelectionProps {
|
|
return 'categorySuggestions' in props;
|
|
}
|
|
|
|
/**
|
|
* Controls to query for posts.
|
|
*
|
|
* ```jsx
|
|
* const MyQueryControls = () => (
|
|
* <QueryControls
|
|
* { ...{ maxItems, minItems, numberOfItems, order, orderBy } }
|
|
* onOrderByChange={ ( newOrderBy ) => {
|
|
* updateQuery( { orderBy: newOrderBy } )
|
|
* }
|
|
* onOrderChange={ ( newOrder ) => {
|
|
* updateQuery( { order: newOrder } )
|
|
* }
|
|
* categoriesList={ categories }
|
|
* selectedCategoryId={ category }
|
|
* onCategoryChange={ ( newCategory ) => {
|
|
* updateQuery( { category: newCategory } )
|
|
* }
|
|
* onNumberOfItemsChange={ ( newNumberOfItems ) => {
|
|
* updateQuery( { numberOfItems: newNumberOfItems } )
|
|
* } }
|
|
* />
|
|
* );
|
|
* ```
|
|
*/
|
|
export function QueryControls( {
|
|
__next40pxDefaultSize = false,
|
|
authorList,
|
|
selectedAuthorId,
|
|
numberOfItems,
|
|
order,
|
|
orderBy,
|
|
maxItems = DEFAULT_MAX_ITEMS,
|
|
minItems = DEFAULT_MIN_ITEMS,
|
|
onAuthorChange,
|
|
onNumberOfItemsChange,
|
|
onOrderChange,
|
|
onOrderByChange,
|
|
// Props for single OR multiple category selection are not destructured here,
|
|
// but instead are destructured inline where necessary.
|
|
...props
|
|
}: QueryControlsProps ) {
|
|
return (
|
|
<VStack spacing="4" className="components-query-controls">
|
|
{ [
|
|
onOrderChange && onOrderByChange && (
|
|
<SelectControl
|
|
__nextHasNoMarginBottom
|
|
__next40pxDefaultSize={ __next40pxDefaultSize }
|
|
key="query-controls-order-select"
|
|
label={ __( 'Order by' ) }
|
|
value={ `${ orderBy }/${ order }` }
|
|
options={ [
|
|
{
|
|
label: __( 'Newest to oldest' ),
|
|
value: 'date/desc',
|
|
},
|
|
{
|
|
label: __( 'Oldest to newest' ),
|
|
value: 'date/asc',
|
|
},
|
|
{
|
|
/* translators: label for ordering posts by title in ascending order */
|
|
label: __( 'A → Z' ),
|
|
value: 'title/asc',
|
|
},
|
|
{
|
|
/* translators: label for ordering posts by title in descending order */
|
|
label: __( 'Z → A' ),
|
|
value: 'title/desc',
|
|
},
|
|
] }
|
|
onChange={ ( value ) => {
|
|
if ( typeof value !== 'string' ) {
|
|
return;
|
|
}
|
|
|
|
const [ newOrderBy, newOrder ] = value.split( '/' );
|
|
if ( newOrder !== order ) {
|
|
onOrderChange(
|
|
newOrder as NonNullable<
|
|
QueryControlsProps[ 'order' ]
|
|
>
|
|
);
|
|
}
|
|
if ( newOrderBy !== orderBy ) {
|
|
onOrderByChange(
|
|
newOrderBy as NonNullable<
|
|
QueryControlsProps[ 'orderBy' ]
|
|
>
|
|
);
|
|
}
|
|
} }
|
|
/>
|
|
),
|
|
isSingleCategorySelection( props ) &&
|
|
props.categoriesList &&
|
|
props.onCategoryChange && (
|
|
<CategorySelect
|
|
__next40pxDefaultSize={ __next40pxDefaultSize }
|
|
key="query-controls-category-select"
|
|
categoriesList={ props.categoriesList }
|
|
label={ __( 'Category' ) }
|
|
noOptionLabel={ __( 'All' ) }
|
|
selectedCategoryId={ props.selectedCategoryId }
|
|
onChange={ props.onCategoryChange }
|
|
/>
|
|
),
|
|
isMultipleCategorySelection( props ) &&
|
|
props.categorySuggestions &&
|
|
props.onCategoryChange && (
|
|
<FormTokenField
|
|
__next40pxDefaultSize={ __next40pxDefaultSize }
|
|
__nextHasNoMarginBottom
|
|
key="query-controls-categories-select"
|
|
label={ __( 'Categories' ) }
|
|
value={
|
|
props.selectedCategories &&
|
|
props.selectedCategories.map( ( item ) => ( {
|
|
id: item.id,
|
|
// Keeping the fallback to `item.value` for legacy reasons,
|
|
// even if items of `selectedCategories` should not have a
|
|
// `value` property.
|
|
// @ts-expect-error
|
|
value: item.name || item.value,
|
|
} ) )
|
|
}
|
|
suggestions={ Object.keys(
|
|
props.categorySuggestions
|
|
) }
|
|
onChange={ props.onCategoryChange }
|
|
maxSuggestions={ MAX_CATEGORIES_SUGGESTIONS }
|
|
/>
|
|
),
|
|
onAuthorChange && (
|
|
<AuthorSelect
|
|
__next40pxDefaultSize={ __next40pxDefaultSize }
|
|
key="query-controls-author-select"
|
|
authorList={ authorList }
|
|
label={ __( 'Author' ) }
|
|
noOptionLabel={ __( 'All' ) }
|
|
selectedAuthorId={ selectedAuthorId }
|
|
onChange={ onAuthorChange }
|
|
/>
|
|
),
|
|
onNumberOfItemsChange && (
|
|
<RangeControl
|
|
__nextHasNoMarginBottom
|
|
__next40pxDefaultSize={ __next40pxDefaultSize }
|
|
key="query-controls-range-control"
|
|
label={ __( 'Number of items' ) }
|
|
value={ numberOfItems }
|
|
onChange={ onNumberOfItemsChange }
|
|
min={ minItems }
|
|
max={ maxItems }
|
|
required
|
|
/>
|
|
),
|
|
] }
|
|
</VStack>
|
|
);
|
|
}
|
|
|
|
export default QueryControls;
|