@wordpress/dataviews 0.3.0 → 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/CHANGELOG.md +2 -0
  2. package/build/bulk-actions.js +143 -0
  3. package/build/bulk-actions.js.map +1 -0
  4. package/build/dataviews.js +15 -1
  5. package/build/dataviews.js.map +1 -1
  6. package/build/item-actions.js +4 -4
  7. package/build/item-actions.js.map +1 -1
  8. package/build/pagination.js +2 -2
  9. package/build/pagination.js.map +1 -1
  10. package/build/view-actions.js +3 -2
  11. package/build/view-actions.js.map +1 -1
  12. package/build/view-grid.js +11 -8
  13. package/build/view-grid.js.map +1 -1
  14. package/build/view-list.js +13 -1
  15. package/build/view-list.js.map +1 -1
  16. package/build/view-table.js +116 -9
  17. package/build/view-table.js.map +1 -1
  18. package/build-module/bulk-actions.js +136 -0
  19. package/build-module/bulk-actions.js.map +1 -0
  20. package/build-module/dataviews.js +17 -3
  21. package/build-module/dataviews.js.map +1 -1
  22. package/build-module/item-actions.js +4 -4
  23. package/build-module/item-actions.js.map +1 -1
  24. package/build-module/pagination.js +2 -2
  25. package/build-module/pagination.js.map +1 -1
  26. package/build-module/view-actions.js +4 -3
  27. package/build-module/view-actions.js.map +1 -1
  28. package/build-module/view-grid.js +12 -9
  29. package/build-module/view-grid.js.map +1 -1
  30. package/build-module/view-list.js +13 -1
  31. package/build-module/view-list.js.map +1 -1
  32. package/build-module/view-table.js +120 -13
  33. package/build-module/view-table.js.map +1 -1
  34. package/build-style/style-rtl.css +147 -26
  35. package/build-style/style.css +147 -26
  36. package/package.json +11 -11
  37. package/src/bulk-actions.js +187 -0
  38. package/src/dataviews.js +29 -2
  39. package/src/item-actions.js +4 -7
  40. package/src/pagination.js +2 -6
  41. package/src/style.scss +156 -28
  42. package/src/view-actions.js +8 -14
  43. package/src/view-grid.js +14 -12
  44. package/src/view-list.js +20 -1
  45. package/src/view-table.js +161 -15
@@ -97,7 +97,7 @@
97
97
  --wp-block-synced-color: #7a00df;
98
98
  --wp-block-synced-color--rgb: 122, 0, 223;
99
99
  }
100
- @media (-webkit-min-device-pixel-ratio: 2), (min-resolution: 192dpi) {
100
+ @media (min-resolution: 192dpi) {
101
101
  :root {
102
102
  --wp-admin-border-width-focus: 1.5px;
103
103
  }
@@ -122,6 +122,10 @@
122
122
  max-width: 240px;
123
123
  }
124
124
 
125
+ .dataviews-filters__view-actions.components-h-stack {
126
+ align-items: center;
127
+ }
128
+
125
129
  .dataviews-filters-button {
126
130
  position: relative;
127
131
  }
@@ -160,6 +164,10 @@
160
164
  margin: 32px 0 16px;
161
165
  }
162
166
 
167
+ .dataviews-view-table-wrapper {
168
+ overflow-x: auto;
169
+ }
170
+
163
171
  .dataviews-view-table {
164
172
  width: 100%;
165
173
  text-indent: 0;
@@ -182,12 +190,26 @@
182
190
  .dataviews-view-table td,
183
191
  .dataviews-view-table th {
184
192
  padding: 12px;
185
- min-width: 160px;
193
+ white-space: nowrap;
194
+ }
195
+ @media (min-width: 1440px) {
196
+ .dataviews-view-table td,
197
+ .dataviews-view-table th {
198
+ min-width: 200px;
199
+ }
186
200
  }
187
201
  .dataviews-view-table td[data-field-id=actions],
188
202
  .dataviews-view-table th[data-field-id=actions] {
189
203
  text-align: right;
190
204
  }
205
+ .dataviews-view-table td.dataviews-view-table__checkbox-column,
206
+ .dataviews-view-table th.dataviews-view-table__checkbox-column {
207
+ padding-right: 0;
208
+ }
209
+ .dataviews-view-table td .components-checkbox-control__input-container,
210
+ .dataviews-view-table th .components-checkbox-control__input-container {
211
+ margin: 4px;
212
+ }
191
213
  .dataviews-view-table tr {
192
214
  border-bottom: 1px solid #f0f0f0;
193
215
  }
@@ -211,9 +233,25 @@
211
233
  .dataviews-view-table tr:last-child {
212
234
  border-bottom: 0;
213
235
  }
214
- .dataviews-view-table tr:hover td {
236
+ .dataviews-view-table tr:hover {
215
237
  background-color: #f8f8f8;
216
238
  }
239
+ .dataviews-view-table tr .components-checkbox-control__input {
240
+ opacity: 0;
241
+ }
242
+ .dataviews-view-table tr .components-checkbox-control__input:checked, .dataviews-view-table tr .components-checkbox-control__input:indeterminate, .dataviews-view-table tr .components-checkbox-control__input:focus {
243
+ opacity: 1;
244
+ }
245
+ .dataviews-view-table tr:focus-within .components-checkbox-control__input, .dataviews-view-table tr:hover .components-checkbox-control__input {
246
+ opacity: 1;
247
+ }
248
+ .dataviews-view-table tr.is-selected {
249
+ background-color: rgba(var(--wp-admin-theme-color--rgb), 0.04);
250
+ color: #757575;
251
+ }
252
+ .dataviews-view-table tr.is-selected:hover {
253
+ background-color: rgba(var(--wp-admin-theme-color--rgb), 0.08);
254
+ }
217
255
  .dataviews-view-table thead tr {
218
256
  border: 0;
219
257
  }
@@ -230,6 +268,17 @@
230
268
  font-weight: 500;
231
269
  padding-left: 4px;
232
270
  }
271
+ .dataviews-view-table tbody td {
272
+ vertical-align: top;
273
+ }
274
+ .dataviews-view-table tbody .dataviews-view-table__cell-content-wrapper {
275
+ min-height: 32px;
276
+ display: flex;
277
+ align-items: center;
278
+ }
279
+ .dataviews-view-table tbody .dataviews-view-table__cell-content-wrapper > * {
280
+ flex-grow: 1;
281
+ }
233
282
  .dataviews-view-table .dataviews-view-table-header-button {
234
283
  padding: 4px 8px;
235
284
  font-size: 11px;
@@ -248,10 +297,55 @@
248
297
  .dataviews-view-table .dataviews-view-table-header {
249
298
  padding-left: 4px;
250
299
  }
300
+ .dataviews-view-table .dataviews-view-table__actions-column {
301
+ width: 1%;
302
+ }
303
+
304
+ .dataviews-view-list__primary-field,
305
+ .dataviews-view-grid__primary-field,
306
+ .dataviews-view-table__primary-field {
307
+ font-size: 13px;
308
+ font-weight: 500;
309
+ color: #1e1e1e;
310
+ text-overflow: ellipsis;
311
+ white-space: nowrap;
312
+ overflow: hidden;
313
+ display: block;
314
+ width: 100%;
315
+ }
316
+ .dataviews-view-list__primary-field a,
317
+ .dataviews-view-grid__primary-field a,
318
+ .dataviews-view-table__primary-field a {
319
+ text-decoration: none;
320
+ color: inherit;
321
+ text-overflow: ellipsis;
322
+ white-space: nowrap;
323
+ overflow: hidden;
324
+ display: block;
325
+ width: 100%;
326
+ }
327
+ .dataviews-view-list__primary-field a:hover,
328
+ .dataviews-view-grid__primary-field a:hover,
329
+ .dataviews-view-table__primary-field a:hover {
330
+ color: #1e1e1e;
331
+ }
332
+ .dataviews-view-list__primary-field button.components-button.is-link,
333
+ .dataviews-view-grid__primary-field button.components-button.is-link,
334
+ .dataviews-view-table__primary-field button.components-button.is-link {
335
+ text-decoration: none;
336
+ color: inherit;
337
+ font-weight: inherit;
338
+ text-overflow: ellipsis;
339
+ white-space: nowrap;
340
+ overflow: hidden;
341
+ display: block;
342
+ width: 100%;
343
+ }
251
344
 
252
345
  .dataviews-view-grid {
253
346
  margin-bottom: 24px;
254
347
  grid-template-columns: repeat(2, minmax(0, 1fr)) !important;
348
+ grid-template-rows: max-content;
255
349
  padding: 0 32px;
256
350
  }
257
351
  @media (min-width: 1080px) {
@@ -264,47 +358,45 @@
264
358
  grid-template-columns: repeat(4, minmax(0, 1fr)) !important;
265
359
  }
266
360
  }
267
- .dataviews-view-grid .dataviews-view-grid__card .dataviews-view-grid__primary-field .dataviews-view-grid__title-field {
268
- white-space: nowrap;
269
- overflow: hidden;
270
- text-overflow: ellipsis;
271
- display: block;
272
- font-size: 13px;
273
- width: 100%;
361
+ .dataviews-view-grid .dataviews-view-grid__card {
362
+ border-radius: 4px;
363
+ border: 1px solid #e0e0e0;
364
+ height: 100%;
365
+ justify-content: flex-start;
274
366
  }
275
- .dataviews-view-grid .dataviews-view-grid__card .dataviews-view-grid__primary-field .dataviews-view-grid__title-field a,
276
- .dataviews-view-grid .dataviews-view-grid__card .dataviews-view-grid__primary-field button.dataviews-view-grid__title-field {
277
- font-weight: 500;
278
- color: #1e1e1e;
279
- text-decoration: none;
367
+ .dataviews-view-grid .dataviews-view-grid__card .dataviews-view-grid__title-actions {
368
+ padding: 0 4px;
369
+ }
370
+ .dataviews-view-grid .dataviews-view-grid__card .dataviews-view-grid__primary-field {
371
+ min-height: 40px;
280
372
  }
281
373
  .dataviews-view-grid .dataviews-view-grid__media {
282
374
  width: 100%;
283
375
  min-height: 200px;
284
376
  aspect-ratio: 1/1;
285
- box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.1);
286
- border-radius: 4px;
287
- overflow: hidden;
377
+ border-bottom: 1px solid #e0e0e0;
378
+ background-color: #f0f0f0;
379
+ border-radius: 3px 3px 0 0;
288
380
  }
289
- .dataviews-view-grid .dataviews-view-grid__media > * {
290
- -o-object-fit: cover;
291
- object-fit: cover;
381
+ .dataviews-view-grid .dataviews-view-grid__media img {
382
+ object-fit: cover;
292
383
  width: 100%;
293
384
  height: 100%;
294
385
  }
295
386
  .dataviews-view-grid .dataviews-view-grid__primary-field {
296
- min-height: 24px;
387
+ padding: 8px;
297
388
  }
298
389
  .dataviews-view-grid .dataviews-view-grid__fields {
299
390
  position: relative;
300
391
  font-size: 12px;
301
392
  line-height: 16px;
302
393
  }
303
- .dataviews-view-grid .dataviews-view-grid__fields .dataviews-view-grid__field .dataviews-view-grid__field-header {
304
- color: #757575;
394
+ .dataviews-view-grid .dataviews-view-grid__fields:not(:empty) {
395
+ padding: 12px;
396
+ padding-top: 0;
305
397
  }
306
398
  .dataviews-view-grid .dataviews-view-grid__fields .dataviews-view-grid__field .dataviews-view-grid__field-value {
307
- color: #1e1e1e;
399
+ color: #757575;
308
400
  }
309
401
 
310
402
  .dataviews-view-list {
@@ -331,6 +423,7 @@
331
423
  .dataviews-view-list li:not(.is-selected):hover {
332
424
  color: var(--wp-admin-theme-color);
333
425
  }
426
+ .dataviews-view-list li:not(.is-selected):hover .dataviews-view-list__primary-field,
334
427
  .dataviews-view-list li:not(.is-selected):hover .dataviews-view-list__fields {
335
428
  color: var(--wp-admin-theme-color);
336
429
  }
@@ -339,8 +432,10 @@
339
432
  background-color: var(--wp-admin-theme-color);
340
433
  color: #fff;
341
434
  }
435
+ .dataviews-view-list li.is-selected .dataviews-view-list__item-wrapper .dataviews-view-list__primary-field,
342
436
  .dataviews-view-list li.is-selected .dataviews-view-list__item-wrapper .dataviews-view-list__fields,
343
437
  .dataviews-view-list li.is-selected .dataviews-view-list__item-wrapper .components-button,
438
+ .dataviews-view-list li.is-selected:focus-within .dataviews-view-list__item-wrapper .dataviews-view-list__primary-field,
344
439
  .dataviews-view-list li.is-selected:focus-within .dataviews-view-list__item-wrapper .dataviews-view-list__fields,
345
440
  .dataviews-view-list li.is-selected:focus-within .dataviews-view-list__item-wrapper .components-button {
346
441
  color: #fff;
@@ -371,11 +466,18 @@
371
466
  white-space: nowrap;
372
467
  }
373
468
  .dataviews-view-list .dataviews-view-list__media-wrapper {
374
- min-width: 32px;
469
+ width: 32px;
375
470
  height: 32px;
376
471
  border-radius: 4px;
377
472
  overflow: hidden;
378
473
  position: relative;
474
+ flex-shrink: 0;
475
+ background-color: #f0f0f0;
476
+ }
477
+ .dataviews-view-list .dataviews-view-list__media-wrapper img {
478
+ width: 100%;
479
+ height: 100%;
480
+ object-fit: cover;
379
481
  }
380
482
  .dataviews-view-list .dataviews-view-list__media-wrapper::after {
381
483
  content: "";
@@ -404,6 +506,9 @@
404
506
  .dataviews-view-list .dataviews-view-list__fields .dataviews-view-list__field:last-child {
405
507
  margin-right: 0;
406
508
  }
509
+ .dataviews-view-list .dataviews-view-list__fields .dataviews-view-list__field:empty {
510
+ display: none;
511
+ }
407
512
  .dataviews-view-list + .dataviews-pagination {
408
513
  justify-content: space-between;
409
514
  }
@@ -429,7 +534,23 @@
429
534
  padding: 0 32px;
430
535
  }
431
536
 
537
+ .dataviews-view-table-selection-checkbox label {
538
+ position: absolute;
539
+ width: 1px;
540
+ height: 1px;
541
+ padding: 0;
542
+ margin: -1px;
543
+ overflow: hidden;
544
+ clip: rect(0, 0, 0, 0);
545
+ white-space: nowrap;
546
+ border: 0;
547
+ }
548
+
432
549
  .dataviews-filters__custom-menu-radio-item-prefix {
433
550
  display: block;
434
551
  width: 24px;
552
+ }
553
+
554
+ .dataviews-bulk-edit-button.components-button {
555
+ flex-shrink: 0;
435
556
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wordpress/dataviews",
3
- "version": "0.3.0",
3
+ "version": "0.4.1",
4
4
  "description": "DataViews is a component that provides an API to render datasets using different types of layouts (table, grid, list, etc.).",
5
5
  "author": "The WordPress Contributors",
6
6
  "license": "GPL-2.0-or-later",
@@ -28,15 +28,15 @@
28
28
  "sideEffects": false,
29
29
  "dependencies": {
30
30
  "@babel/runtime": "^7.16.0",
31
- "@wordpress/a11y": "^3.49.0",
32
- "@wordpress/components": "^25.15.0",
33
- "@wordpress/compose": "^6.26.0",
34
- "@wordpress/element": "^5.26.0",
35
- "@wordpress/i18n": "^4.49.0",
36
- "@wordpress/icons": "^9.40.0",
37
- "@wordpress/keycodes": "^3.49.0",
38
- "@wordpress/primitives": "^3.47.0",
39
- "@wordpress/private-apis": "^0.31.0",
31
+ "@wordpress/a11y": "^3.50.0",
32
+ "@wordpress/components": "^25.16.0",
33
+ "@wordpress/compose": "^6.27.0",
34
+ "@wordpress/element": "^5.27.0",
35
+ "@wordpress/i18n": "^4.50.0",
36
+ "@wordpress/icons": "^9.41.0",
37
+ "@wordpress/keycodes": "^3.50.0",
38
+ "@wordpress/primitives": "^3.48.0",
39
+ "@wordpress/private-apis": "^0.32.0",
40
40
  "classnames": "^2.3.1",
41
41
  "remove-accents": "^0.5.0"
42
42
  },
@@ -46,5 +46,5 @@
46
46
  "publishConfig": {
47
47
  "access": "public"
48
48
  },
49
- "gitHead": "5e6f9caa205d3bfdbac131952b7bf9c6ec60569b"
49
+ "gitHead": "fefb6f718fbfd5df9390f366d5733369f613084a"
50
50
  }
@@ -0,0 +1,187 @@
1
+ /**
2
+ * WordPress dependencies
3
+ */
4
+ import {
5
+ privateApis as componentsPrivateApis,
6
+ Button,
7
+ Modal,
8
+ } from '@wordpress/components';
9
+ import { __, sprintf, _n } from '@wordpress/i18n';
10
+ import { useMemo, useState, useCallback } from '@wordpress/element';
11
+
12
+ /**
13
+ * Internal dependencies
14
+ */
15
+ import { unlock } from './lock-unlock';
16
+
17
+ const {
18
+ DropdownMenuV2: DropdownMenu,
19
+ DropdownMenuGroupV2: DropdownMenuGroup,
20
+ DropdownMenuItemV2: DropdownMenuItem,
21
+ DropdownMenuSeparatorV2: DropdownMenuSeparator,
22
+ } = unlock( componentsPrivateApis );
23
+
24
+ function ActionWithModal( {
25
+ action,
26
+ selectedItems,
27
+ setActionWithModal,
28
+ onMenuOpenChange,
29
+ } ) {
30
+ const eligibleItems = useMemo( () => {
31
+ return selectedItems.filter( ( item ) => action.isEligible( item ) );
32
+ }, [ action, selectedItems ] );
33
+ const { RenderModal, hideModalHeader } = action;
34
+ const onCloseModal = useCallback( () => {
35
+ setActionWithModal( undefined );
36
+ }, [ setActionWithModal ] );
37
+ return (
38
+ <Modal
39
+ title={ ! hideModalHeader && action.label }
40
+ __experimentalHideHeader={ !! hideModalHeader }
41
+ onRequestClose={ onCloseModal }
42
+ overlayClassName="dataviews-action-modal"
43
+ >
44
+ <RenderModal
45
+ items={ eligibleItems }
46
+ closeModal={ onCloseModal }
47
+ onPerform={ () => onMenuOpenChange( false ) }
48
+ />
49
+ </Modal>
50
+ );
51
+ }
52
+
53
+ function BulkActionItem( { action, selectedItems, setActionWithModal } ) {
54
+ const eligibleItems = useMemo( () => {
55
+ return selectedItems.filter( ( item ) => action.isEligible( item ) );
56
+ }, [ action, selectedItems ] );
57
+
58
+ const shouldShowModal = !! action.RenderModal;
59
+
60
+ return (
61
+ <DropdownMenuItem
62
+ key={ action.id }
63
+ disabled={ eligibleItems.length === 0 }
64
+ hideOnClick={ ! shouldShowModal }
65
+ onClick={ async () => {
66
+ if ( shouldShowModal ) {
67
+ setActionWithModal( action );
68
+ } else {
69
+ await action.callback( eligibleItems );
70
+ }
71
+ } }
72
+ suffix={
73
+ eligibleItems.length > 0 ? eligibleItems.length : undefined
74
+ }
75
+ >
76
+ { action.label }
77
+ </DropdownMenuItem>
78
+ );
79
+ }
80
+
81
+ function ActionsMenuGroup( { actions, selectedItems, setActionWithModal } ) {
82
+ return (
83
+ <>
84
+ <DropdownMenuGroup>
85
+ { actions.map( ( action ) => (
86
+ <BulkActionItem
87
+ key={ action.id }
88
+ action={ action }
89
+ selectedItems={ selectedItems }
90
+ setActionWithModal={ setActionWithModal }
91
+ />
92
+ ) ) }
93
+ </DropdownMenuGroup>
94
+ <DropdownMenuSeparator />
95
+ </>
96
+ );
97
+ }
98
+
99
+ export default function BulkActions( {
100
+ data,
101
+ actions,
102
+ selection,
103
+ onSelectionChange,
104
+ getItemId,
105
+ } ) {
106
+ const bulkActions = useMemo(
107
+ () => actions.filter( ( action ) => action.supportsBulk ),
108
+ [ actions ]
109
+ );
110
+ const areAllSelected = selection && selection.length === data.length;
111
+ const [ isMenuOpen, onMenuOpenChange ] = useState( false );
112
+ const [ actionWithModal, setActionWithModal ] = useState();
113
+ const selectedItems = useMemo( () => {
114
+ return data.filter( ( item ) =>
115
+ selection.includes( getItemId( item ) )
116
+ );
117
+ }, [ selection, data, getItemId ] );
118
+
119
+ if ( bulkActions.length === 0 ) {
120
+ return null;
121
+ }
122
+ return (
123
+ <>
124
+ <DropdownMenu
125
+ open={ isMenuOpen }
126
+ onOpenChange={ onMenuOpenChange }
127
+ label={ __( 'Bulk actions' ) }
128
+ style={ { minWidth: '240px' } }
129
+ trigger={
130
+ <Button
131
+ className="dataviews-bulk-edit-button"
132
+ __next40pxDefaultSize
133
+ variant="tertiary"
134
+ size="compact"
135
+ >
136
+ { selection.length
137
+ ? sprintf(
138
+ /* translators: %d: Number of items. */
139
+ _n(
140
+ 'Edit %d item',
141
+ 'Edit %d items',
142
+ selection.length
143
+ ),
144
+ selection.length
145
+ )
146
+ : __( 'Bulk edit' ) }
147
+ </Button>
148
+ }
149
+ >
150
+ <ActionsMenuGroup
151
+ actions={ bulkActions }
152
+ setActionWithModal={ setActionWithModal }
153
+ selectedItems={ selectedItems }
154
+ />
155
+ <DropdownMenuGroup>
156
+ <DropdownMenuItem
157
+ disabled={ areAllSelected }
158
+ hideOnClick={ false }
159
+ onClick={ () => {
160
+ onSelectionChange( data );
161
+ } }
162
+ suffix={ data.length }
163
+ >
164
+ { __( 'Select all' ) }
165
+ </DropdownMenuItem>
166
+ <DropdownMenuItem
167
+ disabled={ selection.length === 0 }
168
+ hideOnClick={ false }
169
+ onClick={ () => {
170
+ onSelectionChange( [] );
171
+ } }
172
+ >
173
+ { __( 'Deselect' ) }
174
+ </DropdownMenuItem>
175
+ </DropdownMenuGroup>
176
+ </DropdownMenu>
177
+ { actionWithModal && (
178
+ <ActionWithModal
179
+ action={ actionWithModal }
180
+ selectedItems={ selectedItems }
181
+ setActionWithModal={ setActionWithModal }
182
+ onMenuOpenChange={ onMenuOpenChange }
183
+ />
184
+ ) }
185
+ </>
186
+ );
187
+ }
package/src/dataviews.js CHANGED
@@ -5,7 +5,7 @@ import {
5
5
  __experimentalVStack as VStack,
6
6
  __experimentalHStack as HStack,
7
7
  } from '@wordpress/components';
8
- import { useMemo, useState, useCallback } from '@wordpress/element';
8
+ import { useMemo, useState, useCallback, useEffect } from '@wordpress/element';
9
9
 
10
10
  /**
11
11
  * Internal dependencies
@@ -14,7 +14,8 @@ import Pagination from './pagination';
14
14
  import ViewActions from './view-actions';
15
15
  import Filters from './filters';
16
16
  import Search from './search';
17
- import { VIEW_LAYOUTS } from './constants';
17
+ import { VIEW_LAYOUTS, LAYOUT_TABLE } from './constants';
18
+ import BulkActions from './bulk-actions';
18
19
 
19
20
  const defaultGetItemId = ( item ) => item.id;
20
21
  const defaultOnSelectionChange = () => {};
@@ -37,6 +38,23 @@ export default function DataViews( {
37
38
  } ) {
38
39
  const [ selection, setSelection ] = useState( [] );
39
40
 
41
+ useEffect( () => {
42
+ if (
43
+ selection.length > 0 &&
44
+ selection.some(
45
+ ( id ) => ! data.some( ( item ) => item.id === id )
46
+ )
47
+ ) {
48
+ const newSelection = selection.filter( ( id ) =>
49
+ data.some( ( item ) => item.id === id )
50
+ );
51
+ setSelection( newSelection );
52
+ onSelectionChange(
53
+ data.filter( ( item ) => newSelection.includes( item.id ) )
54
+ );
55
+ }
56
+ }, [ selection, data, onSelectionChange ] );
57
+
40
58
  const onSetSelection = useCallback(
41
59
  ( items ) => {
42
60
  setSelection( items.map( ( item ) => item.id ) );
@@ -75,6 +93,15 @@ export default function DataViews( {
75
93
  onChangeView={ onChangeView }
76
94
  />
77
95
  </HStack>
96
+ { view.type === LAYOUT_TABLE && (
97
+ <BulkActions
98
+ actions={ actions }
99
+ data={ data }
100
+ onSelectionChange={ onSetSelection }
101
+ selection={ selection }
102
+ getItemId={ getItemId }
103
+ />
104
+ ) }
78
105
  <ViewActions
79
106
  fields={ _fields }
80
107
  view={ view }
@@ -59,10 +59,7 @@ function ActionWithModal( { action, item, ActionTrigger } ) {
59
59
  <ActionTrigger { ...actionTriggerProps } />
60
60
  { isModalOpen && (
61
61
  <Modal
62
- title={
63
- ( ! hideModalHeader && action.modalHeader ) ||
64
- action.label
65
- }
62
+ title={ action.modalHeader || action.label }
66
63
  __experimentalHideHeader={ !! hideModalHeader }
67
64
  onRequestClose={ () => {
68
65
  setIsModalOpen( false );
@@ -72,7 +69,7 @@ function ActionWithModal( { action, item, ActionTrigger } ) {
72
69
  ) }` }
73
70
  >
74
71
  <RenderModal
75
- item={ item }
72
+ items={ [ item ] }
76
73
  closeModal={ () => setIsModalOpen( false ) }
77
74
  />
78
75
  </Modal>
@@ -99,7 +96,7 @@ function ActionsDropdownMenuGroup( { actions, item } ) {
99
96
  <DropdownMenuItemTrigger
100
97
  key={ action.id }
101
98
  action={ action }
102
- onClick={ () => action.callback( item ) }
99
+ onClick={ () => action.callback( [ item ] ) }
103
100
  />
104
101
  );
105
102
  } ) }
@@ -160,7 +157,7 @@ export default function ItemActions( { item, actions, isCompact } ) {
160
157
  <ButtonTrigger
161
158
  key={ action.id }
162
159
  action={ action }
163
- onClick={ () => action.callback( item ) }
160
+ onClick={ () => action.callback( [ item ] ) }
164
161
  />
165
162
  );
166
163
  } ) }
package/src/pagination.js CHANGED
@@ -30,12 +30,8 @@ const Pagination = memo( function Pagination( {
30
30
  <HStack justify="flex-start" expanded={ false } spacing={ 2 }>
31
31
  { createInterpolateElement(
32
32
  sprintf(
33
- // translators: %1$s: Current page number, %2$s: Total number of pages.
34
- _x(
35
- 'Page <CurrenPageControl /> of %2$s',
36
- 'paging'
37
- ),
38
- view.page,
33
+ // translators: %s: Total number of pages.
34
+ _x( 'Page <CurrenPageControl /> of %s', 'paging' ),
39
35
  totalPages
40
36
  ),
41
37
  {