From e72a20a2442cbeefcf4834a50072a7db4fe3ed39 Mon Sep 17 00:00:00 2001 From: Jorge Costa Date: Mon, 5 Aug 2024 17:55:15 +0100 Subject: [PATCH] finalize implementation --- packages/base-styles/_z-index.scss | 1 - .../dataviews-bulk-actions-toolbar/index.tsx | 197 --------- .../dataviews-bulk-actions-toolbar/style.scss | 5 - .../dataviews-bulk-actions/index.tsx | 403 +++++++++--------- .../dataviews-bulk-actions/style.scss | 14 +- .../src/components/dataviews-footer/index.tsx | 23 +- .../src/components/dataviews/index.tsx | 2 - 7 files changed, 225 insertions(+), 420 deletions(-) delete mode 100644 packages/dataviews/src/components/dataviews-bulk-actions-toolbar/index.tsx delete mode 100644 packages/dataviews/src/components/dataviews-bulk-actions-toolbar/style.scss diff --git a/packages/base-styles/_z-index.scss b/packages/base-styles/_z-index.scss index 4d5f22e02fa7d..2a83c02cb2037 100644 --- a/packages/base-styles/_z-index.scss +++ b/packages/base-styles/_z-index.scss @@ -131,7 +131,6 @@ $z-layers: ( ".block-editor-template-part__selection-modal": 1000001, ".block-editor-block-rename-modal": 1000001, ".edit-site-list__rename-modal": 1000001, - ".dataviews-bulk-actions__modal": 1000001, ".dataviews-action-modal": 1000001, ".editor-action-modal": 1000001, ".editor-post-template__swap-template-modal": 1000001, diff --git a/packages/dataviews/src/components/dataviews-bulk-actions-toolbar/index.tsx b/packages/dataviews/src/components/dataviews-bulk-actions-toolbar/index.tsx deleted file mode 100644 index 3f6d61d65bc8d..0000000000000 --- a/packages/dataviews/src/components/dataviews-bulk-actions-toolbar/index.tsx +++ /dev/null @@ -1,197 +0,0 @@ -/** - * WordPress dependencies - */ -import { - ToolbarButton, - Toolbar, - ToolbarGroup, - __unstableMotion as motion, - __unstableAnimatePresence as AnimatePresence, - __experimentalHStack as HStack, -} from '@wordpress/components'; -import { useMemo, useState, useRef, useContext } from '@wordpress/element'; -import { _n, sprintf, __ } from '@wordpress/i18n'; -import { closeSmall } from '@wordpress/icons'; -import { useReducedMotion } from '@wordpress/compose'; -import { useRegistry } from '@wordpress/data'; - -/** - * Internal dependencies - */ -import { - useSomeItemHasAPossibleBulkAction, - BulkSelectionCheckbox, -} from '../dataviews-bulk-actions'; -import DataViewsContext from '../dataviews-context'; -import { ActionWithModal } from '../dataviews-item-actions'; -import { LAYOUT_GRID, LAYOUT_TABLE } from '../../constants'; -import type { Action } from '../../types'; -import type { ActionTriggerProps } from '../dataviews-item-actions'; -import type { SetSelection } from '../../private-types'; - -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; -} - -function ActionTrigger< Item >( { - action, - onClick, - isBusy, - items, -}: ActionTriggerProps< Item > ) { - const label = - typeof action.label === 'string' ? action.label : action.label( items ); - 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 ); - action.callback( selectedItems, { - registry, - } ); - } } - items={ selectedEligibleItems } - isBusy={ actionInProgress === action.id } - /> - ); -} - -function ToolbarContent< Item >( { - selection, - actions, - onChangeSelection, - data, - getItemId, -}: ToolbarContentProps< Item > ) { - const bulkActions = useMemo( - () => actions.filter( ( action ) => action.supportsBulk ), - [ actions ] - ); - const selectableItems = useMemo( () => { - return data.filter( ( item ) => { - return bulkActions.some( - ( action ) => ! action.isEligible || action.isEligible( item ) - ); - } ); - }, [ data, bulkActions ] ); - - const selectedItems = useMemo( () => { - return data.filter( - ( item ) => - selection.includes( getItemId( item ) ) && - selectableItems.includes( item ) - ); - }, [ selection, data, getItemId, selectableItems ] ); - const countToShow = - selectedItems.length > 0 - ? selectedItems.length - : selectableItems.length; - return ( - - - - { sprintf( - /* translators: %d: number of items. */ - _n( '%d item', '%d items', countToShow ), - countToShow - ) } - - - ); -} - -export default function BulkActionsToolbar() { - const { - data, - selection, - actions = EMPTY_ARRAY, - onChangeSelection, - getItemId, - } = useContext( DataViewsContext ); - const selectedItems = useMemo( () => { - return data.filter( ( item ) => - selection.includes( getItemId( item ) ) - ); - }, [ selection, data, getItemId ] ); - - const actionsToShow = useMemo( - () => - actions.filter( ( action ) => { - return ( - action.supportsBulk && - action.icon && - selectedItems.some( - ( item ) => - ! action.isEligible || action.isEligible( item ) - ) - ); - } ), - [ actions, selectedItems ] - ); - - return ( - - ); -} diff --git a/packages/dataviews/src/components/dataviews-bulk-actions-toolbar/style.scss b/packages/dataviews/src/components/dataviews-bulk-actions-toolbar/style.scss deleted file mode 100644 index 0451c5b9adb53..0000000000000 --- a/packages/dataviews/src/components/dataviews-bulk-actions-toolbar/style.scss +++ /dev/null @@ -1,5 +0,0 @@ -.dataviews-bulk-actions-toolbar__selection-count { - display: flex; - align-items: center; - margin: 0 $grid-unit-10 0 $grid-unit-10; -} diff --git a/packages/dataviews/src/components/dataviews-bulk-actions/index.tsx b/packages/dataviews/src/components/dataviews-bulk-actions/index.tsx index 121ed1e4ea572..ba9556eed572f 100644 --- a/packages/dataviews/src/components/dataviews-bulk-actions/index.tsx +++ b/packages/dataviews/src/components/dataviews-bulk-actions/index.tsx @@ -2,49 +2,23 @@ * WordPress dependencies */ import { - privateApis as componentsPrivateApis, Button, - Modal, CheckboxControl, + __experimentalHStack as HStack, } from '@wordpress/components'; import { __, sprintf, _n } from '@wordpress/i18n'; -import { useMemo, useState, useCallback, useContext } from '@wordpress/element'; +import { useMemo, useState, useRef, useContext } from '@wordpress/element'; import { useRegistry } from '@wordpress/data'; +import { closeSmall } from '@wordpress/icons'; /** * Internal dependencies */ import DataViewsContext from '../dataviews-context'; -import { LAYOUT_TABLE, LAYOUT_GRID } from '../../constants'; -import { unlock } from '../../lock-unlock'; -import type { Action, ActionModal } from '../../types'; +import { ActionWithModal } from '../dataviews-item-actions'; +import type { Action } from '../../types'; import type { SetSelection } from '../../private-types'; - -const { - DropdownMenuV2: DropdownMenu, - DropdownMenuGroupV2: DropdownMenuGroup, - DropdownMenuItemV2: DropdownMenuItem, - DropdownMenuSeparatorV2: DropdownMenuSeparator, -} = unlock( componentsPrivateApis ); - -interface ActionWithModalProps< Item > { - action: ActionModal< Item >; - selectedItems: Item[]; - setActionWithModal: ( action?: ActionModal< Item > ) => void; - onMenuOpenChange: ( isOpen: boolean ) => void; -} - -interface BulkActionsItemProps< Item > { - action: Action< Item >; - selectedItems: Item[]; - setActionWithModal: ( action?: ActionModal< Item > ) => void; -} - -interface ActionsMenuGroupProps< Item > { - actions: Action< Item >[]; - selectedItems: Item[]; - setActionWithModal: ( action?: ActionModal< Item > ) => void; -} +import type { ActionTriggerProps } from '../dataviews-item-actions'; export function useHasAPossibleBulkAction< Item >( actions: Action< Item >[], @@ -128,121 +102,164 @@ export function BulkSelectionCheckbox< Item >( { ); } -function ActionWithModal< Item >( { +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; +} + +function ActionTrigger< Item >( { action, - selectedItems, - setActionWithModal, - onMenuOpenChange, -}: ActionWithModalProps< Item > ) { - const eligibleItems = useMemo( () => { - return selectedItems.filter( - ( item ) => ! action.isEligible || action.isEligible( item ) - ); - }, [ action, selectedItems ] ); - const { RenderModal, hideModalHeader } = action; - const onCloseModal = useCallback( () => { - setActionWithModal( undefined ); - }, [ setActionWithModal ] ); + onClick, + isBusy, + items, +}: ActionTriggerProps< Item > ) { const label = - typeof action.label === 'string' - ? action.label - : action.label( selectedItems ); + typeof action.label === 'string' ? action.label : action.label( items ); return ( - - onMenuOpenChange( false ) } - /> - + - } - > - - - { - onChangeSelection( - selectableItems.map( ( item ) => - getItemId( item ) - ) - ); - } } - suffix={ numberSelectableItems } - > - { __( 'Select all' ) } - - { - onChangeSelection( [] ); - } } - > - { __( 'Deselect' ) } - - - - { actionWithModal && ( - - ) } - + const actionsToShow = useMemo( + () => + actions.filter( ( action ) => { + return ( + action.supportsBulk && + action.icon && + selectedItems.some( + ( item ) => + ! action.isEligible || action.isEligible( item ) + ) + ); + } ), + [ actions, selectedItems ] ); + if ( ! actionInProgress ) { + if ( footerContent.current ) { + footerContent.current = null; + } + return renderFooterContent( + data, + actions, + getItemId, + selection, + actionsToShow, + selectedItems, + actionInProgress, + setActionInProgress, + onChangeSelection, + selectableItems + ); + } else if ( ! footerContent.current ) { + footerContent.current = renderFooterContent( + data, + actions, + getItemId, + selection, + actionsToShow, + selectedItems, + actionInProgress, + setActionInProgress, + onChangeSelection, + selectableItems + ); + } + return footerContent.current; } -export default function BulkActions() { - const { data, actions = [], view } = useContext( DataViewsContext ); - const hasPossibleBulkAction = useSomeItemHasAPossibleBulkAction( - actions, - data +export function BulkActionsFooter() { + const { + data, + selection, + actions = EMPTY_ARRAY, + onChangeSelection, + getItemId, + } = useContext( DataViewsContext ); + return ( + ); - if ( - ! [ LAYOUT_TABLE, LAYOUT_GRID ].includes( view.type ) || - ! hasPossibleBulkAction - ) { - return null; - } - - return <_BulkActions />; } diff --git a/packages/dataviews/src/components/dataviews-bulk-actions/style.scss b/packages/dataviews/src/components/dataviews-bulk-actions/style.scss index 71f76ce9a6c16..e83bba19c3098 100644 --- a/packages/dataviews/src/components/dataviews-bulk-actions/style.scss +++ b/packages/dataviews/src/components/dataviews-bulk-actions/style.scss @@ -1,7 +1,11 @@ -.dataviews-bulk-actions__modal { - z-index: z-index(".dataviews-bulk-actions__modal"); -} -.dataviews-bulk-actions__edit-button.components-button { - flex-shrink: 0; +.dataviews-bulk-actions-footer__item-count { + color: $gray-900; + font-weight: 500; + margin-right: $grid-unit-15; } + +.dataviews-bulk-actions-footer__container { + margin-right: auto; + height: $grid-unit-40+$grid-unit-05; +} \ No newline at end of file diff --git a/packages/dataviews/src/components/dataviews-footer/index.tsx b/packages/dataviews/src/components/dataviews-footer/index.tsx index 0d7f2e3b4473a..476ddeb8442c9 100644 --- a/packages/dataviews/src/components/dataviews-footer/index.tsx +++ b/packages/dataviews/src/components/dataviews-footer/index.tsx @@ -9,13 +9,30 @@ import { useContext } from '@wordpress/element'; */ import DataViewsContext from '../dataviews-context'; import DataViewsPagination from '../dataviews-pagination'; -import BulkActionsToolbar from '../dataviews-bulk-actions-toolbar'; +import { + BulkActionsFooter, + useSomeItemHasAPossibleBulkAction, +} from '../dataviews-bulk-actions'; +import { LAYOUT_GRID, LAYOUT_TABLE } from '../../constants'; + +const EMPTY_ARRAY: [] = []; export default function DataViewsFooter() { const { + view, paginationInfo: { totalItems = 0, totalPages }, + data, + actions = EMPTY_ARRAY, } = useContext( DataViewsContext ); - if ( ! totalItems || ! totalPages ) { + const hasBulkActions = + useSomeItemHasAPossibleBulkAction( actions, data ) && + [ LAYOUT_TABLE, LAYOUT_GRID ].includes( view.type ); + + if ( + ! totalItems || + ! totalPages || + ( totalPages <= 1 && ! hasBulkActions ) + ) { return null; } return ( @@ -26,7 +43,7 @@ export default function DataViewsFooter() { justify="end" className="dataviews-footer" > - + { hasBulkActions && } ) diff --git a/packages/dataviews/src/components/dataviews/index.tsx b/packages/dataviews/src/components/dataviews/index.tsx index 3f2c84054f983..f4063e1fdac42 100644 --- a/packages/dataviews/src/components/dataviews/index.tsx +++ b/packages/dataviews/src/components/dataviews/index.tsx @@ -12,7 +12,6 @@ import { useMemo, useState } from '@wordpress/element'; /** * Internal dependencies */ -import { default as DataViewsBulkActions } from '../dataviews-bulk-actions'; import DataViewsContext from '../dataviews-context'; import { default as DataViewsFilters, @@ -138,7 +137,6 @@ export default function DataViews< Item >( { setDensity={ setDensity } /> ) } -