@wordpress/dataviews 5.0.0 → 6.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 (250) hide show
  1. package/CHANGELOG.md +31 -1
  2. package/README.md +55 -27
  3. package/build/components/dataviews/index.js +13 -4
  4. package/build/components/dataviews/index.js.map +1 -1
  5. package/build/components/dataviews-context/index.js +3 -1
  6. package/build/components/dataviews-context/index.js.map +1 -1
  7. package/build/components/dataviews-filters/filter.js +15 -8
  8. package/build/components/dataviews-filters/filter.js.map +1 -1
  9. package/build/components/dataviews-filters/index.js +16 -5
  10. package/build/components/dataviews-filters/index.js.map +1 -1
  11. package/build/components/dataviews-filters/input-widget.js +7 -1
  12. package/build/components/dataviews-filters/input-widget.js.map +1 -1
  13. package/build/components/dataviews-filters/reset-filters.js +2 -2
  14. package/build/components/dataviews-filters/reset-filters.js.map +1 -1
  15. package/build/components/dataviews-layout/index.js +5 -2
  16. package/build/components/dataviews-layout/index.js.map +1 -1
  17. package/build/components/dataviews-view-config/index.js +4 -3
  18. package/build/components/dataviews-view-config/index.js.map +1 -1
  19. package/build/dataform-controls/boolean.js +15 -1
  20. package/build/dataform-controls/boolean.js.map +1 -1
  21. package/build/dataform-controls/date.js +385 -0
  22. package/build/dataform-controls/date.js.map +1 -0
  23. package/build/dataform-controls/datetime.js +5 -84
  24. package/build/dataform-controls/datetime.js.map +1 -1
  25. package/build/dataform-controls/email.js +15 -1
  26. package/build/dataform-controls/email.js.map +1 -1
  27. package/build/dataform-controls/index.js +2 -0
  28. package/build/dataform-controls/index.js.map +1 -1
  29. package/build/dataform-controls/integer.js +23 -4
  30. package/build/dataform-controls/integer.js.map +1 -1
  31. package/build/dataform-controls/relative-date-control.js +109 -0
  32. package/build/dataform-controls/relative-date-control.js.map +1 -0
  33. package/build/dataform-controls/select.js +12 -5
  34. package/build/dataform-controls/select.js.map +1 -1
  35. package/build/dataform-controls/text.js +15 -1
  36. package/build/dataform-controls/text.js.map +1 -1
  37. package/build/dataviews-layouts/grid/index.js +40 -23
  38. package/build/dataviews-layouts/grid/index.js.map +1 -1
  39. package/build/dataviews-layouts/grid/preview-size-picker.js +39 -85
  40. package/build/dataviews-layouts/grid/preview-size-picker.js.map +1 -1
  41. package/build/dataviews-layouts/list/index.js +7 -3
  42. package/build/dataviews-layouts/list/index.js.map +1 -1
  43. package/build/dataviews-layouts/table/column-primary.js +18 -3
  44. package/build/dataviews-layouts/table/column-primary.js.map +1 -1
  45. package/build/dataviews-layouts/table/index.js +57 -5
  46. package/build/dataviews-layouts/table/index.js.map +1 -1
  47. package/build/field-types/array.js +27 -18
  48. package/build/field-types/array.js.map +1 -1
  49. package/build/field-types/boolean.js +11 -7
  50. package/build/field-types/boolean.js.map +1 -1
  51. package/build/field-types/date.js +21 -12
  52. package/build/field-types/date.js.map +1 -1
  53. package/build/field-types/datetime.js +19 -10
  54. package/build/field-types/datetime.js.map +1 -1
  55. package/build/field-types/email.js +22 -18
  56. package/build/field-types/email.js.map +1 -1
  57. package/build/field-types/index.js +16 -6
  58. package/build/field-types/index.js.map +1 -1
  59. package/build/field-types/integer.js +22 -17
  60. package/build/field-types/integer.js.map +1 -1
  61. package/build/field-types/media.js +19 -10
  62. package/build/field-types/media.js.map +1 -1
  63. package/build/field-types/text.js +19 -10
  64. package/build/field-types/text.js.map +1 -1
  65. package/build/filter-and-sort-data-view.js +6 -4
  66. package/build/filter-and-sort-data-view.js.map +1 -1
  67. package/build/normalize-fields.js +4 -5
  68. package/build/normalize-fields.js.map +1 -1
  69. package/build/types.js.map +1 -1
  70. package/build/validation.js +15 -2
  71. package/build/validation.js.map +1 -1
  72. package/build-module/components/dataviews/index.js +15 -6
  73. package/build-module/components/dataviews/index.js.map +1 -1
  74. package/build-module/components/dataviews-context/index.js +3 -1
  75. package/build-module/components/dataviews-context/index.js.map +1 -1
  76. package/build-module/components/dataviews-filters/filter.js +15 -8
  77. package/build-module/components/dataviews-filters/filter.js.map +1 -1
  78. package/build-module/components/dataviews-filters/index.js +16 -5
  79. package/build-module/components/dataviews-filters/index.js.map +1 -1
  80. package/build-module/components/dataviews-filters/input-widget.js +7 -1
  81. package/build-module/components/dataviews-filters/input-widget.js.map +1 -1
  82. package/build-module/components/dataviews-filters/reset-filters.js +2 -2
  83. package/build-module/components/dataviews-filters/reset-filters.js.map +1 -1
  84. package/build-module/components/dataviews-layout/index.js +5 -2
  85. package/build-module/components/dataviews-layout/index.js.map +1 -1
  86. package/build-module/components/dataviews-view-config/index.js +4 -3
  87. package/build-module/components/dataviews-view-config/index.js.map +1 -1
  88. package/build-module/dataform-controls/boolean.js +17 -2
  89. package/build-module/dataform-controls/boolean.js.map +1 -1
  90. package/build-module/dataform-controls/date.js +376 -0
  91. package/build-module/dataform-controls/date.js.map +1 -0
  92. package/build-module/dataform-controls/datetime.js +3 -84
  93. package/build-module/dataform-controls/datetime.js.map +1 -1
  94. package/build-module/dataform-controls/email.js +17 -2
  95. package/build-module/dataform-controls/email.js.map +1 -1
  96. package/build-module/dataform-controls/index.js +2 -0
  97. package/build-module/dataform-controls/index.js.map +1 -1
  98. package/build-module/dataform-controls/integer.js +24 -5
  99. package/build-module/dataform-controls/integer.js.map +1 -1
  100. package/build-module/dataform-controls/relative-date-control.js +100 -0
  101. package/build-module/dataform-controls/relative-date-control.js.map +1 -0
  102. package/build-module/dataform-controls/select.js +12 -5
  103. package/build-module/dataform-controls/select.js.map +1 -1
  104. package/build-module/dataform-controls/text.js +17 -2
  105. package/build-module/dataform-controls/text.js.map +1 -1
  106. package/build-module/dataviews-layouts/grid/index.js +41 -24
  107. package/build-module/dataviews-layouts/grid/index.js.map +1 -1
  108. package/build-module/dataviews-layouts/grid/preview-size-picker.js +40 -85
  109. package/build-module/dataviews-layouts/grid/preview-size-picker.js.map +1 -1
  110. package/build-module/dataviews-layouts/list/index.js +7 -3
  111. package/build-module/dataviews-layouts/list/index.js.map +1 -1
  112. package/build-module/dataviews-layouts/table/column-primary.js +18 -3
  113. package/build-module/dataviews-layouts/table/column-primary.js.map +1 -1
  114. package/build-module/dataviews-layouts/table/index.js +58 -6
  115. package/build-module/dataviews-layouts/table/index.js.map +1 -1
  116. package/build-module/field-types/array.js +27 -18
  117. package/build-module/field-types/array.js.map +1 -1
  118. package/build-module/field-types/boolean.js +11 -7
  119. package/build-module/field-types/boolean.js.map +1 -1
  120. package/build-module/field-types/date.js +21 -12
  121. package/build-module/field-types/date.js.map +1 -1
  122. package/build-module/field-types/datetime.js +19 -10
  123. package/build-module/field-types/datetime.js.map +1 -1
  124. package/build-module/field-types/email.js +22 -18
  125. package/build-module/field-types/email.js.map +1 -1
  126. package/build-module/field-types/index.js +16 -6
  127. package/build-module/field-types/index.js.map +1 -1
  128. package/build-module/field-types/integer.js +22 -17
  129. package/build-module/field-types/integer.js.map +1 -1
  130. package/build-module/field-types/media.js +19 -10
  131. package/build-module/field-types/media.js.map +1 -1
  132. package/build-module/field-types/text.js +19 -10
  133. package/build-module/field-types/text.js.map +1 -1
  134. package/build-module/filter-and-sort-data-view.js +6 -4
  135. package/build-module/filter-and-sort-data-view.js.map +1 -1
  136. package/build-module/normalize-fields.js +4 -5
  137. package/build-module/normalize-fields.js.map +1 -1
  138. package/build-module/types.js.map +1 -1
  139. package/build-module/validation.js +15 -2
  140. package/build-module/validation.js.map +1 -1
  141. package/build-style/style-rtl.css +78 -43
  142. package/build-style/style.css +78 -43
  143. package/build-types/components/dataform/stories/index.story.d.ts +21 -0
  144. package/build-types/components/dataform/stories/index.story.d.ts.map +1 -1
  145. package/build-types/components/dataviews/index.d.ts +3 -2
  146. package/build-types/components/dataviews/index.d.ts.map +1 -1
  147. package/build-types/components/dataviews/stories/fixtures.d.ts.map +1 -1
  148. package/build-types/components/dataviews/stories/index.story.d.ts +16 -3
  149. package/build-types/components/dataviews/stories/index.story.d.ts.map +1 -1
  150. package/build-types/components/dataviews-context/index.d.ts +4 -2
  151. package/build-types/components/dataviews-context/index.d.ts.map +1 -1
  152. package/build-types/components/dataviews-filters/filter.d.ts.map +1 -1
  153. package/build-types/components/dataviews-filters/index.d.ts.map +1 -1
  154. package/build-types/components/dataviews-filters/input-widget.d.ts.map +1 -1
  155. package/build-types/components/dataviews-filters/reset-filters.d.ts.map +1 -1
  156. package/build-types/components/dataviews-layout/index.d.ts.map +1 -1
  157. package/build-types/components/dataviews-view-config/index.d.ts.map +1 -1
  158. package/build-types/constants.d.ts +2 -2
  159. package/build-types/dataform-controls/boolean.d.ts.map +1 -1
  160. package/build-types/dataform-controls/date.d.ts +3 -0
  161. package/build-types/dataform-controls/date.d.ts.map +1 -0
  162. package/build-types/dataform-controls/datetime.d.ts.map +1 -1
  163. package/build-types/dataform-controls/email.d.ts.map +1 -1
  164. package/build-types/dataform-controls/index.d.ts.map +1 -1
  165. package/build-types/dataform-controls/integer.d.ts.map +1 -1
  166. package/build-types/dataform-controls/relative-date-control.d.ts +46 -0
  167. package/build-types/dataform-controls/relative-date-control.d.ts.map +1 -0
  168. package/build-types/dataform-controls/select.d.ts.map +1 -1
  169. package/build-types/dataform-controls/text.d.ts.map +1 -1
  170. package/build-types/dataviews-layouts/grid/index.d.ts +1 -1
  171. package/build-types/dataviews-layouts/grid/index.d.ts.map +1 -1
  172. package/build-types/dataviews-layouts/grid/preview-size-picker.d.ts +0 -1
  173. package/build-types/dataviews-layouts/grid/preview-size-picker.d.ts.map +1 -1
  174. package/build-types/dataviews-layouts/index.d.ts +3 -3
  175. package/build-types/dataviews-layouts/list/index.d.ts.map +1 -1
  176. package/build-types/dataviews-layouts/table/column-primary.d.ts.map +1 -1
  177. package/build-types/dataviews-layouts/table/index.d.ts +1 -1
  178. package/build-types/dataviews-layouts/table/index.d.ts.map +1 -1
  179. package/build-types/field-types/array.d.ts.map +1 -1
  180. package/build-types/field-types/boolean.d.ts +5 -4
  181. package/build-types/field-types/boolean.d.ts.map +1 -1
  182. package/build-types/field-types/date.d.ts +9 -5
  183. package/build-types/field-types/date.d.ts.map +1 -1
  184. package/build-types/field-types/datetime.d.ts +4 -3
  185. package/build-types/field-types/datetime.d.ts.map +1 -1
  186. package/build-types/field-types/email.d.ts +4 -3
  187. package/build-types/field-types/email.d.ts.map +1 -1
  188. package/build-types/field-types/index.d.ts.map +1 -1
  189. package/build-types/field-types/integer.d.ts +4 -3
  190. package/build-types/field-types/integer.d.ts.map +1 -1
  191. package/build-types/field-types/media.d.ts +4 -3
  192. package/build-types/field-types/media.d.ts.map +1 -1
  193. package/build-types/field-types/text.d.ts +4 -3
  194. package/build-types/field-types/text.d.ts.map +1 -1
  195. package/build-types/filter-and-sort-data-view.d.ts.map +1 -1
  196. package/build-types/normalize-fields.d.ts.map +1 -1
  197. package/build-types/types.d.ts +20 -7
  198. package/build-types/types.d.ts.map +1 -1
  199. package/build-types/validation.d.ts.map +1 -1
  200. package/build-wp/index.js +1561 -670
  201. package/package.json +15 -14
  202. package/src/components/dataform/stories/index.story.tsx +229 -2
  203. package/src/components/dataviews/index.tsx +30 -10
  204. package/src/components/dataviews/stories/fixtures.tsx +3 -1
  205. package/src/components/dataviews/stories/index.story.tsx +49 -29
  206. package/src/components/dataviews/stories/style.css +6 -0
  207. package/src/components/dataviews-context/index.ts +8 -2
  208. package/src/components/dataviews-filters/filter.tsx +17 -7
  209. package/src/components/dataviews-filters/index.tsx +17 -2
  210. package/src/components/dataviews-filters/input-widget.tsx +7 -1
  211. package/src/components/dataviews-filters/reset-filters.tsx +4 -2
  212. package/src/components/dataviews-filters/style.scss +8 -2
  213. package/src/components/dataviews-layout/index.tsx +3 -0
  214. package/src/components/dataviews-view-config/index.tsx +5 -3
  215. package/src/dataform-controls/boolean.tsx +19 -2
  216. package/src/dataform-controls/date.tsx +499 -0
  217. package/src/dataform-controls/datetime.tsx +5 -91
  218. package/src/dataform-controls/email.tsx +19 -2
  219. package/src/dataform-controls/index.tsx +2 -0
  220. package/src/dataform-controls/integer.tsx +30 -4
  221. package/src/dataform-controls/relative-date-control.tsx +106 -0
  222. package/src/dataform-controls/select.tsx +23 -13
  223. package/src/dataform-controls/style.scss +19 -2
  224. package/src/dataform-controls/text.tsx +19 -2
  225. package/src/dataviews-layouts/grid/index.tsx +46 -24
  226. package/src/dataviews-layouts/grid/preview-size-picker.tsx +48 -73
  227. package/src/dataviews-layouts/grid/style.scss +15 -28
  228. package/src/dataviews-layouts/list/index.tsx +7 -4
  229. package/src/dataviews-layouts/list/style.scss +3 -3
  230. package/src/dataviews-layouts/table/column-primary.tsx +29 -5
  231. package/src/dataviews-layouts/table/index.tsx +134 -42
  232. package/src/dataviews-layouts/table/style.scss +45 -1
  233. package/src/field-types/array.tsx +33 -21
  234. package/src/field-types/boolean.tsx +15 -9
  235. package/src/field-types/date.ts +51 -15
  236. package/src/field-types/datetime.tsx +19 -13
  237. package/src/field-types/email.tsx +26 -21
  238. package/src/field-types/index.tsx +18 -8
  239. package/src/field-types/integer.tsx +26 -22
  240. package/src/field-types/media.tsx +19 -13
  241. package/src/field-types/text.tsx +19 -13
  242. package/src/filter-and-sort-data-view.ts +11 -4
  243. package/src/normalize-fields.ts +4 -8
  244. package/src/test/dataviews.tsx +129 -0
  245. package/src/test/filter-and-sort-data-view.js +52 -2
  246. package/src/test/validation.ts +4 -15
  247. package/src/types.ts +28 -8
  248. package/src/validation.ts +30 -1
  249. package/tsconfig.json +1 -0
  250. package/tsconfig.tsbuildinfo +1 -1
@@ -1,103 +1,17 @@
1
1
  /**
2
2
  * WordPress dependencies
3
3
  */
4
- import {
5
- BaseControl,
6
- TimePicker,
7
- VisuallyHidden,
8
- SelectControl,
9
- __experimentalNumberControl as NumberControl,
10
- __experimentalHStack as HStack,
11
- } from '@wordpress/components';
4
+ import { BaseControl, TimePicker, VisuallyHidden } from '@wordpress/components';
12
5
  import { useCallback } from '@wordpress/element';
13
- import { __ } from '@wordpress/i18n';
14
6
 
15
7
  /**
16
8
  * Internal dependencies
17
9
  */
18
10
  import type { DataFormControlProps } from '../types';
19
11
  import { OPERATOR_IN_THE_PAST, OPERATOR_OVER } from '../constants';
20
-
21
- const TIME_UNITS_OPTIONS = {
22
- [ OPERATOR_IN_THE_PAST ]: [
23
- { value: 'days', label: __( 'Days' ) },
24
- { value: 'weeks', label: __( 'Weeks' ) },
25
- { value: 'months', label: __( 'Months' ) },
26
- { value: 'years', label: __( 'Years' ) },
27
- ],
28
- [ OPERATOR_OVER ]: [
29
- { value: 'days', label: __( 'Days ago' ) },
30
- { value: 'weeks', label: __( 'Weeks ago' ) },
31
- { value: 'months', label: __( 'Months ago' ) },
32
- { value: 'years', label: __( 'Years ago' ) },
33
- ],
34
- };
35
-
36
- function RelativeDateControls( {
37
- id,
38
- value,
39
- onChange,
40
- label,
41
- hideLabelFromVision,
42
- options,
43
- }: {
44
- id: string;
45
- value: { value?: string | number; unit?: string };
46
- onChange: ( value: any ) => void;
47
- label: string;
48
- hideLabelFromVision?: boolean;
49
- options: { value: string; label: string }[];
50
- } ) {
51
- const { value: relValue = '', unit = options[ 0 ].value } = value;
52
-
53
- const onChangeValue = useCallback(
54
- ( newValue: string | undefined ) =>
55
- onChange( {
56
- [ id ]: { value: Number( newValue ), unit },
57
- } ),
58
- [ id, onChange, unit ]
59
- );
60
-
61
- const onChangeUnit = useCallback(
62
- ( newUnit: string | undefined ) =>
63
- onChange( {
64
- [ id ]: { value: relValue, unit: newUnit },
65
- } ),
66
- [ id, onChange, relValue ]
67
- );
68
-
69
- return (
70
- <BaseControl
71
- id={ id }
72
- __nextHasNoMarginBottom
73
- className="dataviews-controls__datetime"
74
- label={ label }
75
- hideLabelFromVision={ hideLabelFromVision }
76
- >
77
- <HStack spacing={ 2.5 }>
78
- <NumberControl
79
- __next40pxDefaultSize
80
- className="dataviews-controls__datetime-number"
81
- spinControls="none"
82
- min={ 1 }
83
- step={ 1 }
84
- value={ relValue }
85
- onChange={ onChangeValue }
86
- />
87
- <SelectControl
88
- className="dataviews-controls__datetime-unit"
89
- __next40pxDefaultSize
90
- __nextHasNoMarginBottom
91
- label={ __( 'Unit' ) }
92
- value={ unit }
93
- options={ options }
94
- onChange={ onChangeUnit }
95
- hideLabelFromVision
96
- />
97
- </HStack>
98
- </BaseControl>
99
- );
100
- }
12
+ import RelativeDateControl, {
13
+ TIME_UNITS_OPTIONS,
14
+ } from './relative-date-control';
101
15
 
102
16
  export default function DateTime< Item >( {
103
17
  data,
@@ -116,7 +30,7 @@ export default function DateTime< Item >( {
116
30
 
117
31
  if ( operator === OPERATOR_IN_THE_PAST || operator === OPERATOR_OVER ) {
118
32
  return (
119
- <RelativeDateControls
33
+ <RelativeDateControl
120
34
  id={ id }
121
35
  value={ value && typeof value === 'object' ? value : {} }
122
36
  onChange={ onChange }
@@ -1,13 +1,16 @@
1
1
  /**
2
2
  * WordPress dependencies
3
3
  */
4
- import { TextControl } from '@wordpress/components';
4
+ import { privateApis } from '@wordpress/components';
5
5
  import { useCallback } from '@wordpress/element';
6
6
 
7
7
  /**
8
8
  * Internal dependencies
9
9
  */
10
10
  import type { DataFormControlProps } from '../types';
11
+ import { unlock } from '../lock-unlock';
12
+
13
+ const { ValidatedTextControl } = unlock( privateApis );
11
14
 
12
15
  export default function Email< Item >( {
13
16
  data,
@@ -27,7 +30,21 @@ export default function Email< Item >( {
27
30
  );
28
31
 
29
32
  return (
30
- <TextControl
33
+ <ValidatedTextControl
34
+ required={ !! field.isValid?.required }
35
+ customValidator={ ( newValue: any ) => {
36
+ if ( field.isValid?.custom ) {
37
+ return field.isValid.custom(
38
+ {
39
+ ...data,
40
+ [ id ]: newValue,
41
+ },
42
+ field
43
+ );
44
+ }
45
+
46
+ return null;
47
+ } }
31
48
  type="email"
32
49
  label={ label }
33
50
  placeholder={ placeholder }
@@ -13,6 +13,7 @@ import type {
13
13
  } from '../types';
14
14
  import checkbox from './checkbox';
15
15
  import datetime from './datetime';
16
+ import date from './date';
16
17
  import email from './email';
17
18
  import integer from './integer';
18
19
  import radio from './radio';
@@ -29,6 +30,7 @@ const FORM_CONTROLS: FormControls = {
29
30
  boolean,
30
31
  checkbox,
31
32
  datetime,
33
+ date,
32
34
  email,
33
35
  integer,
34
36
  radio,
@@ -5,6 +5,7 @@ import {
5
5
  Flex,
6
6
  BaseControl,
7
7
  __experimentalNumberControl as NumberControl,
8
+ privateApis,
8
9
  } from '@wordpress/components';
9
10
  import { useCallback } from '@wordpress/element';
10
11
  import { __ } from '@wordpress/i18n';
@@ -14,6 +15,9 @@ import { __ } from '@wordpress/i18n';
14
15
  */
15
16
  import { OPERATOR_BETWEEN } from '../constants';
16
17
  import type { DataFormControlProps } from '../types';
18
+ import { unlock } from '../lock-unlock';
19
+
20
+ const { ValidatedNumberControl } = unlock( privateApis );
17
21
 
18
22
  function BetweenControls< Item >( {
19
23
  id,
@@ -81,10 +85,16 @@ export default function Integer< Item >( {
81
85
  const { id, label, description } = field;
82
86
  const value = field.getValue( { item: data } ) ?? '';
83
87
  const onChangeControl = useCallback(
84
- ( newValue: string | undefined ) =>
88
+ ( newValue: string | undefined ) => {
85
89
  onChange( {
86
- [ id ]: Number( newValue ),
87
- } ),
90
+ // Do not convert an empty string or undefined to a number,
91
+ // otherwise there's a mismatch between the UI control (empty)
92
+ // and the data relied by onChange (0).
93
+ [ id ]: [ '', undefined ].includes( newValue )
94
+ ? undefined
95
+ : Number( newValue ),
96
+ } );
97
+ },
88
98
  [ id, onChange ]
89
99
  );
90
100
 
@@ -100,7 +110,23 @@ export default function Integer< Item >( {
100
110
  }
101
111
 
102
112
  return (
103
- <NumberControl
113
+ <ValidatedNumberControl
114
+ required={ !! field.isValid?.required }
115
+ customValidator={ ( newValue: any ) => {
116
+ if ( field.isValid?.custom ) {
117
+ return field.isValid.custom(
118
+ {
119
+ ...data,
120
+ [ id ]: [ undefined, '', null ].includes( newValue )
121
+ ? undefined
122
+ : Number( newValue ),
123
+ },
124
+ field
125
+ );
126
+ }
127
+
128
+ return null;
129
+ } }
104
130
  label={ label }
105
131
  help={ description }
106
132
  value={ value }
@@ -0,0 +1,106 @@
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import clsx from 'clsx';
5
+
6
+ /**
7
+ * WordPress dependencies
8
+ */
9
+ import {
10
+ BaseControl,
11
+ SelectControl,
12
+ __experimentalNumberControl as NumberControl,
13
+ __experimentalHStack as HStack,
14
+ } from '@wordpress/components';
15
+ import { useCallback } from '@wordpress/element';
16
+ import { __ } from '@wordpress/i18n';
17
+
18
+ /**
19
+ * Internal dependencies
20
+ */
21
+ import { OPERATOR_IN_THE_PAST, OPERATOR_OVER } from '../constants';
22
+
23
+ interface RelativeDateControlProps {
24
+ id: string;
25
+ value: { value?: string | number; unit?: string };
26
+ onChange: ( value: any ) => void;
27
+ label: string;
28
+ hideLabelFromVision?: boolean;
29
+ options: { value: string; label: string }[];
30
+ className?: string;
31
+ }
32
+
33
+ export const TIME_UNITS_OPTIONS = {
34
+ [ OPERATOR_IN_THE_PAST ]: [
35
+ { value: 'days', label: __( 'Days' ) },
36
+ { value: 'weeks', label: __( 'Weeks' ) },
37
+ { value: 'months', label: __( 'Months' ) },
38
+ { value: 'years', label: __( 'Years' ) },
39
+ ],
40
+ [ OPERATOR_OVER ]: [
41
+ { value: 'days', label: __( 'Days ago' ) },
42
+ { value: 'weeks', label: __( 'Weeks ago' ) },
43
+ { value: 'months', label: __( 'Months ago' ) },
44
+ { value: 'years', label: __( 'Years ago' ) },
45
+ ],
46
+ };
47
+
48
+ export default function RelativeDateControl( {
49
+ id,
50
+ value,
51
+ onChange,
52
+ label,
53
+ hideLabelFromVision,
54
+ options,
55
+ className,
56
+ }: RelativeDateControlProps ) {
57
+ const { value: relValue = '', unit = options[ 0 ].value } = value;
58
+
59
+ const onChangeValue = useCallback(
60
+ ( newValue: string | undefined ) =>
61
+ onChange( {
62
+ [ id ]: { value: Number( newValue ), unit },
63
+ } ),
64
+ [ id, onChange, unit ]
65
+ );
66
+
67
+ const onChangeUnit = useCallback(
68
+ ( newUnit: string | undefined ) =>
69
+ onChange( {
70
+ [ id ]: { value: relValue, unit: newUnit },
71
+ } ),
72
+ [ id, onChange, relValue ]
73
+ );
74
+
75
+ return (
76
+ <BaseControl
77
+ id={ id }
78
+ __nextHasNoMarginBottom
79
+ className={ clsx( className, 'dataviews-controls__relative-date' ) }
80
+ label={ label }
81
+ hideLabelFromVision={ hideLabelFromVision }
82
+ >
83
+ <HStack spacing={ 2.5 }>
84
+ <NumberControl
85
+ __next40pxDefaultSize
86
+ className="dataviews-controls__relative-date-number"
87
+ spinControls="none"
88
+ min={ 1 }
89
+ step={ 1 }
90
+ value={ relValue }
91
+ onChange={ onChangeValue }
92
+ />
93
+ <SelectControl
94
+ className="dataviews-controls__relative-date-unit"
95
+ __next40pxDefaultSize
96
+ __nextHasNoMarginBottom
97
+ label={ __( 'Unit' ) }
98
+ value={ unit }
99
+ options={ options }
100
+ onChange={ onChangeUnit }
101
+ hideLabelFromVision
102
+ />
103
+ </HStack>
104
+ </BaseControl>
105
+ );
106
+ }
@@ -16,8 +16,9 @@ export default function Select< Item >( {
16
16
  onChange,
17
17
  hideLabelFromVision,
18
18
  }: DataFormControlProps< Item > ) {
19
- const { id, label } = field;
20
- const value = field.getValue( { item: data } ) ?? '';
19
+ const { id, label, type } = field;
20
+ const isMultiple = type === 'array';
21
+ const value = field.getValue( { item: data } ) ?? ( isMultiple ? [] : '' );
21
22
  const onChangeControl = useCallback(
22
23
  ( newValue: any ) =>
23
24
  onChange( {
@@ -26,17 +27,25 @@ export default function Select< Item >( {
26
27
  [ id, onChange ]
27
28
  );
28
29
 
29
- const elements = [
30
- /*
31
- * Value can be undefined when:
32
- *
33
- * - the field is not required
34
- * - in bulk editing
35
- *
36
- */
37
- { label: __( 'Select item' ), value: '' },
38
- ...( field?.elements ?? [] ),
39
- ];
30
+ const fieldElements = field?.elements ?? [];
31
+ const hasEmptyValue = fieldElements.some(
32
+ ( { value: elementValue } ) => elementValue === ''
33
+ );
34
+
35
+ const elements =
36
+ hasEmptyValue || isMultiple
37
+ ? fieldElements
38
+ : [
39
+ /*
40
+ * Value can be undefined when:
41
+ *
42
+ * - the field is not required
43
+ * - in bulk editing
44
+ *
45
+ */
46
+ { label: __( 'Select item' ), value: '' },
47
+ ...fieldElements,
48
+ ];
40
49
 
41
50
  return (
42
51
  <SelectControl
@@ -48,6 +57,7 @@ export default function Select< Item >( {
48
57
  __next40pxDefaultSize
49
58
  __nextHasNoMarginBottom
50
59
  hideLabelFromVision={ hideLabelFromVision }
60
+ multiple={ isMultiple }
51
61
  />
52
62
  );
53
63
  }
@@ -3,7 +3,24 @@
3
3
  padding: 0;
4
4
  }
5
5
 
6
- .dataviews-controls__datetime-number,
7
- .dataviews-controls__datetime-unit {
6
+ .dataviews-controls__relative-date-number,
7
+ .dataviews-controls__relative-date-unit {
8
8
  flex: 1 1 50%;
9
9
  }
10
+
11
+ .dataviews-controls__date {
12
+ // Hide the native date picker icon since we're using our own calendar.
13
+ input[type="date"]::-webkit-inner-spin-button,
14
+ input[type="date"]::-webkit-calendar-picker-indicator {
15
+ display: none;
16
+ -webkit-appearance: none;
17
+ }
18
+ }
19
+
20
+ .dataviews-controls__date-preset {
21
+ border: 1px solid #ddd;
22
+
23
+ &:active {
24
+ background-color: $black;
25
+ }
26
+ }
@@ -1,13 +1,16 @@
1
1
  /**
2
2
  * WordPress dependencies
3
3
  */
4
- import { TextControl } from '@wordpress/components';
4
+ import { privateApis } from '@wordpress/components';
5
5
  import { useCallback } from '@wordpress/element';
6
6
 
7
7
  /**
8
8
  * Internal dependencies
9
9
  */
10
10
  import type { DataFormControlProps } from '../types';
11
+ import { unlock } from '../lock-unlock';
12
+
13
+ const { ValidatedTextControl } = unlock( privateApis );
11
14
 
12
15
  export default function Text< Item >( {
13
16
  data,
@@ -27,7 +30,21 @@ export default function Text< Item >( {
27
30
  );
28
31
 
29
32
  return (
30
- <TextControl
33
+ <ValidatedTextControl
34
+ required={ !! field.isValid?.required }
35
+ customValidator={ ( newValue: any ) => {
36
+ if ( field.isValid?.custom ) {
37
+ return field.isValid.custom(
38
+ {
39
+ ...data,
40
+ [ id ]: newValue,
41
+ },
42
+ field
43
+ );
44
+ }
45
+
46
+ return null;
47
+ } }
31
48
  label={ label }
32
49
  placeholder={ placeholder }
33
50
  value={ value ?? '' }
@@ -8,7 +8,6 @@ import type { ComponentProps, ReactElement } from 'react';
8
8
  * WordPress dependencies
9
9
  */
10
10
  import {
11
- __experimentalGrid as Grid,
12
11
  __experimentalHStack as HStack,
13
12
  __experimentalVStack as VStack,
14
13
  Spinner,
@@ -18,6 +17,8 @@ import {
18
17
  } from '@wordpress/components';
19
18
  import { __, sprintf } from '@wordpress/i18n';
20
19
  import { useInstanceId } from '@wordpress/compose';
20
+ import { isAppleOS } from '@wordpress/keycodes';
21
+ import { useContext } from '@wordpress/element';
21
22
 
22
23
  /**
23
24
  * Internal dependencies
@@ -25,6 +26,7 @@ import { useInstanceId } from '@wordpress/compose';
25
26
  import { unlock } from '../../lock-unlock';
26
27
  import ItemActions from '../../components/dataviews-item-actions';
27
28
  import DataViewsSelectionCheckbox from '../../components/dataviews-selection-checkbox';
29
+ import DataViewsContext from '../../components/dataviews-context';
28
30
  import {
29
31
  useHasAPossibleBulkAction,
30
32
  useSomeItemHasAPossibleBulkAction,
@@ -37,7 +39,6 @@ import type {
37
39
  } from '../../types';
38
40
  import type { SetSelection } from '../../private-types';
39
41
  import { ItemClickWrapper } from '../utils/item-click-wrapper';
40
- import { useUpdatedPreviewSizeOnViewportChange } from './preview-size-picker';
41
42
  const { Badge } = unlock( componentsPrivateApis );
42
43
 
43
44
  interface GridItemProps< Item > {
@@ -60,6 +61,9 @@ interface GridItemProps< Item > {
60
61
  regularFields: NormalizedField< Item >[];
61
62
  badgeFields: NormalizedField< Item >[];
62
63
  hasBulkActions: boolean;
64
+ config: {
65
+ sizes: string;
66
+ };
63
67
  }
64
68
 
65
69
  function GridItem< Item >( {
@@ -78,6 +82,7 @@ function GridItem< Item >( {
78
82
  regularFields,
79
83
  badgeFields,
80
84
  hasBulkActions,
85
+ config,
81
86
  }: GridItemProps< Item > ) {
82
87
  const { showTitle = true, showMedia = true, showDescription = true } = view;
83
88
  const hasBulkAction = useHasAPossibleBulkAction( actions, item );
@@ -85,7 +90,11 @@ function GridItem< Item >( {
85
90
  const instanceId = useInstanceId( GridItem );
86
91
  const isSelected = selection.includes( id );
87
92
  const renderedMediaField = mediaField?.render ? (
88
- <mediaField.render item={ item } field={ mediaField } />
93
+ <mediaField.render
94
+ item={ item }
95
+ field={ mediaField }
96
+ config={ config }
97
+ />
89
98
  ) : null;
90
99
  const renderedTitleField =
91
100
  showTitle && titleField?.render ? (
@@ -117,7 +126,7 @@ function GridItem< Item >( {
117
126
  'is-selected': hasBulkAction && isSelected,
118
127
  } ) }
119
128
  onClickCapture={ ( event ) => {
120
- if ( event.ctrlKey || event.metaKey ) {
129
+ if ( isAppleOS() ? event.metaKey : event.ctrlKey ) {
121
130
  event.stopPropagation();
122
131
  event.preventDefault();
123
132
  if ( ! hasBulkAction ) {
@@ -254,7 +263,9 @@ function ViewGrid< Item >( {
254
263
  selection,
255
264
  view,
256
265
  className,
266
+ empty,
257
267
  }: ViewGridProps< Item > ) {
268
+ const { resizeObserverRef } = useContext( DataViewsContext );
258
269
  const titleField = fields.find(
259
270
  ( field ) => field.id === view?.titleField
260
271
  );
@@ -285,14 +296,15 @@ function ViewGrid< Item >( {
285
296
  { regularFields: [], badgeFields: [] }
286
297
  );
287
298
  const hasData = !! data?.length;
288
- const updatedPreviewSize = useUpdatedPreviewSizeOnViewportChange();
289
299
  const hasBulkActions = useSomeItemHasAPossibleBulkAction( actions, data );
290
- const usedPreviewSize = updatedPreviewSize || view.layout?.previewSize;
291
- const gridStyle = usedPreviewSize
292
- ? {
293
- gridTemplateColumns: `repeat(${ usedPreviewSize }, minmax(0, 1fr))`,
294
- }
295
- : {};
300
+ const usedPreviewSize = view.layout?.previewSize;
301
+ /*
302
+ * This is the maximum width that an image can achieve in the grid. The reasoning is:
303
+ * The biggest min image width available is 430px (see /dataviews-layouts/grid/preview-size-picker.tsx).
304
+ * Because the grid is responsive, once there is room for another column, the images shrink to accommodate it.
305
+ * So each image will never grow past 2*430px plus a little more to account for the gaps.
306
+ */
307
+ const size = '900px';
296
308
 
297
309
  const groupField = view.groupByField
298
310
  ? fields.find( ( f ) => f.id === view.groupByField )
@@ -327,16 +339,18 @@ function ViewGrid< Item >( {
327
339
  groupName
328
340
  ) }
329
341
  </h3>
330
- <Grid
331
- gap={ 8 }
332
- columns={ 2 }
333
- alignment="top"
342
+ <div
334
343
  className={ clsx(
335
344
  'dataviews-view-grid',
336
345
  className
337
346
  ) }
338
- style={ gridStyle }
347
+ style={ {
348
+ gridTemplateColumns:
349
+ usedPreviewSize &&
350
+ `repeat(auto-fill, minmax(${ usedPreviewSize }px, 1fr))`,
351
+ } }
339
352
  aria-busy={ isLoading }
353
+ ref={ resizeObserverRef }
340
354
  >
341
355
  { groupItems.map( ( item ) => {
342
356
  return (
@@ -369,10 +383,13 @@ function ViewGrid< Item >( {
369
383
  hasBulkActions={
370
384
  hasBulkActions
371
385
  }
386
+ config={ {
387
+ sizes: size,
388
+ } }
372
389
  />
373
390
  );
374
391
  } ) }
375
- </Grid>
392
+ </div>
376
393
  </VStack>
377
394
  )
378
395
  ) }
@@ -383,13 +400,15 @@ function ViewGrid< Item >( {
383
400
  {
384
401
  // Render a single grid with all data.
385
402
  hasData && ! dataByGroup && (
386
- <Grid
387
- gap={ 8 }
388
- columns={ 2 }
389
- alignment="top"
403
+ <div
390
404
  className={ clsx( 'dataviews-view-grid', className ) }
391
- style={ gridStyle }
405
+ style={ {
406
+ gridTemplateColumns:
407
+ usedPreviewSize &&
408
+ `repeat(auto-fill, minmax(${ usedPreviewSize }px, 1fr))`,
409
+ } }
392
410
  aria-busy={ isLoading }
411
+ ref={ resizeObserverRef }
393
412
  >
394
413
  { data.map( ( item ) => {
395
414
  return (
@@ -410,10 +429,13 @@ function ViewGrid< Item >( {
410
429
  regularFields={ regularFields }
411
430
  badgeFields={ badgeFields }
412
431
  hasBulkActions={ hasBulkActions }
432
+ config={ {
433
+ sizes: size,
434
+ } }
413
435
  />
414
436
  );
415
437
  } ) }
416
- </Grid>
438
+ </div>
417
439
  )
418
440
  }
419
441
  {
@@ -425,7 +447,7 @@ function ViewGrid< Item >( {
425
447
  'dataviews-no-results': ! isLoading,
426
448
  } ) }
427
449
  >
428
- <p>{ isLoading ? <Spinner /> : __( 'No results' ) }</p>
450
+ <p>{ isLoading ? <Spinner /> : empty }</p>
429
451
  </div>
430
452
  )
431
453
  }