@wordpress/dataviews 9.0.1-next.6f42e1382.0 → 9.1.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 (286) hide show
  1. package/CHANGELOG.md +25 -2
  2. package/README.md +112 -15
  3. package/build/components/dataviews/index.js +4 -6
  4. package/build/components/dataviews/index.js.map +1 -1
  5. package/build/components/dataviews-filters/filters-toggled.js +32 -0
  6. package/build/components/dataviews-filters/filters-toggled.js.map +1 -0
  7. package/build/components/dataviews-filters/filters.js +73 -0
  8. package/build/components/dataviews-filters/filters.js.map +1 -0
  9. package/build/components/dataviews-filters/index.js +26 -190
  10. package/build/components/dataviews-filters/index.js.map +1 -1
  11. package/build/components/dataviews-filters/input-widget.js +48 -4
  12. package/build/components/dataviews-filters/input-widget.js.map +1 -1
  13. package/build/components/dataviews-filters/toggle.js +99 -0
  14. package/build/components/dataviews-filters/toggle.js.map +1 -0
  15. package/build/components/dataviews-filters/use-filters.js +63 -0
  16. package/build/components/dataviews-filters/use-filters.js.map +1 -0
  17. package/build/components/dataviews-picker/index.js +4 -6
  18. package/build/components/dataviews-picker/index.js.map +1 -1
  19. package/build/components/dataviews-view-config/index.js +22 -3
  20. package/build/components/dataviews-view-config/index.js.map +1 -1
  21. package/build/dataform-controls/array.js +117 -29
  22. package/build/dataform-controls/array.js.map +1 -1
  23. package/build/dataform-controls/checkbox.js +31 -20
  24. package/build/dataform-controls/checkbox.js.map +1 -1
  25. package/build/dataform-controls/color.js +29 -24
  26. package/build/dataform-controls/color.js.map +1 -1
  27. package/build/dataform-controls/date.js +32 -24
  28. package/build/dataform-controls/date.js.map +1 -1
  29. package/build/dataform-controls/datetime.js +133 -19
  30. package/build/dataform-controls/datetime.js.map +1 -1
  31. package/build/dataform-controls/email.js +7 -1
  32. package/build/dataform-controls/email.js.map +1 -1
  33. package/build/dataform-controls/index.js +25 -0
  34. package/build/dataform-controls/index.js.map +1 -1
  35. package/build/dataform-controls/integer.js +7 -106
  36. package/build/dataform-controls/integer.js.map +1 -1
  37. package/build/dataform-controls/number.js +21 -0
  38. package/build/dataform-controls/number.js.map +1 -0
  39. package/build/dataform-controls/radio.js +42 -9
  40. package/build/dataform-controls/radio.js.map +1 -1
  41. package/build/dataform-controls/relative-date-control.js +6 -10
  42. package/build/dataform-controls/relative-date-control.js.map +1 -1
  43. package/build/dataform-controls/select.js +41 -10
  44. package/build/dataform-controls/select.js.map +1 -1
  45. package/build/dataform-controls/telephone.js +7 -1
  46. package/build/dataform-controls/telephone.js.map +1 -1
  47. package/build/dataform-controls/text.js +14 -2
  48. package/build/dataform-controls/text.js.map +1 -1
  49. package/build/dataform-controls/textarea.js +33 -20
  50. package/build/dataform-controls/textarea.js.map +1 -1
  51. package/build/dataform-controls/toggle-group.js +36 -6
  52. package/build/dataform-controls/toggle-group.js.map +1 -1
  53. package/build/dataform-controls/toggle.js +33 -22
  54. package/build/dataform-controls/toggle.js.map +1 -1
  55. package/build/dataform-controls/url.js +7 -1
  56. package/build/dataform-controls/url.js.map +1 -1
  57. package/build/dataform-controls/utils/validated-input.js +34 -32
  58. package/build/dataform-controls/utils/validated-input.js.map +1 -1
  59. package/build/dataform-controls/utils/validated-number.js +146 -0
  60. package/build/dataform-controls/utils/validated-number.js.map +1 -0
  61. package/build/dataforms-layouts/panel/dropdown.js +10 -14
  62. package/build/dataforms-layouts/panel/dropdown.js.map +1 -1
  63. package/build/dataforms-layouts/panel/index.js +24 -11
  64. package/build/dataforms-layouts/panel/index.js.map +1 -1
  65. package/build/dataforms-layouts/panel/modal.js +22 -27
  66. package/build/dataforms-layouts/panel/modal.js.map +1 -1
  67. package/build/dataforms-layouts/panel/summary-button.js +67 -0
  68. package/build/dataforms-layouts/panel/summary-button.js.map +1 -0
  69. package/build/dataviews-layouts/picker-grid/index.js +4 -1
  70. package/build/dataviews-layouts/picker-grid/index.js.map +1 -1
  71. package/build/field-types/array.js +0 -6
  72. package/build/field-types/array.js.map +1 -1
  73. package/build/field-types/index.js +4 -0
  74. package/build/field-types/index.js.map +1 -1
  75. package/build/field-types/number.js +71 -0
  76. package/build/field-types/number.js.map +1 -0
  77. package/build/index.js +7 -0
  78. package/build/index.js.map +1 -1
  79. package/build/normalize-fields.js +17 -0
  80. package/build/normalize-fields.js.map +1 -1
  81. package/build/types.js.map +1 -1
  82. package/build/validation.js +18 -1
  83. package/build/validation.js.map +1 -1
  84. package/build-module/components/dataviews/index.js +5 -7
  85. package/build-module/components/dataviews/index.js.map +1 -1
  86. package/build-module/components/dataviews-filters/filters-toggled.js +24 -0
  87. package/build-module/components/dataviews-filters/filters-toggled.js.map +1 -0
  88. package/build-module/components/dataviews-filters/filters.js +65 -0
  89. package/build-module/components/dataviews-filters/filters.js.map +1 -0
  90. package/build-module/components/dataviews-filters/index.js +4 -186
  91. package/build-module/components/dataviews-filters/index.js.map +1 -1
  92. package/build-module/components/dataviews-filters/input-widget.js +48 -4
  93. package/build-module/components/dataviews-filters/input-widget.js.map +1 -1
  94. package/build-module/components/dataviews-filters/toggle.js +91 -0
  95. package/build-module/components/dataviews-filters/toggle.js.map +1 -0
  96. package/build-module/components/dataviews-filters/use-filters.js +56 -0
  97. package/build-module/components/dataviews-filters/use-filters.js.map +1 -0
  98. package/build-module/components/dataviews-picker/index.js +5 -7
  99. package/build-module/components/dataviews-picker/index.js.map +1 -1
  100. package/build-module/components/dataviews-view-config/index.js +22 -3
  101. package/build-module/components/dataviews-view-config/index.js.map +1 -1
  102. package/build-module/dataform-controls/array.js +120 -32
  103. package/build-module/dataform-controls/array.js.map +1 -1
  104. package/build-module/dataform-controls/checkbox.js +31 -21
  105. package/build-module/dataform-controls/checkbox.js.map +1 -1
  106. package/build-module/dataform-controls/color.js +28 -24
  107. package/build-module/dataform-controls/color.js.map +1 -1
  108. package/build-module/dataform-controls/date.js +32 -24
  109. package/build-module/dataform-controls/date.js.map +1 -1
  110. package/build-module/dataform-controls/datetime.js +135 -21
  111. package/build-module/dataform-controls/datetime.js.map +1 -1
  112. package/build-module/dataform-controls/email.js +7 -1
  113. package/build-module/dataform-controls/email.js.map +1 -1
  114. package/build-module/dataform-controls/index.js +25 -0
  115. package/build-module/dataform-controls/index.js.map +1 -1
  116. package/build-module/dataform-controls/integer.js +7 -106
  117. package/build-module/dataform-controls/integer.js.map +1 -1
  118. package/build-module/dataform-controls/number.js +14 -0
  119. package/build-module/dataform-controls/number.js.map +1 -0
  120. package/build-module/dataform-controls/radio.js +44 -11
  121. package/build-module/dataform-controls/radio.js.map +1 -1
  122. package/build-module/dataform-controls/relative-date-control.js +6 -10
  123. package/build-module/dataform-controls/relative-date-control.js.map +1 -1
  124. package/build-module/dataform-controls/select.js +43 -12
  125. package/build-module/dataform-controls/select.js.map +1 -1
  126. package/build-module/dataform-controls/telephone.js +7 -1
  127. package/build-module/dataform-controls/telephone.js.map +1 -1
  128. package/build-module/dataform-controls/text.js +14 -2
  129. package/build-module/dataform-controls/text.js.map +1 -1
  130. package/build-module/dataform-controls/textarea.js +32 -20
  131. package/build-module/dataform-controls/textarea.js.map +1 -1
  132. package/build-module/dataform-controls/toggle-group.js +38 -8
  133. package/build-module/dataform-controls/toggle-group.js.map +1 -1
  134. package/build-module/dataform-controls/toggle.js +33 -23
  135. package/build-module/dataform-controls/toggle.js.map +1 -1
  136. package/build-module/dataform-controls/url.js +7 -1
  137. package/build-module/dataform-controls/url.js.map +1 -1
  138. package/build-module/dataform-controls/utils/validated-input.js +34 -33
  139. package/build-module/dataform-controls/utils/validated-input.js.map +1 -1
  140. package/build-module/dataform-controls/utils/validated-number.js +138 -0
  141. package/build-module/dataform-controls/utils/validated-number.js.map +1 -0
  142. package/build-module/dataforms-layouts/panel/dropdown.js +10 -15
  143. package/build-module/dataforms-layouts/panel/dropdown.js.map +1 -1
  144. package/build-module/dataforms-layouts/panel/index.js +24 -11
  145. package/build-module/dataforms-layouts/panel/index.js.map +1 -1
  146. package/build-module/dataforms-layouts/panel/modal.js +22 -28
  147. package/build-module/dataforms-layouts/panel/modal.js.map +1 -1
  148. package/build-module/dataforms-layouts/panel/summary-button.js +60 -0
  149. package/build-module/dataforms-layouts/panel/summary-button.js.map +1 -0
  150. package/build-module/dataviews-layouts/picker-grid/index.js +4 -1
  151. package/build-module/dataviews-layouts/picker-grid/index.js.map +1 -1
  152. package/build-module/field-types/array.js +0 -6
  153. package/build-module/field-types/array.js.map +1 -1
  154. package/build-module/field-types/index.js +4 -0
  155. package/build-module/field-types/index.js.map +1 -1
  156. package/build-module/field-types/number.js +65 -0
  157. package/build-module/field-types/number.js.map +1 -0
  158. package/build-module/index.js +1 -0
  159. package/build-module/index.js.map +1 -1
  160. package/build-module/normalize-fields.js +15 -0
  161. package/build-module/normalize-fields.js.map +1 -1
  162. package/build-module/types.js.map +1 -1
  163. package/build-module/validation.js +18 -1
  164. package/build-module/validation.js.map +1 -1
  165. package/build-types/components/dataform/stories/index.story.d.ts +3 -0
  166. package/build-types/components/dataform/stories/index.story.d.ts.map +1 -1
  167. package/build-types/components/dataviews/index.d.ts +3 -2
  168. package/build-types/components/dataviews/index.d.ts.map +1 -1
  169. package/build-types/components/dataviews/stories/fixtures.d.ts +4 -2
  170. package/build-types/components/dataviews/stories/fixtures.d.ts.map +1 -1
  171. package/build-types/components/dataviews-filters/filters-toggled.d.ts +5 -0
  172. package/build-types/components/dataviews-filters/filters-toggled.d.ts.map +1 -0
  173. package/build-types/components/dataviews-filters/filters.d.ts +6 -0
  174. package/build-types/components/dataviews-filters/filters.d.ts.map +1 -0
  175. package/build-types/components/dataviews-filters/index.d.ts +4 -8
  176. package/build-types/components/dataviews-filters/index.d.ts.map +1 -1
  177. package/build-types/components/dataviews-filters/input-widget.d.ts.map +1 -1
  178. package/build-types/components/dataviews-filters/toggle.d.ts +3 -0
  179. package/build-types/components/dataviews-filters/toggle.d.ts.map +1 -0
  180. package/build-types/components/dataviews-filters/use-filters.d.ts +4 -0
  181. package/build-types/components/dataviews-filters/use-filters.d.ts.map +1 -0
  182. package/build-types/components/dataviews-picker/index.d.ts +3 -2
  183. package/build-types/components/dataviews-picker/index.d.ts.map +1 -1
  184. package/build-types/components/dataviews-view-config/index.d.ts.map +1 -1
  185. package/build-types/dataform-controls/array.d.ts.map +1 -1
  186. package/build-types/dataform-controls/checkbox.d.ts.map +1 -1
  187. package/build-types/dataform-controls/color.d.ts.map +1 -1
  188. package/build-types/dataform-controls/date.d.ts.map +1 -1
  189. package/build-types/dataform-controls/datetime.d.ts.map +1 -1
  190. package/build-types/dataform-controls/email.d.ts.map +1 -1
  191. package/build-types/dataform-controls/index.d.ts +1 -1
  192. package/build-types/dataform-controls/index.d.ts.map +1 -1
  193. package/build-types/dataform-controls/integer.d.ts +4 -1
  194. package/build-types/dataform-controls/integer.d.ts.map +1 -1
  195. package/build-types/dataform-controls/number.d.ts +6 -0
  196. package/build-types/dataform-controls/number.d.ts.map +1 -0
  197. package/build-types/dataform-controls/radio.d.ts.map +1 -1
  198. package/build-types/dataform-controls/relative-date-control.d.ts +6 -5
  199. package/build-types/dataform-controls/relative-date-control.d.ts.map +1 -1
  200. package/build-types/dataform-controls/select.d.ts.map +1 -1
  201. package/build-types/dataform-controls/telephone.d.ts.map +1 -1
  202. package/build-types/dataform-controls/text.d.ts +1 -1
  203. package/build-types/dataform-controls/text.d.ts.map +1 -1
  204. package/build-types/dataform-controls/textarea.d.ts +1 -1
  205. package/build-types/dataform-controls/textarea.d.ts.map +1 -1
  206. package/build-types/dataform-controls/toggle-group.d.ts.map +1 -1
  207. package/build-types/dataform-controls/toggle.d.ts.map +1 -1
  208. package/build-types/dataform-controls/url.d.ts.map +1 -1
  209. package/build-types/dataform-controls/utils/validated-input.d.ts +4 -4
  210. package/build-types/dataform-controls/utils/validated-input.d.ts.map +1 -1
  211. package/build-types/dataform-controls/utils/validated-number.d.ts +9 -0
  212. package/build-types/dataform-controls/utils/validated-number.d.ts.map +1 -0
  213. package/build-types/dataforms-layouts/panel/dropdown.d.ts +2 -1
  214. package/build-types/dataforms-layouts/panel/dropdown.d.ts.map +1 -1
  215. package/build-types/dataforms-layouts/panel/index.d.ts.map +1 -1
  216. package/build-types/dataforms-layouts/panel/modal.d.ts +2 -1
  217. package/build-types/dataforms-layouts/panel/modal.d.ts.map +1 -1
  218. package/build-types/dataforms-layouts/panel/summary-button.d.ts +15 -0
  219. package/build-types/dataforms-layouts/panel/summary-button.d.ts.map +1 -0
  220. package/build-types/dataviews-layouts/picker-grid/index.d.ts.map +1 -1
  221. package/build-types/field-types/array.d.ts.map +1 -1
  222. package/build-types/field-types/index.d.ts.map +1 -1
  223. package/build-types/field-types/number.d.ts +20 -0
  224. package/build-types/field-types/number.d.ts.map +1 -0
  225. package/build-types/field-types/stories/index.story.d.ts +106 -57
  226. package/build-types/field-types/stories/index.story.d.ts.map +1 -1
  227. package/build-types/index.d.ts +1 -0
  228. package/build-types/index.d.ts.map +1 -1
  229. package/build-types/normalize-fields.d.ts +3 -0
  230. package/build-types/normalize-fields.d.ts.map +1 -1
  231. package/build-types/types.d.ts +69 -5
  232. package/build-types/types.d.ts.map +1 -1
  233. package/build-types/validation.d.ts.map +1 -1
  234. package/build-wp/index.js +2354 -1717
  235. package/package.json +16 -15
  236. package/src/components/dataform/stories/index.story.tsx +528 -8
  237. package/src/components/dataviews/index.tsx +8 -14
  238. package/src/components/dataviews/stories/fixtures.tsx +99 -41
  239. package/src/components/dataviews/stories/index.story.tsx +2 -2
  240. package/src/components/dataviews-filters/filters-toggled.tsx +20 -0
  241. package/src/components/dataviews-filters/filters.tsx +73 -0
  242. package/src/components/dataviews-filters/index.tsx +4 -246
  243. package/src/components/dataviews-filters/input-widget.tsx +44 -5
  244. package/src/components/dataviews-filters/toggle.tsx +118 -0
  245. package/src/components/dataviews-filters/use-filters.ts +73 -0
  246. package/src/components/dataviews-picker/index.tsx +8 -14
  247. package/src/components/dataviews-picker/stories/index.story.tsx +1 -1
  248. package/src/components/dataviews-view-config/index.tsx +18 -3
  249. package/src/dataform-controls/array.tsx +139 -44
  250. package/src/dataform-controls/checkbox.tsx +41 -24
  251. package/src/dataform-controls/color.tsx +33 -24
  252. package/src/dataform-controls/date.tsx +47 -21
  253. package/src/dataform-controls/datetime.tsx +171 -23
  254. package/src/dataform-controls/email.tsx +9 -1
  255. package/src/dataform-controls/index.tsx +28 -0
  256. package/src/dataform-controls/integer.tsx +3 -146
  257. package/src/dataform-controls/number.tsx +10 -0
  258. package/src/dataform-controls/radio.tsx +53 -11
  259. package/src/dataform-controls/relative-date-control.tsx +11 -10
  260. package/src/dataform-controls/select.tsx +53 -10
  261. package/src/dataform-controls/telephone.tsx +9 -1
  262. package/src/dataform-controls/text.tsx +18 -1
  263. package/src/dataform-controls/textarea.tsx +38 -24
  264. package/src/dataform-controls/toggle-group.tsx +50 -10
  265. package/src/dataform-controls/toggle.tsx +41 -24
  266. package/src/dataform-controls/url.tsx +9 -1
  267. package/src/dataform-controls/utils/validated-input.tsx +50 -50
  268. package/src/dataform-controls/utils/validated-number.tsx +209 -0
  269. package/src/dataforms-layouts/panel/dropdown.tsx +12 -23
  270. package/src/dataforms-layouts/panel/index.tsx +39 -16
  271. package/src/dataforms-layouts/panel/modal.tsx +24 -30
  272. package/src/dataforms-layouts/panel/summary-button.tsx +92 -0
  273. package/src/dataviews-layouts/picker-grid/index.tsx +15 -8
  274. package/src/field-types/array.tsx +0 -8
  275. package/src/field-types/index.tsx +5 -0
  276. package/src/field-types/number.tsx +104 -0
  277. package/src/field-types/stories/index.story.tsx +170 -16
  278. package/src/index.ts +1 -0
  279. package/src/normalize-fields.ts +18 -0
  280. package/src/test/dataform.tsx +36 -0
  281. package/src/test/filter-and-sort-data-view.js +182 -138
  282. package/src/test/normalize-fields.ts +136 -0
  283. package/src/test/validation.ts +235 -0
  284. package/src/types.ts +76 -4
  285. package/src/validation.ts +32 -0
  286. package/tsconfig.tsbuildinfo +1 -1
@@ -1,246 +1,4 @@
1
- /**
2
- * WordPress dependencies
3
- */
4
- import {
5
- memo,
6
- useContext,
7
- useRef,
8
- useMemo,
9
- useCallback,
10
- useEffect,
11
- } from '@wordpress/element';
12
- import { __experimentalHStack as HStack, Button } from '@wordpress/components';
13
- import { funnel } from '@wordpress/icons';
14
- import { __, _x } from '@wordpress/i18n';
15
-
16
- /**
17
- * Internal dependencies
18
- */
19
- import Filter from './filter';
20
- import { default as AddFilter, AddFilterMenu } from './add-filter';
21
- import ResetFilters from './reset-filters';
22
- import DataViewsContext from '../dataviews-context';
23
- import { ALL_OPERATORS, SINGLE_SELECTION_OPERATORS } from '../../constants';
24
- import type { NormalizedFilter, NormalizedField, View } from '../../types';
25
-
26
- export function useFilters( fields: NormalizedField< any >[], view: View ) {
27
- return useMemo( () => {
28
- const filters: NormalizedFilter[] = [];
29
- fields.forEach( ( field ) => {
30
- if (
31
- field.filterBy === false ||
32
- ( ! field.elements?.length && ! field.Edit )
33
- ) {
34
- return;
35
- }
36
-
37
- const operators = field.filterBy.operators;
38
- const isPrimary = !! field.filterBy?.isPrimary;
39
- const isLocked =
40
- view.filters?.some(
41
- ( f ) => f.field === field.id && !! f.isLocked
42
- ) ?? false;
43
- filters.push( {
44
- field: field.id,
45
- name: field.label,
46
- elements: field.elements ?? [],
47
- singleSelection: operators.some( ( op ) =>
48
- SINGLE_SELECTION_OPERATORS.includes( op )
49
- ),
50
- operators,
51
- isVisible:
52
- isLocked ||
53
- isPrimary ||
54
- !! view.filters?.some(
55
- ( f ) =>
56
- f.field === field.id &&
57
- ALL_OPERATORS.includes( f.operator )
58
- ),
59
- isPrimary,
60
- isLocked,
61
- } );
62
- } );
63
-
64
- // Sort filters by:
65
- // - locked filters go first
66
- // - primary filters go next
67
- // - then, sort by name
68
- filters.sort( ( a, b ) => {
69
- if ( a.isLocked && ! b.isLocked ) {
70
- return -1;
71
- }
72
- if ( ! a.isLocked && b.isLocked ) {
73
- return 1;
74
- }
75
- if ( a.isPrimary && ! b.isPrimary ) {
76
- return -1;
77
- }
78
- if ( ! a.isPrimary && b.isPrimary ) {
79
- return 1;
80
- }
81
- return a.name.localeCompare( b.name );
82
- } );
83
- return filters;
84
- }, [ fields, view ] );
85
- }
86
-
87
- export function FiltersToggle() {
88
- const {
89
- filters,
90
- view,
91
- onChangeView,
92
- setOpenedFilter,
93
- isShowingFilter,
94
- setIsShowingFilter,
95
- } = useContext( DataViewsContext );
96
-
97
- const buttonRef = useRef< HTMLButtonElement >( null );
98
- const onChangeViewWithFilterVisibility = useCallback(
99
- ( _view: View ) => {
100
- onChangeView( _view );
101
- setIsShowingFilter( true );
102
- },
103
- [ onChangeView, setIsShowingFilter ]
104
- );
105
- const visibleFilters = filters.filter( ( filter ) => filter.isVisible );
106
-
107
- const hasVisibleFilters = !! visibleFilters.length;
108
- if ( filters.length === 0 ) {
109
- return null;
110
- }
111
-
112
- const addFilterButtonProps = {
113
- label: __( 'Add filter' ),
114
- 'aria-expanded': false,
115
- isPressed: false,
116
- };
117
- const toggleFiltersButtonProps = {
118
- label: _x( 'Filter', 'verb' ),
119
- 'aria-expanded': isShowingFilter,
120
- isPressed: isShowingFilter,
121
- onClick: () => {
122
- if ( ! isShowingFilter ) {
123
- setOpenedFilter( null );
124
- }
125
- setIsShowingFilter( ! isShowingFilter );
126
- },
127
- };
128
- const buttonComponent = (
129
- <Button
130
- ref={ buttonRef }
131
- className="dataviews-filters__visibility-toggle"
132
- size="compact"
133
- icon={ funnel }
134
- { ...( hasVisibleFilters
135
- ? toggleFiltersButtonProps
136
- : addFilterButtonProps ) }
137
- />
138
- );
139
- return (
140
- <div className="dataviews-filters__container-visibility-toggle">
141
- { ! hasVisibleFilters ? (
142
- <AddFilterMenu
143
- filters={ filters }
144
- view={ view }
145
- onChangeView={ onChangeViewWithFilterVisibility }
146
- setOpenedFilter={ setOpenedFilter }
147
- triggerProps={ { render: buttonComponent } }
148
- />
149
- ) : (
150
- <FilterVisibilityToggle
151
- buttonRef={ buttonRef }
152
- filtersCount={ view.filters?.length }
153
- >
154
- { buttonComponent }
155
- </FilterVisibilityToggle>
156
- ) }
157
- </div>
158
- );
159
- }
160
-
161
- function FilterVisibilityToggle( {
162
- buttonRef,
163
- filtersCount,
164
- children,
165
- }: {
166
- buttonRef: React.RefObject< HTMLButtonElement >;
167
- filtersCount?: number;
168
- children: React.ReactNode;
169
- } ) {
170
- // Focus the `add filter` button when unmounts.
171
- useEffect(
172
- () => () => {
173
- buttonRef.current?.focus();
174
- },
175
- [ buttonRef ]
176
- );
177
- return (
178
- <>
179
- { children }
180
- { !! filtersCount && (
181
- <span className="dataviews-filters-toggle__count">
182
- { filtersCount }
183
- </span>
184
- ) }
185
- </>
186
- );
187
- }
188
-
189
- function Filters( { className }: { className?: string } ) {
190
- const { fields, view, onChangeView, openedFilter, setOpenedFilter } =
191
- useContext( DataViewsContext );
192
- const addFilterRef = useRef< HTMLButtonElement >( null );
193
- const filters = useFilters( fields, view );
194
- const addFilter = (
195
- <AddFilter
196
- key="add-filter"
197
- filters={ filters }
198
- view={ view }
199
- onChangeView={ onChangeView }
200
- ref={ addFilterRef }
201
- setOpenedFilter={ setOpenedFilter }
202
- />
203
- );
204
- const visibleFilters = filters.filter( ( filter ) => filter.isVisible );
205
- if ( visibleFilters.length === 0 ) {
206
- return null;
207
- }
208
- const filterComponents = [
209
- ...visibleFilters.map( ( filter ) => {
210
- return (
211
- <Filter
212
- key={ filter.field }
213
- filter={ filter }
214
- view={ view }
215
- fields={ fields }
216
- onChangeView={ onChangeView }
217
- addFilterRef={ addFilterRef }
218
- openedFilter={ openedFilter }
219
- />
220
- );
221
- } ),
222
- addFilter,
223
- ];
224
-
225
- filterComponents.push(
226
- <ResetFilters
227
- key="reset-filters"
228
- filters={ filters }
229
- view={ view }
230
- onChangeView={ onChangeView }
231
- />
232
- );
233
-
234
- return (
235
- <HStack
236
- justify="flex-start"
237
- style={ { width: 'fit-content' } }
238
- wrap
239
- className={ className }
240
- >
241
- { filterComponents }
242
- </HStack>
243
- );
244
- }
245
-
246
- export default memo( Filters );
1
+ export { default as Filters } from './filters';
2
+ export { default as FiltersToggle } from './toggle';
3
+ export { default as useFilters } from './use-filters';
4
+ export { default as FiltersToggled } from './filters-toggled';
@@ -32,13 +32,52 @@ export default function InputWidget( {
32
32
  const currentFilter = view.filters?.find(
33
33
  ( f ) => f.field === filter.field
34
34
  );
35
-
36
- const field = fields.find( ( f ) => f.id === filter.field );
37
35
  const currentValue = getCurrentValue( filter, currentFilter );
36
+
37
+ /*
38
+ * We are reusing the field.Edit component for filters. By doing so,
39
+ * we get for free a filter control specific to the field type
40
+ * and other aspects of the field API (Edit control configuration, etc.).
41
+ *
42
+ * This approach comes with an issue: the field.Edit controls work with getValue
43
+ * and setValue methods, which take an item (Item) as parameter. But, at this point,
44
+ * we don't have an item and we don't know how to create one, either.
45
+ *
46
+ * So, what we do is to prepare the data and the relevant field configuration
47
+ * as if Item was a plain object whose keys are the field ids:
48
+ *
49
+ * {
50
+ * [ fieldOne.id ]: value,
51
+ * [ fieldTwo.id ]: value,
52
+ * }
53
+ *
54
+ */
55
+ const field = useMemo( () => {
56
+ const currentField = fields.find( ( f ) => f.id === filter.field );
57
+ if ( currentField ) {
58
+ return {
59
+ ...currentField,
60
+ // Deactivate validation for filters.
61
+ isValid: {
62
+ required: false,
63
+ custom: () => null,
64
+ },
65
+ // Configure getValue/setValue as if Item was a plain object.
66
+ getValue: ( { item }: { item: any } ) =>
67
+ item[ currentField.id ],
68
+ setValue: ( { value }: { value: any } ) => ( {
69
+ [ currentField.id ]: value,
70
+ } ),
71
+ };
72
+ }
73
+ return currentField;
74
+ }, [ fields, filter.field ] );
75
+
38
76
  const data = useMemo( () => {
39
77
  return ( view.filters ?? [] ).reduce(
40
- ( acc, f ) => {
41
- acc[ f.field ] = f.value;
78
+ ( acc, activeFilter ) => {
79
+ // We can now assume the field is stored as a Item prop.
80
+ acc[ activeFilter.field ] = activeFilter.value;
42
81
  return acc;
43
82
  },
44
83
  {} as Record< string, any >
@@ -49,7 +88,7 @@ export default function InputWidget( {
49
88
  if ( ! field || ! currentFilter ) {
50
89
  return;
51
90
  }
52
- const nextValue = updatedData[ field.id ];
91
+ const nextValue = field.getValue( { item: updatedData } );
53
92
  if ( fastDeepEqual( nextValue, currentValue ) ) {
54
93
  return;
55
94
  }
@@ -0,0 +1,118 @@
1
+ /**
2
+ * WordPress dependencies
3
+ */
4
+ import { useContext, useRef, useCallback, useEffect } from '@wordpress/element';
5
+ import { Button } from '@wordpress/components';
6
+ import { funnel } from '@wordpress/icons';
7
+ import { __, _x } from '@wordpress/i18n';
8
+
9
+ /**
10
+ * Internal dependencies
11
+ */
12
+ import { AddFilterMenu } from './add-filter';
13
+ import DataViewsContext from '../dataviews-context';
14
+ import type { View } from '../../types';
15
+
16
+ function FiltersToggle() {
17
+ const {
18
+ filters,
19
+ view,
20
+ onChangeView,
21
+ setOpenedFilter,
22
+ isShowingFilter,
23
+ setIsShowingFilter,
24
+ } = useContext( DataViewsContext );
25
+
26
+ const buttonRef = useRef< HTMLButtonElement >( null );
27
+ const onChangeViewWithFilterVisibility = useCallback(
28
+ ( _view: View ) => {
29
+ onChangeView( _view );
30
+ setIsShowingFilter( true );
31
+ },
32
+ [ onChangeView, setIsShowingFilter ]
33
+ );
34
+ const visibleFilters = filters.filter( ( filter ) => filter.isVisible );
35
+
36
+ const hasVisibleFilters = !! visibleFilters.length;
37
+ if ( filters.length === 0 ) {
38
+ return null;
39
+ }
40
+
41
+ const addFilterButtonProps = {
42
+ label: __( 'Add filter' ),
43
+ 'aria-expanded': false,
44
+ isPressed: false,
45
+ };
46
+ const toggleFiltersButtonProps = {
47
+ label: _x( 'Filter', 'verb' ),
48
+ 'aria-expanded': isShowingFilter,
49
+ isPressed: isShowingFilter,
50
+ onClick: () => {
51
+ if ( ! isShowingFilter ) {
52
+ setOpenedFilter( null );
53
+ }
54
+ setIsShowingFilter( ! isShowingFilter );
55
+ },
56
+ };
57
+ const buttonComponent = (
58
+ <Button
59
+ ref={ buttonRef }
60
+ className="dataviews-filters__visibility-toggle"
61
+ size="compact"
62
+ icon={ funnel }
63
+ { ...( hasVisibleFilters
64
+ ? toggleFiltersButtonProps
65
+ : addFilterButtonProps ) }
66
+ />
67
+ );
68
+ return (
69
+ <div className="dataviews-filters__container-visibility-toggle">
70
+ { ! hasVisibleFilters ? (
71
+ <AddFilterMenu
72
+ filters={ filters }
73
+ view={ view }
74
+ onChangeView={ onChangeViewWithFilterVisibility }
75
+ setOpenedFilter={ setOpenedFilter }
76
+ triggerProps={ { render: buttonComponent } }
77
+ />
78
+ ) : (
79
+ <FilterVisibilityToggle
80
+ buttonRef={ buttonRef }
81
+ filtersCount={ view.filters?.length }
82
+ >
83
+ { buttonComponent }
84
+ </FilterVisibilityToggle>
85
+ ) }
86
+ </div>
87
+ );
88
+ }
89
+
90
+ function FilterVisibilityToggle( {
91
+ buttonRef,
92
+ filtersCount,
93
+ children,
94
+ }: {
95
+ buttonRef: React.RefObject< HTMLButtonElement >;
96
+ filtersCount?: number;
97
+ children: React.ReactNode;
98
+ } ) {
99
+ // Focus the `add filter` button when unmounts.
100
+ useEffect(
101
+ () => () => {
102
+ buttonRef.current?.focus();
103
+ },
104
+ [ buttonRef ]
105
+ );
106
+ return (
107
+ <>
108
+ { children }
109
+ { !! filtersCount && (
110
+ <span className="dataviews-filters-toggle__count">
111
+ { filtersCount }
112
+ </span>
113
+ ) }
114
+ </>
115
+ );
116
+ }
117
+
118
+ export default FiltersToggle;
@@ -0,0 +1,73 @@
1
+ /**
2
+ * WordPress dependencies
3
+ */
4
+ import { useMemo } from '@wordpress/element';
5
+
6
+ /**
7
+ * Internal dependencies
8
+ */
9
+ import { ALL_OPERATORS, SINGLE_SELECTION_OPERATORS } from '../../constants';
10
+ import type { NormalizedFilter, NormalizedField, View } from '../../types';
11
+
12
+ function useFilters( fields: NormalizedField< any >[], view: View ) {
13
+ return useMemo( () => {
14
+ const filters: NormalizedFilter[] = [];
15
+ fields.forEach( ( field ) => {
16
+ if (
17
+ field.filterBy === false ||
18
+ ( ! field.elements?.length && ! field.Edit )
19
+ ) {
20
+ return;
21
+ }
22
+
23
+ const operators = field.filterBy.operators;
24
+ const isPrimary = !! field.filterBy?.isPrimary;
25
+ const isLocked =
26
+ view.filters?.some(
27
+ ( f ) => f.field === field.id && !! f.isLocked
28
+ ) ?? false;
29
+ filters.push( {
30
+ field: field.id,
31
+ name: field.label,
32
+ elements: field.elements ?? [],
33
+ singleSelection: operators.some( ( op ) =>
34
+ SINGLE_SELECTION_OPERATORS.includes( op )
35
+ ),
36
+ operators,
37
+ isVisible:
38
+ isLocked ||
39
+ isPrimary ||
40
+ !! view.filters?.some(
41
+ ( f ) =>
42
+ f.field === field.id &&
43
+ ALL_OPERATORS.includes( f.operator )
44
+ ),
45
+ isPrimary,
46
+ isLocked,
47
+ } );
48
+ } );
49
+
50
+ // Sort filters by:
51
+ // - locked filters go first
52
+ // - primary filters go next
53
+ // - then, sort by name
54
+ filters.sort( ( a, b ) => {
55
+ if ( a.isLocked && ! b.isLocked ) {
56
+ return -1;
57
+ }
58
+ if ( ! a.isLocked && b.isLocked ) {
59
+ return 1;
60
+ }
61
+ if ( a.isPrimary && ! b.isPrimary ) {
62
+ return -1;
63
+ }
64
+ if ( ! a.isPrimary && b.isPrimary ) {
65
+ return 1;
66
+ }
67
+ return a.name.localeCompare( b.name );
68
+ } );
69
+ return filters;
70
+ }, [ fields, view ] );
71
+ }
72
+
73
+ export default useFilters;
@@ -7,13 +7,7 @@ import type { ReactNode } from 'react';
7
7
  * WordPress dependencies
8
8
  */
9
9
  import { __experimentalHStack as HStack } from '@wordpress/components';
10
- import {
11
- useContext,
12
- useEffect,
13
- useMemo,
14
- useRef,
15
- useState,
16
- } from '@wordpress/element';
10
+ import { useEffect, useMemo, useRef, useState } from '@wordpress/element';
17
11
  import { useResizeObserver, throttle } from '@wordpress/compose';
18
12
 
19
13
  /**
@@ -22,7 +16,8 @@ import { useResizeObserver, throttle } from '@wordpress/compose';
22
16
  import DataViewsContext from '../dataviews-context';
23
17
  import { VIEW_LAYOUTS } from '../../dataviews-layouts';
24
18
  import {
25
- default as DataViewsFilters,
19
+ Filters,
20
+ FiltersToggled,
26
21
  useFilters,
27
22
  FiltersToggle,
28
23
  } from '../dataviews-filters';
@@ -84,7 +79,6 @@ function DefaultUI( {
84
79
  search = true,
85
80
  searchLabel = undefined,
86
81
  }: DefaultUIProps ) {
87
- const { isShowingFilter } = useContext( DataViewsContext );
88
82
  return (
89
83
  <>
90
84
  <HStack
@@ -109,9 +103,7 @@ function DefaultUI( {
109
103
  <DataViewsViewConfig />
110
104
  </HStack>
111
105
  </HStack>
112
- { isShowingFilter && (
113
- <DataViewsFilters className="dataviews-filters__container" />
114
- ) }
106
+ <FiltersToggled className="dataviews-filters__container" />
115
107
  <DataViewsLayout />
116
108
  <DataViewsPickerFooter />
117
109
  </>
@@ -263,7 +255,8 @@ function DataViewsPicker< Item >( {
263
255
  const DataViewsPickerSubComponents =
264
256
  DataViewsPicker as typeof DataViewsPicker & {
265
257
  BulkActionToolbar: typeof DataViewsPickerFooter;
266
- Filters: typeof DataViewsFilters;
258
+ Filters: typeof Filters;
259
+ FiltersToggled: typeof FiltersToggled;
267
260
  FiltersToggle: typeof FiltersToggle;
268
261
  Layout: typeof DataViewsLayout;
269
262
  LayoutSwitcher: typeof ViewTypeMenu;
@@ -273,7 +266,8 @@ const DataViewsPickerSubComponents =
273
266
  };
274
267
 
275
268
  DataViewsPickerSubComponents.BulkActionToolbar = DataViewsPickerFooter;
276
- DataViewsPickerSubComponents.Filters = DataViewsFilters;
269
+ DataViewsPickerSubComponents.Filters = Filters;
270
+ DataViewsPickerSubComponents.FiltersToggled = FiltersToggled;
277
271
  DataViewsPickerSubComponents.FiltersToggle = FiltersToggle;
278
272
  DataViewsPickerSubComponents.Layout = DataViewsLayout;
279
273
  DataViewsPickerSubComponents.LayoutSwitcher = ViewTypeMenu;
@@ -86,7 +86,7 @@ export const Default = ( {
86
86
  .filter(
87
87
  ( item ) => selection?.includes( String( item.id ) )
88
88
  )
89
- .map( ( item ) => item.title )
89
+ .map( ( item ) => item.name.title )
90
90
  .join( ', ' );
91
91
  // eslint-disable-next-line no-alert
92
92
  window.alert( selectedItemNames );
@@ -562,9 +562,10 @@ function FieldControl() {
562
562
  ( f ) =>
563
563
  ! visibleFieldIds.includes( f.id ) &&
564
564
  ! togglableFields.includes( f.id ) &&
565
- f.type !== 'media'
565
+ f.type !== 'media' &&
566
+ f.enableHiding !== false
566
567
  );
567
- const visibleFields = visibleFieldIds
568
+ let visibleFields = visibleFieldIds
568
569
  .map( ( fieldId ) => fields.find( ( f ) => f.id === fieldId ) )
569
570
  .filter( isDefined );
570
571
 
@@ -622,7 +623,7 @@ function FieldControl() {
622
623
  isVisibleFlag: 'showDescription',
623
624
  },
624
625
  ].filter( ( { field } ) => isDefined( field ) );
625
- const visibleLockedFields = lockedFields.filter(
626
+ let visibleLockedFields = lockedFields.filter(
626
627
  ( { field, isVisibleFlag } ) =>
627
628
  // @ts-expect-error
628
629
  isDefined( field ) && ( view[ isVisibleFlag ] ?? true )
@@ -631,6 +632,20 @@ function FieldControl() {
631
632
  isVisibleFlag: string;
632
633
  ui?: ReactNode;
633
634
  } >;
635
+
636
+ // If only one locked field is visible, prevent it from being hidden.
637
+ if ( visibleLockedFields.length === 1 ) {
638
+ visibleLockedFields = visibleLockedFields.map( ( locked ) => ( {
639
+ ...locked,
640
+ field: { ...locked.field, enableHiding: false },
641
+ } ) );
642
+ }
643
+
644
+ // If no locked fields are visible but there are visibleFields, lock the last visible field.
645
+ if ( visibleLockedFields.length === 0 && visibleFields.length === 1 ) {
646
+ visibleFields = [ { ...visibleFields[ 0 ], enableHiding: false } ];
647
+ }
648
+
634
649
  const hiddenLockedFields = lockedFields.filter(
635
650
  ( { field, isVisibleFlag } ) =>
636
651
  // @ts-expect-error