@wordpress/dataviews 0.7.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.
- package/CHANGELOG.md +7 -0
- package/README.md +48 -15
- package/build/constants.js +28 -7
- package/build/constants.js.map +1 -1
- package/build/dataviews.js +11 -5
- package/build/dataviews.js.map +1 -1
- package/build/filter-summary.js +33 -12
- package/build/filter-summary.js.map +1 -1
- package/build/filters.js +2 -1
- package/build/filters.js.map +1 -1
- package/build/item-actions.js +20 -39
- package/build/item-actions.js.map +1 -1
- package/build/pagination.js +2 -2
- package/build/pagination.js.map +1 -1
- package/build/search-widget.js +34 -10
- package/build/search-widget.js.map +1 -1
- package/build/utils.js +24 -2
- package/build/utils.js.map +1 -1
- package/build/view-grid.js +4 -1
- package/build/view-grid.js.map +1 -1
- package/build/view-table.js +35 -4
- package/build/view-table.js.map +1 -1
- package/build-module/constants.js +27 -6
- package/build-module/constants.js.map +1 -1
- package/build-module/dataviews.js +11 -5
- package/build-module/dataviews.js.map +1 -1
- package/build-module/filter-summary.js +34 -13
- package/build-module/filter-summary.js.map +1 -1
- package/build-module/filters.js +3 -2
- package/build-module/filters.js.map +1 -1
- package/build-module/item-actions.js +20 -39
- package/build-module/item-actions.js.map +1 -1
- package/build-module/pagination.js +2 -2
- package/build-module/pagination.js.map +1 -1
- package/build-module/search-widget.js +35 -11
- package/build-module/search-widget.js.map +1 -1
- package/build-module/utils.js +25 -3
- package/build-module/utils.js.map +1 -1
- package/build-module/view-grid.js +4 -1
- package/build-module/view-grid.js.map +1 -1
- package/build-module/view-table.js +36 -5
- package/build-module/view-table.js.map +1 -1
- package/build-style/style-rtl.css +9 -3
- package/build-style/style.css +9 -3
- package/package.json +11 -11
- package/src/constants.js +35 -6
- package/src/dataviews.js +11 -5
- package/src/filter-summary.js +76 -23
- package/src/filters.js +10 -4
- package/src/item-actions.js +19 -55
- package/src/pagination.js +2 -2
- package/src/search-widget.js +63 -21
- package/src/stories/fixtures.js +12 -1
- package/src/stories/index.story.js +43 -4
- package/src/style.scss +9 -3
- package/src/utils.js +38 -4
- package/src/view-grid.js +4 -1
- package/src/view-table.js +50 -4
package/src/search-widget.js
CHANGED
|
@@ -15,7 +15,7 @@ import {
|
|
|
15
15
|
Icon,
|
|
16
16
|
privateApis as componentsPrivateApis,
|
|
17
17
|
} from '@wordpress/components';
|
|
18
|
-
import { search } from '@wordpress/icons';
|
|
18
|
+
import { search, check } from '@wordpress/icons';
|
|
19
19
|
import { SVG, Circle } from '@wordpress/primitives';
|
|
20
20
|
|
|
21
21
|
/**
|
|
@@ -39,6 +39,36 @@ function normalizeSearchInput( input = '' ) {
|
|
|
39
39
|
return removeAccents( input.trim().toLowerCase() );
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
+
const getCurrentValue = ( filterDefinition, currentFilter ) => {
|
|
43
|
+
if ( filterDefinition.singleSelection ) {
|
|
44
|
+
return currentFilter?.value;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if ( Array.isArray( currentFilter?.value ) ) {
|
|
48
|
+
return currentFilter.value;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if ( ! Array.isArray( currentFilter?.value ) && !! currentFilter?.value ) {
|
|
52
|
+
return [ currentFilter.value ];
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return [];
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
const getNewValue = ( filterDefinition, currentFilter, value ) => {
|
|
59
|
+
if ( filterDefinition.singleSelection ) {
|
|
60
|
+
return value;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if ( Array.isArray( currentFilter?.value ) ) {
|
|
64
|
+
return currentFilter.value.includes( value )
|
|
65
|
+
? currentFilter.value.filter( ( v ) => v !== value )
|
|
66
|
+
: [ ...currentFilter.value, value ];
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return [ value ];
|
|
70
|
+
};
|
|
71
|
+
|
|
42
72
|
function ListBox( { view, filter, onChangeView } ) {
|
|
43
73
|
const compositeStore = useCompositeStore( {
|
|
44
74
|
virtualFocus: true,
|
|
@@ -48,10 +78,10 @@ function ListBox( { view, filter, onChangeView } ) {
|
|
|
48
78
|
// so the first item is not selected, since the focus is on the operators control.
|
|
49
79
|
defaultActiveId: filter.operators?.length === 1 ? undefined : null,
|
|
50
80
|
} );
|
|
51
|
-
const
|
|
52
|
-
(
|
|
81
|
+
const currentFilter = view.filters.find(
|
|
82
|
+
( f ) => f.field === filter.field
|
|
53
83
|
);
|
|
54
|
-
const
|
|
84
|
+
const currentValue = getCurrentValue( filter, currentFilter );
|
|
55
85
|
return (
|
|
56
86
|
<Composite
|
|
57
87
|
store={ compositeStore }
|
|
@@ -83,10 +113,6 @@ function ListBox( { view, filter, onChangeView } ) {
|
|
|
83
113
|
/>
|
|
84
114
|
}
|
|
85
115
|
onClick={ () => {
|
|
86
|
-
const currentFilter = view.filters.find(
|
|
87
|
-
( _filter ) =>
|
|
88
|
-
_filter.field === filter.field
|
|
89
|
-
);
|
|
90
116
|
const newFilters = currentFilter
|
|
91
117
|
? [
|
|
92
118
|
...view.filters.map(
|
|
@@ -101,7 +127,11 @@ function ListBox( { view, filter, onChangeView } ) {
|
|
|
101
127
|
currentFilter.operator ||
|
|
102
128
|
filter
|
|
103
129
|
.operators[ 0 ],
|
|
104
|
-
value:
|
|
130
|
+
value: getNewValue(
|
|
131
|
+
filter,
|
|
132
|
+
currentFilter,
|
|
133
|
+
element.value
|
|
134
|
+
),
|
|
105
135
|
};
|
|
106
136
|
}
|
|
107
137
|
return _filter;
|
|
@@ -113,7 +143,11 @@ function ListBox( { view, filter, onChangeView } ) {
|
|
|
113
143
|
{
|
|
114
144
|
field: filter.field,
|
|
115
145
|
operator: filter.operators[ 0 ],
|
|
116
|
-
value:
|
|
146
|
+
value: getNewValue(
|
|
147
|
+
filter,
|
|
148
|
+
currentFilter,
|
|
149
|
+
element.value
|
|
150
|
+
),
|
|
117
151
|
},
|
|
118
152
|
];
|
|
119
153
|
onChangeView( {
|
|
@@ -126,9 +160,14 @@ function ListBox( { view, filter, onChangeView } ) {
|
|
|
126
160
|
}
|
|
127
161
|
>
|
|
128
162
|
<span className="dataviews-search-widget-listitem-check">
|
|
129
|
-
{
|
|
130
|
-
|
|
131
|
-
|
|
163
|
+
{ filter.singleSelection &&
|
|
164
|
+
currentValue === element.value && (
|
|
165
|
+
<Icon icon={ radioCheck } />
|
|
166
|
+
) }
|
|
167
|
+
{ ! filter.singleSelection &&
|
|
168
|
+
currentValue.includes( element.value ) && (
|
|
169
|
+
<Icon icon={ check } />
|
|
170
|
+
) }
|
|
132
171
|
</span>
|
|
133
172
|
<span>
|
|
134
173
|
{ element.label }
|
|
@@ -147,10 +186,10 @@ function ListBox( { view, filter, onChangeView } ) {
|
|
|
147
186
|
function ComboboxList( { view, filter, onChangeView } ) {
|
|
148
187
|
const [ searchValue, setSearchValue ] = useState( '' );
|
|
149
188
|
const deferredSearchValue = useDeferredValue( searchValue );
|
|
150
|
-
const
|
|
189
|
+
const currentFilter = view.filters.find(
|
|
151
190
|
( _filter ) => _filter.field === filter.field
|
|
152
191
|
);
|
|
153
|
-
const
|
|
192
|
+
const currentValue = getCurrentValue( filter, currentFilter );
|
|
154
193
|
const matches = useMemo( () => {
|
|
155
194
|
const normalizedSearch = normalizeSearchInput( deferredSearchValue );
|
|
156
195
|
return filter.elements.filter( ( item ) =>
|
|
@@ -160,10 +199,8 @@ function ComboboxList( { view, filter, onChangeView } ) {
|
|
|
160
199
|
return (
|
|
161
200
|
<Ariakit.ComboboxProvider
|
|
162
201
|
value={ searchValue }
|
|
202
|
+
selectedValue={ currentValue }
|
|
163
203
|
setSelectedValue={ ( value ) => {
|
|
164
|
-
const currentFilter = view.filters.find(
|
|
165
|
-
( _filter ) => _filter.field === filter.field
|
|
166
|
-
);
|
|
167
204
|
const newFilters = currentFilter
|
|
168
205
|
? [
|
|
169
206
|
...view.filters.map( ( _filter ) => {
|
|
@@ -223,9 +260,14 @@ function ComboboxList( { view, filter, onChangeView } ) {
|
|
|
223
260
|
focusOnHover
|
|
224
261
|
>
|
|
225
262
|
<span className="dataviews-search-widget-listitem-check">
|
|
226
|
-
{
|
|
227
|
-
|
|
228
|
-
|
|
263
|
+
{ filter.singleSelection &&
|
|
264
|
+
currentValue === element.value && (
|
|
265
|
+
<Icon icon={ radioCheck } />
|
|
266
|
+
) }
|
|
267
|
+
{ ! filter.singleSelection &&
|
|
268
|
+
currentValue.includes( element.value ) && (
|
|
269
|
+
<Icon icon={ check } />
|
|
270
|
+
) }
|
|
229
271
|
</span>
|
|
230
272
|
<span>
|
|
231
273
|
<Ariakit.ComboboxItemValue
|
package/src/stories/fixtures.js
CHANGED
|
@@ -20,66 +20,77 @@ export const data = [
|
|
|
20
20
|
title: 'Apollo',
|
|
21
21
|
description: 'Apollo description',
|
|
22
22
|
image: 'https://live.staticflickr.com/5725/21726228300_51333bd62c_b.jpg',
|
|
23
|
+
type: 'Not a planet',
|
|
23
24
|
},
|
|
24
25
|
{
|
|
25
26
|
id: 2,
|
|
26
27
|
title: 'Space',
|
|
27
28
|
description: 'Space description',
|
|
28
29
|
image: 'https://live.staticflickr.com/5678/21911065441_92e2d44708_b.jpg',
|
|
30
|
+
type: 'Not a planet',
|
|
29
31
|
},
|
|
30
32
|
{
|
|
31
33
|
id: 3,
|
|
32
34
|
title: 'NASA',
|
|
33
35
|
description: 'NASA photo',
|
|
34
36
|
image: 'https://live.staticflickr.com/742/21712365770_8f70a2c91e_b.jpg',
|
|
37
|
+
type: 'Not a planet',
|
|
35
38
|
},
|
|
36
39
|
{
|
|
37
40
|
id: 4,
|
|
38
41
|
title: 'Neptune',
|
|
39
42
|
description: 'Neptune description',
|
|
40
43
|
image: 'https://live.staticflickr.com/5725/21726228300_51333bd62c_b.jpg',
|
|
44
|
+
type: 'Ice giant',
|
|
41
45
|
},
|
|
42
46
|
{
|
|
43
47
|
id: 5,
|
|
44
48
|
title: 'Mercury',
|
|
45
49
|
description: 'Mercury description',
|
|
46
50
|
image: 'https://live.staticflickr.com/5725/21726228300_51333bd62c_b.jpg',
|
|
51
|
+
type: 'Terrestrial',
|
|
47
52
|
},
|
|
48
53
|
{
|
|
49
54
|
id: 6,
|
|
50
55
|
title: 'Venus',
|
|
51
56
|
description: 'Venus description',
|
|
52
57
|
image: 'https://live.staticflickr.com/5725/21726228300_51333bd62c_b.jpg',
|
|
58
|
+
type: 'Terrestrial',
|
|
53
59
|
},
|
|
54
60
|
{
|
|
55
61
|
id: 7,
|
|
56
62
|
title: 'Earth',
|
|
57
63
|
description: 'Earth description',
|
|
58
64
|
image: 'https://live.staticflickr.com/5725/21726228300_51333bd62c_b.jpg',
|
|
65
|
+
type: 'Terrestrial',
|
|
59
66
|
},
|
|
60
67
|
{
|
|
61
68
|
id: 8,
|
|
62
69
|
title: 'Mars',
|
|
63
70
|
description: 'Mars description',
|
|
64
71
|
image: 'https://live.staticflickr.com/5725/21726228300_51333bd62c_b.jpg',
|
|
72
|
+
type: 'Terrestrial',
|
|
65
73
|
},
|
|
66
74
|
{
|
|
67
75
|
id: 9,
|
|
68
76
|
title: 'Jupiter',
|
|
69
77
|
description: 'Jupiter description',
|
|
70
78
|
image: 'https://live.staticflickr.com/5725/21726228300_51333bd62c_b.jpg',
|
|
79
|
+
type: 'Gas giant',
|
|
71
80
|
},
|
|
72
81
|
{
|
|
73
82
|
id: 10,
|
|
74
83
|
title: 'Saturn',
|
|
75
84
|
description: 'Saturn description',
|
|
76
85
|
image: 'https://live.staticflickr.com/5725/21726228300_51333bd62c_b.jpg',
|
|
86
|
+
type: 'Gas giant',
|
|
77
87
|
},
|
|
78
88
|
{
|
|
79
89
|
id: 11,
|
|
80
90
|
title: 'Uranus',
|
|
81
91
|
description: 'Uranus description',
|
|
82
92
|
image: 'https://live.staticflickr.com/5725/21726228300_51333bd62c_b.jpg',
|
|
93
|
+
type: 'Ice giant',
|
|
83
94
|
},
|
|
84
95
|
];
|
|
85
96
|
|
|
@@ -88,7 +99,7 @@ export const DEFAULT_VIEW = {
|
|
|
88
99
|
search: '',
|
|
89
100
|
page: 1,
|
|
90
101
|
perPage: 10,
|
|
91
|
-
hiddenFields: [ 'image' ],
|
|
102
|
+
hiddenFields: [ 'image', 'type' ],
|
|
92
103
|
layout: {},
|
|
93
104
|
filters: [],
|
|
94
105
|
};
|
|
@@ -8,10 +8,15 @@ import { useState, useMemo, useCallback } from '@wordpress/element';
|
|
|
8
8
|
*/
|
|
9
9
|
import { DataViews } from '../index';
|
|
10
10
|
import { DEFAULT_VIEW, actions, data } from './fixtures';
|
|
11
|
-
import {
|
|
11
|
+
import {
|
|
12
|
+
LAYOUT_GRID,
|
|
13
|
+
LAYOUT_TABLE,
|
|
14
|
+
OPERATOR_IS_NONE,
|
|
15
|
+
OPERATOR_IS_ANY,
|
|
16
|
+
} from '../constants';
|
|
12
17
|
|
|
13
18
|
const meta = {
|
|
14
|
-
title: 'DataViews
|
|
19
|
+
title: 'DataViews/DataViews',
|
|
15
20
|
component: DataViews,
|
|
16
21
|
};
|
|
17
22
|
export default meta;
|
|
@@ -45,14 +50,25 @@ const fields = [
|
|
|
45
50
|
{
|
|
46
51
|
header: 'Title',
|
|
47
52
|
id: 'title',
|
|
48
|
-
getValue: ( { item } ) => item.title,
|
|
49
53
|
maxWidth: 400,
|
|
50
54
|
enableHiding: false,
|
|
51
55
|
},
|
|
56
|
+
{
|
|
57
|
+
header: 'Type',
|
|
58
|
+
id: 'type',
|
|
59
|
+
maxWidth: 400,
|
|
60
|
+
enableHiding: false,
|
|
61
|
+
type: 'enumeration',
|
|
62
|
+
elements: [
|
|
63
|
+
{ value: 'Not a planet', label: 'Not a planet' },
|
|
64
|
+
{ value: 'Ice giant', label: 'Ice giant' },
|
|
65
|
+
{ value: 'Terrestrial', label: 'Terrestrial' },
|
|
66
|
+
{ value: 'Gas giant', label: 'Gas giant' },
|
|
67
|
+
],
|
|
68
|
+
},
|
|
52
69
|
{
|
|
53
70
|
header: 'Description',
|
|
54
71
|
id: 'description',
|
|
55
|
-
getValue: ( { item } ) => item.description,
|
|
56
72
|
maxWidth: 200,
|
|
57
73
|
enableSorting: false,
|
|
58
74
|
},
|
|
@@ -72,6 +88,29 @@ export const Default = ( props ) => {
|
|
|
72
88
|
].some( ( field ) => field.includes( normalizedSearch ) );
|
|
73
89
|
} );
|
|
74
90
|
}
|
|
91
|
+
|
|
92
|
+
if ( view.filters.length > 0 ) {
|
|
93
|
+
view.filters.forEach( ( filter ) => {
|
|
94
|
+
if (
|
|
95
|
+
filter.field === 'type' &&
|
|
96
|
+
filter.operator === OPERATOR_IS_ANY &&
|
|
97
|
+
filter?.value?.length > 0
|
|
98
|
+
) {
|
|
99
|
+
filteredData = filteredData.filter( ( item ) => {
|
|
100
|
+
return filter.value.includes( item.type );
|
|
101
|
+
} );
|
|
102
|
+
} else if (
|
|
103
|
+
filter.field === 'type' &&
|
|
104
|
+
filter.operator === OPERATOR_IS_NONE &&
|
|
105
|
+
filter?.value?.length > 0
|
|
106
|
+
) {
|
|
107
|
+
filteredData = filteredData.filter( ( item ) => {
|
|
108
|
+
return ! filter.value.includes( item.type );
|
|
109
|
+
} );
|
|
110
|
+
}
|
|
111
|
+
} );
|
|
112
|
+
}
|
|
113
|
+
|
|
75
114
|
// Handle sorting.
|
|
76
115
|
if ( view.sort ) {
|
|
77
116
|
const stringSortingFields = [ 'title' ];
|
package/src/style.scss
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
.dataviews-wrapper {
|
|
2
|
-
width: 100%;
|
|
3
2
|
height: 100%;
|
|
4
3
|
overflow: auto;
|
|
5
4
|
box-sizing: border-box;
|
|
@@ -7,6 +6,7 @@
|
|
|
7
6
|
}
|
|
8
7
|
|
|
9
8
|
.dataviews-filters__view-actions {
|
|
9
|
+
box-sizing: border-box;
|
|
10
10
|
padding: $grid-unit-15 $grid-unit-40 0;
|
|
11
11
|
margin-bottom: $grid-unit-15;
|
|
12
12
|
flex-shrink: 0;
|
|
@@ -123,7 +123,7 @@
|
|
|
123
123
|
border-bottom: 0;
|
|
124
124
|
}
|
|
125
125
|
|
|
126
|
-
|
|
126
|
+
&.is-hovered {
|
|
127
127
|
background-color: #f8f8f8;
|
|
128
128
|
}
|
|
129
129
|
|
|
@@ -137,9 +137,15 @@
|
|
|
137
137
|
}
|
|
138
138
|
}
|
|
139
139
|
|
|
140
|
+
.dataviews-item-actions .components-button:not(.dataviews-all-actions-button) {
|
|
141
|
+
opacity: 0;
|
|
142
|
+
}
|
|
143
|
+
|
|
140
144
|
&:focus-within,
|
|
145
|
+
&.is-hovered,
|
|
141
146
|
&:hover {
|
|
142
|
-
.components-checkbox-control__input
|
|
147
|
+
.components-checkbox-control__input,
|
|
148
|
+
.dataviews-item-actions .components-button:not(.dataviews-all-actions-button) {
|
|
143
149
|
opacity: 1;
|
|
144
150
|
}
|
|
145
151
|
}
|
package/src/utils.js
CHANGED
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Internal dependencies
|
|
3
3
|
*/
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
ALL_OPERATORS,
|
|
6
|
+
OPERATOR_IS,
|
|
7
|
+
OPERATOR_IS_NOT,
|
|
8
|
+
OPERATOR_IS_ANY,
|
|
9
|
+
OPERATOR_IS_NONE,
|
|
10
|
+
} from './constants';
|
|
5
11
|
|
|
6
12
|
/**
|
|
7
13
|
* Helper util to sort data by text fields, when sorting is done client side.
|
|
@@ -57,10 +63,38 @@ export function getPaginationResults( { data, view } ) {
|
|
|
57
63
|
|
|
58
64
|
export const sanitizeOperators = ( field ) => {
|
|
59
65
|
let operators = field.filterBy?.operators;
|
|
66
|
+
|
|
67
|
+
// Assign default values.
|
|
60
68
|
if ( ! operators || ! Array.isArray( operators ) ) {
|
|
61
|
-
operators =
|
|
69
|
+
operators = [ OPERATOR_IS_ANY, OPERATOR_IS_NONE ];
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Transform legacy in, notIn operators to is, isNot.
|
|
73
|
+
// To be removed in the future.
|
|
74
|
+
if ( operators.includes( 'in' ) ) {
|
|
75
|
+
operators = operators.filter( ( operator ) => operator !== 'is' );
|
|
76
|
+
operators.push( 'is' );
|
|
77
|
+
}
|
|
78
|
+
if ( operators.includes( 'notIn' ) ) {
|
|
79
|
+
operators = operators.filter( ( operator ) => operator !== 'notIn' );
|
|
80
|
+
operators.push( 'isNot' );
|
|
62
81
|
}
|
|
63
|
-
|
|
64
|
-
|
|
82
|
+
|
|
83
|
+
// Make sure only valid operators are used.
|
|
84
|
+
operators = operators.filter( ( operator ) =>
|
|
85
|
+
ALL_OPERATORS.includes( operator )
|
|
65
86
|
);
|
|
87
|
+
|
|
88
|
+
// Do not allow mixing single & multiselection operators.
|
|
89
|
+
// Remove multiselection operators if any of the single selection ones is present.
|
|
90
|
+
if (
|
|
91
|
+
operators.includes( OPERATOR_IS ) ||
|
|
92
|
+
operators.includes( OPERATOR_IS_NOT )
|
|
93
|
+
) {
|
|
94
|
+
operators = operators.filter( ( operator ) =>
|
|
95
|
+
[ OPERATOR_IS, OPERATOR_IS_NOT ].includes( operator )
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return operators;
|
|
66
100
|
};
|
package/src/view-grid.js
CHANGED
|
@@ -46,9 +46,12 @@ function GridItem( {
|
|
|
46
46
|
'is-selected': hasBulkAction && isSelected,
|
|
47
47
|
} ) }
|
|
48
48
|
onClickCapture={ ( event ) => {
|
|
49
|
-
if (
|
|
49
|
+
if ( event.ctrlKey || event.metaKey ) {
|
|
50
50
|
event.stopPropagation();
|
|
51
51
|
event.preventDefault();
|
|
52
|
+
if ( ! hasBulkAction ) {
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
52
55
|
if ( ! isSelected ) {
|
|
53
56
|
onSelectionChange(
|
|
54
57
|
data.filter( ( _item ) => {
|
package/src/view-table.js
CHANGED
|
@@ -22,9 +22,9 @@ import {
|
|
|
22
22
|
useId,
|
|
23
23
|
useRef,
|
|
24
24
|
useState,
|
|
25
|
+
useMemo,
|
|
25
26
|
Children,
|
|
26
27
|
Fragment,
|
|
27
|
-
useMemo,
|
|
28
28
|
} from '@wordpress/element';
|
|
29
29
|
|
|
30
30
|
/**
|
|
@@ -49,7 +49,7 @@ const {
|
|
|
49
49
|
DropdownMenuSeparatorV2: DropdownMenuSeparator,
|
|
50
50
|
} = unlock( componentsPrivateApis );
|
|
51
51
|
|
|
52
|
-
function
|
|
52
|
+
function WithDropDownMenuSeparators( { children } ) {
|
|
53
53
|
return Children.toArray( children )
|
|
54
54
|
.filter( Boolean )
|
|
55
55
|
.map( ( child, i ) => (
|
|
@@ -102,7 +102,7 @@ const HeaderMenu = forwardRef( function HeaderMenu(
|
|
|
102
102
|
}
|
|
103
103
|
style={ { minWidth: '240px' } }
|
|
104
104
|
>
|
|
105
|
-
<
|
|
105
|
+
<WithDropDownMenuSeparators>
|
|
106
106
|
{ isSortable && (
|
|
107
107
|
<DropdownMenuGroup>
|
|
108
108
|
{ Object.entries( SORTING_DIRECTIONS ).map(
|
|
@@ -187,7 +187,7 @@ const HeaderMenu = forwardRef( function HeaderMenu(
|
|
|
187
187
|
</DropdownMenuItemLabel>
|
|
188
188
|
</DropdownMenuItem>
|
|
189
189
|
) }
|
|
190
|
-
</
|
|
190
|
+
</WithDropDownMenuSeparators>
|
|
191
191
|
</DropdownMenu>
|
|
192
192
|
);
|
|
193
193
|
} );
|
|
@@ -237,12 +237,58 @@ function TableRow( {
|
|
|
237
237
|
data,
|
|
238
238
|
} ) {
|
|
239
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
|
+
|
|
240
253
|
return (
|
|
241
254
|
<tr
|
|
242
255
|
className={ classnames( 'dataviews-view-table__row', {
|
|
243
256
|
'is-selected':
|
|
244
257
|
hasPossibleBulkAction && selection.includes( id ),
|
|
258
|
+
'is-hovered': isHovered,
|
|
245
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
|
+
} }
|
|
246
292
|
>
|
|
247
293
|
{ hasBulkActions && (
|
|
248
294
|
<td
|