@wordpress/dataviews 2.1.0 → 3.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 +24 -5
- package/README.md +5 -5
- package/build/add-filter.js +1 -1
- package/build/add-filter.js.map +1 -1
- package/build/bulk-actions-toolbar.js +5 -2
- package/build/bulk-actions-toolbar.js.map +1 -1
- package/build/bulk-actions.js +11 -21
- package/build/bulk-actions.js.map +1 -1
- package/build/dataform.js +78 -0
- package/build/dataform.js.map +1 -0
- package/build/dataviews.js +26 -31
- package/build/dataviews.js.map +1 -1
- package/build/filter-and-sort-data-view.js +4 -1
- package/build/filter-and-sort-data-view.js.map +1 -1
- package/build/filter-summary.js +6 -5
- package/build/filter-summary.js.map +1 -1
- package/build/filters.js +1 -1
- package/build/filters.js.map +1 -1
- package/build/index.js +7 -0
- package/build/index.js.map +1 -1
- package/build/item-actions.js +17 -6
- package/build/item-actions.js.map +1 -1
- package/build/lock-unlock.js +1 -1
- package/build/lock-unlock.js.map +1 -1
- package/build/normalize-fields.js.map +1 -1
- package/build/pagination.js +2 -2
- package/build/pagination.js.map +1 -1
- package/build/private-types.js +6 -0
- package/build/private-types.js.map +1 -0
- package/build/reset-filters.js +1 -1
- package/build/reset-filters.js.map +1 -1
- package/build/search-widget.js +8 -6
- package/build/search-widget.js.map +1 -1
- package/build/single-selection-checkbox.js +5 -16
- package/build/single-selection-checkbox.js.map +1 -1
- package/build/types.js.map +1 -1
- package/build/utils.js.map +1 -1
- package/build/view-actions.js +76 -65
- package/build/view-actions.js.map +1 -1
- package/build/view-grid.js +7 -19
- package/build/view-grid.js.map +1 -1
- package/build/view-list.js +15 -8
- package/build/view-list.js.map +1 -1
- package/build/view-table.js +22 -25
- package/build/view-table.js.map +1 -1
- package/build-module/add-filter.js +1 -1
- package/build-module/add-filter.js.map +1 -1
- package/build-module/bulk-actions-toolbar.js +5 -2
- package/build-module/bulk-actions-toolbar.js.map +1 -1
- package/build-module/bulk-actions.js +12 -22
- package/build-module/bulk-actions.js.map +1 -1
- package/build-module/dataform.js +72 -0
- package/build-module/dataform.js.map +1 -0
- package/build-module/dataviews.js +24 -31
- package/build-module/dataviews.js.map +1 -1
- package/build-module/filter-and-sort-data-view.js +4 -1
- package/build-module/filter-and-sort-data-view.js.map +1 -1
- package/build-module/filter-summary.js +6 -5
- package/build-module/filter-summary.js.map +1 -1
- package/build-module/filters.js +1 -1
- package/build-module/filters.js.map +1 -1
- package/build-module/index.js +1 -0
- package/build-module/index.js.map +1 -1
- package/build-module/item-actions.js +17 -6
- package/build-module/item-actions.js.map +1 -1
- package/build-module/lock-unlock.js +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 +2 -2
- package/build-module/pagination.js.map +1 -1
- package/build-module/private-types.js +2 -0
- package/build-module/private-types.js.map +1 -0
- package/build-module/reset-filters.js +1 -1
- package/build-module/reset-filters.js.map +1 -1
- package/build-module/search-widget.js +8 -6
- package/build-module/search-widget.js.map +1 -1
- package/build-module/single-selection-checkbox.js +5 -16
- package/build-module/single-selection-checkbox.js.map +1 -1
- package/build-module/types.js.map +1 -1
- package/build-module/utils.js.map +1 -1
- package/build-module/view-actions.js +80 -68
- package/build-module/view-actions.js.map +1 -1
- package/build-module/view-grid.js +7 -19
- package/build-module/view-grid.js.map +1 -1
- package/build-module/view-list.js +15 -8
- package/build-module/view-list.js.map +1 -1
- package/build-module/view-table.js +22 -25
- package/build-module/view-table.js.map +1 -1
- package/build-style/style-rtl.css +8 -24
- package/build-style/style.css +8 -24
- package/build-types/bulk-actions-toolbar.d.ts +5 -4
- package/build-types/bulk-actions-toolbar.d.ts.map +1 -1
- package/build-types/bulk-actions.d.ts +7 -6
- package/build-types/bulk-actions.d.ts.map +1 -1
- package/build-types/dataform.d.ts +17 -0
- package/build-types/dataform.d.ts.map +1 -0
- package/build-types/dataviews.d.ts +15 -6
- package/build-types/dataviews.d.ts.map +1 -1
- package/build-types/filter-and-sort-data-view.d.ts +2 -2
- package/build-types/filter-and-sort-data-view.d.ts.map +1 -1
- package/build-types/filter-summary.d.ts.map +1 -1
- package/build-types/filters.d.ts +3 -3
- package/build-types/filters.d.ts.map +1 -1
- package/build-types/index.d.ts +1 -0
- package/build-types/index.d.ts.map +1 -1
- package/build-types/item-actions.d.ts +10 -10
- package/build-types/item-actions.d.ts.map +1 -1
- package/build-types/normalize-fields.d.ts +2 -2
- package/build-types/normalize-fields.d.ts.map +1 -1
- package/build-types/private-types.d.ts +3 -0
- package/build-types/private-types.d.ts.map +1 -0
- package/build-types/single-selection-checkbox.d.ts +5 -5
- package/build-types/single-selection-checkbox.d.ts.map +1 -1
- package/build-types/stories/fixtures.d.ts +14 -1
- package/build-types/stories/fixtures.d.ts.map +1 -1
- package/build-types/stories/index.story.d.ts +15 -1
- package/build-types/stories/index.story.d.ts.map +1 -1
- package/build-types/types.d.ts +73 -38
- package/build-types/types.d.ts.map +1 -1
- package/build-types/utils.d.ts +2 -2
- package/build-types/utils.d.ts.map +1 -1
- package/build-types/view-actions.d.ts +4 -4
- package/build-types/view-actions.d.ts.map +1 -1
- package/build-types/view-grid.d.ts +2 -2
- package/build-types/view-grid.d.ts.map +1 -1
- package/build-types/view-list.d.ts +2 -2
- package/build-types/view-list.d.ts.map +1 -1
- package/build-types/view-table.d.ts +2 -2
- package/build-types/view-table.d.ts.map +1 -1
- package/package.json +10 -9
- package/src/add-filter.tsx +1 -1
- package/src/bulk-actions-toolbar.tsx +18 -14
- package/src/bulk-actions.tsx +31 -45
- package/src/dataform.tsx +106 -0
- package/src/dataviews.tsx +55 -60
- package/src/filter-and-sort-data-view.ts +13 -3
- package/src/filter-summary.tsx +18 -12
- package/src/filters.tsx +4 -4
- package/src/index.ts +1 -0
- package/src/item-actions.tsx +27 -24
- package/src/lock-unlock.ts +1 -1
- package/src/normalize-fields.ts +5 -3
- package/src/pagination.tsx +2 -2
- package/src/private-types.tsx +2 -0
- package/src/reset-filters.tsx +1 -1
- package/src/search-widget.tsx +6 -6
- package/src/single-selection-checkbox.tsx +14 -29
- package/src/stories/fixtures.js +17 -1
- package/src/stories/index.story.js +15 -28
- package/src/style.scss +10 -22
- package/src/test/filter-and-sort-data-view.js +16 -1
- package/src/types.ts +75 -47
- package/src/utils.ts +2 -4
- package/src/view-actions.tsx +105 -102
- package/src/view-grid.tsx +21 -38
- package/src/view-list.tsx +22 -22
- package/src/view-table.tsx +45 -45
- package/tsconfig.json +1 -0
- package/tsconfig.tsbuildinfo +1 -1
package/src/bulk-actions.tsx
CHANGED
|
@@ -7,13 +7,15 @@ import {
|
|
|
7
7
|
Modal,
|
|
8
8
|
} from '@wordpress/components';
|
|
9
9
|
import { __, sprintf, _n } from '@wordpress/i18n';
|
|
10
|
-
import { useMemo, useState, useCallback
|
|
10
|
+
import { useMemo, useState, useCallback } from '@wordpress/element';
|
|
11
|
+
import { useRegistry } from '@wordpress/data';
|
|
11
12
|
|
|
12
13
|
/**
|
|
13
14
|
* Internal dependencies
|
|
14
15
|
*/
|
|
15
16
|
import { unlock } from './lock-unlock';
|
|
16
|
-
import type { Action, ActionModal
|
|
17
|
+
import type { Action, ActionModal } from './types';
|
|
18
|
+
import type { SetSelection } from './private-types';
|
|
17
19
|
|
|
18
20
|
const {
|
|
19
21
|
DropdownMenuV2: DropdownMenu,
|
|
@@ -22,34 +24,34 @@ const {
|
|
|
22
24
|
DropdownMenuSeparatorV2: DropdownMenuSeparator,
|
|
23
25
|
} = unlock( componentsPrivateApis );
|
|
24
26
|
|
|
25
|
-
interface ActionWithModalProps< Item
|
|
27
|
+
interface ActionWithModalProps< Item > {
|
|
26
28
|
action: ActionModal< Item >;
|
|
27
29
|
selectedItems: Item[];
|
|
28
30
|
setActionWithModal: ( action?: ActionModal< Item > ) => void;
|
|
29
31
|
onMenuOpenChange: ( isOpen: boolean ) => void;
|
|
30
32
|
}
|
|
31
33
|
|
|
32
|
-
interface BulkActionsItemProps< Item
|
|
34
|
+
interface BulkActionsItemProps< Item > {
|
|
33
35
|
action: Action< Item >;
|
|
34
36
|
selectedItems: Item[];
|
|
35
37
|
setActionWithModal: ( action?: ActionModal< Item > ) => void;
|
|
36
38
|
}
|
|
37
39
|
|
|
38
|
-
interface ActionsMenuGroupProps< Item
|
|
40
|
+
interface ActionsMenuGroupProps< Item > {
|
|
39
41
|
actions: Action< Item >[];
|
|
40
42
|
selectedItems: Item[];
|
|
41
43
|
setActionWithModal: ( action?: ActionModal< Item > ) => void;
|
|
42
44
|
}
|
|
43
45
|
|
|
44
|
-
interface BulkActionsProps< Item
|
|
46
|
+
interface BulkActionsProps< Item > {
|
|
45
47
|
data: Item[];
|
|
46
48
|
actions: Action< Item >[];
|
|
47
49
|
selection: string[];
|
|
48
|
-
onSelectionChange:
|
|
50
|
+
onSelectionChange: SetSelection;
|
|
49
51
|
getItemId: ( item: Item ) => string;
|
|
50
52
|
}
|
|
51
53
|
|
|
52
|
-
export function useHasAPossibleBulkAction< Item
|
|
54
|
+
export function useHasAPossibleBulkAction< Item >(
|
|
53
55
|
actions: Action< Item >[],
|
|
54
56
|
item: Item
|
|
55
57
|
) {
|
|
@@ -63,7 +65,7 @@ export function useHasAPossibleBulkAction< Item extends AnyItem >(
|
|
|
63
65
|
}, [ actions, item ] );
|
|
64
66
|
}
|
|
65
67
|
|
|
66
|
-
export function useSomeItemHasAPossibleBulkAction< Item
|
|
68
|
+
export function useSomeItemHasAPossibleBulkAction< Item >(
|
|
67
69
|
actions: Action< Item >[],
|
|
68
70
|
data: Item[]
|
|
69
71
|
) {
|
|
@@ -79,7 +81,7 @@ export function useSomeItemHasAPossibleBulkAction< Item extends AnyItem >(
|
|
|
79
81
|
}, [ actions, data ] );
|
|
80
82
|
}
|
|
81
83
|
|
|
82
|
-
function ActionWithModal< Item
|
|
84
|
+
function ActionWithModal< Item >( {
|
|
83
85
|
action,
|
|
84
86
|
selectedItems,
|
|
85
87
|
setActionWithModal,
|
|
@@ -114,11 +116,12 @@ function ActionWithModal< Item extends AnyItem >( {
|
|
|
114
116
|
);
|
|
115
117
|
}
|
|
116
118
|
|
|
117
|
-
function BulkActionItem< Item
|
|
119
|
+
function BulkActionItem< Item >( {
|
|
118
120
|
action,
|
|
119
121
|
selectedItems,
|
|
120
122
|
setActionWithModal,
|
|
121
123
|
}: BulkActionsItemProps< Item > ) {
|
|
124
|
+
const registry = useRegistry();
|
|
122
125
|
const eligibleItems = useMemo( () => {
|
|
123
126
|
return selectedItems.filter(
|
|
124
127
|
( item ) => ! action.isEligible || action.isEligible( item )
|
|
@@ -136,7 +139,7 @@ function BulkActionItem< Item extends AnyItem >( {
|
|
|
136
139
|
if ( shouldShowModal ) {
|
|
137
140
|
setActionWithModal( action );
|
|
138
141
|
} else {
|
|
139
|
-
|
|
142
|
+
action.callback( eligibleItems, { registry } );
|
|
140
143
|
}
|
|
141
144
|
} }
|
|
142
145
|
suffix={
|
|
@@ -148,7 +151,7 @@ function BulkActionItem< Item extends AnyItem >( {
|
|
|
148
151
|
);
|
|
149
152
|
}
|
|
150
153
|
|
|
151
|
-
function ActionsMenuGroup< Item
|
|
154
|
+
function ActionsMenuGroup< Item >( {
|
|
152
155
|
actions,
|
|
153
156
|
selectedItems,
|
|
154
157
|
setActionWithModal,
|
|
@@ -170,7 +173,7 @@ function ActionsMenuGroup< Item extends AnyItem >( {
|
|
|
170
173
|
);
|
|
171
174
|
}
|
|
172
175
|
|
|
173
|
-
export default function BulkActions< Item
|
|
176
|
+
export default function BulkActions< Item >( {
|
|
174
177
|
data,
|
|
175
178
|
actions,
|
|
176
179
|
selection,
|
|
@@ -194,37 +197,16 @@ export default function BulkActions< Item extends AnyItem >( {
|
|
|
194
197
|
}, [ data, bulkActions ] );
|
|
195
198
|
|
|
196
199
|
const numberSelectableItems = selectableItems.length;
|
|
197
|
-
const areAllSelected =
|
|
198
|
-
selection && selection.length === numberSelectableItems;
|
|
199
200
|
|
|
200
201
|
const selectedItems = useMemo( () => {
|
|
201
|
-
return data.filter(
|
|
202
|
-
|
|
202
|
+
return data.filter(
|
|
203
|
+
( item ) =>
|
|
204
|
+
selection.includes( getItemId( item ) ) &&
|
|
205
|
+
selectableItems.includes( item )
|
|
203
206
|
);
|
|
204
|
-
}, [ selection, data, getItemId ] );
|
|
207
|
+
}, [ selection, data, getItemId, selectableItems ] );
|
|
205
208
|
|
|
206
|
-
const
|
|
207
|
-
return selectedItems.some( ( item ) => {
|
|
208
|
-
return ! selectableItems.includes( item );
|
|
209
|
-
} );
|
|
210
|
-
}, [ selectedItems, selectableItems ] );
|
|
211
|
-
useEffect( () => {
|
|
212
|
-
if ( hasNonSelectableItemSelected ) {
|
|
213
|
-
onSelectionChange(
|
|
214
|
-
selectedItems.filter( ( selectedItem ) => {
|
|
215
|
-
return selectableItems.some( ( item ) => {
|
|
216
|
-
return getItemId( selectedItem ) === getItemId( item );
|
|
217
|
-
} );
|
|
218
|
-
} )
|
|
219
|
-
);
|
|
220
|
-
}
|
|
221
|
-
}, [
|
|
222
|
-
hasNonSelectableItemSelected,
|
|
223
|
-
selectedItems,
|
|
224
|
-
selectableItems,
|
|
225
|
-
getItemId,
|
|
226
|
-
onSelectionChange,
|
|
227
|
-
] );
|
|
209
|
+
const areAllSelected = selectedItems.length === numberSelectableItems;
|
|
228
210
|
|
|
229
211
|
if ( bulkActions.length === 0 ) {
|
|
230
212
|
return null;
|
|
@@ -243,15 +225,15 @@ export default function BulkActions< Item extends AnyItem >( {
|
|
|
243
225
|
variant="tertiary"
|
|
244
226
|
size="compact"
|
|
245
227
|
>
|
|
246
|
-
{
|
|
228
|
+
{ selectedItems.length
|
|
247
229
|
? sprintf(
|
|
248
230
|
/* translators: %d: Number of items. */
|
|
249
231
|
_n(
|
|
250
232
|
'Edit %d item',
|
|
251
233
|
'Edit %d items',
|
|
252
|
-
|
|
234
|
+
selectedItems.length
|
|
253
235
|
),
|
|
254
|
-
|
|
236
|
+
selectedItems.length
|
|
255
237
|
)
|
|
256
238
|
: __( 'Bulk edit' ) }
|
|
257
239
|
</Button>
|
|
@@ -267,7 +249,11 @@ export default function BulkActions< Item extends AnyItem >( {
|
|
|
267
249
|
disabled={ areAllSelected }
|
|
268
250
|
hideOnClick={ false }
|
|
269
251
|
onClick={ () => {
|
|
270
|
-
onSelectionChange(
|
|
252
|
+
onSelectionChange(
|
|
253
|
+
selectableItems.map( ( item ) =>
|
|
254
|
+
getItemId( item )
|
|
255
|
+
)
|
|
256
|
+
);
|
|
271
257
|
} }
|
|
272
258
|
suffix={ numberSelectableItems }
|
|
273
259
|
>
|
package/src/dataform.tsx
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* External dependencies
|
|
3
|
+
*/
|
|
4
|
+
import type { Dispatch, SetStateAction } from 'react';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* WordPress dependencies
|
|
8
|
+
*/
|
|
9
|
+
import { TextControl } from '@wordpress/components';
|
|
10
|
+
import { useCallback, useMemo } from '@wordpress/element';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Internal dependencies
|
|
14
|
+
*/
|
|
15
|
+
import type { Form, Field, NormalizedField } from './types';
|
|
16
|
+
import { normalizeFields } from './normalize-fields';
|
|
17
|
+
|
|
18
|
+
type DataFormProps< Item > = {
|
|
19
|
+
data: Item;
|
|
20
|
+
fields: Field< Item >[];
|
|
21
|
+
form: Form;
|
|
22
|
+
onChange: Dispatch< SetStateAction< Item > >;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
type DataFormControlProps< Item > = {
|
|
26
|
+
data: Item;
|
|
27
|
+
field: NormalizedField< Item >;
|
|
28
|
+
onChange: Dispatch< SetStateAction< Item > >;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
function DataFormTextControl< Item >( {
|
|
32
|
+
data,
|
|
33
|
+
field,
|
|
34
|
+
onChange,
|
|
35
|
+
}: DataFormControlProps< Item > ) {
|
|
36
|
+
const { id, header, placeholder } = field;
|
|
37
|
+
const value = field.getValue( { item: data } );
|
|
38
|
+
|
|
39
|
+
const onChangeControl = useCallback(
|
|
40
|
+
( newValue: string ) =>
|
|
41
|
+
onChange( ( prevItem: Item ) => ( {
|
|
42
|
+
...prevItem,
|
|
43
|
+
[ id ]: newValue,
|
|
44
|
+
} ) ),
|
|
45
|
+
[ id, onChange ]
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
return (
|
|
49
|
+
<TextControl
|
|
50
|
+
label={ header }
|
|
51
|
+
placeholder={ placeholder }
|
|
52
|
+
value={ value }
|
|
53
|
+
onChange={ onChangeControl }
|
|
54
|
+
__next40pxDefaultSize
|
|
55
|
+
/>
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const controls: {
|
|
60
|
+
[ key: string ]: < Item >(
|
|
61
|
+
props: DataFormControlProps< Item >
|
|
62
|
+
) => JSX.Element;
|
|
63
|
+
} = {
|
|
64
|
+
text: DataFormTextControl,
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
function getControlForField< Item >( field: NormalizedField< Item > ) {
|
|
68
|
+
if ( ! field.type ) {
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if ( ! Object.keys( controls ).includes( field.type ) ) {
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return controls[ field.type ];
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export default function DataForm< Item >( {
|
|
80
|
+
data,
|
|
81
|
+
fields,
|
|
82
|
+
form,
|
|
83
|
+
onChange,
|
|
84
|
+
}: DataFormProps< Item > ) {
|
|
85
|
+
const visibleFields = useMemo(
|
|
86
|
+
() =>
|
|
87
|
+
normalizeFields(
|
|
88
|
+
fields.filter(
|
|
89
|
+
( { id } ) => !! form.visibleFields?.includes( id )
|
|
90
|
+
)
|
|
91
|
+
),
|
|
92
|
+
[ fields, form.visibleFields ]
|
|
93
|
+
);
|
|
94
|
+
|
|
95
|
+
return visibleFields.map( ( field ) => {
|
|
96
|
+
const DataFormControl = getControlForField( field );
|
|
97
|
+
return DataFormControl ? (
|
|
98
|
+
<DataFormControl
|
|
99
|
+
key={ field.id }
|
|
100
|
+
data={ data }
|
|
101
|
+
field={ field }
|
|
102
|
+
onChange={ onChange }
|
|
103
|
+
/>
|
|
104
|
+
) : null;
|
|
105
|
+
} );
|
|
106
|
+
}
|
package/src/dataviews.tsx
CHANGED
|
@@ -7,7 +7,7 @@ import type { ComponentType } from 'react';
|
|
|
7
7
|
* WordPress dependencies
|
|
8
8
|
*/
|
|
9
9
|
import { __experimentalHStack as HStack } from '@wordpress/components';
|
|
10
|
-
import { useMemo, useState
|
|
10
|
+
import { useMemo, useState } from '@wordpress/element';
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
13
|
* Internal dependencies
|
|
@@ -18,12 +18,24 @@ import Filters from './filters';
|
|
|
18
18
|
import Search from './search';
|
|
19
19
|
import { LAYOUT_TABLE, LAYOUT_GRID } from './constants';
|
|
20
20
|
import { VIEW_LAYOUTS } from './layouts';
|
|
21
|
-
import
|
|
21
|
+
import {
|
|
22
|
+
default as BulkActions,
|
|
23
|
+
useSomeItemHasAPossibleBulkAction,
|
|
24
|
+
} from './bulk-actions';
|
|
22
25
|
import { normalizeFields } from './normalize-fields';
|
|
23
26
|
import BulkActionsToolbar from './bulk-actions-toolbar';
|
|
24
|
-
import type {
|
|
27
|
+
import type {
|
|
28
|
+
Action,
|
|
29
|
+
Field,
|
|
30
|
+
View,
|
|
31
|
+
ViewBaseProps,
|
|
32
|
+
SupportedLayouts,
|
|
33
|
+
} from './types';
|
|
34
|
+
import type { SetSelection, SelectionOrUpdater } from './private-types';
|
|
25
35
|
|
|
26
|
-
|
|
36
|
+
type ItemWithId = { id: string };
|
|
37
|
+
|
|
38
|
+
type DataViewsProps< Item > = {
|
|
27
39
|
view: View;
|
|
28
40
|
onChangeView: ( view: View ) => void;
|
|
29
41
|
fields: Field< Item >[];
|
|
@@ -31,36 +43,24 @@ interface DataViewsProps< Item extends AnyItem > {
|
|
|
31
43
|
searchLabel?: string;
|
|
32
44
|
actions?: Action< Item >[];
|
|
33
45
|
data: Item[];
|
|
34
|
-
getItemId?: ( item: Item ) => string;
|
|
35
46
|
isLoading?: boolean;
|
|
36
47
|
paginationInfo: {
|
|
37
48
|
totalItems: number;
|
|
38
49
|
totalPages: number;
|
|
39
50
|
};
|
|
40
|
-
|
|
51
|
+
defaultLayouts: SupportedLayouts;
|
|
52
|
+
selection?: string[];
|
|
53
|
+
setSelection?: SetSelection;
|
|
41
54
|
onSelectionChange?: ( items: Item[] ) => void;
|
|
42
|
-
}
|
|
55
|
+
} & ( Item extends ItemWithId
|
|
56
|
+
? { getItemId?: ( item: Item ) => string }
|
|
57
|
+
: { getItemId: ( item: Item ) => string } );
|
|
43
58
|
|
|
44
|
-
const defaultGetItemId = ( item:
|
|
45
|
-
const defaultOnSelectionChange = () => {};
|
|
59
|
+
const defaultGetItemId = ( item: ItemWithId ) => item.id;
|
|
46
60
|
|
|
47
|
-
|
|
48
|
-
actions: Action< Item >[],
|
|
49
|
-
data: Item[]
|
|
50
|
-
) {
|
|
51
|
-
return useMemo( () => {
|
|
52
|
-
return data.some( ( item ) => {
|
|
53
|
-
return actions.some( ( action ) => {
|
|
54
|
-
return (
|
|
55
|
-
action.supportsBulk &&
|
|
56
|
-
( ! action.isEligible || action.isEligible( item ) )
|
|
57
|
-
);
|
|
58
|
-
} );
|
|
59
|
-
} );
|
|
60
|
-
}, [ actions, data ] );
|
|
61
|
-
}
|
|
61
|
+
const defaultOnSelectionChange = () => {};
|
|
62
62
|
|
|
63
|
-
export default function DataViews< Item
|
|
63
|
+
export default function DataViews< Item >( {
|
|
64
64
|
view,
|
|
65
65
|
onChangeView,
|
|
66
66
|
fields,
|
|
@@ -71,38 +71,28 @@ export default function DataViews< Item extends AnyItem >( {
|
|
|
71
71
|
getItemId = defaultGetItemId,
|
|
72
72
|
isLoading = false,
|
|
73
73
|
paginationInfo,
|
|
74
|
-
|
|
74
|
+
defaultLayouts,
|
|
75
|
+
selection: selectionProperty,
|
|
76
|
+
setSelection: setSelectionProperty,
|
|
75
77
|
onSelectionChange = defaultOnSelectionChange,
|
|
76
78
|
}: DataViewsProps< Item > ) {
|
|
77
|
-
const [
|
|
79
|
+
const [ selectionState, setSelectionState ] = useState< string[] >( [] );
|
|
80
|
+
const isUncontrolled =
|
|
81
|
+
selectionProperty === undefined || setSelectionProperty === undefined;
|
|
82
|
+
const selection = isUncontrolled ? selectionState : selectionProperty;
|
|
83
|
+
const setSelection = isUncontrolled
|
|
84
|
+
? setSelectionState
|
|
85
|
+
: setSelectionProperty;
|
|
78
86
|
const [ openedFilter, setOpenedFilter ] = useState< string | null >( null );
|
|
79
87
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
selection
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
)
|
|
87
|
-
|
|
88
|
-
data.some( ( item ) => getItemId( item ) === id )
|
|
89
|
-
);
|
|
90
|
-
setSelection( newSelection );
|
|
91
|
-
onSelectionChange(
|
|
92
|
-
data.filter( ( item ) =>
|
|
93
|
-
newSelection.includes( getItemId( item ) )
|
|
94
|
-
)
|
|
95
|
-
);
|
|
96
|
-
}
|
|
97
|
-
}, [ selection, data, getItemId, onSelectionChange ] );
|
|
98
|
-
|
|
99
|
-
const onSetSelection = useCallback(
|
|
100
|
-
( items: Item[] ) => {
|
|
101
|
-
setSelection( items.map( ( item ) => getItemId( item ) ) );
|
|
102
|
-
onSelectionChange( items );
|
|
103
|
-
},
|
|
104
|
-
[ setSelection, getItemId, onSelectionChange ]
|
|
105
|
-
);
|
|
88
|
+
function setSelectionWithChange( value: SelectionOrUpdater ) {
|
|
89
|
+
const newValue =
|
|
90
|
+
typeof value === 'function' ? value( selection ) : value;
|
|
91
|
+
onSelectionChange(
|
|
92
|
+
data.filter( ( item ) => newValue.includes( getItemId( item ) ) )
|
|
93
|
+
);
|
|
94
|
+
return setSelection( value );
|
|
95
|
+
}
|
|
106
96
|
|
|
107
97
|
const ViewComponent = VIEW_LAYOUTS.find( ( v ) => v.type === view.type )
|
|
108
98
|
?.component as ComponentType< ViewBaseProps< Item > >;
|
|
@@ -112,6 +102,11 @@ export default function DataViews< Item extends AnyItem >( {
|
|
|
112
102
|
actions,
|
|
113
103
|
data
|
|
114
104
|
);
|
|
105
|
+
const _selection = useMemo( () => {
|
|
106
|
+
return selection.filter( ( id ) =>
|
|
107
|
+
data.some( ( item ) => getItemId( item ) === id )
|
|
108
|
+
);
|
|
109
|
+
}, [ selection, data, getItemId ] );
|
|
115
110
|
return (
|
|
116
111
|
<div className="dataviews-wrapper">
|
|
117
112
|
<HStack
|
|
@@ -144,8 +139,8 @@ export default function DataViews< Item extends AnyItem >( {
|
|
|
144
139
|
<BulkActions
|
|
145
140
|
actions={ actions }
|
|
146
141
|
data={ data }
|
|
147
|
-
onSelectionChange={
|
|
148
|
-
selection={
|
|
142
|
+
onSelectionChange={ setSelectionWithChange }
|
|
143
|
+
selection={ _selection }
|
|
149
144
|
getItemId={ getItemId }
|
|
150
145
|
/>
|
|
151
146
|
) }
|
|
@@ -153,7 +148,7 @@ export default function DataViews< Item extends AnyItem >( {
|
|
|
153
148
|
fields={ _fields }
|
|
154
149
|
view={ view }
|
|
155
150
|
onChangeView={ onChangeView }
|
|
156
|
-
|
|
151
|
+
defaultLayouts={ defaultLayouts }
|
|
157
152
|
/>
|
|
158
153
|
</HStack>
|
|
159
154
|
<ViewComponent
|
|
@@ -163,8 +158,8 @@ export default function DataViews< Item extends AnyItem >( {
|
|
|
163
158
|
getItemId={ getItemId }
|
|
164
159
|
isLoading={ isLoading }
|
|
165
160
|
onChangeView={ onChangeView }
|
|
166
|
-
onSelectionChange={
|
|
167
|
-
selection={
|
|
161
|
+
onSelectionChange={ setSelectionWithChange }
|
|
162
|
+
selection={ _selection }
|
|
168
163
|
setOpenedFilter={ setOpenedFilter }
|
|
169
164
|
view={ view }
|
|
170
165
|
/>
|
|
@@ -178,8 +173,8 @@ export default function DataViews< Item extends AnyItem >( {
|
|
|
178
173
|
<BulkActionsToolbar
|
|
179
174
|
data={ data }
|
|
180
175
|
actions={ actions }
|
|
181
|
-
selection={
|
|
182
|
-
onSelectionChange={
|
|
176
|
+
selection={ _selection }
|
|
177
|
+
onSelectionChange={ setSelectionWithChange }
|
|
183
178
|
getItemId={ getItemId }
|
|
184
179
|
/>
|
|
185
180
|
) }
|
|
@@ -15,7 +15,7 @@ import {
|
|
|
15
15
|
OPERATOR_IS_NOT_ALL,
|
|
16
16
|
} from './constants';
|
|
17
17
|
import { normalizeFields } from './normalize-fields';
|
|
18
|
-
import type { Field,
|
|
18
|
+
import type { Field, View } from './types';
|
|
19
19
|
|
|
20
20
|
function normalizeSearchInput( input = '' ) {
|
|
21
21
|
return removeAccents( input.trim().toLowerCase() );
|
|
@@ -32,7 +32,7 @@ const EMPTY_ARRAY: [] = [];
|
|
|
32
32
|
*
|
|
33
33
|
* @return Filtered, sorted and paginated data.
|
|
34
34
|
*/
|
|
35
|
-
export function filterSortAndPaginate< Item
|
|
35
|
+
export function filterSortAndPaginate< Item >(
|
|
36
36
|
data: Item[],
|
|
37
37
|
view: View,
|
|
38
38
|
fields: Field< Item >[]
|
|
@@ -61,7 +61,7 @@ export function filterSortAndPaginate< Item extends AnyItem >(
|
|
|
61
61
|
} );
|
|
62
62
|
}
|
|
63
63
|
|
|
64
|
-
if ( view.filters.length > 0 ) {
|
|
64
|
+
if ( view.filters && view.filters?.length > 0 ) {
|
|
65
65
|
view.filters.forEach( ( filter ) => {
|
|
66
66
|
const field = _fields.find(
|
|
67
67
|
( _field ) => _field.id === filter.field
|
|
@@ -142,6 +142,16 @@ export function filterSortAndPaginate< Item extends AnyItem >(
|
|
|
142
142
|
filteredData.sort( ( a, b ) => {
|
|
143
143
|
const valueA = fieldToSort.getValue( { item: a } ) ?? '';
|
|
144
144
|
const valueB = fieldToSort.getValue( { item: b } ) ?? '';
|
|
145
|
+
|
|
146
|
+
if (
|
|
147
|
+
typeof valueA === 'number' &&
|
|
148
|
+
typeof valueB === 'number'
|
|
149
|
+
) {
|
|
150
|
+
return view.sort?.direction === 'asc'
|
|
151
|
+
? valueA - valueB
|
|
152
|
+
: valueB - valueA;
|
|
153
|
+
}
|
|
154
|
+
|
|
145
155
|
return view.sort?.direction === 'asc'
|
|
146
156
|
? valueA.localeCompare( valueB )
|
|
147
157
|
: valueB.localeCompare( valueA );
|
package/src/filter-summary.tsx
CHANGED
|
@@ -157,7 +157,7 @@ function OperatorSelector( {
|
|
|
157
157
|
value: operator,
|
|
158
158
|
label: OPERATORS[ operator ]?.label,
|
|
159
159
|
} ) );
|
|
160
|
-
const currentFilter = view.filters
|
|
160
|
+
const currentFilter = view.filters?.find(
|
|
161
161
|
( _filter ) => _filter.field === filter.field
|
|
162
162
|
);
|
|
163
163
|
const value = currentFilter?.operator || filter.operators[ 0 ];
|
|
@@ -180,18 +180,22 @@ function OperatorSelector( {
|
|
|
180
180
|
const operator = newValue as Operator;
|
|
181
181
|
const newFilters = currentFilter
|
|
182
182
|
? [
|
|
183
|
-
...view.filters
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
183
|
+
...( view.filters ?? [] ).map(
|
|
184
|
+
( _filter ) => {
|
|
185
|
+
if (
|
|
186
|
+
_filter.field === filter.field
|
|
187
|
+
) {
|
|
188
|
+
return {
|
|
189
|
+
..._filter,
|
|
190
|
+
operator,
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
return _filter;
|
|
189
194
|
}
|
|
190
|
-
|
|
191
|
-
} ),
|
|
195
|
+
),
|
|
192
196
|
]
|
|
193
197
|
: [
|
|
194
|
-
...view.filters,
|
|
198
|
+
...( view.filters ?? [] ),
|
|
195
199
|
{
|
|
196
200
|
field: filter.field,
|
|
197
201
|
operator,
|
|
@@ -220,7 +224,9 @@ export default function FilterSummary( {
|
|
|
220
224
|
}: FilterSummaryProps ) {
|
|
221
225
|
const toggleRef = useRef< HTMLDivElement >( null );
|
|
222
226
|
const { filter, view, onChangeView } = commonProps;
|
|
223
|
-
const filterInView = view.filters
|
|
227
|
+
const filterInView = view.filters?.find(
|
|
228
|
+
( f ) => f.field === filter.field
|
|
229
|
+
);
|
|
224
230
|
const activeElements = filter.elements.filter( ( element ) => {
|
|
225
231
|
if ( filter.singleSelection ) {
|
|
226
232
|
return element.value === filterInView?.value;
|
|
@@ -290,7 +296,7 @@ export default function FilterSummary( {
|
|
|
290
296
|
onChangeView( {
|
|
291
297
|
...view,
|
|
292
298
|
page: 1,
|
|
293
|
-
filters: view.filters
|
|
299
|
+
filters: view.filters?.filter(
|
|
294
300
|
( _filter ) =>
|
|
295
301
|
_filter.field !== filter.field
|
|
296
302
|
),
|
package/src/filters.tsx
CHANGED
|
@@ -12,9 +12,9 @@ import AddFilter from './add-filter';
|
|
|
12
12
|
import ResetFilters from './reset-filters';
|
|
13
13
|
import { sanitizeOperators } from './utils';
|
|
14
14
|
import { ALL_OPERATORS, OPERATOR_IS, OPERATOR_IS_NOT } from './constants';
|
|
15
|
-
import type {
|
|
15
|
+
import type { NormalizedField, NormalizedFilter, View } from './types';
|
|
16
16
|
|
|
17
|
-
interface FiltersProps< Item
|
|
17
|
+
interface FiltersProps< Item > {
|
|
18
18
|
fields: NormalizedField< Item >[];
|
|
19
19
|
view: View;
|
|
20
20
|
onChangeView: ( view: View ) => void;
|
|
@@ -22,7 +22,7 @@ interface FiltersProps< Item extends AnyItem > {
|
|
|
22
22
|
setOpenedFilter: ( openedFilter: string | null ) => void;
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
-
function _Filters< Item
|
|
25
|
+
function _Filters< Item >( {
|
|
26
26
|
fields,
|
|
27
27
|
view,
|
|
28
28
|
onChangeView,
|
|
@@ -52,7 +52,7 @@ function _Filters< Item extends AnyItem >( {
|
|
|
52
52
|
operators,
|
|
53
53
|
isVisible:
|
|
54
54
|
isPrimary ||
|
|
55
|
-
view.filters
|
|
55
|
+
!! view.filters?.some(
|
|
56
56
|
( f ) =>
|
|
57
57
|
f.field === field.id &&
|
|
58
58
|
ALL_OPERATORS.includes( f.operator )
|
package/src/index.ts
CHANGED