@wordpress/dataviews 8.0.1-next.e256d081a.0 → 9.0.1-next.6870dfe5b.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 (269) hide show
  1. package/CHANGELOG.md +24 -1
  2. package/README.md +96 -1
  3. package/build/components/dataform-context/index.js +1 -0
  4. package/build/components/dataform-context/index.js.map +1 -1
  5. package/build/components/dataviews/index.js +11 -1
  6. package/build/components/dataviews/index.js.map +1 -1
  7. package/build/components/dataviews-context/index.js +1 -0
  8. package/build/components/dataviews-context/index.js.map +1 -1
  9. package/build/components/dataviews-layout/index.js +2 -1
  10. package/build/components/dataviews-layout/index.js.map +1 -1
  11. package/build/components/dataviews-picker/footer.js +145 -0
  12. package/build/components/dataviews-picker/footer.js.map +1 -0
  13. package/build/components/dataviews-picker/index.js +201 -0
  14. package/build/components/dataviews-picker/index.js.map +1 -0
  15. package/build/components/dataviews-selection-checkbox/index.js +4 -2
  16. package/build/components/dataviews-selection-checkbox/index.js.map +1 -1
  17. package/build/components/dataviews-view-config/index.js +1 -0
  18. package/build/components/dataviews-view-config/index.js.map +1 -1
  19. package/build/constants.js +4 -1
  20. package/build/constants.js.map +1 -1
  21. package/build/dataform-controls/checkbox.js +23 -2
  22. package/build/dataform-controls/checkbox.js.map +1 -1
  23. package/build/dataform-controls/color.js +128 -0
  24. package/build/dataform-controls/color.js.map +1 -0
  25. package/build/dataform-controls/email.js +10 -45
  26. package/build/dataform-controls/email.js.map +1 -1
  27. package/build/dataform-controls/index.js +8 -2
  28. package/build/dataform-controls/index.js.map +1 -1
  29. package/build/dataform-controls/telephone.js +34 -0
  30. package/build/dataform-controls/telephone.js.map +1 -0
  31. package/build/dataform-controls/text.js +7 -48
  32. package/build/dataform-controls/text.js.map +1 -1
  33. package/build/dataform-controls/{boolean.js → toggle.js} +6 -4
  34. package/build/dataform-controls/toggle.js.map +1 -0
  35. package/build/dataform-controls/url.js +34 -0
  36. package/build/dataform-controls/url.js.map +1 -0
  37. package/build/dataform-controls/utils/validated-text.js +76 -0
  38. package/build/dataform-controls/utils/validated-text.js.map +1 -0
  39. package/build/dataforms-layouts/card/index.js +6 -7
  40. package/build/dataforms-layouts/card/index.js.map +1 -1
  41. package/build/dataforms-layouts/data-form-layout.js +16 -4
  42. package/build/dataforms-layouts/data-form-layout.js.map +1 -1
  43. package/build/dataforms-layouts/index.js +31 -1
  44. package/build/dataforms-layouts/index.js.map +1 -1
  45. package/build/dataforms-layouts/row/index.js +113 -0
  46. package/build/dataforms-layouts/row/index.js.map +1 -0
  47. package/build/dataviews-layouts/grid/index.js +16 -11
  48. package/build/dataviews-layouts/grid/index.js.map +1 -1
  49. package/build/dataviews-layouts/index.js +9 -1
  50. package/build/dataviews-layouts/index.js.map +1 -1
  51. package/build/dataviews-layouts/picker-grid/index.js +357 -0
  52. package/build/dataviews-layouts/picker-grid/index.js.map +1 -0
  53. package/build/dataviews-layouts/utils/grid-items.js +37 -0
  54. package/build/dataviews-layouts/utils/grid-items.js.map +1 -0
  55. package/build/dataviews-layouts/utils/preview-size-picker.js +81 -0
  56. package/build/dataviews-layouts/utils/preview-size-picker.js.map +1 -0
  57. package/build/field-types/boolean.js +1 -1
  58. package/build/field-types/boolean.js.map +1 -1
  59. package/build/field-types/color.js +113 -0
  60. package/build/field-types/color.js.map +1 -0
  61. package/build/field-types/index.js +12 -0
  62. package/build/field-types/index.js.map +1 -1
  63. package/build/field-types/telephone.js +57 -0
  64. package/build/field-types/telephone.js.map +1 -0
  65. package/build/field-types/url.js +57 -0
  66. package/build/field-types/url.js.map +1 -0
  67. package/build/normalize-form-fields.js +6 -0
  68. package/build/normalize-form-fields.js.map +1 -1
  69. package/build/types.js.map +1 -1
  70. package/build/validation.js +1 -1
  71. package/build/validation.js.map +1 -1
  72. package/build-module/components/dataform-context/index.js +1 -0
  73. package/build-module/components/dataform-context/index.js.map +1 -1
  74. package/build-module/components/dataviews/index.js +11 -1
  75. package/build-module/components/dataviews/index.js.map +1 -1
  76. package/build-module/components/dataviews-context/index.js +1 -0
  77. package/build-module/components/dataviews-context/index.js.map +1 -1
  78. package/build-module/components/dataviews-layout/index.js +2 -1
  79. package/build-module/components/dataviews-layout/index.js.map +1 -1
  80. package/build-module/components/dataviews-picker/footer.js +136 -0
  81. package/build-module/components/dataviews-picker/footer.js.map +1 -0
  82. package/build-module/components/dataviews-picker/index.js +191 -0
  83. package/build-module/components/dataviews-picker/index.js.map +1 -0
  84. package/build-module/components/dataviews-selection-checkbox/index.js +4 -2
  85. package/build-module/components/dataviews-selection-checkbox/index.js.map +1 -1
  86. package/build-module/components/dataviews-view-config/index.js +1 -0
  87. package/build-module/components/dataviews-view-config/index.js.map +1 -1
  88. package/build-module/constants.js +3 -0
  89. package/build-module/constants.js.map +1 -1
  90. package/build-module/dataform-controls/checkbox.js +25 -3
  91. package/build-module/dataform-controls/checkbox.js.map +1 -1
  92. package/build-module/dataform-controls/color.js +122 -0
  93. package/build-module/dataform-controls/color.js.map +1 -0
  94. package/build-module/dataform-controls/email.js +9 -45
  95. package/build-module/dataform-controls/email.js.map +1 -1
  96. package/build-module/dataform-controls/index.js +8 -2
  97. package/build-module/dataform-controls/index.js.map +1 -1
  98. package/build-module/dataform-controls/telephone.js +27 -0
  99. package/build-module/dataform-controls/telephone.js.map +1 -0
  100. package/build-module/dataform-controls/text.js +6 -48
  101. package/build-module/dataform-controls/text.js.map +1 -1
  102. package/build-module/dataform-controls/{boolean.js → toggle.js} +5 -3
  103. package/build-module/dataform-controls/toggle.js.map +1 -0
  104. package/build-module/dataform-controls/url.js +27 -0
  105. package/build-module/dataform-controls/url.js.map +1 -0
  106. package/build-module/dataform-controls/utils/validated-text.js +70 -0
  107. package/build-module/dataform-controls/utils/validated-text.js.map +1 -0
  108. package/build-module/dataforms-layouts/card/index.js +6 -7
  109. package/build-module/dataforms-layouts/card/index.js.map +1 -1
  110. package/build-module/dataforms-layouts/data-form-layout.js +14 -4
  111. package/build-module/dataforms-layouts/data-form-layout.js.map +1 -1
  112. package/build-module/dataforms-layouts/index.js +32 -1
  113. package/build-module/dataforms-layouts/index.js.map +1 -1
  114. package/build-module/dataforms-layouts/row/index.js +106 -0
  115. package/build-module/dataforms-layouts/row/index.js.map +1 -0
  116. package/build-module/dataviews-layouts/grid/index.js +16 -11
  117. package/build-module/dataviews-layouts/grid/index.js.map +1 -1
  118. package/build-module/dataviews-layouts/index.js +10 -2
  119. package/build-module/dataviews-layouts/index.js.map +1 -1
  120. package/build-module/dataviews-layouts/picker-grid/index.js +348 -0
  121. package/build-module/dataviews-layouts/picker-grid/index.js.map +1 -0
  122. package/build-module/dataviews-layouts/utils/grid-items.js +29 -0
  123. package/build-module/dataviews-layouts/utils/grid-items.js.map +1 -0
  124. package/build-module/dataviews-layouts/utils/preview-size-picker.js +73 -0
  125. package/build-module/dataviews-layouts/utils/preview-size-picker.js.map +1 -0
  126. package/build-module/field-types/boolean.js +1 -1
  127. package/build-module/field-types/boolean.js.map +1 -1
  128. package/build-module/field-types/color.js +107 -0
  129. package/build-module/field-types/color.js.map +1 -0
  130. package/build-module/field-types/index.js +12 -0
  131. package/build-module/field-types/index.js.map +1 -1
  132. package/build-module/field-types/telephone.js +51 -0
  133. package/build-module/field-types/telephone.js.map +1 -0
  134. package/build-module/field-types/url.js +51 -0
  135. package/build-module/field-types/url.js.map +1 -0
  136. package/build-module/normalize-form-fields.js +6 -0
  137. package/build-module/normalize-form-fields.js.map +1 -1
  138. package/build-module/types.js.map +1 -1
  139. package/build-module/validation.js +1 -1
  140. package/build-module/validation.js.map +1 -1
  141. package/build-style/style-rtl.css +252 -12
  142. package/build-style/style.css +252 -12
  143. package/build-types/components/dataform/stories/index.story.d.ts +19 -4
  144. package/build-types/components/dataform/stories/index.story.d.ts.map +1 -1
  145. package/build-types/components/dataform-context/index.d.ts.map +1 -1
  146. package/build-types/components/dataviews/index.d.ts +1 -1
  147. package/build-types/components/dataviews/index.d.ts.map +1 -1
  148. package/build-types/components/dataviews/stories/index.story.d.ts.map +1 -1
  149. package/build-types/components/dataviews-context/index.d.ts +1 -0
  150. package/build-types/components/dataviews-context/index.d.ts.map +1 -1
  151. package/build-types/components/dataviews-layout/index.d.ts.map +1 -1
  152. package/build-types/components/dataviews-picker/footer.d.ts +4 -0
  153. package/build-types/components/dataviews-picker/footer.d.ts.map +1 -0
  154. package/build-types/components/dataviews-picker/index.d.ts +55 -0
  155. package/build-types/components/dataviews-picker/index.d.ts.map +1 -0
  156. package/build-types/components/dataviews-picker/stories/index.story.d.ts +42 -0
  157. package/build-types/components/dataviews-picker/stories/index.story.d.ts.map +1 -0
  158. package/build-types/components/dataviews-selection-checkbox/index.d.ts +2 -1
  159. package/build-types/components/dataviews-selection-checkbox/index.d.ts.map +1 -1
  160. package/build-types/components/dataviews-view-config/index.d.ts.map +1 -1
  161. package/build-types/constants.d.ts +1 -0
  162. package/build-types/constants.d.ts.map +1 -1
  163. package/build-types/dataform-controls/checkbox.d.ts.map +1 -1
  164. package/build-types/dataform-controls/color.d.ts +6 -0
  165. package/build-types/dataform-controls/color.d.ts.map +1 -0
  166. package/build-types/dataform-controls/email.d.ts.map +1 -1
  167. package/build-types/dataform-controls/index.d.ts.map +1 -1
  168. package/build-types/dataform-controls/telephone.d.ts +6 -0
  169. package/build-types/dataform-controls/telephone.d.ts.map +1 -0
  170. package/build-types/dataform-controls/text.d.ts.map +1 -1
  171. package/build-types/dataform-controls/toggle.d.ts +6 -0
  172. package/build-types/dataform-controls/toggle.d.ts.map +1 -0
  173. package/build-types/dataform-controls/url.d.ts +6 -0
  174. package/build-types/dataform-controls/url.d.ts.map +1 -0
  175. package/build-types/dataform-controls/utils/validated-text.d.ts +16 -0
  176. package/build-types/dataform-controls/utils/validated-text.d.ts.map +1 -0
  177. package/build-types/dataforms-layouts/card/index.d.ts +0 -3
  178. package/build-types/dataforms-layouts/card/index.d.ts.map +1 -1
  179. package/build-types/dataforms-layouts/data-form-layout.d.ts +4 -1
  180. package/build-types/dataforms-layouts/data-form-layout.d.ts.map +1 -1
  181. package/build-types/dataforms-layouts/index.d.ts +10 -0
  182. package/build-types/dataforms-layouts/index.d.ts.map +1 -1
  183. package/build-types/dataforms-layouts/row/index.d.ts +6 -0
  184. package/build-types/dataforms-layouts/row/index.d.ts.map +1 -0
  185. package/build-types/dataviews-layouts/grid/index.d.ts.map +1 -1
  186. package/build-types/dataviews-layouts/index.d.ts +12 -1
  187. package/build-types/dataviews-layouts/index.d.ts.map +1 -1
  188. package/build-types/dataviews-layouts/picker-grid/index.d.ts +4 -0
  189. package/build-types/dataviews-layouts/picker-grid/index.d.ts.map +1 -0
  190. package/build-types/dataviews-layouts/utils/grid-items.d.ts +5 -0
  191. package/build-types/dataviews-layouts/utils/grid-items.d.ts.map +1 -0
  192. package/build-types/dataviews-layouts/utils/preview-size-picker.d.ts +2 -0
  193. package/build-types/dataviews-layouts/utils/preview-size-picker.d.ts.map +1 -0
  194. package/build-types/field-types/color.d.ts +20 -0
  195. package/build-types/field-types/color.d.ts.map +1 -0
  196. package/build-types/field-types/index.d.ts.map +1 -1
  197. package/build-types/field-types/stories/index.story.d.ts +81 -0
  198. package/build-types/field-types/stories/index.story.d.ts.map +1 -0
  199. package/build-types/field-types/telephone.d.ts +20 -0
  200. package/build-types/field-types/telephone.d.ts.map +1 -0
  201. package/build-types/field-types/url.d.ts +20 -0
  202. package/build-types/field-types/url.d.ts.map +1 -0
  203. package/build-types/normalize-form-fields.d.ts.map +1 -1
  204. package/build-types/test/dataviews-picker.d.ts +2 -0
  205. package/build-types/test/dataviews-picker.d.ts.map +1 -0
  206. package/build-types/types.d.ts +36 -5
  207. package/build-types/types.d.ts.map +1 -1
  208. package/build-types/validation.d.ts.map +1 -1
  209. package/build-wp/index.js +5061 -4013
  210. package/package.json +16 -15
  211. package/src/components/dataform/stories/index.story.tsx +333 -11
  212. package/src/components/dataform-context/index.tsx +1 -0
  213. package/src/components/dataviews/index.tsx +25 -1
  214. package/src/components/dataviews/stories/fixtures.tsx +1 -1
  215. package/src/components/dataviews/stories/index.story.tsx +14 -0
  216. package/src/components/dataviews/style.scss +4 -2
  217. package/src/components/dataviews-context/index.ts +3 -0
  218. package/src/components/dataviews-layout/index.tsx +4 -2
  219. package/src/components/dataviews-picker/footer.tsx +207 -0
  220. package/src/components/dataviews-picker/index.tsx +284 -0
  221. package/src/components/dataviews-picker/stories/index.story.tsx +251 -0
  222. package/src/components/dataviews-picker/style.scss +10 -0
  223. package/src/components/dataviews-selection-checkbox/index.tsx +3 -0
  224. package/src/components/dataviews-view-config/index.tsx +1 -0
  225. package/src/constants.ts +3 -0
  226. package/src/dataform-controls/checkbox.tsx +33 -3
  227. package/src/dataform-controls/color.tsx +139 -0
  228. package/src/dataform-controls/email.tsx +10 -52
  229. package/src/dataform-controls/index.tsx +8 -2
  230. package/src/dataform-controls/telephone.tsx +30 -0
  231. package/src/dataform-controls/text.tsx +2 -57
  232. package/src/dataform-controls/{boolean.tsx → toggle.tsx} +3 -2
  233. package/src/dataform-controls/url.tsx +30 -0
  234. package/src/dataform-controls/utils/validated-text.tsx +96 -0
  235. package/src/dataforms-layouts/card/index.tsx +5 -4
  236. package/src/dataforms-layouts/card/style.scss +7 -0
  237. package/src/dataforms-layouts/data-form-layout.tsx +15 -3
  238. package/src/dataforms-layouts/index.tsx +35 -0
  239. package/src/dataforms-layouts/row/index.tsx +115 -0
  240. package/src/dataforms-layouts/row/style.scss +3 -0
  241. package/src/dataviews-layouts/grid/index.tsx +38 -33
  242. package/src/dataviews-layouts/grid/style.scss +42 -20
  243. package/src/dataviews-layouts/index.ts +16 -2
  244. package/src/dataviews-layouts/picker-grid/index.tsx +490 -0
  245. package/src/dataviews-layouts/picker-grid/style.scss +171 -0
  246. package/src/dataviews-layouts/utils/grid-items.scss +21 -0
  247. package/src/dataviews-layouts/utils/grid-items.tsx +35 -0
  248. package/src/dataviews-layouts/utils/preview-size-picker.tsx +87 -0
  249. package/src/field-types/boolean.tsx +1 -1
  250. package/src/field-types/color.tsx +115 -0
  251. package/src/field-types/index.tsx +15 -0
  252. package/src/field-types/stories/index.story.tsx +719 -0
  253. package/src/field-types/telephone.tsx +71 -0
  254. package/src/field-types/url.tsx +71 -0
  255. package/src/normalize-form-fields.ts +6 -0
  256. package/src/style.scss +4 -0
  257. package/src/test/dataform.tsx +2 -2
  258. package/src/test/dataviews-picker.tsx +478 -0
  259. package/src/test/dataviews.tsx +86 -0
  260. package/src/types.ts +56 -4
  261. package/src/validation.ts +3 -0
  262. package/tsconfig.tsbuildinfo +1 -1
  263. package/build/dataform-controls/boolean.js.map +0 -1
  264. package/build-module/dataform-controls/boolean.js.map +0 -1
  265. package/build-types/components/stories/index.story.d.ts +0 -63
  266. package/build-types/components/stories/index.story.d.ts.map +0 -1
  267. package/build-types/dataform-controls/boolean.d.ts +0 -6
  268. package/build-types/dataform-controls/boolean.d.ts.map +0 -1
  269. package/src/components/stories/index.story.tsx +0 -372
@@ -0,0 +1,207 @@
1
+ /**
2
+ * WordPress dependencies
3
+ */
4
+ import {
5
+ Button,
6
+ CheckboxControl,
7
+ __experimentalHStack as HStack,
8
+ } from '@wordpress/components';
9
+ import { useRegistry } from '@wordpress/data';
10
+ import { useContext, useMemo, useState } from '@wordpress/element';
11
+ import { __, sprintf, _n } from '@wordpress/i18n';
12
+
13
+ /**
14
+ * Internal dependencies
15
+ */
16
+ import DataViewsPagination from '../dataviews-pagination';
17
+ import DataViewsContext from '../dataviews-context';
18
+ import type { SetSelection } from '../../private-types';
19
+ import type { Action } from '../../types';
20
+
21
+ const EMPTY_ARRAY: [] = [];
22
+
23
+ export function useIsMultiselectPicker< Item >(
24
+ actions: Action< Item >[] | undefined
25
+ ) {
26
+ return useMemo( () => {
27
+ return actions?.every( ( action ) => action.supportsBulk );
28
+ }, [ actions ] );
29
+ }
30
+
31
+ function BulkSelectionCheckbox< Item >( {
32
+ selection,
33
+ selectedItems,
34
+ onChangeSelection,
35
+ data,
36
+ getItemId,
37
+ }: {
38
+ selection: string[];
39
+ selectedItems: Item[];
40
+ onChangeSelection: SetSelection;
41
+ data: Item[];
42
+ getItemId: ( item: Item ) => string;
43
+ } ) {
44
+ const areAllSelected = selectedItems.length === data.length;
45
+
46
+ return (
47
+ <CheckboxControl
48
+ className="dataviews-view-table-selection-checkbox"
49
+ __nextHasNoMarginBottom
50
+ checked={ areAllSelected }
51
+ indeterminate={ ! areAllSelected && !! selectedItems.length }
52
+ onChange={ () => {
53
+ if ( areAllSelected ) {
54
+ // Deselect all - remove the current page from the total selection.
55
+ onChangeSelection(
56
+ selection.filter(
57
+ ( id ) =>
58
+ ! data.some(
59
+ ( item ) => id === getItemId( item )
60
+ )
61
+ )
62
+ );
63
+ } else {
64
+ // Select all - merge the current page into the total selection.
65
+ const selectionSet = new Set( [
66
+ ...selection,
67
+ ...data.map( ( item ) => getItemId( item ) ),
68
+ ] );
69
+ onChangeSelection( Array.from( selectionSet ) );
70
+ }
71
+ } }
72
+ aria-label={
73
+ areAllSelected ? __( 'Deselect all' ) : __( 'Select all' )
74
+ }
75
+ />
76
+ );
77
+ }
78
+
79
+ function ActionButtons< Item >( {
80
+ actions,
81
+ items,
82
+ selection,
83
+ }: {
84
+ actions: Action< Item >[];
85
+ items: Item[];
86
+ selection: string[];
87
+ } ) {
88
+ const registry = useRegistry();
89
+ const [ actionInProgress, setActionInProgress ] = useState< string | null >(
90
+ null
91
+ );
92
+
93
+ return (
94
+ <HStack expanded={ false } spacing={ 1 }>
95
+ { actions.map( ( action ) => {
96
+ // Only support actions with callbacks for DataViewsPicker.
97
+ // This is because many use cases of the picker will be already within modals.
98
+ if ( ! ( 'callback' in action ) ) {
99
+ return null;
100
+ }
101
+
102
+ const { id, label, icon, isPrimary, isDestructive, callback } =
103
+ action;
104
+
105
+ const _label =
106
+ typeof label === 'string' ? label : label( items );
107
+ const variant = isPrimary ? 'primary' : 'tertiary';
108
+ const isInProgress = id === actionInProgress;
109
+
110
+ return (
111
+ <Button
112
+ key={ id }
113
+ accessibleWhenDisabled
114
+ icon={ icon }
115
+ disabled={ isInProgress || ! selection?.length }
116
+ isBusy={ isInProgress }
117
+ onClick={ async () => {
118
+ setActionInProgress( id );
119
+ await callback( items, {
120
+ registry,
121
+ } );
122
+ setActionInProgress( null );
123
+ } }
124
+ size="compact"
125
+ isDestructive={ isDestructive }
126
+ variant={ variant }
127
+ >
128
+ { _label }
129
+ </Button>
130
+ );
131
+ } ) }
132
+ </HStack>
133
+ );
134
+ }
135
+
136
+ export function DataViewsPickerFooter() {
137
+ const {
138
+ data,
139
+ selection,
140
+ onChangeSelection,
141
+ getItemId,
142
+ actions = EMPTY_ARRAY,
143
+ } = useContext( DataViewsContext );
144
+
145
+ const selectionCount = selection.length;
146
+ const isMultiselect = useIsMultiselectPicker( actions );
147
+
148
+ const message =
149
+ selectionCount > 0
150
+ ? sprintf(
151
+ /* translators: %d: number of items. */
152
+ _n(
153
+ '%d Item selected',
154
+ '%d Items selected',
155
+ selectionCount
156
+ ),
157
+ selectionCount
158
+ )
159
+ : sprintf(
160
+ /* translators: %d: number of items. */
161
+ _n( '%d Item', '%d Items', data.length ),
162
+ data.length
163
+ );
164
+
165
+ const selectedItems = useMemo(
166
+ () =>
167
+ data.filter( ( item ) => selection.includes( getItemId( item ) ) ),
168
+ [ selection, getItemId, data ]
169
+ );
170
+
171
+ return (
172
+ <HStack
173
+ expanded={ false }
174
+ justify="space-between"
175
+ className="dataviews-footer"
176
+ >
177
+ <HStack
178
+ className="dataviews-picker-footer__bulk-selection"
179
+ expanded={ false }
180
+ spacing={ 3 }
181
+ >
182
+ { isMultiselect && (
183
+ <BulkSelectionCheckbox
184
+ selection={ selection }
185
+ selectedItems={ selectedItems }
186
+ onChangeSelection={ onChangeSelection }
187
+ data={ data }
188
+ getItemId={ getItemId }
189
+ />
190
+ ) }
191
+ <span className="dataviews-bulk-actions-footer__item-count">
192
+ { message }
193
+ </span>
194
+ </HStack>
195
+ <DataViewsPagination />
196
+ { Boolean( actions?.length ) && (
197
+ <div className="dataviews-picker-footer__actions">
198
+ <ActionButtons
199
+ actions={ actions }
200
+ items={ selectedItems }
201
+ selection={ selection }
202
+ />
203
+ </div>
204
+ ) }
205
+ </HStack>
206
+ );
207
+ }
@@ -0,0 +1,284 @@
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import type { ReactNode } from 'react';
5
+
6
+ /**
7
+ * WordPress dependencies
8
+ */
9
+ import { __experimentalHStack as HStack } from '@wordpress/components';
10
+ import {
11
+ useContext,
12
+ useEffect,
13
+ useMemo,
14
+ useRef,
15
+ useState,
16
+ } from '@wordpress/element';
17
+ import { useResizeObserver, throttle } from '@wordpress/compose';
18
+
19
+ /**
20
+ * Internal dependencies
21
+ */
22
+ import DataViewsContext from '../dataviews-context';
23
+ import { VIEW_LAYOUTS } from '../../dataviews-layouts';
24
+ import {
25
+ default as DataViewsFilters,
26
+ useFilters,
27
+ FiltersToggle,
28
+ } from '../dataviews-filters';
29
+ import DataViewsLayout from '../dataviews-layout';
30
+ import { DataViewsPickerFooter } from './footer';
31
+ import DataViewsSearch from '../dataviews-search';
32
+ import { DataViewsPagination } from '../dataviews-pagination';
33
+ import DataViewsViewConfig, {
34
+ DataviewsViewConfigDropdown,
35
+ ViewTypeMenu,
36
+ } from '../dataviews-view-config';
37
+ import { normalizeFields } from '../../normalize-fields';
38
+ import type { ActionButton, Field, View, SupportedLayouts } from '../../types';
39
+ import type { SelectionOrUpdater } from '../../private-types';
40
+ type ItemWithId = { id: string };
41
+
42
+ const isItemClickable = () => false;
43
+
44
+ const dataViewsPickerLayouts = VIEW_LAYOUTS.filter(
45
+ ( viewLayout ) => viewLayout.isPicker
46
+ );
47
+
48
+ type DataViewsPickerProps< Item > = {
49
+ view: View;
50
+ onChangeView: ( view: View ) => void;
51
+ fields: Field< Item >[];
52
+ actions?: ActionButton< Item >[];
53
+ search?: boolean;
54
+ searchLabel?: string;
55
+ data: Item[];
56
+ isLoading?: boolean;
57
+ paginationInfo: {
58
+ totalItems: number;
59
+ totalPages: number;
60
+ infiniteScrollHandler?: () => void;
61
+ };
62
+ defaultLayouts: SupportedLayouts;
63
+ selection: string[];
64
+ onChangeSelection: ( items: string[] ) => void;
65
+ children?: ReactNode;
66
+ config?: {
67
+ perPageSizes: number[];
68
+ };
69
+ itemListLabel?: string;
70
+ empty?: ReactNode;
71
+ } & ( Item extends ItemWithId
72
+ ? { getItemId?: ( item: Item ) => string }
73
+ : { getItemId: ( item: Item ) => string } );
74
+
75
+ const defaultGetItemId = ( item: ItemWithId ) => item.id;
76
+ const EMPTY_ARRAY: any[] = [];
77
+
78
+ type DefaultUIProps = Pick<
79
+ DataViewsPickerProps< any >,
80
+ 'search' | 'searchLabel'
81
+ >;
82
+
83
+ function DefaultUI( {
84
+ search = true,
85
+ searchLabel = undefined,
86
+ }: DefaultUIProps ) {
87
+ const { isShowingFilter } = useContext( DataViewsContext );
88
+ return (
89
+ <>
90
+ <HStack
91
+ alignment="top"
92
+ justify="space-between"
93
+ className="dataviews__view-actions"
94
+ spacing={ 1 }
95
+ >
96
+ <HStack
97
+ justify="start"
98
+ expanded={ false }
99
+ className="dataviews__search"
100
+ >
101
+ { search && <DataViewsSearch label={ searchLabel } /> }
102
+ <FiltersToggle />
103
+ </HStack>
104
+ <HStack
105
+ spacing={ 1 }
106
+ expanded={ false }
107
+ style={ { flexShrink: 0 } }
108
+ >
109
+ <DataViewsViewConfig />
110
+ </HStack>
111
+ </HStack>
112
+ { isShowingFilter && (
113
+ <DataViewsFilters className="dataviews-filters__container" />
114
+ ) }
115
+ <DataViewsLayout />
116
+ <DataViewsPickerFooter />
117
+ </>
118
+ );
119
+ }
120
+
121
+ function DataViewsPicker< Item >( {
122
+ view,
123
+ onChangeView,
124
+ fields,
125
+ search = true,
126
+ searchLabel = undefined,
127
+ actions = EMPTY_ARRAY,
128
+ data,
129
+ getItemId = defaultGetItemId,
130
+ isLoading = false,
131
+ paginationInfo,
132
+ defaultLayouts: defaultLayoutsProperty,
133
+ selection,
134
+ onChangeSelection,
135
+ children,
136
+ config = { perPageSizes: [ 10, 20, 50, 100 ] },
137
+ itemListLabel,
138
+ empty,
139
+ }: DataViewsPickerProps< Item > ) {
140
+ const { infiniteScrollHandler } = paginationInfo;
141
+ const containerRef = useRef< HTMLDivElement | null >( null );
142
+ const [ containerWidth, setContainerWidth ] = useState( 0 );
143
+ const resizeObserverRef = useResizeObserver(
144
+ ( resizeObserverEntries: any ) => {
145
+ setContainerWidth(
146
+ resizeObserverEntries[ 0 ].borderBoxSize[ 0 ].inlineSize
147
+ );
148
+ },
149
+ { box: 'border-box' }
150
+ );
151
+ const [ openedFilter, setOpenedFilter ] = useState< string | null >( null );
152
+ function setSelectionWithChange( value: SelectionOrUpdater ) {
153
+ const newValue =
154
+ typeof value === 'function' ? value( selection ) : value;
155
+ if ( onChangeSelection ) {
156
+ onChangeSelection( newValue );
157
+ }
158
+ }
159
+ const _fields = useMemo( () => normalizeFields( fields ), [ fields ] );
160
+ const filters = useFilters( _fields, view );
161
+ const hasPrimaryOrLockedFilters = useMemo(
162
+ () =>
163
+ ( filters || [] ).some(
164
+ ( filter ) => filter.isPrimary || filter.isLocked
165
+ ),
166
+ [ filters ]
167
+ );
168
+ const [ isShowingFilter, setIsShowingFilter ] = useState< boolean >(
169
+ hasPrimaryOrLockedFilters
170
+ );
171
+
172
+ useEffect( () => {
173
+ if ( hasPrimaryOrLockedFilters && ! isShowingFilter ) {
174
+ setIsShowingFilter( true );
175
+ }
176
+ }, [ hasPrimaryOrLockedFilters, isShowingFilter ] );
177
+
178
+ // Attach scroll event listener for infinite scroll
179
+ useEffect( () => {
180
+ if ( ! view.infiniteScrollEnabled || ! containerRef.current ) {
181
+ return;
182
+ }
183
+
184
+ const handleScroll = throttle( ( event: unknown ) => {
185
+ const target = ( event as Event ).target as HTMLElement;
186
+ const scrollTop = target.scrollTop;
187
+ const scrollHeight = target.scrollHeight;
188
+ const clientHeight = target.clientHeight;
189
+
190
+ // Check if user has scrolled near the bottom
191
+ if ( scrollTop + clientHeight >= scrollHeight - 100 ) {
192
+ infiniteScrollHandler?.();
193
+ }
194
+ }, 100 ); // Throttle to 100ms
195
+
196
+ const container = containerRef.current;
197
+ container.addEventListener( 'scroll', handleScroll );
198
+
199
+ return () => {
200
+ container.removeEventListener( 'scroll', handleScroll );
201
+ handleScroll.cancel(); // Cancel any pending throttled calls
202
+ };
203
+ }, [ infiniteScrollHandler, view.infiniteScrollEnabled ] );
204
+
205
+ // Filter out DataViewsPicker layouts.
206
+ const defaultLayouts = useMemo(
207
+ () =>
208
+ Object.fromEntries(
209
+ Object.entries( defaultLayoutsProperty ).filter(
210
+ ( [ layoutType ] ) => {
211
+ return dataViewsPickerLayouts.some(
212
+ ( viewLayout ) => viewLayout.type === layoutType
213
+ );
214
+ }
215
+ )
216
+ ),
217
+ [ defaultLayoutsProperty ]
218
+ );
219
+
220
+ if ( ! defaultLayouts[ view.type ] ) {
221
+ return null;
222
+ }
223
+
224
+ return (
225
+ <DataViewsContext.Provider
226
+ value={ {
227
+ view,
228
+ onChangeView,
229
+ fields: _fields,
230
+ actions,
231
+ data,
232
+ isLoading,
233
+ paginationInfo,
234
+ isItemClickable,
235
+ selection,
236
+ onChangeSelection: setSelectionWithChange,
237
+ openedFilter,
238
+ setOpenedFilter,
239
+ getItemId,
240
+ containerWidth,
241
+ containerRef,
242
+ resizeObserverRef,
243
+ defaultLayouts,
244
+ filters,
245
+ isShowingFilter,
246
+ setIsShowingFilter,
247
+ config,
248
+ itemListLabel,
249
+ empty,
250
+ hasInfiniteScrollHandler: !! infiniteScrollHandler,
251
+ } }
252
+ >
253
+ <div className="dataviews-picker-wrapper" ref={ containerRef }>
254
+ { children ?? (
255
+ <DefaultUI search={ search } searchLabel={ searchLabel } />
256
+ ) }
257
+ </div>
258
+ </DataViewsContext.Provider>
259
+ );
260
+ }
261
+
262
+ // Populate the DataViews sub components
263
+ const DataViewsPickerSubComponents =
264
+ DataViewsPicker as typeof DataViewsPicker & {
265
+ BulkActionToolbar: typeof DataViewsPickerFooter;
266
+ Filters: typeof DataViewsFilters;
267
+ FiltersToggle: typeof FiltersToggle;
268
+ Layout: typeof DataViewsLayout;
269
+ LayoutSwitcher: typeof ViewTypeMenu;
270
+ Pagination: typeof DataViewsPagination;
271
+ Search: typeof DataViewsSearch;
272
+ ViewConfig: typeof DataviewsViewConfigDropdown;
273
+ };
274
+
275
+ DataViewsPickerSubComponents.BulkActionToolbar = DataViewsPickerFooter;
276
+ DataViewsPickerSubComponents.Filters = DataViewsFilters;
277
+ DataViewsPickerSubComponents.FiltersToggle = FiltersToggle;
278
+ DataViewsPickerSubComponents.Layout = DataViewsLayout;
279
+ DataViewsPickerSubComponents.LayoutSwitcher = ViewTypeMenu;
280
+ DataViewsPickerSubComponents.Pagination = DataViewsPagination;
281
+ DataViewsPickerSubComponents.Search = DataViewsSearch;
282
+ DataViewsPickerSubComponents.ViewConfig = DataviewsViewConfigDropdown;
283
+
284
+ export default DataViewsPickerSubComponents;