/** * External dependencies */ import type { ReactElement } from 'react'; /** * WordPress dependencies */ import { Button, CheckboxControl } from '@wordpress/components'; import { __ } from '@wordpress/i18n'; import { useMemo, useState, useRef, useContext } from '@wordpress/element'; import { useRegistry } from '@wordpress/data'; import { closeSmall } from '@wordpress/icons'; import { useViewportMatch } from '@wordpress/compose'; import { Stack } from '@wordpress/ui'; /** * Internal dependencies */ import DataViewsContext from '../dataviews-context'; import { ActionModal } from '../dataviews-item-actions'; import type { Action, ActionModal as ActionModalType } from '../../types'; import type { SetSelection } from '../../types/private'; import type { ActionTriggerProps } from '../dataviews-item-actions'; import getFooterMessage from '../../utils/get-footer-message'; interface ActionWithModalProps< Item > { action: ActionModalType< Item >; items: Item[]; ActionTriggerComponent: ( props: ActionTriggerProps< Item > ) => ReactElement; } function ActionWithModal< Item >( { action, items, ActionTriggerComponent, }: ActionWithModalProps< Item > ) { const [ isModalOpen, setIsModalOpen ] = useState( false ); const actionTriggerProps = { action, onClick: () => { setIsModalOpen( true ); }, items, }; return ( <> { isModalOpen && ( setIsModalOpen( false ) } /> ) } ); } export function useHasAPossibleBulkAction< Item >( actions: Action< Item >[], item: Item ) { return useMemo( () => { return actions.some( ( action ) => { return ( action.supportsBulk && ( ! action.isEligible || action.isEligible( item ) ) ); } ); }, [ actions, item ] ); } export function useSomeItemHasAPossibleBulkAction< Item >( actions: Action< Item >[], data: Item[] ) { return useMemo( () => { return data.some( ( item ) => { return actions.some( ( action ) => { return ( action.supportsBulk && ( ! action.isEligible || action.isEligible( item ) ) ); } ); } ); }, [ actions, data ] ); } interface BulkSelectionCheckboxProps< Item > { selection: string[]; onChangeSelection: SetSelection; data: Item[]; actions: Action< Item >[]; getItemId: ( item: Item ) => string; disableSelectAll?: boolean; } export function BulkSelectionCheckbox< Item >( { selection, onChangeSelection, data, actions, getItemId, disableSelectAll = false, }: BulkSelectionCheckboxProps< Item > ) { const selectableItems = useMemo( () => { return data.filter( ( item ) => { return actions.some( ( action ) => action.supportsBulk && ( ! action.isEligible || action.isEligible( item ) ) ); } ); }, [ data, actions ] ); const selectedItems = data.filter( ( item ) => selection.includes( getItemId( item ) ) && selectableItems.includes( item ) ); const hasSelection = selection.length > 0; const areAllSelected = selectedItems.length === selectableItems.length; if ( disableSelectAll ) { return ( { onChangeSelection( [] ); } } aria-label={ __( 'Deselect all' ) } /> ); } return ( { if ( areAllSelected ) { onChangeSelection( [] ); } else { onChangeSelection( selectableItems.map( ( item ) => getItemId( item ) ) ); } } } aria-label={ areAllSelected ? __( 'Deselect all' ) : __( 'Select all' ) } /> ); } interface ActionButtonProps< Item > { action: Action< Item >; selectedItems: Item[]; actionInProgress: string | null; setActionInProgress: ( actionId: string | null ) => void; } interface ToolbarContentProps< Item > { selection: string[]; onChangeSelection: SetSelection; data: Item[]; actions: Action< Item >[]; getItemId: ( item: Item ) => string; isInfiniteScroll: boolean; paginationInfo: { totalItems: number; totalPages: number; }; } function ActionTrigger< Item >( { action, onClick, isBusy, items, }: ActionTriggerProps< Item > ) { const label = typeof action.label === 'string' ? action.label : action.label( items ); const isMobile = useViewportMatch( 'medium', '<' ); if ( isMobile ) { return ( ); } const EMPTY_ARRAY: [] = []; function ActionButton< Item >( { action, selectedItems, actionInProgress, setActionInProgress, }: ActionButtonProps< Item > ) { const registry = useRegistry(); const selectedEligibleItems = useMemo( () => { return selectedItems.filter( ( item ) => { return ! action.isEligible || action.isEligible( item ); } ); }, [ action, selectedItems ] ); if ( 'RenderModal' in action ) { return ( ); } return ( { setActionInProgress( action.id ); await action.callback( selectedItems, { registry, } ); setActionInProgress( null ); } } items={ selectedEligibleItems } isBusy={ actionInProgress === action.id } /> ); } function renderFooterContent< Item >( data: Item[], actions: Action< Item >[], getItemId: ( item: Item ) => string, isInfiniteScroll: boolean, selection: string[], actionsToShow: Action< Item >[], selectedItems: Item[], actionInProgress: string | null, setActionInProgress: ( actionId: string | null ) => void, onChangeSelection: SetSelection, paginationInfo: { totalItems: number; totalPages: number; } ) { const message = getFooterMessage( selection.length, data.length, paginationInfo.totalItems, isInfiniteScroll ); return ( { message } { actionsToShow.map( ( action ) => { return ( ); } ) } { selectedItems.length > 0 && (