@wordpress/dataviews 0.6.0 → 0.8.0

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 (86) hide show
  1. package/CHANGELOG.md +9 -0
  2. package/README.md +48 -15
  3. package/build/add-filter.js +0 -2
  4. package/build/add-filter.js.map +1 -1
  5. package/build/bulk-actions.js +41 -3
  6. package/build/bulk-actions.js.map +1 -1
  7. package/build/constants.js +28 -7
  8. package/build/constants.js.map +1 -1
  9. package/build/dataviews.js +34 -21
  10. package/build/dataviews.js.map +1 -1
  11. package/build/filter-summary.js +33 -12
  12. package/build/filter-summary.js.map +1 -1
  13. package/build/filters.js +10 -2
  14. package/build/filters.js.map +1 -1
  15. package/build/item-actions.js +20 -39
  16. package/build/item-actions.js.map +1 -1
  17. package/build/pagination.js +4 -3
  18. package/build/pagination.js.map +1 -1
  19. package/build/reset-filters.js +2 -1
  20. package/build/reset-filters.js.map +1 -1
  21. package/build/search-widget.js +117 -8
  22. package/build/search-widget.js.map +1 -1
  23. package/build/single-selection-checkbox.js +7 -2
  24. package/build/single-selection-checkbox.js.map +1 -1
  25. package/build/utils.js +24 -2
  26. package/build/utils.js.map +1 -1
  27. package/build/view-actions.js.map +1 -1
  28. package/build/view-grid.js +12 -13
  29. package/build/view-grid.js.map +1 -1
  30. package/build/view-list.js +1 -1
  31. package/build/view-list.js.map +1 -1
  32. package/build/view-table.js +111 -47
  33. package/build/view-table.js.map +1 -1
  34. package/build-module/add-filter.js +0 -2
  35. package/build-module/add-filter.js.map +1 -1
  36. package/build-module/bulk-actions.js +40 -4
  37. package/build-module/bulk-actions.js.map +1 -1
  38. package/build-module/constants.js +27 -6
  39. package/build-module/constants.js.map +1 -1
  40. package/build-module/dataviews.js +35 -22
  41. package/build-module/dataviews.js.map +1 -1
  42. package/build-module/filter-summary.js +34 -13
  43. package/build-module/filter-summary.js.map +1 -1
  44. package/build-module/filters.js +11 -3
  45. package/build-module/filters.js.map +1 -1
  46. package/build-module/item-actions.js +20 -39
  47. package/build-module/item-actions.js.map +1 -1
  48. package/build-module/pagination.js +4 -3
  49. package/build-module/pagination.js.map +1 -1
  50. package/build-module/reset-filters.js +2 -1
  51. package/build-module/reset-filters.js.map +1 -1
  52. package/build-module/search-widget.js +120 -11
  53. package/build-module/search-widget.js.map +1 -1
  54. package/build-module/single-selection-checkbox.js +7 -2
  55. package/build-module/single-selection-checkbox.js.map +1 -1
  56. package/build-module/utils.js +25 -3
  57. package/build-module/utils.js.map +1 -1
  58. package/build-module/view-actions.js.map +1 -1
  59. package/build-module/view-grid.js +13 -14
  60. package/build-module/view-grid.js.map +1 -1
  61. package/build-module/view-list.js +2 -2
  62. package/build-module/view-list.js.map +1 -1
  63. package/build-module/view-table.js +113 -49
  64. package/build-module/view-table.js.map +1 -1
  65. package/build-style/style-rtl.css +76 -46
  66. package/build-style/style.css +76 -46
  67. package/package.json +11 -11
  68. package/src/add-filter.js +0 -2
  69. package/src/bulk-actions.js +54 -4
  70. package/src/constants.js +35 -6
  71. package/src/dataviews.js +66 -49
  72. package/src/filter-summary.js +76 -23
  73. package/src/filters.js +16 -5
  74. package/src/item-actions.js +19 -55
  75. package/src/pagination.js +8 -3
  76. package/src/reset-filters.js +2 -1
  77. package/src/search-widget.js +182 -15
  78. package/src/single-selection-checkbox.js +7 -1
  79. package/src/stories/fixtures.js +12 -1
  80. package/src/stories/index.story.js +43 -4
  81. package/src/style.scss +108 -73
  82. package/src/utils.js +38 -4
  83. package/src/view-actions.js +1 -1
  84. package/src/view-grid.js +13 -12
  85. package/src/view-list.js +2 -1
  86. package/src/view-table.js +162 -81
package/src/view-table.js CHANGED
@@ -14,6 +14,7 @@ import {
14
14
  Icon,
15
15
  privateApis as componentsPrivateApis,
16
16
  CheckboxControl,
17
+ Spinner,
17
18
  } from '@wordpress/components';
18
19
  import {
19
20
  forwardRef,
@@ -21,6 +22,7 @@ import {
21
22
  useId,
22
23
  useRef,
23
24
  useState,
25
+ useMemo,
24
26
  Children,
25
27
  Fragment,
26
28
  } from '@wordpress/element';
@@ -33,6 +35,10 @@ import { unlock } from './lock-unlock';
33
35
  import ItemActions from './item-actions';
34
36
  import { sanitizeOperators } from './utils';
35
37
  import { ENUMERATION_TYPE, SORTING_DIRECTIONS } from './constants';
38
+ import {
39
+ useSomeItemHasAPossibleBulkAction,
40
+ useHasAPossibleBulkAction,
41
+ } from './bulk-actions';
36
42
 
37
43
  const {
38
44
  DropdownMenuV2: DropdownMenu,
@@ -43,7 +49,7 @@ const {
43
49
  DropdownMenuSeparatorV2: DropdownMenuSeparator,
44
50
  } = unlock( componentsPrivateApis );
45
51
 
46
- function WithSeparators( { children } ) {
52
+ function WithDropDownMenuSeparators( { children } ) {
47
53
  return Children.toArray( children )
48
54
  .filter( Boolean )
49
55
  .map( ( child, i ) => (
@@ -96,7 +102,7 @@ const HeaderMenu = forwardRef( function HeaderMenu(
96
102
  }
97
103
  style={ { minWidth: '240px' } }
98
104
  >
99
- <WithSeparators>
105
+ <WithDropDownMenuSeparators>
100
106
  { isSortable && (
101
107
  <DropdownMenuGroup>
102
108
  { Object.entries( SORTING_DIRECTIONS ).map(
@@ -181,13 +187,25 @@ const HeaderMenu = forwardRef( function HeaderMenu(
181
187
  </DropdownMenuItemLabel>
182
188
  </DropdownMenuItem>
183
189
  ) }
184
- </WithSeparators>
190
+ </WithDropDownMenuSeparators>
185
191
  </DropdownMenu>
186
192
  );
187
193
  } );
188
194
 
189
- function BulkSelectionCheckbox( { selection, onSelectionChange, data } ) {
190
- const areAllSelected = selection.length === data.length;
195
+ function BulkSelectionCheckbox( {
196
+ selection,
197
+ onSelectionChange,
198
+ data,
199
+ actions,
200
+ } ) {
201
+ const selectableItems = useMemo( () => {
202
+ return data.filter( ( item ) => {
203
+ return actions.some(
204
+ ( action ) => action.supportsBulk && action.isEligible( item )
205
+ );
206
+ } );
207
+ }, [ data, actions ] );
208
+ const areAllSelected = selection.length === selectableItems.length;
191
209
  return (
192
210
  <CheckboxControl
193
211
  className="dataviews-view-table-selection-checkbox"
@@ -198,7 +216,7 @@ function BulkSelectionCheckbox( { selection, onSelectionChange, data } ) {
198
216
  if ( areAllSelected ) {
199
217
  onSelectionChange( [] );
200
218
  } else {
201
- onSelectionChange( data );
219
+ onSelectionChange( selectableItems );
202
220
  }
203
221
  } }
204
222
  label={ areAllSelected ? __( 'Deselect all' ) : __( 'Select all' ) }
@@ -206,6 +224,127 @@ function BulkSelectionCheckbox( { selection, onSelectionChange, data } ) {
206
224
  );
207
225
  }
208
226
 
227
+ function TableRow( {
228
+ hasBulkActions,
229
+ item,
230
+ actions,
231
+ id,
232
+ visibleFields,
233
+ primaryField,
234
+ selection,
235
+ getItemId,
236
+ onSelectionChange,
237
+ data,
238
+ } ) {
239
+ const hasPossibleBulkAction = useHasAPossibleBulkAction( actions, item );
240
+
241
+ const isSelected = selection.includes( id );
242
+
243
+ const [ isHovered, setIsHovered ] = useState( false );
244
+
245
+ const handleMouseEnter = () => {
246
+ setIsHovered( true );
247
+ };
248
+
249
+ const handleMouseLeave = () => {
250
+ setIsHovered( false );
251
+ };
252
+
253
+ return (
254
+ <tr
255
+ className={ classnames( 'dataviews-view-table__row', {
256
+ 'is-selected':
257
+ hasPossibleBulkAction && selection.includes( id ),
258
+ 'is-hovered': isHovered,
259
+ } ) }
260
+ onMouseEnter={ handleMouseEnter }
261
+ onMouseLeave={ handleMouseLeave }
262
+ onClickCapture={ ( event ) => {
263
+ if ( event.ctrlKey || event.metaKey ) {
264
+ event.stopPropagation();
265
+ event.preventDefault();
266
+ if ( ! hasPossibleBulkAction ) {
267
+ return;
268
+ }
269
+ if ( ! isSelected ) {
270
+ onSelectionChange(
271
+ data.filter( ( _item ) => {
272
+ const itemId = getItemId?.( _item );
273
+ return (
274
+ itemId === id ||
275
+ selection.includes( itemId )
276
+ );
277
+ } )
278
+ );
279
+ } else {
280
+ onSelectionChange(
281
+ data.filter( ( _item ) => {
282
+ const itemId = getItemId?.( _item );
283
+ return (
284
+ itemId !== id &&
285
+ selection.includes( itemId )
286
+ );
287
+ } )
288
+ );
289
+ }
290
+ }
291
+ } }
292
+ >
293
+ { hasBulkActions && (
294
+ <td
295
+ className="dataviews-view-table__checkbox-column"
296
+ style={ {
297
+ width: 20,
298
+ minWidth: 20,
299
+ } }
300
+ >
301
+ <div className="dataviews-view-table__cell-content-wrapper">
302
+ <SingleSelectionCheckbox
303
+ id={ id }
304
+ item={ item }
305
+ selection={ selection }
306
+ onSelectionChange={ onSelectionChange }
307
+ getItemId={ getItemId }
308
+ data={ data }
309
+ primaryField={ primaryField }
310
+ disabled={ ! hasPossibleBulkAction }
311
+ />
312
+ </div>
313
+ </td>
314
+ ) }
315
+ { visibleFields.map( ( field ) => (
316
+ <td
317
+ key={ field.id }
318
+ style={ {
319
+ width: field.width || undefined,
320
+ minWidth: field.minWidth || undefined,
321
+ maxWidth: field.maxWidth || undefined,
322
+ } }
323
+ >
324
+ <div
325
+ className={ classnames(
326
+ 'dataviews-view-table__cell-content-wrapper',
327
+ {
328
+ 'dataviews-view-table__primary-field':
329
+ primaryField?.id === field.id,
330
+ }
331
+ ) }
332
+ >
333
+ { field.render( {
334
+ item,
335
+ } ) }
336
+ </div>
337
+ </td>
338
+ ) ) }
339
+ { !! actions?.length && (
340
+ <td className="dataviews-view-table__actions-column">
341
+ <ItemActions item={ item } actions={ actions } />
342
+ </td>
343
+ ) }
344
+ </tr>
345
+ );
346
+ }
347
+
209
348
  function ViewTable( {
210
349
  view,
211
350
  onChangeView,
@@ -219,10 +358,10 @@ function ViewTable( {
219
358
  onSelectionChange,
220
359
  setOpenedFilter,
221
360
  } ) {
222
- const hasBulkActions = actions?.some( ( action ) => action.supportsBulk );
223
361
  const headerMenuRefs = useRef( new Map() );
224
362
  const headerMenuToFocusRef = useRef();
225
363
  const [ nextHeaderMenuToFocus, setNextHeaderMenuToFocus ] = useState();
364
+ const hasBulkActions = useSomeItemHasAPossibleBulkAction( actions, data );
226
365
 
227
366
  useEffect( () => {
228
367
  if ( headerMenuToFocusRef.current ) {
@@ -263,7 +402,7 @@ function ViewTable( {
263
402
  );
264
403
 
265
404
  return (
266
- <div className="dataviews-view-table-wrapper">
405
+ <>
267
406
  <table
268
407
  className="dataviews-view-table"
269
408
  aria-busy={ isLoading }
@@ -285,6 +424,7 @@ function ViewTable( {
285
424
  selection={ selection }
286
425
  onSelectionChange={ onSelectionChange }
287
426
  data={ data }
427
+ actions={ actions }
288
428
  />
289
429
  </th>
290
430
  ) }
@@ -347,78 +487,19 @@ function ViewTable( {
347
487
  <tbody>
348
488
  { hasData &&
349
489
  usedData.map( ( item, index ) => (
350
- <tr
490
+ <TableRow
351
491
  key={ getItemId( item ) }
352
- className={ classnames(
353
- 'dataviews-view-table__row',
354
- {
355
- 'is-selected': selection.includes(
356
- getItemId( item ) || index
357
- ),
358
- }
359
- ) }
360
- >
361
- { hasBulkActions && (
362
- <td
363
- className="dataviews-view-table__checkbox-column"
364
- style={ {
365
- width: 20,
366
- minWidth: 20,
367
- } }
368
- >
369
- <div className="dataviews-view-table__cell-content-wrapper">
370
- <SingleSelectionCheckbox
371
- id={
372
- getItemId( item ) || index
373
- }
374
- item={ item }
375
- selection={ selection }
376
- onSelectionChange={
377
- onSelectionChange
378
- }
379
- getItemId={ getItemId }
380
- data={ data }
381
- primaryField={ primaryField }
382
- />
383
- </div>
384
- </td>
385
- ) }
386
- { visibleFields.map( ( field ) => (
387
- <td
388
- key={ field.id }
389
- style={ {
390
- width: field.width || undefined,
391
- minWidth:
392
- field.minWidth || undefined,
393
- maxWidth:
394
- field.maxWidth || undefined,
395
- } }
396
- >
397
- <div
398
- className={ classnames(
399
- 'dataviews-view-table__cell-content-wrapper',
400
- {
401
- 'dataviews-view-table__primary-field':
402
- primaryField?.id ===
403
- field.id,
404
- }
405
- ) }
406
- >
407
- { field.render( {
408
- item,
409
- } ) }
410
- </div>
411
- </td>
412
- ) ) }
413
- { !! actions?.length && (
414
- <td className="dataviews-view-table__actions-column">
415
- <ItemActions
416
- item={ item }
417
- actions={ actions }
418
- />
419
- </td>
420
- ) }
421
- </tr>
492
+ item={ item }
493
+ hasBulkActions={ hasBulkActions }
494
+ actions={ actions }
495
+ id={ getItemId( item ) || index }
496
+ visibleFields={ visibleFields }
497
+ primaryField={ primaryField }
498
+ selection={ selection }
499
+ getItemId={ getItemId }
500
+ onSelectionChange={ onSelectionChange }
501
+ data={ data }
502
+ />
422
503
  ) ) }
423
504
  </tbody>
424
505
  </table>
@@ -430,10 +511,10 @@ function ViewTable( {
430
511
  id={ tableNoticeId }
431
512
  >
432
513
  { ! hasData && (
433
- <p>{ isLoading ? __( 'Loading…' ) : __( 'No results' ) }</p>
514
+ <p>{ isLoading ? <Spinner /> : __( 'No results' ) }</p>
434
515
  ) }
435
516
  </div>
436
- </div>
517
+ </>
437
518
  );
438
519
  }
439
520