-
Notifications
You must be signed in to change notification settings - Fork 4.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add: Bulk actions to dataviews with the new design. (#57255)
* Add: Bulk actions to dataviews with the new design. * post rebase fixes. * Add missing secondary variant * Move bulk actions button position * Don't render bulk actions functionality when there are no bulk actions avaliable. * Fix button padding * Fix focus loss * fix flex shring * Fix focus issues * remove data items meanwhile removed from the selection * remove line accidently added * Fix vertical alignment post rebase. * Fix unrequired decodeEntities usage. * Apply feedback to the promises * lint fix * Feedback application * Labels object instead of function * Introduce removeTemplates action * fix css rebase issue * lint fixes. * remove labels * simplify logic condition * fix some classnames * multiple small changes of feedback * remove getItemTitle * Inspect Promise.allSettled result * typo * case fixing * update patterns actions * Added a selection mark * lint fixes * Style adjustments --------- Co-authored-by: James Koster <[email protected]>
- Loading branch information
1 parent
167c439
commit f154dc7
Showing
11 changed files
with
628 additions
and
115 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,187 @@ | ||
/** | ||
* WordPress dependencies | ||
*/ | ||
import { | ||
privateApis as componentsPrivateApis, | ||
Button, | ||
Modal, | ||
} from '@wordpress/components'; | ||
import { __, sprintf, _n } from '@wordpress/i18n'; | ||
import { useMemo, useState, useCallback } from '@wordpress/element'; | ||
|
||
/** | ||
* Internal dependencies | ||
*/ | ||
import { unlock } from './lock-unlock'; | ||
|
||
const { | ||
DropdownMenuV2: DropdownMenu, | ||
DropdownMenuGroupV2: DropdownMenuGroup, | ||
DropdownMenuItemV2: DropdownMenuItem, | ||
DropdownMenuSeparatorV2: DropdownMenuSeparator, | ||
} = unlock( componentsPrivateApis ); | ||
|
||
function ActionWithModal( { | ||
action, | ||
selectedItems, | ||
setActionWithModal, | ||
onMenuOpenChange, | ||
} ) { | ||
const eligibleItems = useMemo( () => { | ||
return selectedItems.filter( ( item ) => action.isEligible( item ) ); | ||
}, [ action, selectedItems ] ); | ||
const { RenderModal, hideModalHeader } = action; | ||
const onCloseModal = useCallback( () => { | ||
setActionWithModal( undefined ); | ||
}, [ setActionWithModal ] ); | ||
return ( | ||
<Modal | ||
title={ ! hideModalHeader && action.label } | ||
__experimentalHideHeader={ !! hideModalHeader } | ||
onRequestClose={ onCloseModal } | ||
overlayClassName="dataviews-action-modal" | ||
> | ||
<RenderModal | ||
items={ eligibleItems } | ||
closeModal={ onCloseModal } | ||
onPerform={ () => onMenuOpenChange( false ) } | ||
/> | ||
</Modal> | ||
); | ||
} | ||
|
||
function BulkActionItem( { action, selectedItems, setActionWithModal } ) { | ||
const eligibleItems = useMemo( () => { | ||
return selectedItems.filter( ( item ) => action.isEligible( item ) ); | ||
}, [ action, selectedItems ] ); | ||
|
||
const shouldShowModal = !! action.RenderModal; | ||
|
||
return ( | ||
<DropdownMenuItem | ||
key={ action.id } | ||
disabled={ eligibleItems.length === 0 } | ||
hideOnClick={ ! shouldShowModal } | ||
onClick={ async () => { | ||
if ( shouldShowModal ) { | ||
setActionWithModal( action ); | ||
} else { | ||
await action.callback( eligibleItems ); | ||
} | ||
} } | ||
suffix={ | ||
eligibleItems.length > 0 ? eligibleItems.length : undefined | ||
} | ||
> | ||
{ action.label } | ||
</DropdownMenuItem> | ||
); | ||
} | ||
|
||
function ActionsMenuGroup( { actions, selectedItems, setActionWithModal } ) { | ||
return ( | ||
<> | ||
<DropdownMenuGroup> | ||
{ actions.map( ( action ) => ( | ||
<BulkActionItem | ||
key={ action.id } | ||
action={ action } | ||
selectedItems={ selectedItems } | ||
setActionWithModal={ setActionWithModal } | ||
/> | ||
) ) } | ||
</DropdownMenuGroup> | ||
<DropdownMenuSeparator /> | ||
</> | ||
); | ||
} | ||
|
||
export default function BulkActions( { | ||
data, | ||
actions, | ||
selection, | ||
onSelectionChange, | ||
getItemId, | ||
} ) { | ||
const bulkActions = useMemo( | ||
() => actions.filter( ( action ) => action.supportsBulk ), | ||
[ actions ] | ||
); | ||
const areAllSelected = selection && selection.length === data.length; | ||
const [ isMenuOpen, onMenuOpenChange ] = useState( false ); | ||
const [ actionWithModal, setActionWithModal ] = useState(); | ||
const selectedItems = useMemo( () => { | ||
return data.filter( ( item ) => | ||
selection.includes( getItemId( item ) ) | ||
); | ||
}, [ selection, data, getItemId ] ); | ||
|
||
if ( bulkActions.length === 0 ) { | ||
return null; | ||
} | ||
return ( | ||
<> | ||
<DropdownMenu | ||
open={ isMenuOpen } | ||
onOpenChange={ onMenuOpenChange } | ||
label={ __( 'Bulk actions' ) } | ||
style={ { minWidth: '240px' } } | ||
trigger={ | ||
<Button | ||
className="dataviews-bulk-edit-button" | ||
__next40pxDefaultSize | ||
variant="tertiary" | ||
size="compact" | ||
> | ||
{ selection.length | ||
? sprintf( | ||
/* translators: %d: Number of items. */ | ||
_n( | ||
'Edit %d item', | ||
'Edit %d items', | ||
selection.length | ||
), | ||
selection.length | ||
) | ||
: __( 'Bulk edit' ) } | ||
</Button> | ||
} | ||
> | ||
<ActionsMenuGroup | ||
actions={ bulkActions } | ||
setActionWithModal={ setActionWithModal } | ||
selectedItems={ selectedItems } | ||
/> | ||
<DropdownMenuGroup> | ||
<DropdownMenuItem | ||
disabled={ areAllSelected } | ||
hideOnClick={ false } | ||
onClick={ () => { | ||
onSelectionChange( data ); | ||
} } | ||
suffix={ data.length } | ||
> | ||
{ __( 'Select all' ) } | ||
</DropdownMenuItem> | ||
<DropdownMenuItem | ||
disabled={ selection.length === 0 } | ||
hideOnClick={ false } | ||
onClick={ () => { | ||
onSelectionChange( [] ); | ||
} } | ||
> | ||
{ __( 'Deselect' ) } | ||
</DropdownMenuItem> | ||
</DropdownMenuGroup> | ||
</DropdownMenu> | ||
{ actionWithModal && ( | ||
<ActionWithModal | ||
action={ actionWithModal } | ||
selectedItems={ selectedItems } | ||
setActionWithModal={ setActionWithModal } | ||
onMenuOpenChange={ onMenuOpenChange } | ||
/> | ||
) } | ||
</> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.