@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.
Files changed (159) hide show
  1. package/CHANGELOG.md +24 -5
  2. package/README.md +5 -5
  3. package/build/add-filter.js +1 -1
  4. package/build/add-filter.js.map +1 -1
  5. package/build/bulk-actions-toolbar.js +5 -2
  6. package/build/bulk-actions-toolbar.js.map +1 -1
  7. package/build/bulk-actions.js +11 -21
  8. package/build/bulk-actions.js.map +1 -1
  9. package/build/dataform.js +78 -0
  10. package/build/dataform.js.map +1 -0
  11. package/build/dataviews.js +26 -31
  12. package/build/dataviews.js.map +1 -1
  13. package/build/filter-and-sort-data-view.js +4 -1
  14. package/build/filter-and-sort-data-view.js.map +1 -1
  15. package/build/filter-summary.js +6 -5
  16. package/build/filter-summary.js.map +1 -1
  17. package/build/filters.js +1 -1
  18. package/build/filters.js.map +1 -1
  19. package/build/index.js +7 -0
  20. package/build/index.js.map +1 -1
  21. package/build/item-actions.js +17 -6
  22. package/build/item-actions.js.map +1 -1
  23. package/build/lock-unlock.js +1 -1
  24. package/build/lock-unlock.js.map +1 -1
  25. package/build/normalize-fields.js.map +1 -1
  26. package/build/pagination.js +2 -2
  27. package/build/pagination.js.map +1 -1
  28. package/build/private-types.js +6 -0
  29. package/build/private-types.js.map +1 -0
  30. package/build/reset-filters.js +1 -1
  31. package/build/reset-filters.js.map +1 -1
  32. package/build/search-widget.js +8 -6
  33. package/build/search-widget.js.map +1 -1
  34. package/build/single-selection-checkbox.js +5 -16
  35. package/build/single-selection-checkbox.js.map +1 -1
  36. package/build/types.js.map +1 -1
  37. package/build/utils.js.map +1 -1
  38. package/build/view-actions.js +76 -65
  39. package/build/view-actions.js.map +1 -1
  40. package/build/view-grid.js +7 -19
  41. package/build/view-grid.js.map +1 -1
  42. package/build/view-list.js +15 -8
  43. package/build/view-list.js.map +1 -1
  44. package/build/view-table.js +22 -25
  45. package/build/view-table.js.map +1 -1
  46. package/build-module/add-filter.js +1 -1
  47. package/build-module/add-filter.js.map +1 -1
  48. package/build-module/bulk-actions-toolbar.js +5 -2
  49. package/build-module/bulk-actions-toolbar.js.map +1 -1
  50. package/build-module/bulk-actions.js +12 -22
  51. package/build-module/bulk-actions.js.map +1 -1
  52. package/build-module/dataform.js +72 -0
  53. package/build-module/dataform.js.map +1 -0
  54. package/build-module/dataviews.js +24 -31
  55. package/build-module/dataviews.js.map +1 -1
  56. package/build-module/filter-and-sort-data-view.js +4 -1
  57. package/build-module/filter-and-sort-data-view.js.map +1 -1
  58. package/build-module/filter-summary.js +6 -5
  59. package/build-module/filter-summary.js.map +1 -1
  60. package/build-module/filters.js +1 -1
  61. package/build-module/filters.js.map +1 -1
  62. package/build-module/index.js +1 -0
  63. package/build-module/index.js.map +1 -1
  64. package/build-module/item-actions.js +17 -6
  65. package/build-module/item-actions.js.map +1 -1
  66. package/build-module/lock-unlock.js +1 -1
  67. package/build-module/lock-unlock.js.map +1 -1
  68. package/build-module/normalize-fields.js.map +1 -1
  69. package/build-module/pagination.js +2 -2
  70. package/build-module/pagination.js.map +1 -1
  71. package/build-module/private-types.js +2 -0
  72. package/build-module/private-types.js.map +1 -0
  73. package/build-module/reset-filters.js +1 -1
  74. package/build-module/reset-filters.js.map +1 -1
  75. package/build-module/search-widget.js +8 -6
  76. package/build-module/search-widget.js.map +1 -1
  77. package/build-module/single-selection-checkbox.js +5 -16
  78. package/build-module/single-selection-checkbox.js.map +1 -1
  79. package/build-module/types.js.map +1 -1
  80. package/build-module/utils.js.map +1 -1
  81. package/build-module/view-actions.js +80 -68
  82. package/build-module/view-actions.js.map +1 -1
  83. package/build-module/view-grid.js +7 -19
  84. package/build-module/view-grid.js.map +1 -1
  85. package/build-module/view-list.js +15 -8
  86. package/build-module/view-list.js.map +1 -1
  87. package/build-module/view-table.js +22 -25
  88. package/build-module/view-table.js.map +1 -1
  89. package/build-style/style-rtl.css +8 -24
  90. package/build-style/style.css +8 -24
  91. package/build-types/bulk-actions-toolbar.d.ts +5 -4
  92. package/build-types/bulk-actions-toolbar.d.ts.map +1 -1
  93. package/build-types/bulk-actions.d.ts +7 -6
  94. package/build-types/bulk-actions.d.ts.map +1 -1
  95. package/build-types/dataform.d.ts +17 -0
  96. package/build-types/dataform.d.ts.map +1 -0
  97. package/build-types/dataviews.d.ts +15 -6
  98. package/build-types/dataviews.d.ts.map +1 -1
  99. package/build-types/filter-and-sort-data-view.d.ts +2 -2
  100. package/build-types/filter-and-sort-data-view.d.ts.map +1 -1
  101. package/build-types/filter-summary.d.ts.map +1 -1
  102. package/build-types/filters.d.ts +3 -3
  103. package/build-types/filters.d.ts.map +1 -1
  104. package/build-types/index.d.ts +1 -0
  105. package/build-types/index.d.ts.map +1 -1
  106. package/build-types/item-actions.d.ts +10 -10
  107. package/build-types/item-actions.d.ts.map +1 -1
  108. package/build-types/normalize-fields.d.ts +2 -2
  109. package/build-types/normalize-fields.d.ts.map +1 -1
  110. package/build-types/private-types.d.ts +3 -0
  111. package/build-types/private-types.d.ts.map +1 -0
  112. package/build-types/single-selection-checkbox.d.ts +5 -5
  113. package/build-types/single-selection-checkbox.d.ts.map +1 -1
  114. package/build-types/stories/fixtures.d.ts +14 -1
  115. package/build-types/stories/fixtures.d.ts.map +1 -1
  116. package/build-types/stories/index.story.d.ts +15 -1
  117. package/build-types/stories/index.story.d.ts.map +1 -1
  118. package/build-types/types.d.ts +73 -38
  119. package/build-types/types.d.ts.map +1 -1
  120. package/build-types/utils.d.ts +2 -2
  121. package/build-types/utils.d.ts.map +1 -1
  122. package/build-types/view-actions.d.ts +4 -4
  123. package/build-types/view-actions.d.ts.map +1 -1
  124. package/build-types/view-grid.d.ts +2 -2
  125. package/build-types/view-grid.d.ts.map +1 -1
  126. package/build-types/view-list.d.ts +2 -2
  127. package/build-types/view-list.d.ts.map +1 -1
  128. package/build-types/view-table.d.ts +2 -2
  129. package/build-types/view-table.d.ts.map +1 -1
  130. package/package.json +10 -9
  131. package/src/add-filter.tsx +1 -1
  132. package/src/bulk-actions-toolbar.tsx +18 -14
  133. package/src/bulk-actions.tsx +31 -45
  134. package/src/dataform.tsx +106 -0
  135. package/src/dataviews.tsx +55 -60
  136. package/src/filter-and-sort-data-view.ts +13 -3
  137. package/src/filter-summary.tsx +18 -12
  138. package/src/filters.tsx +4 -4
  139. package/src/index.ts +1 -0
  140. package/src/item-actions.tsx +27 -24
  141. package/src/lock-unlock.ts +1 -1
  142. package/src/normalize-fields.ts +5 -3
  143. package/src/pagination.tsx +2 -2
  144. package/src/private-types.tsx +2 -0
  145. package/src/reset-filters.tsx +1 -1
  146. package/src/search-widget.tsx +6 -6
  147. package/src/single-selection-checkbox.tsx +14 -29
  148. package/src/stories/fixtures.js +17 -1
  149. package/src/stories/index.story.js +15 -28
  150. package/src/style.scss +10 -22
  151. package/src/test/filter-and-sort-data-view.js +16 -1
  152. package/src/types.ts +75 -47
  153. package/src/utils.ts +2 -4
  154. package/src/view-actions.tsx +105 -102
  155. package/src/view-grid.tsx +21 -38
  156. package/src/view-list.tsx +22 -22
  157. package/src/view-table.tsx +45 -45
  158. package/tsconfig.json +1 -0
  159. package/tsconfig.tsbuildinfo +1 -1
@@ -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, useEffect } from '@wordpress/element';
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, AnyItem } from './types';
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 extends AnyItem > {
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 extends AnyItem > {
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 extends AnyItem > {
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 extends AnyItem > {
46
+ interface BulkActionsProps< Item > {
45
47
  data: Item[];
46
48
  actions: Action< Item >[];
47
49
  selection: string[];
48
- onSelectionChange: ( selection: Item[] ) => void;
50
+ onSelectionChange: SetSelection;
49
51
  getItemId: ( item: Item ) => string;
50
52
  }
51
53
 
52
- export function useHasAPossibleBulkAction< Item extends AnyItem >(
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 extends AnyItem >(
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 extends AnyItem >( {
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 extends AnyItem >( {
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
- await action.callback( eligibleItems );
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 extends AnyItem >( {
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 extends AnyItem >( {
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( ( item ) =>
202
- selection.includes( getItemId( item ) )
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 hasNonSelectableItemSelected = useMemo( () => {
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
- { selection.length
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
- selection.length
234
+ selectedItems.length
253
235
  ),
254
- selection.length
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( selectableItems );
252
+ onSelectionChange(
253
+ selectableItems.map( ( item ) =>
254
+ getItemId( item )
255
+ )
256
+ );
271
257
  } }
272
258
  suffix={ numberSelectableItems }
273
259
  >
@@ -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, useCallback, useEffect } from '@wordpress/element';
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 BulkActions from './bulk-actions';
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 { Action, AnyItem, Field, View, ViewBaseProps } from './types';
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
- interface DataViewsProps< Item extends AnyItem > {
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
- supportedLayouts: string[];
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: AnyItem ) => item.id;
45
- const defaultOnSelectionChange = () => {};
59
+ const defaultGetItemId = ( item: ItemWithId ) => item.id;
46
60
 
47
- function useSomeItemHasAPossibleBulkAction< Item extends AnyItem >(
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 extends AnyItem >( {
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
- supportedLayouts,
74
+ defaultLayouts,
75
+ selection: selectionProperty,
76
+ setSelection: setSelectionProperty,
75
77
  onSelectionChange = defaultOnSelectionChange,
76
78
  }: DataViewsProps< Item > ) {
77
- const [ selection, setSelection ] = useState< string[] >( [] );
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
- useEffect( () => {
81
- if (
82
- selection.length > 0 &&
83
- selection.some(
84
- ( id ) => ! data.some( ( item ) => getItemId( item ) === id )
85
- )
86
- ) {
87
- const newSelection = selection.filter( ( id ) =>
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={ onSetSelection }
148
- selection={ 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
- supportedLayouts={ supportedLayouts }
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={ onSetSelection }
167
- selection={ 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={ selection }
182
- onSelectionChange={ onSetSelection }
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, AnyItem, View } from './types';
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 extends AnyItem >(
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 );
@@ -157,7 +157,7 @@ function OperatorSelector( {
157
157
  value: operator,
158
158
  label: OPERATORS[ operator ]?.label,
159
159
  } ) );
160
- const currentFilter = view.filters.find(
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.map( ( _filter ) => {
184
- if ( _filter.field === filter.field ) {
185
- return {
186
- ..._filter,
187
- operator,
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
- return _filter;
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.find( ( f ) => f.field === filter.field );
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.filter(
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 { AnyItem, NormalizedField, NormalizedFilter, View } from './types';
15
+ import type { NormalizedField, NormalizedFilter, View } from './types';
16
16
 
17
- interface FiltersProps< Item extends AnyItem > {
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 extends AnyItem >( {
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.some(
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
@@ -2,3 +2,4 @@ export { default as DataViews } from './dataviews';
2
2
  export { VIEW_LAYOUTS } from './layouts';
3
3
  export { filterSortAndPaginate } from './filter-and-sort-data-view';
4
4
  export type * from './types';
5
+ export { default as DataForm } from './dataform';