@wordpress/dataviews 0.5.3 → 0.5.5

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 (39) hide show
  1. package/build/bulk-actions.js +41 -3
  2. package/build/bulk-actions.js.map +1 -1
  3. package/build/dataviews.js +22 -12
  4. package/build/dataviews.js.map +1 -1
  5. package/build/filters.js +8 -1
  6. package/build/filters.js.map +1 -1
  7. package/build/pagination.js +2 -1
  8. package/build/pagination.js.map +1 -1
  9. package/build/single-selection-checkbox.js +7 -2
  10. package/build/single-selection-checkbox.js.map +1 -1
  11. package/build/view-grid.js +6 -3
  12. package/build/view-grid.js.map +1 -1
  13. package/build/view-table.js +76 -41
  14. package/build/view-table.js.map +1 -1
  15. package/build-module/bulk-actions.js +40 -4
  16. package/build-module/bulk-actions.js.map +1 -1
  17. package/build-module/dataviews.js +22 -12
  18. package/build-module/dataviews.js.map +1 -1
  19. package/build-module/filters.js +8 -1
  20. package/build-module/filters.js.map +1 -1
  21. package/build-module/pagination.js +2 -1
  22. package/build-module/pagination.js.map +1 -1
  23. package/build-module/single-selection-checkbox.js +7 -2
  24. package/build-module/single-selection-checkbox.js.map +1 -1
  25. package/build-module/view-grid.js +6 -3
  26. package/build-module/view-grid.js.map +1 -1
  27. package/build-module/view-table.js +77 -42
  28. package/build-module/view-table.js.map +1 -1
  29. package/build-style/style-rtl.css +24 -14
  30. package/build-style/style.css +24 -14
  31. package/package.json +4 -4
  32. package/src/bulk-actions.js +54 -4
  33. package/src/dataviews.js +43 -27
  34. package/src/filters.js +6 -1
  35. package/src/pagination.js +6 -1
  36. package/src/single-selection-checkbox.js +7 -1
  37. package/src/style.scss +21 -12
  38. package/src/view-grid.js +6 -2
  39. package/src/view-table.js +109 -75
@@ -96,6 +96,7 @@
96
96
  --wp-admin-border-width-focus: 2px;
97
97
  --wp-block-synced-color: #7a00df;
98
98
  --wp-block-synced-color--rgb: 122, 0, 223;
99
+ --wp-bound-block-color: #9747ff;
99
100
  }
100
101
  @media (min-resolution: 192dpi) {
101
102
  :root {
@@ -117,19 +118,12 @@
117
118
  .dataviews-filters__view-actions {
118
119
  padding: 12px 32px 0;
119
120
  }
120
- .dataviews-filters__view-actions .components-search-control {
121
- flex-grow: 1;
122
- }
123
121
  .dataviews-filters__view-actions .components-search-control .components-base-control__field {
124
122
  max-width: 240px;
125
123
  }
126
124
 
127
125
  .dataviews-filters__container {
128
- padding: 0 32px;
129
- }
130
-
131
- .dataviews-filters__view-actions.components-h-stack {
132
- align-items: center;
126
+ padding-right: 32px;
133
127
  }
134
128
 
135
129
  .dataviews-filters-button {
@@ -148,6 +142,13 @@
148
142
  color: #757575;
149
143
  }
150
144
 
145
+ .dataviews-pagination__page-selection {
146
+ font-size: 11px;
147
+ text-transform: uppercase;
148
+ font-weight: 500;
149
+ color: #1e1e1e;
150
+ }
151
+
151
152
  .dataviews-filters-options {
152
153
  margin: 32px 0 16px;
153
154
  }
@@ -218,10 +219,10 @@
218
219
  .dataviews-view-table tr:hover {
219
220
  background-color: #f8f8f8;
220
221
  }
221
- .dataviews-view-table tr .components-checkbox-control__input {
222
+ .dataviews-view-table tr .components-checkbox-control__input.components-checkbox-control__input {
222
223
  opacity: 0;
223
224
  }
224
- .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 {
225
+ .dataviews-view-table tr .components-checkbox-control__input.components-checkbox-control__input:checked, .dataviews-view-table tr .components-checkbox-control__input.components-checkbox-control__input:indeterminate, .dataviews-view-table tr .components-checkbox-control__input.components-checkbox-control__input:focus {
225
226
  opacity: 1;
226
227
  }
227
228
  .dataviews-view-table tr:focus-within .components-checkbox-control__input, .dataviews-view-table tr:hover .components-checkbox-control__input {
@@ -282,6 +283,9 @@
282
283
  .dataviews-view-table .dataviews-view-table__actions-column {
283
284
  width: 1%;
284
285
  }
286
+ .dataviews-view-table:has(tr.is-selected) .components-checkbox-control__input {
287
+ opacity: 1;
288
+ }
285
289
 
286
290
  .dataviews-view-list__primary-field,
287
291
  .dataviews-view-grid__primary-field,
@@ -291,7 +295,6 @@
291
295
  color: #1e1e1e;
292
296
  text-overflow: ellipsis;
293
297
  white-space: nowrap;
294
- overflow: hidden;
295
298
  display: block;
296
299
  width: 100%;
297
300
  }
@@ -311,6 +314,13 @@
311
314
  .dataviews-view-table__primary-field a:hover {
312
315
  color: #1e1e1e;
313
316
  }
317
+ .dataviews-view-list__primary-field a:focus,
318
+ .dataviews-view-grid__primary-field a:focus,
319
+ .dataviews-view-table__primary-field a:focus {
320
+ color: var(--wp-admin-theme-color--rgb);
321
+ box-shadow: 0 0 0 var(--wp-admin-border-width-focus) var(--wp-admin-theme-color, #007cba);
322
+ border-radius: 2px;
323
+ }
314
324
  .dataviews-view-list__primary-field button.components-button.is-link,
315
325
  .dataviews-view-grid__primary-field button.components-button.is-link,
316
326
  .dataviews-view-table__primary-field button.components-button.is-link {
@@ -705,7 +715,7 @@
705
715
  .dataviews-filter-summary__chip-container .dataviews-filter-summary__chip.has-reset {
706
716
  padding-inline-end: 28px;
707
717
  }
708
- .dataviews-filter-summary__chip-container .dataviews-filter-summary__chip:hover, .dataviews-filter-summary__chip-container .dataviews-filter-summary__chip:focus-visible {
718
+ .dataviews-filter-summary__chip-container .dataviews-filter-summary__chip:hover, .dataviews-filter-summary__chip-container .dataviews-filter-summary__chip:focus-visible, .dataviews-filter-summary__chip-container .dataviews-filter-summary__chip[aria-expanded=true] {
709
719
  background: #e0e0e0;
710
720
  color: #1e1e1e;
711
721
  }
@@ -713,8 +723,8 @@
713
723
  color: var(--wp-admin-theme-color);
714
724
  background: rgba(var(--wp-admin-theme-color--rgb), 0.04);
715
725
  }
716
- .dataviews-filter-summary__chip-container .dataviews-filter-summary__chip.has-values:hover {
717
- background: rgba(var(--wp-admin-theme-color--rgb), 0.08);
726
+ .dataviews-filter-summary__chip-container .dataviews-filter-summary__chip.has-values:hover, .dataviews-filter-summary__chip-container .dataviews-filter-summary__chip.has-values[aria-expanded=true] {
727
+ background: rgba(var(--wp-admin-theme-color--rgb), 0.12);
718
728
  }
719
729
  .dataviews-filter-summary__chip-container .dataviews-filter-summary__chip:focus-visible {
720
730
  outline: none;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wordpress/dataviews",
3
- "version": "0.5.3",
3
+ "version": "0.5.5",
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",
@@ -30,11 +30,11 @@
30
30
  "@ariakit/react": "^0.3.12",
31
31
  "@babel/runtime": "^7.16.0",
32
32
  "@wordpress/a11y": "^3.51.1",
33
- "@wordpress/components": "^26.0.2",
33
+ "@wordpress/components": "^26.0.4",
34
34
  "@wordpress/compose": "^6.28.1",
35
35
  "@wordpress/element": "^5.28.1",
36
36
  "@wordpress/i18n": "^4.51.1",
37
- "@wordpress/icons": "^9.42.1",
37
+ "@wordpress/icons": "^9.42.3",
38
38
  "@wordpress/keycodes": "^3.51.1",
39
39
  "@wordpress/primitives": "^3.49.1",
40
40
  "@wordpress/private-apis": "^0.33.1",
@@ -47,5 +47,5 @@
47
47
  "publishConfig": {
48
48
  "access": "public"
49
49
  },
50
- "gitHead": "b12d75c5c5256fda2a0509bb432e20ddd3354d5e"
50
+ "gitHead": "4927ea437069f9aed12f696df294a79bd8e12fd5"
51
51
  }
@@ -7,7 +7,7 @@ import {
7
7
  Modal,
8
8
  } from '@wordpress/components';
9
9
  import { __, sprintf, _n } from '@wordpress/i18n';
10
- import { useMemo, useState, useCallback } from '@wordpress/element';
10
+ import { useMemo, useState, useCallback, useEffect } from '@wordpress/element';
11
11
 
12
12
  /**
13
13
  * Internal dependencies
@@ -21,6 +21,24 @@ const {
21
21
  DropdownMenuSeparatorV2: DropdownMenuSeparator,
22
22
  } = unlock( componentsPrivateApis );
23
23
 
24
+ export function useHasAPossibleBulkAction( actions, item ) {
25
+ return useMemo( () => {
26
+ return actions.some( ( action ) => {
27
+ return action.supportsBulk && action.isEligible( item );
28
+ } );
29
+ }, [ actions, item ] );
30
+ }
31
+
32
+ export function useSomeItemHasAPossibleBulkAction( actions, data ) {
33
+ return useMemo( () => {
34
+ return data.some( ( item ) => {
35
+ return actions.some( ( action ) => {
36
+ return action.supportsBulk && action.isEligible( item );
37
+ } );
38
+ } );
39
+ }, [ actions, data ] );
40
+ }
41
+
24
42
  function ActionWithModal( {
25
43
  action,
26
44
  selectedItems,
@@ -107,15 +125,47 @@ export default function BulkActions( {
107
125
  () => actions.filter( ( action ) => action.supportsBulk ),
108
126
  [ actions ]
109
127
  );
110
- const areAllSelected = selection && selection.length === data.length;
111
128
  const [ isMenuOpen, onMenuOpenChange ] = useState( false );
112
129
  const [ actionWithModal, setActionWithModal ] = useState();
130
+ const selectableItems = useMemo( () => {
131
+ return data.filter( ( item ) => {
132
+ return bulkActions.some( ( action ) => action.isEligible( item ) );
133
+ } );
134
+ }, [ data, bulkActions ] );
135
+
136
+ const numberSelectableItems = selectableItems.length;
137
+ const areAllSelected =
138
+ selection && selection.length === numberSelectableItems;
139
+
113
140
  const selectedItems = useMemo( () => {
114
141
  return data.filter( ( item ) =>
115
142
  selection.includes( getItemId( item ) )
116
143
  );
117
144
  }, [ selection, data, getItemId ] );
118
145
 
146
+ const hasNonSelectableItemSelected = useMemo( () => {
147
+ return selectedItems.some( ( item ) => {
148
+ return ! selectableItems.includes( item );
149
+ } );
150
+ }, [ selectedItems, selectableItems ] );
151
+ useEffect( () => {
152
+ if ( hasNonSelectableItemSelected ) {
153
+ onSelectionChange(
154
+ selectedItems.filter( ( selectedItem ) => {
155
+ return selectableItems.some( ( item ) => {
156
+ return getItemId( selectedItem ) === getItemId( item );
157
+ } );
158
+ } )
159
+ );
160
+ }
161
+ }, [
162
+ hasNonSelectableItemSelected,
163
+ selectedItems,
164
+ selectableItems,
165
+ getItemId,
166
+ onSelectionChange,
167
+ ] );
168
+
119
169
  if ( bulkActions.length === 0 ) {
120
170
  return null;
121
171
  }
@@ -157,9 +207,9 @@ export default function BulkActions( {
157
207
  disabled={ areAllSelected }
158
208
  hideOnClick={ false }
159
209
  onClick={ () => {
160
- onSelectionChange( data );
210
+ onSelectionChange( selectableItems );
161
211
  } }
162
- suffix={ data.length }
212
+ suffix={ numberSelectableItems }
163
213
  >
164
214
  { __( 'Select all' ) }
165
215
  </DropdownMenuItem>
package/src/dataviews.js CHANGED
@@ -20,6 +20,16 @@ import BulkActions from './bulk-actions';
20
20
  const defaultGetItemId = ( item ) => item.id;
21
21
  const defaultOnSelectionChange = () => {};
22
22
 
23
+ function useSomeItemHasAPossibleBulkAction( actions, data ) {
24
+ return useMemo( () => {
25
+ return data.some( ( item ) => {
26
+ return actions.some( ( action ) => {
27
+ return action.supportsBulk && action.isEligible( item );
28
+ } );
29
+ } );
30
+ }, [ actions, data ] );
31
+ }
32
+
23
33
  export default function DataViews( {
24
34
  view,
25
35
  onChangeView,
@@ -75,30 +85,49 @@ export default function DataViews( {
75
85
  render: field.render || field.getValue,
76
86
  } ) );
77
87
  }, [ fields ] );
88
+
89
+ const hasPossibleBulkAction = useSomeItemHasAPossibleBulkAction(
90
+ actions,
91
+ data
92
+ );
78
93
  return (
79
94
  <div className="dataviews-wrapper">
80
95
  <VStack spacing={ 3 } justify="flex-start">
81
96
  <HStack
82
- alignment="flex-start"
97
+ alignment="top"
83
98
  justify="start"
84
99
  className="dataviews-filters__view-actions"
85
100
  >
86
- { search && (
87
- <Search
88
- label={ searchLabel }
101
+ <HStack
102
+ justify="start"
103
+ className="dataviews-filters__container"
104
+ wrap
105
+ >
106
+ { search && (
107
+ <Search
108
+ label={ searchLabel }
109
+ view={ view }
110
+ onChangeView={ onChangeView }
111
+ />
112
+ ) }
113
+ <Filters
114
+ fields={ _fields }
89
115
  view={ view }
90
116
  onChangeView={ onChangeView }
117
+ openedFilter={ openedFilter }
118
+ setOpenedFilter={ setOpenedFilter }
91
119
  />
92
- ) }
93
- { [ LAYOUT_TABLE, LAYOUT_GRID ].includes( view.type ) && (
94
- <BulkActions
95
- actions={ actions }
96
- data={ data }
97
- onSelectionChange={ onSetSelection }
98
- selection={ selection }
99
- getItemId={ getItemId }
100
- />
101
- ) }
120
+ </HStack>
121
+ { [ LAYOUT_TABLE, LAYOUT_GRID ].includes( view.type ) &&
122
+ hasPossibleBulkAction && (
123
+ <BulkActions
124
+ actions={ actions }
125
+ data={ data }
126
+ onSelectionChange={ onSetSelection }
127
+ selection={ selection }
128
+ getItemId={ getItemId }
129
+ />
130
+ ) }
102
131
  <ViewActions
103
132
  fields={ _fields }
104
133
  view={ view }
@@ -106,19 +135,6 @@ export default function DataViews( {
106
135
  supportedLayouts={ supportedLayouts }
107
136
  />
108
137
  </HStack>
109
- <HStack
110
- justify="start"
111
- className="dataviews-filters__container"
112
- wrap
113
- >
114
- <Filters
115
- fields={ _fields }
116
- view={ view }
117
- onChangeView={ onChangeView }
118
- openedFilter={ openedFilter }
119
- setOpenedFilter={ setOpenedFilter }
120
- />
121
- </HStack>
122
138
  <ViewComponent
123
139
  fields={ _fields }
124
140
  view={ view }
package/src/filters.js CHANGED
@@ -11,6 +11,7 @@ import AddFilter from './add-filter';
11
11
  import ResetFilters from './reset-filters';
12
12
  import { sanitizeOperators } from './utils';
13
13
  import { ENUMERATION_TYPE, OPERATOR_IN, OPERATOR_NOT_IN } from './constants';
14
+ import { __experimentalHStack as HStack } from '@wordpress/components';
14
15
 
15
16
  const Filters = memo( function Filters( {
16
17
  fields,
@@ -108,7 +109,11 @@ const Filters = memo( function Filters( {
108
109
  );
109
110
  }
110
111
 
111
- return filterComponents;
112
+ return (
113
+ <HStack justify="flex-start" style={ { width: 'fit-content' } } wrap>
114
+ { filterComponents }
115
+ </HStack>
116
+ );
112
117
  } );
113
118
 
114
119
  export default Filters;
package/src/pagination.js CHANGED
@@ -27,7 +27,12 @@ const Pagination = memo( function Pagination( {
27
27
  justify="end"
28
28
  className="dataviews-pagination"
29
29
  >
30
- <HStack justify="flex-start" expanded={ false } spacing={ 2 }>
30
+ <HStack
31
+ justify="flex-start"
32
+ expanded={ false }
33
+ spacing={ 2 }
34
+ className="dataviews-pagination__page-selection"
35
+ >
31
36
  { createInterpolateElement(
32
37
  sprintf(
33
38
  // translators: %s: Total number of pages.
@@ -11,6 +11,7 @@ export default function SingleSelectionCheckbox( {
11
11
  data,
12
12
  getItemId,
13
13
  primaryField,
14
+ disabled,
14
15
  } ) {
15
16
  const id = getItemId( item );
16
17
  const isSelected = selection.includes( id );
@@ -31,9 +32,14 @@ export default function SingleSelectionCheckbox( {
31
32
  <CheckboxControl
32
33
  className="dataviews-view-table-selection-checkbox"
33
34
  __nextHasNoMarginBottom
34
- checked={ isSelected }
35
35
  label={ selectionLabel }
36
+ aria-disabled={ disabled }
37
+ checked={ isSelected }
36
38
  onChange={ () => {
39
+ if ( disabled ) {
40
+ return;
41
+ }
42
+
37
43
  if ( ! isSelected ) {
38
44
  onSelectionChange(
39
45
  data.filter( ( _item ) => {
package/src/style.scss CHANGED
@@ -13,8 +13,6 @@
13
13
  .dataviews-filters__view-actions {
14
14
  padding: $grid-unit-15 $grid-unit-40 0;
15
15
  .components-search-control {
16
- flex-grow: 1;
17
-
18
16
  .components-base-control__field {
19
17
  max-width: 240px;
20
18
  }
@@ -22,11 +20,7 @@
22
20
  }
23
21
 
24
22
  .dataviews-filters__container {
25
- padding: 0 $grid-unit-40;
26
- }
27
-
28
- .dataviews-filters__view-actions.components-h-stack {
29
- align-items: center;
23
+ padding-right: $grid-unit-40;
30
24
  }
31
25
 
32
26
  .dataviews-filters-button {
@@ -44,6 +38,13 @@
44
38
  color: $gray-700;
45
39
  }
46
40
 
41
+ .dataviews-pagination__page-selection {
42
+ font-size: 11px;
43
+ text-transform: uppercase;
44
+ font-weight: 500;
45
+ color: $gray-900;
46
+ }
47
+
47
48
  .dataviews-filters-options {
48
49
  margin: $grid-unit-40 0 $grid-unit-20;
49
50
  }
@@ -118,7 +119,7 @@
118
119
  background-color: #f8f8f8;
119
120
  }
120
121
 
121
- .components-checkbox-control__input {
122
+ .components-checkbox-control__input.components-checkbox-control__input {
122
123
  opacity: 0;
123
124
 
124
125
  &:checked,
@@ -202,6 +203,12 @@
202
203
  .dataviews-view-table__actions-column {
203
204
  width: 1%;
204
205
  }
206
+
207
+ &:has(tr.is-selected) {
208
+ .components-checkbox-control__input {
209
+ opacity: 1;
210
+ }
211
+ }
205
212
  }
206
213
 
207
214
  .dataviews-view-list__primary-field,
@@ -212,7 +219,6 @@
212
219
  color: $gray-900;
213
220
  text-overflow: ellipsis;
214
221
  white-space: nowrap;
215
- overflow: hidden;
216
222
  display: block;
217
223
  width: 100%;
218
224
 
@@ -228,6 +234,7 @@
228
234
  &:hover {
229
235
  color: $gray-900;
230
236
  }
237
+ @include link-reset();
231
238
  }
232
239
 
233
240
  button.components-button.is-link {
@@ -648,7 +655,8 @@
648
655
  }
649
656
 
650
657
  &:hover,
651
- &:focus-visible {
658
+ &:focus-visible,
659
+ &[aria-expanded="true"] {
652
660
  background: $gray-200;
653
661
  color: $gray-900;
654
662
  }
@@ -657,8 +665,9 @@
657
665
  color: var(--wp-admin-theme-color);
658
666
  background: rgba(var(--wp-admin-theme-color--rgb), 0.04);
659
667
 
660
- &:hover {
661
- background: rgba(var(--wp-admin-theme-color--rgb), 0.08);
668
+ &:hover,
669
+ &[aria-expanded="true"] {
670
+ background: rgba(var(--wp-admin-theme-color--rgb), 0.12);
662
671
  }
663
672
  }
664
673
 
package/src/view-grid.js CHANGED
@@ -22,6 +22,8 @@ import { useState } from '@wordpress/element';
22
22
  import ItemActions from './item-actions';
23
23
  import SingleSelectionCheckbox from './single-selection-checkbox';
24
24
 
25
+ import { useHasAPossibleBulkAction } from './bulk-actions';
26
+
25
27
  function GridItem( {
26
28
  selection,
27
29
  data,
@@ -34,6 +36,7 @@ function GridItem( {
34
36
  visibleFields,
35
37
  } ) {
36
38
  const [ hasNoPointerEvents, setHasNoPointerEvents ] = useState( false );
39
+ const hasBulkAction = useHasAPossibleBulkAction( actions, item );
37
40
  const id = getItemId( item );
38
41
  const isSelected = selection.includes( id );
39
42
  return (
@@ -41,11 +44,11 @@ function GridItem( {
41
44
  spacing={ 0 }
42
45
  key={ id }
43
46
  className={ classnames( 'dataviews-view-grid__card', {
44
- 'is-selected': isSelected,
47
+ 'is-selected': hasBulkAction && isSelected,
45
48
  'has-no-pointer-events': hasNoPointerEvents,
46
49
  } ) }
47
50
  onMouseDown={ ( event ) => {
48
- if ( event.ctrlKey || event.metaKey ) {
51
+ if ( hasBulkAction && ( event.ctrlKey || event.metaKey ) ) {
49
52
  setHasNoPointerEvents( true );
50
53
  if ( ! isSelected ) {
51
54
  onSelectionChange(
@@ -91,6 +94,7 @@ function GridItem( {
91
94
  getItemId={ getItemId }
92
95
  data={ data }
93
96
  primaryField={ primaryField }
97
+ disabled={ ! hasBulkAction }
94
98
  />
95
99
  <HStack className="dataviews-view-grid__primary-field">
96
100
  { primaryField?.render( { item } ) }