@wordpress/dataviews 1.1.0 → 2.0.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 +27 -5
- package/README.md +33 -30
- package/build/add-filter.js +30 -22
- package/build/add-filter.js.map +1 -1
- package/build/bulk-actions-toolbar.js +187 -0
- package/build/bulk-actions-toolbar.js.map +1 -0
- package/build/bulk-actions.js +75 -62
- package/build/bulk-actions.js.map +1 -1
- package/build/constants.js +17 -10
- package/build/constants.js.map +1 -1
- package/build/dataviews.js +64 -50
- package/build/dataviews.js.map +1 -1
- package/build/filter-and-sort-data-view.js +2 -2
- package/build/filter-and-sort-data-view.js.map +1 -1
- package/build/filter-summary.js +106 -96
- package/build/filter-summary.js.map +1 -1
- package/build/filters.js +18 -17
- package/build/filters.js.map +1 -1
- package/build/index.js.map +1 -1
- package/build/item-actions.js +101 -69
- package/build/item-actions.js.map +1 -1
- package/build/layouts.js.map +1 -1
- package/build/lock-unlock.js.map +1 -1
- package/build/normalize-fields.js.map +1 -1
- package/build/pagination.js +66 -57
- package/build/pagination.js.map +1 -1
- package/build/reset-filters.js +9 -4
- package/build/reset-filters.js.map +1 -1
- package/build/search-widget.js +108 -89
- package/build/search-widget.js.map +1 -1
- package/build/search.js +13 -6
- package/build/search.js.map +1 -1
- package/build/single-selection-checkbox.js +6 -2
- package/build/single-selection-checkbox.js.map +1 -1
- package/build/types.js.map +1 -1
- package/build/utils.js +3 -15
- package/build/utils.js.map +1 -1
- package/build/view-actions.js +168 -120
- package/build/view-actions.js.map +1 -1
- package/build/view-grid.js +119 -106
- package/build/view-grid.js.map +1 -1
- package/build/view-list.js +217 -83
- package/build/view-list.js.map +1 -1
- package/build/view-table.js +227 -199
- package/build/view-table.js.map +1 -1
- package/build-module/add-filter.js +30 -22
- package/build-module/add-filter.js.map +1 -1
- package/build-module/bulk-actions-toolbar.js +182 -0
- package/build-module/bulk-actions-toolbar.js.map +1 -0
- package/build-module/bulk-actions.js +77 -62
- package/build-module/bulk-actions.js.map +1 -1
- package/build-module/constants.js +16 -9
- package/build-module/constants.js.map +1 -1
- package/build-module/dataviews.js +65 -50
- package/build-module/dataviews.js.map +1 -1
- package/build-module/filter-and-sort-data-view.js +2 -2
- package/build-module/filter-and-sort-data-view.js.map +1 -1
- package/build-module/filter-summary.js +107 -97
- package/build-module/filter-summary.js.map +1 -1
- package/build-module/filters.js +18 -17
- package/build-module/filters.js.map +1 -1
- package/build-module/index.js.map +1 -1
- package/build-module/item-actions.js +102 -71
- package/build-module/item-actions.js.map +1 -1
- package/build-module/layouts.js.map +1 -1
- package/build-module/lock-unlock.js.map +1 -1
- package/build-module/normalize-fields.js.map +1 -1
- package/build-module/pagination.js +67 -57
- package/build-module/pagination.js.map +1 -1
- package/build-module/reset-filters.js +9 -4
- package/build-module/reset-filters.js.map +1 -1
- package/build-module/search-widget.js +109 -89
- package/build-module/search-widget.js.map +1 -1
- package/build-module/search.js +13 -6
- package/build-module/search.js.map +1 -1
- package/build-module/single-selection-checkbox.js +6 -2
- package/build-module/single-selection-checkbox.js.map +1 -1
- package/build-module/types.js.map +1 -1
- package/build-module/utils.js +2 -13
- package/build-module/utils.js.map +1 -1
- package/build-module/view-actions.js +170 -121
- package/build-module/view-actions.js.map +1 -1
- package/build-module/view-grid.js +121 -106
- package/build-module/view-grid.js.map +1 -1
- package/build-module/view-list.js +219 -85
- package/build-module/view-list.js.map +1 -1
- package/build-module/view-table.js +230 -201
- package/build-module/view-table.js.map +1 -1
- package/build-style/style-rtl.css +168 -44
- package/build-style/style.css +168 -44
- package/build-types/add-filter.d.ts +11 -0
- package/build-types/add-filter.d.ts.map +1 -0
- package/build-types/bulk-actions-toolbar.d.ts +12 -0
- package/build-types/bulk-actions-toolbar.d.ts.map +1 -0
- package/build-types/bulk-actions.d.ts +14 -0
- package/build-types/bulk-actions.d.ts.map +1 -0
- package/build-types/constants.d.ts +19 -32
- package/build-types/constants.d.ts.map +1 -1
- package/build-types/dataviews.d.ts +22 -0
- package/build-types/dataviews.d.ts.map +1 -0
- package/build-types/filter-and-sort-data-view.d.ts +3 -3
- package/build-types/filter-and-sort-data-view.d.ts.map +1 -1
- package/build-types/filter-summary.d.ts +14 -0
- package/build-types/filter-summary.d.ts.map +1 -0
- package/build-types/filters.d.ts +13 -0
- package/build-types/filters.d.ts.map +1 -0
- package/build-types/index.d.ts +4 -0
- package/build-types/index.d.ts.map +1 -0
- package/build-types/item-actions.d.ts +35 -0
- package/build-types/item-actions.d.ts.map +1 -0
- package/build-types/layouts.d.ts +24 -0
- package/build-types/layouts.d.ts.map +1 -0
- package/build-types/lock-unlock.d.ts +2 -0
- package/build-types/lock-unlock.d.ts.map +1 -0
- package/build-types/normalize-fields.d.ts +2 -2
- package/build-types/normalize-fields.d.ts.map +1 -1
- package/build-types/pagination.d.ts +16 -0
- package/build-types/pagination.d.ts.map +1 -0
- package/build-types/reset-filters.d.ts +13 -0
- package/build-types/reset-filters.d.ts.map +1 -0
- package/build-types/search-widget.d.ts +10 -0
- package/build-types/search-widget.d.ts.map +1 -0
- package/build-types/search.d.ts +13 -0
- package/build-types/search.d.ts.map +1 -0
- package/build-types/single-selection-checkbox.d.ts +17 -0
- package/build-types/single-selection-checkbox.d.ts.map +1 -0
- package/build-types/stories/fixtures.d.ts +114 -0
- package/build-types/stories/fixtures.d.ts.map +1 -0
- package/build-types/stories/index.story.d.ts +15 -0
- package/build-types/stories/index.story.d.ts.map +1 -0
- package/build-types/types.d.ts +221 -21
- package/build-types/types.d.ts.map +1 -1
- package/build-types/utils.d.ts +3 -0
- package/build-types/utils.d.ts.map +1 -0
- package/build-types/view-actions.d.ts +12 -0
- package/build-types/view-actions.d.ts.map +1 -0
- package/build-types/view-grid.d.ts +4 -0
- package/build-types/view-grid.d.ts.map +1 -0
- package/build-types/view-list.d.ts +4 -0
- package/build-types/view-list.d.ts.map +1 -0
- package/build-types/view-table.d.ts +5 -0
- package/build-types/view-table.d.ts.map +1 -0
- package/package.json +12 -13
- package/src/{add-filter.js → add-filter.tsx} +17 -1
- package/src/bulk-actions-toolbar.tsx +272 -0
- package/src/{bulk-actions.js → bulk-actions.tsx} +77 -17
- package/src/constants.ts +12 -5
- package/src/{dataviews.js → dataviews.tsx} +54 -14
- package/src/filter-and-sort-data-view.ts +13 -8
- package/src/{filter-summary.js → filter-summary.tsx} +38 -9
- package/src/{filters.js → filters.tsx} +18 -6
- package/src/{item-actions.js → item-actions.tsx} +119 -30
- package/src/normalize-fields.ts +4 -2
- package/src/{pagination.js → pagination.tsx} +29 -8
- package/src/{reset-filters.js → reset-filters.tsx} +17 -2
- package/src/{search-widget.js → search-widget.tsx} +27 -7
- package/src/{search.js → search.tsx} +22 -5
- package/src/{single-selection-checkbox.js → single-selection-checkbox.tsx} +17 -2
- package/src/style.scss +166 -43
- package/src/types.ts +286 -21
- package/src/{utils.js → utils.ts} +5 -13
- package/src/{view-actions.js → view-actions.tsx} +105 -49
- package/src/{view-grid.js → view-grid.tsx} +31 -18
- package/src/view-list.tsx +410 -0
- package/src/{view-table.js → view-table.tsx} +99 -40
- package/tsconfig.json +3 -4
- package/tsconfig.tsbuildinfo +1 -1
- package/build/dropdown-menu-helper.js +0 -71
- package/build/dropdown-menu-helper.js.map +0 -1
- package/build-module/dropdown-menu-helper.js +0 -64
- package/build-module/dropdown-menu-helper.js.map +0 -1
- package/src/dropdown-menu-helper.js +0 -61
- package/src/view-list.js +0 -207
- /package/src/{index.js → index.ts} +0 -0
- /package/src/{layouts.js → layouts.ts} +0 -0
- /package/src/{lock-unlock.js → lock-unlock.ts} +0 -0
|
@@ -15,13 +15,13 @@ import {
|
|
|
15
15
|
OPERATOR_IS_NOT_ALL,
|
|
16
16
|
} from './constants';
|
|
17
17
|
import { normalizeFields } from './normalize-fields';
|
|
18
|
-
import type {
|
|
18
|
+
import type { Field, AnyItem, View } from './types';
|
|
19
19
|
|
|
20
20
|
function normalizeSearchInput( input = '' ) {
|
|
21
21
|
return removeAccents( input.trim().toLowerCase() );
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
const EMPTY_ARRAY:
|
|
24
|
+
const EMPTY_ARRAY: [] = [];
|
|
25
25
|
|
|
26
26
|
/**
|
|
27
27
|
* Applies the filtering, sorting and pagination to the raw data based on the view configuration.
|
|
@@ -32,11 +32,14 @@ const EMPTY_ARRAY: Data = [];
|
|
|
32
32
|
*
|
|
33
33
|
* @return Filtered, sorted and paginated data.
|
|
34
34
|
*/
|
|
35
|
-
export function filterSortAndPaginate(
|
|
36
|
-
data:
|
|
35
|
+
export function filterSortAndPaginate< Item extends AnyItem >(
|
|
36
|
+
data: Item[],
|
|
37
37
|
view: View,
|
|
38
|
-
fields: Field[]
|
|
39
|
-
): {
|
|
38
|
+
fields: Field< Item >[]
|
|
39
|
+
): {
|
|
40
|
+
data: Item[];
|
|
41
|
+
paginationInfo: { totalItems: number; totalPages: number };
|
|
42
|
+
} {
|
|
40
43
|
if ( ! data ) {
|
|
41
44
|
return {
|
|
42
45
|
data: EMPTY_ARRAY,
|
|
@@ -100,7 +103,9 @@ export function filterSortAndPaginate(
|
|
|
100
103
|
) {
|
|
101
104
|
filteredData = filteredData.filter( ( item ) => {
|
|
102
105
|
return filter.value.every( ( value: any ) => {
|
|
103
|
-
return field
|
|
106
|
+
return field
|
|
107
|
+
.getValue( { item } )
|
|
108
|
+
?.includes( value );
|
|
104
109
|
} );
|
|
105
110
|
} );
|
|
106
111
|
} else if (
|
|
@@ -111,7 +116,7 @@ export function filterSortAndPaginate(
|
|
|
111
116
|
return filter.value.every( ( value: any ) => {
|
|
112
117
|
return ! field
|
|
113
118
|
.getValue( { item } )
|
|
114
|
-
|
|
119
|
+
?.includes( value );
|
|
115
120
|
} );
|
|
116
121
|
} );
|
|
117
122
|
} else if ( filter.operator === OPERATOR_IS ) {
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* External dependencies
|
|
3
3
|
*/
|
|
4
|
-
import
|
|
4
|
+
import clsx from 'clsx';
|
|
5
|
+
import type { RefObject } from 'react';
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* WordPress dependencies
|
|
@@ -35,8 +36,30 @@ import {
|
|
|
35
36
|
OPERATOR_IS_ALL,
|
|
36
37
|
OPERATOR_IS_NOT_ALL,
|
|
37
38
|
} from './constants';
|
|
39
|
+
import type { Filter, NormalizedFilter, Operator, Option, View } from './types';
|
|
38
40
|
|
|
39
|
-
|
|
41
|
+
interface FilterTextProps {
|
|
42
|
+
activeElements: Option[];
|
|
43
|
+
filterInView?: Filter;
|
|
44
|
+
filter: NormalizedFilter;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
interface OperatorSelectorProps {
|
|
48
|
+
filter: NormalizedFilter;
|
|
49
|
+
view: View;
|
|
50
|
+
onChangeView: ( view: View ) => void;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
interface FilterSummaryProps extends OperatorSelectorProps {
|
|
54
|
+
addFilterRef: RefObject< HTMLButtonElement >;
|
|
55
|
+
openedFilter: string | null;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const FilterText = ( {
|
|
59
|
+
activeElements,
|
|
60
|
+
filterInView,
|
|
61
|
+
filter,
|
|
62
|
+
}: FilterTextProps ) => {
|
|
40
63
|
if ( activeElements === undefined || activeElements.length === 0 ) {
|
|
41
64
|
return filter.name;
|
|
42
65
|
}
|
|
@@ -125,7 +148,11 @@ const FilterText = ( { activeElements, filterInView, filter } ) => {
|
|
|
125
148
|
);
|
|
126
149
|
};
|
|
127
150
|
|
|
128
|
-
function OperatorSelector( {
|
|
151
|
+
function OperatorSelector( {
|
|
152
|
+
filter,
|
|
153
|
+
view,
|
|
154
|
+
onChangeView,
|
|
155
|
+
}: OperatorSelectorProps ) {
|
|
129
156
|
const operatorOptions = filter.operators?.map( ( operator ) => ( {
|
|
130
157
|
value: operator,
|
|
131
158
|
label: OPERATORS[ operator ]?.label,
|
|
@@ -150,13 +177,14 @@ function OperatorSelector( { filter, view, onChangeView } ) {
|
|
|
150
177
|
value={ value }
|
|
151
178
|
options={ operatorOptions }
|
|
152
179
|
onChange={ ( newValue ) => {
|
|
180
|
+
const operator = newValue as Operator;
|
|
153
181
|
const newFilters = currentFilter
|
|
154
182
|
? [
|
|
155
183
|
...view.filters.map( ( _filter ) => {
|
|
156
184
|
if ( _filter.field === filter.field ) {
|
|
157
185
|
return {
|
|
158
186
|
..._filter,
|
|
159
|
-
operator
|
|
187
|
+
operator,
|
|
160
188
|
};
|
|
161
189
|
}
|
|
162
190
|
return _filter;
|
|
@@ -166,7 +194,8 @@ function OperatorSelector( { filter, view, onChangeView } ) {
|
|
|
166
194
|
...view.filters,
|
|
167
195
|
{
|
|
168
196
|
field: filter.field,
|
|
169
|
-
operator
|
|
197
|
+
operator,
|
|
198
|
+
value: undefined,
|
|
170
199
|
},
|
|
171
200
|
];
|
|
172
201
|
onChangeView( {
|
|
@@ -188,8 +217,8 @@ export default function FilterSummary( {
|
|
|
188
217
|
addFilterRef,
|
|
189
218
|
openedFilter,
|
|
190
219
|
...commonProps
|
|
191
|
-
} ) {
|
|
192
|
-
const toggleRef = useRef();
|
|
220
|
+
}: FilterSummaryProps ) {
|
|
221
|
+
const toggleRef = useRef< HTMLDivElement >( null );
|
|
193
222
|
const { filter, view, onChangeView } = commonProps;
|
|
194
223
|
const filterInView = view.filters.find( ( f ) => f.field === filter.field );
|
|
195
224
|
const activeElements = filter.elements.filter( ( element ) => {
|
|
@@ -220,7 +249,7 @@ export default function FilterSummary( {
|
|
|
220
249
|
placement="top"
|
|
221
250
|
>
|
|
222
251
|
<div
|
|
223
|
-
className={
|
|
252
|
+
className={ clsx(
|
|
224
253
|
'dataviews-filter-summary__chip',
|
|
225
254
|
{
|
|
226
255
|
'has-reset': canResetOrRemove,
|
|
@@ -253,7 +282,7 @@ export default function FilterSummary( {
|
|
|
253
282
|
placement="top"
|
|
254
283
|
>
|
|
255
284
|
<button
|
|
256
|
-
className={
|
|
285
|
+
className={ clsx(
|
|
257
286
|
'dataviews-filter-summary__chip-remove',
|
|
258
287
|
{ 'has-values': hasValues }
|
|
259
288
|
) }
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
* WordPress dependencies
|
|
3
3
|
*/
|
|
4
4
|
import { memo, useRef } from '@wordpress/element';
|
|
5
|
+
import { __experimentalHStack as HStack } from '@wordpress/components';
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* Internal dependencies
|
|
@@ -11,17 +12,25 @@ import AddFilter from './add-filter';
|
|
|
11
12
|
import ResetFilters from './reset-filters';
|
|
12
13
|
import { sanitizeOperators } from './utils';
|
|
13
14
|
import { ALL_OPERATORS, OPERATOR_IS, OPERATOR_IS_NOT } from './constants';
|
|
14
|
-
import {
|
|
15
|
+
import type { AnyItem, NormalizedField, NormalizedFilter, View } from './types';
|
|
15
16
|
|
|
16
|
-
|
|
17
|
+
interface FiltersProps< Item extends AnyItem > {
|
|
18
|
+
fields: NormalizedField< Item >[];
|
|
19
|
+
view: View;
|
|
20
|
+
onChangeView: ( view: View ) => void;
|
|
21
|
+
openedFilter: string | null;
|
|
22
|
+
setOpenedFilter: ( openedFilter: string | null ) => void;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function _Filters< Item extends AnyItem >( {
|
|
17
26
|
fields,
|
|
18
27
|
view,
|
|
19
28
|
onChangeView,
|
|
20
29
|
openedFilter,
|
|
21
30
|
setOpenedFilter,
|
|
22
|
-
} ) {
|
|
23
|
-
const addFilterRef = useRef();
|
|
24
|
-
const filters = [];
|
|
31
|
+
}: FiltersProps< Item > ) {
|
|
32
|
+
const addFilterRef = useRef< HTMLButtonElement >( null );
|
|
33
|
+
const filters: NormalizedFilter[] = [];
|
|
25
34
|
fields.forEach( ( field ) => {
|
|
26
35
|
if ( ! field.elements?.length ) {
|
|
27
36
|
return;
|
|
@@ -108,6 +117,9 @@ const Filters = memo( function Filters( {
|
|
|
108
117
|
{ filterComponents }
|
|
109
118
|
</HStack>
|
|
110
119
|
);
|
|
111
|
-
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// A type assertion is used here to keep the type argument.
|
|
123
|
+
const Filters = memo( _Filters ) as typeof _Filters;
|
|
112
124
|
|
|
113
125
|
export default Filters;
|
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* External dependencies
|
|
3
|
+
*/
|
|
4
|
+
import type { MouseEventHandler, ReactElement } from 'react';
|
|
5
|
+
|
|
1
6
|
/**
|
|
2
7
|
* WordPress dependencies
|
|
3
8
|
*/
|
|
@@ -15,6 +20,7 @@ import { moreVertical } from '@wordpress/icons';
|
|
|
15
20
|
* Internal dependencies
|
|
16
21
|
*/
|
|
17
22
|
import { unlock } from './lock-unlock';
|
|
23
|
+
import type { Action, ActionModal as ActionModalType, AnyItem } from './types';
|
|
18
24
|
|
|
19
25
|
const {
|
|
20
26
|
DropdownMenuV2: DropdownMenu,
|
|
@@ -24,10 +30,51 @@ const {
|
|
|
24
30
|
kebabCase,
|
|
25
31
|
} = unlock( componentsPrivateApis );
|
|
26
32
|
|
|
27
|
-
|
|
33
|
+
export interface ActionTriggerProps< Item extends AnyItem > {
|
|
34
|
+
action: Action< Item >;
|
|
35
|
+
onClick: MouseEventHandler;
|
|
36
|
+
isBusy?: boolean;
|
|
37
|
+
items: Item[];
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
interface ActionModalProps< Item extends AnyItem > {
|
|
41
|
+
action: ActionModalType< Item >;
|
|
42
|
+
items: Item[];
|
|
43
|
+
closeModal?: () => void;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
interface ActionWithModalProps< Item extends AnyItem >
|
|
47
|
+
extends ActionModalProps< Item > {
|
|
48
|
+
ActionTrigger: ( props: ActionTriggerProps< Item > ) => ReactElement;
|
|
49
|
+
isBusy?: boolean;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
interface ActionsDropdownMenuGroupProps< Item extends AnyItem > {
|
|
53
|
+
actions: Action< Item >[];
|
|
54
|
+
item: Item;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
interface ItemActionsProps< Item extends AnyItem > {
|
|
58
|
+
item: Item;
|
|
59
|
+
actions: Action< Item >[];
|
|
60
|
+
isCompact?: boolean;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
interface CompactItemActionsProps< Item extends AnyItem > {
|
|
64
|
+
item: Item;
|
|
65
|
+
actions: Action< Item >[];
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function ButtonTrigger< Item extends AnyItem >( {
|
|
69
|
+
action,
|
|
70
|
+
onClick,
|
|
71
|
+
items,
|
|
72
|
+
}: ActionTriggerProps< Item > ) {
|
|
73
|
+
const label =
|
|
74
|
+
typeof action.label === 'string' ? action.label : action.label( items );
|
|
28
75
|
return (
|
|
29
76
|
<Button
|
|
30
|
-
label={
|
|
77
|
+
label={ label }
|
|
31
78
|
icon={ action.icon }
|
|
32
79
|
isDestructive={ action.isDestructive }
|
|
33
80
|
size="compact"
|
|
@@ -36,58 +83,91 @@ function ButtonTrigger( { action, onClick } ) {
|
|
|
36
83
|
);
|
|
37
84
|
}
|
|
38
85
|
|
|
39
|
-
function DropdownMenuItemTrigger
|
|
86
|
+
function DropdownMenuItemTrigger< Item extends AnyItem >( {
|
|
87
|
+
action,
|
|
88
|
+
onClick,
|
|
89
|
+
items,
|
|
90
|
+
}: ActionTriggerProps< Item > ) {
|
|
91
|
+
const label =
|
|
92
|
+
typeof action.label === 'string' ? action.label : action.label( items );
|
|
40
93
|
return (
|
|
41
94
|
<DropdownMenuItem
|
|
42
95
|
onClick={ onClick }
|
|
43
|
-
hideOnClick={ !
|
|
96
|
+
hideOnClick={ ! ( 'RenderModal' in action ) }
|
|
44
97
|
>
|
|
45
|
-
<DropdownMenuItemLabel>{
|
|
98
|
+
<DropdownMenuItemLabel>{ label }</DropdownMenuItemLabel>
|
|
46
99
|
</DropdownMenuItem>
|
|
47
100
|
);
|
|
48
101
|
}
|
|
49
102
|
|
|
50
|
-
function
|
|
103
|
+
export function ActionModal< Item extends AnyItem >( {
|
|
104
|
+
action,
|
|
105
|
+
items,
|
|
106
|
+
closeModal,
|
|
107
|
+
}: ActionModalProps< Item > ) {
|
|
108
|
+
const label =
|
|
109
|
+
typeof action.label === 'string' ? action.label : action.label( items );
|
|
110
|
+
return (
|
|
111
|
+
<Modal
|
|
112
|
+
title={ action.modalHeader || label }
|
|
113
|
+
__experimentalHideHeader={ !! action.hideModalHeader }
|
|
114
|
+
onRequestClose={ closeModal ?? ( () => {} ) }
|
|
115
|
+
overlayClassName={ `dataviews-action-modal dataviews-action-modal__${ kebabCase(
|
|
116
|
+
action.id
|
|
117
|
+
) }` }
|
|
118
|
+
>
|
|
119
|
+
<action.RenderModal
|
|
120
|
+
items={ items }
|
|
121
|
+
closeModal={ closeModal }
|
|
122
|
+
onActionStart={ action.onActionStart }
|
|
123
|
+
onActionPerformed={ action.onActionPerformed }
|
|
124
|
+
/>
|
|
125
|
+
</Modal>
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export function ActionWithModal< Item extends AnyItem >( {
|
|
130
|
+
action,
|
|
131
|
+
items,
|
|
132
|
+
ActionTrigger,
|
|
133
|
+
isBusy,
|
|
134
|
+
}: ActionWithModalProps< Item > ) {
|
|
51
135
|
const [ isModalOpen, setIsModalOpen ] = useState( false );
|
|
52
136
|
const actionTriggerProps = {
|
|
53
137
|
action,
|
|
54
|
-
onClick: () =>
|
|
138
|
+
onClick: () => {
|
|
139
|
+
setIsModalOpen( true );
|
|
140
|
+
},
|
|
141
|
+
items,
|
|
142
|
+
isBusy,
|
|
55
143
|
};
|
|
56
|
-
const { RenderModal, hideModalHeader } = action;
|
|
57
144
|
return (
|
|
58
145
|
<>
|
|
59
146
|
<ActionTrigger { ...actionTriggerProps } />
|
|
60
147
|
{ isModalOpen && (
|
|
61
|
-
<
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
} }
|
|
67
|
-
overlayClassName={ `dataviews-action-modal dataviews-action-modal__${ kebabCase(
|
|
68
|
-
action.id
|
|
69
|
-
) }` }
|
|
70
|
-
>
|
|
71
|
-
<RenderModal
|
|
72
|
-
items={ [ item ] }
|
|
73
|
-
closeModal={ () => setIsModalOpen( false ) }
|
|
74
|
-
/>
|
|
75
|
-
</Modal>
|
|
148
|
+
<ActionModal
|
|
149
|
+
action={ action }
|
|
150
|
+
items={ items }
|
|
151
|
+
closeModal={ () => setIsModalOpen( false ) }
|
|
152
|
+
/>
|
|
76
153
|
) }
|
|
77
154
|
</>
|
|
78
155
|
);
|
|
79
156
|
}
|
|
80
157
|
|
|
81
|
-
function ActionsDropdownMenuGroup
|
|
158
|
+
export function ActionsDropdownMenuGroup< Item extends AnyItem >( {
|
|
159
|
+
actions,
|
|
160
|
+
item,
|
|
161
|
+
}: ActionsDropdownMenuGroupProps< Item > ) {
|
|
82
162
|
return (
|
|
83
163
|
<DropdownMenuGroup>
|
|
84
164
|
{ actions.map( ( action ) => {
|
|
85
|
-
if (
|
|
165
|
+
if ( 'RenderModal' in action ) {
|
|
86
166
|
return (
|
|
87
167
|
<ActionWithModal
|
|
88
168
|
key={ action.id }
|
|
89
169
|
action={ action }
|
|
90
|
-
|
|
170
|
+
items={ [ item ] }
|
|
91
171
|
ActionTrigger={ DropdownMenuItemTrigger }
|
|
92
172
|
/>
|
|
93
173
|
);
|
|
@@ -97,6 +177,7 @@ function ActionsDropdownMenuGroup( { actions, item } ) {
|
|
|
97
177
|
key={ action.id }
|
|
98
178
|
action={ action }
|
|
99
179
|
onClick={ () => action.callback( [ item ] ) }
|
|
180
|
+
items={ [ item ] }
|
|
100
181
|
/>
|
|
101
182
|
);
|
|
102
183
|
} ) }
|
|
@@ -104,7 +185,11 @@ function ActionsDropdownMenuGroup( { actions, item } ) {
|
|
|
104
185
|
);
|
|
105
186
|
}
|
|
106
187
|
|
|
107
|
-
export default function ItemActions
|
|
188
|
+
export default function ItemActions< Item extends AnyItem >( {
|
|
189
|
+
item,
|
|
190
|
+
actions,
|
|
191
|
+
isCompact,
|
|
192
|
+
}: ItemActionsProps< Item > ) {
|
|
108
193
|
const { primaryActions, eligibleActions } = useMemo( () => {
|
|
109
194
|
// If an action is eligible for all items, doesn't need
|
|
110
195
|
// to provide the `isEligible` function.
|
|
@@ -134,12 +219,12 @@ export default function ItemActions( { item, actions, isCompact } ) {
|
|
|
134
219
|
>
|
|
135
220
|
{ !! primaryActions.length &&
|
|
136
221
|
primaryActions.map( ( action ) => {
|
|
137
|
-
if (
|
|
222
|
+
if ( 'RenderModal' in action ) {
|
|
138
223
|
return (
|
|
139
224
|
<ActionWithModal
|
|
140
225
|
key={ action.id }
|
|
141
226
|
action={ action }
|
|
142
|
-
|
|
227
|
+
items={ [ item ] }
|
|
143
228
|
ActionTrigger={ ButtonTrigger }
|
|
144
229
|
/>
|
|
145
230
|
);
|
|
@@ -149,6 +234,7 @@ export default function ItemActions( { item, actions, isCompact } ) {
|
|
|
149
234
|
key={ action.id }
|
|
150
235
|
action={ action }
|
|
151
236
|
onClick={ () => action.callback( [ item ] ) }
|
|
237
|
+
items={ [ item ] }
|
|
152
238
|
/>
|
|
153
239
|
);
|
|
154
240
|
} ) }
|
|
@@ -157,7 +243,10 @@ export default function ItemActions( { item, actions, isCompact } ) {
|
|
|
157
243
|
);
|
|
158
244
|
}
|
|
159
245
|
|
|
160
|
-
function CompactItemActions
|
|
246
|
+
function CompactItemActions< Item extends AnyItem >( {
|
|
247
|
+
item,
|
|
248
|
+
actions,
|
|
249
|
+
}: CompactItemActionsProps< Item > ) {
|
|
161
250
|
return (
|
|
162
251
|
<DropdownMenu
|
|
163
252
|
trigger={
|
package/src/normalize-fields.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Internal dependencies
|
|
3
3
|
*/
|
|
4
|
-
import type { Field, NormalizedField } from './types';
|
|
4
|
+
import type { Field, AnyItem, NormalizedField } from './types';
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* Apply default values and normalize the fields config.
|
|
@@ -9,7 +9,9 @@ import type { Field, NormalizedField } from './types';
|
|
|
9
9
|
* @param fields Fields config.
|
|
10
10
|
* @return Normalized fields config.
|
|
11
11
|
*/
|
|
12
|
-
export function normalizeFields
|
|
12
|
+
export function normalizeFields< Item extends AnyItem >(
|
|
13
|
+
fields: Field< Item >[]
|
|
14
|
+
): NormalizedField< Item >[] {
|
|
13
15
|
return fields.map( ( field ) => {
|
|
14
16
|
const getValue = field.getValue || ( ( { item } ) => item[ field.id ] );
|
|
15
17
|
|
|
@@ -10,14 +10,29 @@ import { createInterpolateElement, memo } from '@wordpress/element';
|
|
|
10
10
|
import { sprintf, __, _x } from '@wordpress/i18n';
|
|
11
11
|
import { chevronRight, chevronLeft } from '@wordpress/icons';
|
|
12
12
|
|
|
13
|
+
/**
|
|
14
|
+
* Internal dependencies
|
|
15
|
+
*/
|
|
16
|
+
import type { View } from './types';
|
|
17
|
+
|
|
18
|
+
interface PaginationProps {
|
|
19
|
+
view: View;
|
|
20
|
+
onChangeView: ( view: View ) => void;
|
|
21
|
+
paginationInfo: {
|
|
22
|
+
totalItems: number;
|
|
23
|
+
totalPages: number;
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
|
|
13
27
|
const Pagination = memo( function Pagination( {
|
|
14
28
|
view,
|
|
15
29
|
onChangeView,
|
|
16
30
|
paginationInfo: { totalItems = 0, totalPages },
|
|
17
|
-
} ) {
|
|
31
|
+
}: PaginationProps ) {
|
|
18
32
|
if ( ! totalItems || ! totalPages ) {
|
|
19
33
|
return null;
|
|
20
34
|
}
|
|
35
|
+
const currentPage = view.page ?? 1;
|
|
21
36
|
return (
|
|
22
37
|
!! totalItems &&
|
|
23
38
|
totalPages !== 1 && (
|
|
@@ -43,12 +58,15 @@ const Pagination = memo( function Pagination( {
|
|
|
43
58
|
CurrentPageControl: (
|
|
44
59
|
<SelectControl
|
|
45
60
|
aria-label={ __( 'Current page' ) }
|
|
46
|
-
value={ view.page }
|
|
61
|
+
value={ view.page?.toString() }
|
|
47
62
|
options={ Array.from(
|
|
48
63
|
Array( totalPages )
|
|
49
64
|
).map( ( _, i ) => {
|
|
50
65
|
const page = i + 1;
|
|
51
|
-
return {
|
|
66
|
+
return {
|
|
67
|
+
value: page.toString(),
|
|
68
|
+
label: page.toString(),
|
|
69
|
+
};
|
|
52
70
|
} ) }
|
|
53
71
|
onChange={ ( newValue ) => {
|
|
54
72
|
onChangeView( {
|
|
@@ -56,7 +74,7 @@ const Pagination = memo( function Pagination( {
|
|
|
56
74
|
page: +newValue,
|
|
57
75
|
} );
|
|
58
76
|
} }
|
|
59
|
-
size=
|
|
77
|
+
size="compact"
|
|
60
78
|
__nextHasNoMarginBottom
|
|
61
79
|
/>
|
|
62
80
|
),
|
|
@@ -66,9 +84,12 @@ const Pagination = memo( function Pagination( {
|
|
|
66
84
|
<HStack expanded={ false } spacing={ 1 }>
|
|
67
85
|
<Button
|
|
68
86
|
onClick={ () =>
|
|
69
|
-
onChangeView( {
|
|
87
|
+
onChangeView( {
|
|
88
|
+
...view,
|
|
89
|
+
page: currentPage - 1,
|
|
90
|
+
} )
|
|
70
91
|
}
|
|
71
|
-
disabled={
|
|
92
|
+
disabled={ currentPage === 1 }
|
|
72
93
|
__experimentalIsFocusable
|
|
73
94
|
label={ __( 'Previous page' ) }
|
|
74
95
|
icon={ chevronLeft }
|
|
@@ -78,9 +99,9 @@ const Pagination = memo( function Pagination( {
|
|
|
78
99
|
/>
|
|
79
100
|
<Button
|
|
80
101
|
onClick={ () =>
|
|
81
|
-
onChangeView( { ...view, page:
|
|
102
|
+
onChangeView( { ...view, page: currentPage + 1 } )
|
|
82
103
|
}
|
|
83
|
-
disabled={
|
|
104
|
+
disabled={ currentPage >= totalPages }
|
|
84
105
|
__experimentalIsFocusable
|
|
85
106
|
label={ __( 'Next page' ) }
|
|
86
107
|
icon={ chevronRight }
|
|
@@ -4,8 +4,23 @@
|
|
|
4
4
|
import { Button } from '@wordpress/components';
|
|
5
5
|
import { __ } from '@wordpress/i18n';
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
/**
|
|
8
|
+
* Internal dependencies
|
|
9
|
+
*/
|
|
10
|
+
import type { NormalizedFilter, View } from './types';
|
|
11
|
+
|
|
12
|
+
interface ResetFilterProps {
|
|
13
|
+
filters: NormalizedFilter[];
|
|
14
|
+
view: View;
|
|
15
|
+
onChangeView: ( view: View ) => void;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export default function ResetFilter( {
|
|
19
|
+
filters,
|
|
20
|
+
view,
|
|
21
|
+
onChangeView,
|
|
22
|
+
}: ResetFilterProps ) {
|
|
23
|
+
const isPrimary = ( field: string ) =>
|
|
9
24
|
filters.some(
|
|
10
25
|
( _filter ) => _filter.field === field && _filter.isPrimary
|
|
11
26
|
);
|
|
@@ -22,6 +22,7 @@ import { SVG, Circle } from '@wordpress/primitives';
|
|
|
22
22
|
* Internal dependencies
|
|
23
23
|
*/
|
|
24
24
|
import { unlock } from './lock-unlock';
|
|
25
|
+
import type { Filter, NormalizedFilter, View } from './types';
|
|
25
26
|
|
|
26
27
|
const {
|
|
27
28
|
CompositeV2: Composite,
|
|
@@ -29,6 +30,12 @@ const {
|
|
|
29
30
|
useCompositeStoreV2: useCompositeStore,
|
|
30
31
|
} = unlock( componentsPrivateApis );
|
|
31
32
|
|
|
33
|
+
interface SearchWidgetProps {
|
|
34
|
+
view: View;
|
|
35
|
+
filter: NormalizedFilter;
|
|
36
|
+
onChangeView: ( view: View ) => void;
|
|
37
|
+
}
|
|
38
|
+
|
|
32
39
|
const radioCheck = (
|
|
33
40
|
<SVG xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
|
34
41
|
<Circle cx={ 12 } cy={ 12 } r={ 3 }></Circle>
|
|
@@ -39,8 +46,11 @@ function normalizeSearchInput( input = '' ) {
|
|
|
39
46
|
return removeAccents( input.trim().toLowerCase() );
|
|
40
47
|
}
|
|
41
48
|
|
|
42
|
-
const EMPTY_ARRAY = [];
|
|
43
|
-
const getCurrentValue = (
|
|
49
|
+
const EMPTY_ARRAY: [] = [];
|
|
50
|
+
const getCurrentValue = (
|
|
51
|
+
filterDefinition: NormalizedFilter,
|
|
52
|
+
currentFilter?: Filter
|
|
53
|
+
) => {
|
|
44
54
|
if ( filterDefinition.singleSelection ) {
|
|
45
55
|
return currentFilter?.value;
|
|
46
56
|
}
|
|
@@ -56,7 +66,11 @@ const getCurrentValue = ( filterDefinition, currentFilter ) => {
|
|
|
56
66
|
return EMPTY_ARRAY;
|
|
57
67
|
};
|
|
58
68
|
|
|
59
|
-
const getNewValue = (
|
|
69
|
+
const getNewValue = (
|
|
70
|
+
filterDefinition: NormalizedFilter,
|
|
71
|
+
currentFilter: Filter | undefined,
|
|
72
|
+
value: any
|
|
73
|
+
) => {
|
|
60
74
|
if ( filterDefinition.singleSelection ) {
|
|
61
75
|
return value;
|
|
62
76
|
}
|
|
@@ -70,7 +84,7 @@ const getNewValue = ( filterDefinition, currentFilter, value ) => {
|
|
|
70
84
|
return [ value ];
|
|
71
85
|
};
|
|
72
86
|
|
|
73
|
-
function ListBox( { view, filter, onChangeView } ) {
|
|
87
|
+
function ListBox( { view, filter, onChangeView }: SearchWidgetProps ) {
|
|
74
88
|
const compositeStore = useCompositeStore( {
|
|
75
89
|
virtualFocus: true,
|
|
76
90
|
focusLoop: true,
|
|
@@ -184,7 +198,7 @@ function ListBox( { view, filter, onChangeView } ) {
|
|
|
184
198
|
);
|
|
185
199
|
}
|
|
186
200
|
|
|
187
|
-
function ComboboxList( { view, filter, onChangeView } ) {
|
|
201
|
+
function ComboboxList( { view, filter, onChangeView }: SearchWidgetProps ) {
|
|
188
202
|
const [ searchValue, setSearchValue ] = useState( '' );
|
|
189
203
|
const deferredSearchValue = useDeferredValue( searchValue );
|
|
190
204
|
const currentFilter = view.filters.find(
|
|
@@ -234,7 +248,13 @@ function ComboboxList( { view, filter, onChangeView } ) {
|
|
|
234
248
|
setValue={ setSearchValue }
|
|
235
249
|
>
|
|
236
250
|
<div className="dataviews-search-widget-filter-combobox__wrapper">
|
|
237
|
-
<Ariakit.ComboboxLabel
|
|
251
|
+
<Ariakit.ComboboxLabel
|
|
252
|
+
render={
|
|
253
|
+
<VisuallyHidden>
|
|
254
|
+
{ __( 'Search items' ) }
|
|
255
|
+
</VisuallyHidden>
|
|
256
|
+
}
|
|
257
|
+
>
|
|
238
258
|
{ __( 'Search items' ) }
|
|
239
259
|
</Ariakit.ComboboxLabel>
|
|
240
260
|
<Ariakit.Combobox
|
|
@@ -290,7 +310,7 @@ function ComboboxList( { view, filter, onChangeView } ) {
|
|
|
290
310
|
);
|
|
291
311
|
}
|
|
292
312
|
|
|
293
|
-
export default function SearchWidget( props ) {
|
|
313
|
+
export default function SearchWidget( props: SearchWidgetProps ) {
|
|
294
314
|
const Widget = props.filter.elements.length > 10 ? ComboboxList : ListBox;
|
|
295
315
|
return <Widget { ...props } />;
|
|
296
316
|
}
|