@wordpress/dataviews 0.4.1 → 0.5.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 +2 -0
- package/README.md +1 -0
- package/build/add-filter.js +25 -108
- package/build/add-filter.js.map +1 -1
- package/build/constants.js +9 -18
- package/build/constants.js.map +1 -1
- package/build/dataviews.js +22 -16
- package/build/dataviews.js.map +1 -1
- package/build/dropdown-menu-helper.js +1 -2
- package/build/dropdown-menu-helper.js.map +1 -1
- package/build/filter-summary.js +180 -77
- package/build/filter-summary.js.map +1 -1
- package/build/filters.js +32 -18
- package/build/filters.js.map +1 -1
- package/build/pagination.js +1 -2
- package/build/pagination.js.map +1 -1
- package/build/reset-filters.js +4 -1
- package/build/reset-filters.js.map +1 -1
- package/build/search-widget.js +111 -0
- package/build/search-widget.js.map +1 -0
- package/build/search.js +2 -3
- package/build/search.js.map +1 -1
- package/build/single-selection-checkbox.js +54 -0
- package/build/single-selection-checkbox.js.map +1 -0
- package/build/utils.js +14 -1
- package/build/utils.js.map +1 -1
- package/build/view-actions.js +2 -3
- package/build/view-actions.js.map +1 -1
- package/build/view-grid.js +92 -22
- package/build/view-grid.js.map +1 -1
- package/build/view-list.js +2 -1
- package/build/view-list.js.map +1 -1
- package/build/view-table.js +43 -132
- package/build/view-table.js.map +1 -1
- package/build-module/add-filter.js +28 -111
- package/build-module/add-filter.js.map +1 -1
- package/build-module/dataviews.js +23 -17
- package/build-module/dataviews.js.map +1 -1
- package/build-module/filter-summary.js +181 -79
- package/build-module/filter-summary.js.map +1 -1
- package/build-module/filters.js +32 -17
- package/build-module/filters.js.map +1 -1
- package/build-module/reset-filters.js +4 -1
- package/build-module/reset-filters.js.map +1 -1
- package/build-module/search-widget.js +101 -0
- package/build-module/search-widget.js.map +1 -0
- package/build-module/search.js +1 -1
- package/build-module/search.js.map +1 -1
- package/build-module/single-selection-checkbox.js +47 -0
- package/build-module/single-selection-checkbox.js.map +1 -0
- package/build-module/utils.js +12 -0
- package/build-module/utils.js.map +1 -1
- package/build-module/view-actions.js +1 -1
- package/build-module/view-actions.js.map +1 -1
- package/build-module/view-grid.js +92 -22
- package/build-module/view-grid.js.map +1 -1
- package/build-module/view-list.js +2 -1
- package/build-module/view-list.js.map +1 -1
- package/build-module/view-table.js +43 -131
- package/build-module/view-table.js.map +1 -1
- package/build-style/style-rtl.css +253 -44
- package/build-style/style.css +253 -44
- package/package.json +12 -11
- package/src/add-filter.js +39 -230
- package/src/dataviews.js +31 -20
- package/src/filter-summary.js +228 -135
- package/src/filters.js +42 -29
- package/src/reset-filters.js +12 -2
- package/src/search-widget.js +128 -0
- package/src/search.js +1 -1
- package/src/single-selection-checkbox.js +59 -0
- package/src/style.scss +259 -44
- package/src/utils.js +15 -0
- package/src/view-actions.js +1 -2
- package/src/view-grid.js +127 -53
- package/src/view-list.js +5 -1
- package/src/view-table.js +57 -230
package/src/dataviews.js
CHANGED
|
@@ -14,7 +14,7 @@ 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, LAYOUT_TABLE } from './constants';
|
|
17
|
+
import { VIEW_LAYOUTS, LAYOUT_TABLE, LAYOUT_GRID } from './constants';
|
|
18
18
|
import BulkActions from './bulk-actions';
|
|
19
19
|
|
|
20
20
|
const defaultGetItemId = ( item ) => item.id;
|
|
@@ -37,30 +37,33 @@ export default function DataViews( {
|
|
|
37
37
|
deferredRendering = false,
|
|
38
38
|
} ) {
|
|
39
39
|
const [ selection, setSelection ] = useState( [] );
|
|
40
|
+
const [ openedFilter, setOpenedFilter ] = useState( null );
|
|
40
41
|
|
|
41
42
|
useEffect( () => {
|
|
42
43
|
if (
|
|
43
44
|
selection.length > 0 &&
|
|
44
45
|
selection.some(
|
|
45
|
-
( id ) => ! data.some( ( item ) => item
|
|
46
|
+
( id ) => ! data.some( ( item ) => getItemId( item ) === id )
|
|
46
47
|
)
|
|
47
48
|
) {
|
|
48
49
|
const newSelection = selection.filter( ( id ) =>
|
|
49
|
-
data.some( ( item ) => item
|
|
50
|
+
data.some( ( item ) => getItemId( item ) === id )
|
|
50
51
|
);
|
|
51
52
|
setSelection( newSelection );
|
|
52
53
|
onSelectionChange(
|
|
53
|
-
data.filter( ( item ) =>
|
|
54
|
+
data.filter( ( item ) =>
|
|
55
|
+
newSelection.includes( getItemId( item ) )
|
|
56
|
+
)
|
|
54
57
|
);
|
|
55
58
|
}
|
|
56
|
-
}, [ selection, data, onSelectionChange ] );
|
|
59
|
+
}, [ selection, data, getItemId, onSelectionChange ] );
|
|
57
60
|
|
|
58
61
|
const onSetSelection = useCallback(
|
|
59
62
|
( items ) => {
|
|
60
|
-
setSelection( items.map( ( item ) => item
|
|
63
|
+
setSelection( items.map( ( item ) => getItemId( item ) ) );
|
|
61
64
|
onSelectionChange( items );
|
|
62
65
|
},
|
|
63
|
-
[ setSelection, onSelectionChange ]
|
|
66
|
+
[ setSelection, getItemId, onSelectionChange ]
|
|
64
67
|
);
|
|
65
68
|
|
|
66
69
|
const ViewComponent = VIEW_LAYOUTS.find(
|
|
@@ -74,26 +77,20 @@ export default function DataViews( {
|
|
|
74
77
|
}, [ fields ] );
|
|
75
78
|
return (
|
|
76
79
|
<div className="dataviews-wrapper">
|
|
77
|
-
<VStack spacing={
|
|
80
|
+
<VStack spacing={ 3 } justify="flex-start">
|
|
78
81
|
<HStack
|
|
79
82
|
alignment="flex-start"
|
|
83
|
+
justify="start"
|
|
80
84
|
className="dataviews-filters__view-actions"
|
|
81
85
|
>
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
label={ searchLabel }
|
|
86
|
-
view={ view }
|
|
87
|
-
onChangeView={ onChangeView }
|
|
88
|
-
/>
|
|
89
|
-
) }
|
|
90
|
-
<Filters
|
|
91
|
-
fields={ _fields }
|
|
86
|
+
{ search && (
|
|
87
|
+
<Search
|
|
88
|
+
label={ searchLabel }
|
|
92
89
|
view={ view }
|
|
93
90
|
onChangeView={ onChangeView }
|
|
94
91
|
/>
|
|
95
|
-
|
|
96
|
-
{ view.type
|
|
92
|
+
) }
|
|
93
|
+
{ [ LAYOUT_TABLE, LAYOUT_GRID ].includes( view.type ) && (
|
|
97
94
|
<BulkActions
|
|
98
95
|
actions={ actions }
|
|
99
96
|
data={ data }
|
|
@@ -109,6 +106,19 @@ export default function DataViews( {
|
|
|
109
106
|
supportedLayouts={ supportedLayouts }
|
|
110
107
|
/>
|
|
111
108
|
</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>
|
|
112
122
|
<ViewComponent
|
|
113
123
|
fields={ _fields }
|
|
114
124
|
view={ view }
|
|
@@ -121,6 +131,7 @@ export default function DataViews( {
|
|
|
121
131
|
onDetailsChange={ onDetailsChange }
|
|
122
132
|
selection={ selection }
|
|
123
133
|
deferredRendering={ deferredRendering }
|
|
134
|
+
setOpenedFilter={ setOpenedFilter }
|
|
124
135
|
/>
|
|
125
136
|
<Pagination
|
|
126
137
|
view={ view }
|
package/src/filter-summary.js
CHANGED
|
@@ -1,45 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* External dependencies
|
|
3
|
+
*/
|
|
4
|
+
import classnames from 'classnames';
|
|
5
|
+
|
|
1
6
|
/**
|
|
2
7
|
* WordPress dependencies
|
|
3
8
|
*/
|
|
4
9
|
import {
|
|
10
|
+
Dropdown,
|
|
5
11
|
Button,
|
|
6
|
-
|
|
12
|
+
__experimentalVStack as VStack,
|
|
13
|
+
__experimentalHStack as HStack,
|
|
14
|
+
FlexItem,
|
|
15
|
+
SelectControl,
|
|
16
|
+
Tooltip,
|
|
7
17
|
Icon,
|
|
8
18
|
} from '@wordpress/components';
|
|
9
|
-
import { chevronDown } from '@wordpress/icons';
|
|
10
19
|
import { __, sprintf } from '@wordpress/i18n';
|
|
11
|
-
import {
|
|
20
|
+
import { useRef, createInterpolateElement } from '@wordpress/element';
|
|
21
|
+
import { closeSmall } from '@wordpress/icons';
|
|
22
|
+
import { ENTER, SPACE } from '@wordpress/keycodes';
|
|
12
23
|
|
|
13
24
|
/**
|
|
14
25
|
* Internal dependencies
|
|
15
26
|
*/
|
|
27
|
+
import SearchWidget from './search-widget';
|
|
16
28
|
import { OPERATOR_IN, OPERATOR_NOT_IN, OPERATORS } from './constants';
|
|
17
|
-
import { unlock } from './lock-unlock';
|
|
18
|
-
import { DropdownMenuRadioItemCustom } from './dropdown-menu-helper';
|
|
19
|
-
|
|
20
|
-
const {
|
|
21
|
-
DropdownMenuV2: DropdownMenu,
|
|
22
|
-
DropdownMenuGroupV2: DropdownMenuGroup,
|
|
23
|
-
DropdownMenuItemV2: DropdownMenuItem,
|
|
24
|
-
DropdownMenuSeparatorV2: DropdownMenuSeparator,
|
|
25
|
-
DropdownMenuItemLabelV2: DropdownMenuItemLabel,
|
|
26
|
-
DropdownMenuItemHelpTextV2: DropdownMenuItemHelpText,
|
|
27
|
-
} = unlock( componentsPrivateApis );
|
|
28
29
|
|
|
29
30
|
const FilterText = ( { activeElement, filterInView, filter } ) => {
|
|
30
31
|
if ( activeElement === undefined ) {
|
|
31
32
|
return filter.name;
|
|
32
33
|
}
|
|
33
34
|
|
|
35
|
+
const filterTextWrappers = {
|
|
36
|
+
Span1: <span className="dataviews-filter-summary__filter-text-name" />,
|
|
37
|
+
Span2: <span className="dataviews-filter-summary__filter-text-value" />,
|
|
38
|
+
};
|
|
39
|
+
|
|
34
40
|
if (
|
|
35
41
|
activeElement !== undefined &&
|
|
36
42
|
filterInView?.operator === OPERATOR_IN
|
|
37
43
|
) {
|
|
38
|
-
return
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
44
|
+
return createInterpolateElement(
|
|
45
|
+
sprintf(
|
|
46
|
+
/* translators: 1: Filter name. 2: Filter value. e.g.: "Author is Admin". */
|
|
47
|
+
__( '<Span1>%1$s </Span1><Span2>is %2$s</Span2>' ),
|
|
48
|
+
filter.name,
|
|
49
|
+
activeElement.label
|
|
50
|
+
),
|
|
51
|
+
filterTextWrappers
|
|
43
52
|
);
|
|
44
53
|
}
|
|
45
54
|
|
|
@@ -47,11 +56,14 @@ const FilterText = ( { activeElement, filterInView, filter } ) => {
|
|
|
47
56
|
activeElement !== undefined &&
|
|
48
57
|
filterInView?.operator === OPERATOR_NOT_IN
|
|
49
58
|
) {
|
|
50
|
-
return
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
59
|
+
return createInterpolateElement(
|
|
60
|
+
sprintf(
|
|
61
|
+
/* translators: 1: Filter name. 2: Filter value. e.g.: "Author is not Admin". */
|
|
62
|
+
__( '<Span1>%1$s </Span1><Span2>is not %2$s</Span2>' ),
|
|
63
|
+
filter.name,
|
|
64
|
+
activeElement.label
|
|
65
|
+
),
|
|
66
|
+
filterTextWrappers
|
|
55
67
|
);
|
|
56
68
|
}
|
|
57
69
|
|
|
@@ -62,127 +74,208 @@ const FilterText = ( { activeElement, filterInView, filter } ) => {
|
|
|
62
74
|
);
|
|
63
75
|
};
|
|
64
76
|
|
|
65
|
-
function
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
77
|
+
function OperatorSelector( { filter, view, onChangeView } ) {
|
|
78
|
+
const operatorOptions = filter.operators?.map( ( operator ) => ( {
|
|
79
|
+
value: operator,
|
|
80
|
+
label: OPERATORS[ operator ]?.label,
|
|
81
|
+
} ) );
|
|
82
|
+
const currentFilter = view.filters.find(
|
|
83
|
+
( _filter ) => _filter.field === filter.field
|
|
84
|
+
);
|
|
85
|
+
const value = currentFilter?.operator || filter.operators[ 0 ];
|
|
86
|
+
return (
|
|
87
|
+
operatorOptions.length > 1 && (
|
|
88
|
+
<HStack
|
|
89
|
+
spacing={ 2 }
|
|
90
|
+
justify="flex-start"
|
|
91
|
+
className="dataviews-filter-summary__operators-container"
|
|
92
|
+
>
|
|
93
|
+
<FlexItem className="dataviews-filter-summary__operators-filter-name">
|
|
94
|
+
{ filter.name }
|
|
95
|
+
</FlexItem>
|
|
96
|
+
|
|
97
|
+
<SelectControl
|
|
98
|
+
label={ __( 'Conditions' ) }
|
|
99
|
+
value={ value }
|
|
100
|
+
options={ operatorOptions }
|
|
101
|
+
onChange={ ( newValue ) => {
|
|
102
|
+
const newFilters = currentFilter
|
|
103
|
+
? [
|
|
104
|
+
...view.filters.map( ( _filter ) => {
|
|
105
|
+
if ( _filter.field === filter.field ) {
|
|
106
|
+
return {
|
|
107
|
+
..._filter,
|
|
108
|
+
operator: newValue,
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
return _filter;
|
|
112
|
+
} ),
|
|
113
|
+
]
|
|
114
|
+
: [
|
|
115
|
+
...view.filters,
|
|
116
|
+
{
|
|
117
|
+
field: filter.field,
|
|
118
|
+
operator: newValue,
|
|
119
|
+
},
|
|
120
|
+
];
|
|
121
|
+
onChangeView( {
|
|
122
|
+
...view,
|
|
123
|
+
page: 1,
|
|
124
|
+
filters: newFilters,
|
|
125
|
+
} );
|
|
126
|
+
} }
|
|
127
|
+
size="small"
|
|
128
|
+
__nextHasNoMarginBottom
|
|
129
|
+
hideLabelFromVision
|
|
130
|
+
/>
|
|
131
|
+
</HStack>
|
|
132
|
+
)
|
|
133
|
+
);
|
|
74
134
|
}
|
|
75
135
|
|
|
76
|
-
|
|
77
|
-
const
|
|
78
|
-
|
|
79
|
-
(
|
|
136
|
+
function ResetFilter( { filter, view, onChangeView, addFilterRef } ) {
|
|
137
|
+
const isDisabled =
|
|
138
|
+
filter.isPrimary &&
|
|
139
|
+
view.filters.find( ( _filter ) => _filter.field === filter.field )
|
|
140
|
+
?.value === undefined;
|
|
141
|
+
return (
|
|
142
|
+
<div className="dataviews-filter-summary__reset">
|
|
143
|
+
<Button
|
|
144
|
+
disabled={ isDisabled }
|
|
145
|
+
__experimentalIsFocusable
|
|
146
|
+
size="compact"
|
|
147
|
+
variant="tertiary"
|
|
148
|
+
style={ { justifyContent: 'center', width: '100%' } }
|
|
149
|
+
onClick={ () => {
|
|
150
|
+
onChangeView( {
|
|
151
|
+
...view,
|
|
152
|
+
page: 1,
|
|
153
|
+
filters: view.filters.filter(
|
|
154
|
+
( _filter ) => _filter.field !== filter.field
|
|
155
|
+
),
|
|
156
|
+
} );
|
|
157
|
+
// If the filter is not primary and can be removed, it will be added
|
|
158
|
+
// back to the available filters from `Add filter` component.
|
|
159
|
+
if ( ! filter.isPrimary ) {
|
|
160
|
+
addFilterRef.current?.focus();
|
|
161
|
+
}
|
|
162
|
+
} }
|
|
163
|
+
>
|
|
164
|
+
{ filter.isPrimary ? __( 'Reset' ) : __( 'Remove' ) }
|
|
165
|
+
</Button>
|
|
166
|
+
</div>
|
|
80
167
|
);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
export default function FilterSummary( {
|
|
171
|
+
addFilterRef,
|
|
172
|
+
openedFilter,
|
|
173
|
+
...commonProps
|
|
174
|
+
} ) {
|
|
175
|
+
const toggleRef = useRef();
|
|
176
|
+
const { filter, view, onChangeView } = commonProps;
|
|
177
|
+
const filterInView = view.filters.find( ( f ) => f.field === filter.field );
|
|
81
178
|
const activeElement = filter.elements.find(
|
|
82
179
|
( element ) => element.value === filterInView?.value
|
|
83
180
|
);
|
|
84
|
-
const
|
|
85
|
-
|
|
181
|
+
const isPrimary = filter.isPrimary;
|
|
182
|
+
const hasValues = filterInView?.value !== undefined;
|
|
183
|
+
const canResetOrRemove = ! isPrimary || hasValues;
|
|
86
184
|
return (
|
|
87
|
-
<
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
<
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
185
|
+
<Dropdown
|
|
186
|
+
defaultOpen={ openedFilter === filter.field }
|
|
187
|
+
contentClassName="dataviews-filter-summary__popover"
|
|
188
|
+
popoverProps={ { placement: 'bottom-start', role: 'dialog' } }
|
|
189
|
+
onClose={ () => {
|
|
190
|
+
toggleRef.current?.focus();
|
|
191
|
+
} }
|
|
192
|
+
renderToggle={ ( { isOpen, onToggle } ) => (
|
|
193
|
+
<div className="dataviews-filter-summary__chip-container">
|
|
194
|
+
<Tooltip
|
|
195
|
+
text={ sprintf(
|
|
196
|
+
/* translators: 1: Filter name. */
|
|
197
|
+
__( 'Filter by: %1$s' ),
|
|
198
|
+
filter.name.toLowerCase()
|
|
199
|
+
) }
|
|
200
|
+
placement="top"
|
|
201
|
+
>
|
|
202
|
+
<div
|
|
203
|
+
className={ classnames(
|
|
204
|
+
'dataviews-filter-summary__chip',
|
|
205
|
+
{
|
|
206
|
+
'has-reset': canResetOrRemove,
|
|
207
|
+
'has-values': hasValues,
|
|
208
|
+
}
|
|
209
|
+
) }
|
|
210
|
+
role="button"
|
|
211
|
+
tabIndex={ 0 }
|
|
212
|
+
onClick={ onToggle }
|
|
213
|
+
onKeyDown={ ( event ) => {
|
|
214
|
+
if (
|
|
215
|
+
[ ENTER, SPACE ].includes( event.keyCode )
|
|
216
|
+
) {
|
|
217
|
+
onToggle();
|
|
218
|
+
event.preventDefault();
|
|
219
|
+
}
|
|
220
|
+
} }
|
|
221
|
+
aria-pressed={ isOpen }
|
|
222
|
+
aria-expanded={ isOpen }
|
|
223
|
+
ref={ toggleRef }
|
|
224
|
+
>
|
|
225
|
+
<FilterText
|
|
226
|
+
activeElement={ activeElement }
|
|
227
|
+
filterInView={ filterInView }
|
|
228
|
+
filter={ filter }
|
|
229
|
+
/>
|
|
230
|
+
</div>
|
|
231
|
+
</Tooltip>
|
|
232
|
+
{ canResetOrRemove && (
|
|
233
|
+
<Tooltip
|
|
234
|
+
text={ isPrimary ? __( 'Reset' ) : __( 'Remove' ) }
|
|
235
|
+
placement="top"
|
|
236
|
+
>
|
|
237
|
+
<button
|
|
238
|
+
className={ classnames(
|
|
239
|
+
'dataviews-filter-summary__chip-remove',
|
|
240
|
+
{ 'has-values': hasValues }
|
|
241
|
+
) }
|
|
242
|
+
onClick={ () => {
|
|
111
243
|
onChangeView( {
|
|
112
244
|
...view,
|
|
113
245
|
page: 1,
|
|
114
|
-
filters:
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
{ element.label }
|
|
129
|
-
</DropdownMenuItemLabel>
|
|
130
|
-
{ !! element.description && (
|
|
131
|
-
<DropdownMenuItemHelpText>
|
|
132
|
-
{ element.description }
|
|
133
|
-
</DropdownMenuItemHelpText>
|
|
134
|
-
) }
|
|
135
|
-
</DropdownMenuRadioItemCustom>
|
|
136
|
-
);
|
|
137
|
-
} ) }
|
|
138
|
-
</DropdownMenuGroup>
|
|
139
|
-
{ filter.operators.length > 1 && (
|
|
140
|
-
<DropdownMenu
|
|
141
|
-
trigger={
|
|
142
|
-
<DropdownMenuItem
|
|
143
|
-
suffix={
|
|
144
|
-
<span aria-hidden="true">
|
|
145
|
-
{ OPERATORS[ activeOperator ]?.label }
|
|
146
|
-
</span>
|
|
147
|
-
}
|
|
246
|
+
filters: view.filters.filter(
|
|
247
|
+
( _filter ) =>
|
|
248
|
+
_filter.field !== filter.field
|
|
249
|
+
),
|
|
250
|
+
} );
|
|
251
|
+
// If the filter is not primary and can be removed, it will be added
|
|
252
|
+
// back to the available filters from `Add filter` component.
|
|
253
|
+
if ( ! isPrimary ) {
|
|
254
|
+
addFilterRef.current?.focus();
|
|
255
|
+
} else {
|
|
256
|
+
// If is primary, focus the toggle button.
|
|
257
|
+
toggleRef.current?.focus();
|
|
258
|
+
}
|
|
259
|
+
} }
|
|
148
260
|
>
|
|
149
|
-
<
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
{
|
|
169
|
-
field: filter.field,
|
|
170
|
-
operator: e.target.value,
|
|
171
|
-
value: filterInView?.value,
|
|
172
|
-
},
|
|
173
|
-
],
|
|
174
|
-
} );
|
|
175
|
-
} }
|
|
176
|
-
>
|
|
177
|
-
<DropdownMenuItemLabel>
|
|
178
|
-
{ label }
|
|
179
|
-
</DropdownMenuItemLabel>
|
|
180
|
-
</DropdownMenuRadioItemCustom>
|
|
181
|
-
)
|
|
182
|
-
) }
|
|
183
|
-
</DropdownMenu>
|
|
184
|
-
) }
|
|
185
|
-
</WithSeparators>
|
|
186
|
-
</DropdownMenu>
|
|
261
|
+
<Icon icon={ closeSmall } />
|
|
262
|
+
</button>
|
|
263
|
+
</Tooltip>
|
|
264
|
+
) }
|
|
265
|
+
</div>
|
|
266
|
+
) }
|
|
267
|
+
renderContent={ () => {
|
|
268
|
+
return (
|
|
269
|
+
<VStack spacing={ 0 } justify="flex-start">
|
|
270
|
+
<OperatorSelector { ...commonProps } />
|
|
271
|
+
<SearchWidget { ...commonProps } />
|
|
272
|
+
<ResetFilter
|
|
273
|
+
{ ...commonProps }
|
|
274
|
+
addFilterRef={ addFilterRef }
|
|
275
|
+
/>
|
|
276
|
+
</VStack>
|
|
277
|
+
);
|
|
278
|
+
} }
|
|
279
|
+
/>
|
|
187
280
|
);
|
|
188
281
|
}
|
package/src/filters.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* WordPress dependencies
|
|
3
3
|
*/
|
|
4
|
-
import { memo } from '@wordpress/element';
|
|
4
|
+
import { memo, useRef } from '@wordpress/element';
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* Internal dependencies
|
|
@@ -9,24 +9,17 @@ import { memo } from '@wordpress/element';
|
|
|
9
9
|
import FilterSummary from './filter-summary';
|
|
10
10
|
import AddFilter from './add-filter';
|
|
11
11
|
import ResetFilters from './reset-filters';
|
|
12
|
-
import {
|
|
13
|
-
|
|
14
|
-
OPERATOR_IN,
|
|
15
|
-
OPERATOR_NOT_IN,
|
|
16
|
-
LAYOUT_LIST,
|
|
17
|
-
} from './constants';
|
|
12
|
+
import { sanitizeOperators } from './utils';
|
|
13
|
+
import { ENUMERATION_TYPE, OPERATOR_IN, OPERATOR_NOT_IN } from './constants';
|
|
18
14
|
|
|
19
|
-
const
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
);
|
|
27
|
-
};
|
|
28
|
-
|
|
29
|
-
const Filters = memo( function Filters( { fields, view, onChangeView } ) {
|
|
15
|
+
const Filters = memo( function Filters( {
|
|
16
|
+
fields,
|
|
17
|
+
view,
|
|
18
|
+
onChangeView,
|
|
19
|
+
openedFilter,
|
|
20
|
+
setOpenedFilter,
|
|
21
|
+
} ) {
|
|
22
|
+
const addFilterRef = useRef();
|
|
30
23
|
const filters = [];
|
|
31
24
|
fields.forEach( ( field ) => {
|
|
32
25
|
if ( ! field.type ) {
|
|
@@ -43,34 +36,50 @@ const Filters = memo( function Filters( { fields, view, onChangeView } ) {
|
|
|
43
36
|
if ( ! field.elements?.length ) {
|
|
44
37
|
return;
|
|
45
38
|
}
|
|
39
|
+
|
|
40
|
+
const isPrimary = !! field.filterBy?.isPrimary;
|
|
46
41
|
filters.push( {
|
|
47
42
|
field: field.id,
|
|
48
43
|
name: field.header,
|
|
49
44
|
elements: field.elements,
|
|
50
45
|
operators,
|
|
51
|
-
isVisible:
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
f.
|
|
56
|
-
|
|
57
|
-
|
|
46
|
+
isVisible:
|
|
47
|
+
isPrimary ||
|
|
48
|
+
view.filters.some(
|
|
49
|
+
( f ) =>
|
|
50
|
+
f.field === field.id &&
|
|
51
|
+
[ OPERATOR_IN, OPERATOR_NOT_IN ].includes(
|
|
52
|
+
f.operator
|
|
53
|
+
)
|
|
54
|
+
),
|
|
55
|
+
isPrimary,
|
|
58
56
|
} );
|
|
59
57
|
}
|
|
60
58
|
} );
|
|
61
|
-
|
|
59
|
+
// Sort filters by primary property. We need the primary filters to be first.
|
|
60
|
+
// Then we sort by name.
|
|
61
|
+
filters.sort( ( a, b ) => {
|
|
62
|
+
if ( a.isPrimary && ! b.isPrimary ) {
|
|
63
|
+
return -1;
|
|
64
|
+
}
|
|
65
|
+
if ( ! a.isPrimary && b.isPrimary ) {
|
|
66
|
+
return 1;
|
|
67
|
+
}
|
|
68
|
+
return a.name.localeCompare( b.name );
|
|
69
|
+
} );
|
|
62
70
|
const addFilter = (
|
|
63
71
|
<AddFilter
|
|
64
72
|
key="add-filter"
|
|
65
73
|
filters={ filters }
|
|
66
74
|
view={ view }
|
|
67
75
|
onChangeView={ onChangeView }
|
|
76
|
+
ref={ addFilterRef }
|
|
77
|
+
setOpenedFilter={ setOpenedFilter }
|
|
68
78
|
/>
|
|
69
79
|
);
|
|
70
80
|
const filterComponents = [
|
|
71
|
-
addFilter,
|
|
72
81
|
...filters.map( ( filter ) => {
|
|
73
|
-
if ( ! filter.isVisible
|
|
82
|
+
if ( ! filter.isVisible ) {
|
|
74
83
|
return null;
|
|
75
84
|
}
|
|
76
85
|
|
|
@@ -80,15 +89,19 @@ const Filters = memo( function Filters( { fields, view, onChangeView } ) {
|
|
|
80
89
|
filter={ filter }
|
|
81
90
|
view={ view }
|
|
82
91
|
onChangeView={ onChangeView }
|
|
92
|
+
addFilterRef={ addFilterRef }
|
|
93
|
+
openedFilter={ openedFilter }
|
|
83
94
|
/>
|
|
84
95
|
);
|
|
85
96
|
} ),
|
|
97
|
+
addFilter,
|
|
86
98
|
];
|
|
87
99
|
|
|
88
|
-
if ( filterComponents.length > 1
|
|
100
|
+
if ( filterComponents.length > 1 ) {
|
|
89
101
|
filterComponents.push(
|
|
90
102
|
<ResetFilters
|
|
91
103
|
key="reset-filters"
|
|
104
|
+
filters={ filters }
|
|
92
105
|
view={ view }
|
|
93
106
|
onChangeView={ onChangeView }
|
|
94
107
|
/>
|
package/src/reset-filters.js
CHANGED
|
@@ -4,10 +4,20 @@
|
|
|
4
4
|
import { Button } from '@wordpress/components';
|
|
5
5
|
import { __ } from '@wordpress/i18n';
|
|
6
6
|
|
|
7
|
-
export default function ResetFilter( { view, onChangeView } ) {
|
|
7
|
+
export default function ResetFilter( { filters, view, onChangeView } ) {
|
|
8
|
+
const isPrimary = ( field ) =>
|
|
9
|
+
filters.some(
|
|
10
|
+
( _filter ) => _filter.field === field && _filter.isPrimary
|
|
11
|
+
);
|
|
12
|
+
const isDisabled =
|
|
13
|
+
! view.search &&
|
|
14
|
+
! view.filters?.some(
|
|
15
|
+
( _filter ) =>
|
|
16
|
+
_filter.value !== undefined || ! isPrimary( _filter.field )
|
|
17
|
+
);
|
|
8
18
|
return (
|
|
9
19
|
<Button
|
|
10
|
-
disabled={
|
|
20
|
+
disabled={ isDisabled }
|
|
11
21
|
__experimentalIsFocusable
|
|
12
22
|
size="compact"
|
|
13
23
|
variant="tertiary"
|