@wordpress/dataviews 4.1.0 → 4.3.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 (223) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/README.md +77 -29
  3. package/build/components/dataviews/index.js +10 -14
  4. package/build/components/dataviews/index.js.map +1 -1
  5. package/build/components/dataviews-bulk-actions/index.js +145 -141
  6. package/build/components/dataviews-bulk-actions/index.js.map +1 -1
  7. package/build/components/dataviews-filters/add-filter.js +4 -6
  8. package/build/components/dataviews-filters/add-filter.js.map +1 -1
  9. package/build/components/dataviews-filters/index.js +3 -0
  10. package/build/components/dataviews-filters/index.js.map +1 -1
  11. package/build/components/dataviews-filters/search-widget.js +30 -23
  12. package/build/components/dataviews-filters/search-widget.js.map +1 -1
  13. package/build/components/dataviews-footer/index.js +45 -0
  14. package/build/components/dataviews-footer/index.js.map +1 -0
  15. package/build/components/dataviews-item-actions/index.js +5 -8
  16. package/build/components/dataviews-item-actions/index.js.map +1 -1
  17. package/build/components/dataviews-pagination/index.js +27 -19
  18. package/build/components/dataviews-pagination/index.js.map +1 -1
  19. package/build/components/dataviews-view-config/index.js +197 -41
  20. package/build/components/dataviews-view-config/index.js.map +1 -1
  21. package/build/dataform-controls/datetime.js +49 -0
  22. package/build/dataform-controls/datetime.js.map +1 -0
  23. package/build/dataform-controls/index.js +50 -0
  24. package/build/dataform-controls/index.js.map +1 -0
  25. package/build/dataform-controls/integer.js +45 -0
  26. package/build/dataform-controls/integer.js.map +1 -0
  27. package/build/dataform-controls/radio.js +45 -0
  28. package/build/dataform-controls/radio.js.map +1 -0
  29. package/build/dataform-controls/select.js +58 -0
  30. package/build/dataform-controls/select.js.map +1 -0
  31. package/build/dataform-controls/text.js +45 -0
  32. package/build/dataform-controls/text.js.map +1 -0
  33. package/build/dataforms-layouts/panel/index.js +10 -4
  34. package/build/dataforms-layouts/panel/index.js.map +1 -1
  35. package/build/dataforms-layouts/regular/index.js +6 -3
  36. package/build/dataforms-layouts/regular/index.js.map +1 -1
  37. package/build/dataviews-layouts/grid/density-picker.js +23 -52
  38. package/build/dataviews-layouts/grid/density-picker.js.map +1 -1
  39. package/build/dataviews-layouts/grid/index.js +1 -1
  40. package/build/dataviews-layouts/grid/index.js.map +1 -1
  41. package/build/dataviews-layouts/index.js +48 -2
  42. package/build/dataviews-layouts/index.js.map +1 -1
  43. package/build/dataviews-layouts/list/index.js +124 -80
  44. package/build/dataviews-layouts/list/index.js.map +1 -1
  45. package/build/dataviews-layouts/table/column-header-menu.js +52 -57
  46. package/build/dataviews-layouts/table/column-header-menu.js.map +1 -1
  47. package/build/dataviews-layouts/table/index.js +7 -35
  48. package/build/dataviews-layouts/table/index.js.map +1 -1
  49. package/build/field-types/datetime.js +30 -0
  50. package/build/field-types/datetime.js.map +1 -0
  51. package/build/field-types/index.js +4 -0
  52. package/build/field-types/index.js.map +1 -1
  53. package/build/field-types/integer.js +1 -60
  54. package/build/field-types/integer.js.map +1 -1
  55. package/build/field-types/text.js +1 -60
  56. package/build/field-types/text.js.map +1 -1
  57. package/build/normalize-fields.js +10 -9
  58. package/build/normalize-fields.js.map +1 -1
  59. package/build/types.js.map +1 -1
  60. package/build-module/components/dataviews/index.js +10 -14
  61. package/build-module/components/dataviews/index.js.map +1 -1
  62. package/build-module/components/dataviews-bulk-actions/index.js +145 -143
  63. package/build-module/components/dataviews-bulk-actions/index.js.map +1 -1
  64. package/build-module/components/dataviews-filters/add-filter.js +4 -6
  65. package/build-module/components/dataviews-filters/add-filter.js.map +1 -1
  66. package/build-module/components/dataviews-filters/index.js +3 -0
  67. package/build-module/components/dataviews-filters/index.js.map +1 -1
  68. package/build-module/components/dataviews-filters/search-widget.js +30 -23
  69. package/build-module/components/dataviews-filters/search-widget.js.map +1 -1
  70. package/build-module/components/dataviews-footer/index.js +38 -0
  71. package/build-module/components/dataviews-footer/index.js.map +1 -0
  72. package/build-module/components/dataviews-item-actions/index.js +5 -8
  73. package/build-module/components/dataviews-item-actions/index.js.map +1 -1
  74. package/build-module/components/dataviews-pagination/index.js +28 -20
  75. package/build-module/components/dataviews-pagination/index.js.map +1 -1
  76. package/build-module/components/dataviews-view-config/index.js +203 -47
  77. package/build-module/components/dataviews-view-config/index.js.map +1 -1
  78. package/build-module/dataform-controls/datetime.js +43 -0
  79. package/build-module/dataform-controls/datetime.js.map +1 -0
  80. package/build-module/dataform-controls/index.js +42 -0
  81. package/build-module/dataform-controls/index.js.map +1 -0
  82. package/build-module/dataform-controls/integer.js +38 -0
  83. package/build-module/dataform-controls/integer.js.map +1 -0
  84. package/build-module/dataform-controls/radio.js +38 -0
  85. package/build-module/dataform-controls/radio.js.map +1 -0
  86. package/build-module/dataform-controls/select.js +51 -0
  87. package/build-module/dataform-controls/select.js.map +1 -0
  88. package/build-module/dataform-controls/text.js +38 -0
  89. package/build-module/dataform-controls/text.js.map +1 -0
  90. package/build-module/dataforms-layouts/panel/index.js +10 -4
  91. package/build-module/dataforms-layouts/panel/index.js.map +1 -1
  92. package/build-module/dataforms-layouts/regular/index.js +6 -3
  93. package/build-module/dataforms-layouts/regular/index.js.map +1 -1
  94. package/build-module/dataviews-layouts/grid/density-picker.js +25 -56
  95. package/build-module/dataviews-layouts/grid/density-picker.js.map +1 -1
  96. package/build-module/dataviews-layouts/grid/index.js +1 -1
  97. package/build-module/dataviews-layouts/grid/index.js.map +1 -1
  98. package/build-module/dataviews-layouts/index.js +45 -1
  99. package/build-module/dataviews-layouts/index.js.map +1 -1
  100. package/build-module/dataviews-layouts/list/index.js +125 -80
  101. package/build-module/dataviews-layouts/list/index.js.map +1 -1
  102. package/build-module/dataviews-layouts/table/column-header-menu.js +52 -57
  103. package/build-module/dataviews-layouts/table/column-header-menu.js.map +1 -1
  104. package/build-module/dataviews-layouts/table/index.js +9 -37
  105. package/build-module/dataviews-layouts/table/index.js.map +1 -1
  106. package/build-module/field-types/datetime.js +24 -0
  107. package/build-module/field-types/datetime.js.map +1 -0
  108. package/build-module/field-types/index.js +4 -0
  109. package/build-module/field-types/index.js.map +1 -1
  110. package/build-module/field-types/integer.js +2 -60
  111. package/build-module/field-types/integer.js.map +1 -1
  112. package/build-module/field-types/text.js +2 -60
  113. package/build-module/field-types/text.js.map +1 -1
  114. package/build-module/normalize-fields.js +11 -9
  115. package/build-module/normalize-fields.js.map +1 -1
  116. package/build-module/types.js.map +1 -1
  117. package/build-style/style-rtl.css +93 -80
  118. package/build-style/style.css +93 -80
  119. package/build-types/components/dataform/stories/index.story.d.ts.map +1 -1
  120. package/build-types/components/dataviews/index.d.ts.map +1 -1
  121. package/build-types/components/dataviews/stories/fixtures.d.ts +28 -113
  122. package/build-types/components/dataviews/stories/fixtures.d.ts.map +1 -1
  123. package/build-types/components/dataviews/stories/index.story.d.ts +12 -44
  124. package/build-types/components/dataviews/stories/index.story.d.ts.map +1 -1
  125. package/build-types/components/dataviews-bulk-actions/index.d.ts +11 -1
  126. package/build-types/components/dataviews-bulk-actions/index.d.ts.map +1 -1
  127. package/build-types/components/dataviews-filters/add-filter.d.ts.map +1 -1
  128. package/build-types/components/dataviews-filters/index.d.ts +1 -1
  129. package/build-types/components/dataviews-filters/index.d.ts.map +1 -1
  130. package/build-types/components/dataviews-filters/search-widget.d.ts.map +1 -1
  131. package/build-types/components/dataviews-footer/index.d.ts +2 -0
  132. package/build-types/components/dataviews-footer/index.d.ts.map +1 -0
  133. package/build-types/components/dataviews-item-actions/index.d.ts.map +1 -1
  134. package/build-types/components/dataviews-pagination/index.d.ts.map +1 -1
  135. package/build-types/components/dataviews-view-config/index.d.ts +4 -3
  136. package/build-types/components/dataviews-view-config/index.d.ts.map +1 -1
  137. package/build-types/dataform-controls/datetime.d.ts +6 -0
  138. package/build-types/dataform-controls/datetime.d.ts.map +1 -0
  139. package/build-types/dataform-controls/index.d.ts +11 -0
  140. package/build-types/dataform-controls/index.d.ts.map +1 -0
  141. package/build-types/dataform-controls/integer.d.ts +6 -0
  142. package/build-types/dataform-controls/integer.d.ts.map +1 -0
  143. package/build-types/dataform-controls/radio.d.ts +6 -0
  144. package/build-types/dataform-controls/radio.d.ts.map +1 -0
  145. package/build-types/dataform-controls/select.d.ts +6 -0
  146. package/build-types/dataform-controls/select.d.ts.map +1 -0
  147. package/build-types/dataform-controls/text.d.ts +6 -0
  148. package/build-types/dataform-controls/text.d.ts.map +1 -0
  149. package/build-types/dataforms-layouts/panel/index.d.ts.map +1 -1
  150. package/build-types/dataforms-layouts/regular/index.d.ts.map +1 -1
  151. package/build-types/dataviews-layouts/grid/density-picker.d.ts.map +1 -1
  152. package/build-types/dataviews-layouts/index.d.ts +4 -2
  153. package/build-types/dataviews-layouts/index.d.ts.map +1 -1
  154. package/build-types/dataviews-layouts/list/index.d.ts.map +1 -1
  155. package/build-types/dataviews-layouts/table/column-header-menu.d.ts.map +1 -1
  156. package/build-types/dataviews-layouts/table/index.d.ts.map +1 -1
  157. package/build-types/field-types/datetime.d.ts +13 -0
  158. package/build-types/field-types/datetime.d.ts.map +1 -0
  159. package/build-types/field-types/index.d.ts +1 -1
  160. package/build-types/field-types/index.d.ts.map +1 -1
  161. package/build-types/field-types/integer.d.ts +2 -3
  162. package/build-types/field-types/integer.d.ts.map +1 -1
  163. package/build-types/field-types/text.d.ts +2 -3
  164. package/build-types/field-types/text.d.ts.map +1 -1
  165. package/build-types/normalize-fields.d.ts.map +1 -1
  166. package/build-types/types.d.ts +43 -21
  167. package/build-types/types.d.ts.map +1 -1
  168. package/package.json +12 -12
  169. package/src/components/dataform/stories/index.story.tsx +43 -2
  170. package/src/components/dataviews/index.tsx +14 -18
  171. package/src/components/dataviews/stories/fixtures.tsx +690 -0
  172. package/src/components/dataviews/stories/index.story.tsx +164 -0
  173. package/src/components/dataviews/style.scss +2 -12
  174. package/src/components/dataviews-bulk-actions/index.tsx +264 -213
  175. package/src/components/dataviews-bulk-actions/style.scss +9 -4
  176. package/src/components/dataviews-filters/add-filter.tsx +7 -11
  177. package/src/components/dataviews-filters/index.tsx +3 -0
  178. package/src/components/dataviews-filters/search-widget.tsx +46 -25
  179. package/src/components/dataviews-filters/style.scss +13 -3
  180. package/src/components/dataviews-footer/index.tsx +50 -0
  181. package/src/components/dataviews-footer/style.scss +40 -0
  182. package/src/components/dataviews-item-actions/index.tsx +8 -14
  183. package/src/components/dataviews-pagination/index.tsx +40 -21
  184. package/src/components/dataviews-pagination/style.scss +7 -21
  185. package/src/components/dataviews-view-config/index.tsx +297 -69
  186. package/src/components/dataviews-view-config/style.scss +25 -0
  187. package/src/dataform-controls/datetime.tsx +43 -0
  188. package/src/dataform-controls/index.tsx +61 -0
  189. package/src/dataform-controls/integer.tsx +38 -0
  190. package/src/dataform-controls/radio.tsx +42 -0
  191. package/src/dataform-controls/select.tsx +52 -0
  192. package/src/dataform-controls/style.scss +4 -0
  193. package/src/dataform-controls/text.tsx +40 -0
  194. package/src/dataforms-layouts/panel/index.tsx +8 -2
  195. package/src/dataforms-layouts/regular/index.tsx +6 -2
  196. package/src/dataviews-layouts/grid/density-picker.tsx +33 -67
  197. package/src/dataviews-layouts/grid/index.tsx +1 -1
  198. package/src/dataviews-layouts/grid/style.scss +1 -5
  199. package/src/dataviews-layouts/index.ts +63 -2
  200. package/src/dataviews-layouts/list/index.tsx +199 -123
  201. package/src/dataviews-layouts/list/style.scss +10 -4
  202. package/src/dataviews-layouts/table/column-header-menu.tsx +86 -90
  203. package/src/dataviews-layouts/table/index.tsx +8 -65
  204. package/src/dataviews-layouts/table/style.scss +0 -5
  205. package/src/field-types/datetime.tsx +28 -0
  206. package/src/field-types/index.tsx +5 -0
  207. package/src/field-types/integer.tsx +2 -71
  208. package/src/field-types/text.tsx +2 -70
  209. package/src/normalize-fields.ts +10 -10
  210. package/src/style.scss +2 -1
  211. package/src/test/filter-and-sort-data-view.js +28 -0
  212. package/src/types.ts +56 -32
  213. package/tsconfig.tsbuildinfo +1 -1
  214. package/build/components/dataviews-bulk-actions-toolbar/index.js +0 -207
  215. package/build/components/dataviews-bulk-actions-toolbar/index.js.map +0 -1
  216. package/build-module/components/dataviews-bulk-actions-toolbar/index.js +0 -201
  217. package/build-module/components/dataviews-bulk-actions-toolbar/index.js.map +0 -1
  218. package/build-types/components/dataviews-bulk-actions-toolbar/index.d.ts +0 -2
  219. package/build-types/components/dataviews-bulk-actions-toolbar/index.d.ts.map +0 -1
  220. package/src/components/dataviews/stories/fixtures.js +0 -222
  221. package/src/components/dataviews/stories/index.story.js +0 -65
  222. package/src/components/dataviews-bulk-actions-toolbar/index.tsx +0 -288
  223. package/src/components/dataviews-bulk-actions-toolbar/style.scss +0 -45
@@ -21,26 +21,35 @@ import {
21
21
  __experimentalHeading as Heading,
22
22
  __experimentalText as Text,
23
23
  privateApis as componentsPrivateApis,
24
+ BaseControl,
24
25
  } from '@wordpress/components';
25
- import { __, _x } from '@wordpress/i18n';
26
+ import { __, _x, sprintf } from '@wordpress/i18n';
26
27
  import { memo, useContext, useState, useMemo } from '@wordpress/element';
27
- import { cog, seen, unseen } from '@wordpress/icons';
28
+ import { chevronDown, chevronUp, cog, seen, unseen } from '@wordpress/icons';
28
29
  import warning from '@wordpress/warning';
29
30
 
30
31
  /**
31
32
  * Internal dependencies
32
33
  */
33
- import { SORTING_DIRECTIONS, sortIcons, sortLabels } from '../../constants';
34
- import { VIEW_LAYOUTS, getMandatoryFields } from '../../dataviews-layouts';
35
- import type { SupportedLayouts } from '../../types';
34
+ import {
35
+ SORTING_DIRECTIONS,
36
+ LAYOUT_GRID,
37
+ LAYOUT_TABLE,
38
+ sortIcons,
39
+ sortLabels,
40
+ } from '../../constants';
41
+ import {
42
+ VIEW_LAYOUTS,
43
+ getNotHidableFieldIds,
44
+ getVisibleFieldIds,
45
+ getHiddenFieldIds,
46
+ } from '../../dataviews-layouts';
47
+ import type { SupportedLayouts, View, Field } from '../../types';
36
48
  import DataViewsContext from '../dataviews-context';
37
49
  import { unlock } from '../../lock-unlock';
50
+ import DensityPicker from '../../dataviews-layouts/grid/density-picker';
38
51
 
39
- const {
40
- DropdownMenuV2: DropdownMenu,
41
- DropdownMenuRadioItemV2: DropdownMenuRadioItem,
42
- DropdownMenuItemLabelV2: DropdownMenuItemLabel,
43
- } = unlock( componentsPrivateApis );
52
+ const { DropdownMenuV2 } = unlock( componentsPrivateApis );
44
53
 
45
54
  interface ViewTypeMenuProps {
46
55
  defaultLayouts?: SupportedLayouts;
@@ -56,7 +65,7 @@ function ViewTypeMenu( {
56
65
  }
57
66
  const activeView = VIEW_LAYOUTS.find( ( v ) => view.type === v.type );
58
67
  return (
59
- <DropdownMenu
68
+ <DropdownMenuV2
60
69
  trigger={
61
70
  <Button
62
71
  size="compact"
@@ -71,7 +80,7 @@ function ViewTypeMenu( {
71
80
  return null;
72
81
  }
73
82
  return (
74
- <DropdownMenuRadioItem
83
+ <DropdownMenuV2.RadioItem
75
84
  key={ layout }
76
85
  value={ layout }
77
86
  name="view-actions-available-view"
@@ -91,20 +100,16 @@ function ViewTypeMenu( {
91
100
  warning( 'Invalid dataview' );
92
101
  } }
93
102
  >
94
- <DropdownMenuItemLabel>
103
+ <DropdownMenuV2.ItemLabel>
95
104
  { config.label }
96
- </DropdownMenuItemLabel>
97
- </DropdownMenuRadioItem>
105
+ </DropdownMenuV2.ItemLabel>
106
+ </DropdownMenuV2.RadioItem>
98
107
  );
99
108
  } ) }
100
- </DropdownMenu>
109
+ </DropdownMenuV2>
101
110
  );
102
111
  }
103
112
 
104
- interface ViewActionsProps {
105
- defaultLayouts?: SupportedLayouts;
106
- }
107
-
108
113
  function SortFieldControl() {
109
114
  const { view, fields, onChangeView } = useContext( DataViewsContext );
110
115
  const orderOptions = useMemo( () => {
@@ -140,7 +145,19 @@ function SortFieldControl() {
140
145
  }
141
146
 
142
147
  function SortDirectionControl() {
143
- const { view, onChangeView } = useContext( DataViewsContext );
148
+ const { view, fields, onChangeView } = useContext( DataViewsContext );
149
+
150
+ const sortableFields = fields.filter(
151
+ ( field ) => field.enableSorting !== false
152
+ );
153
+ if ( sortableFields.length === 0 ) {
154
+ return null;
155
+ }
156
+
157
+ let value = view.sort?.direction;
158
+ if ( ! value && view.sort?.field ) {
159
+ value = 'desc';
160
+ }
144
161
  return (
145
162
  <ToggleGroupControl
146
163
  className="dataviews-view-config__sort-direction"
@@ -148,18 +165,20 @@ function SortDirectionControl() {
148
165
  __next40pxDefaultSize
149
166
  isBlock
150
167
  label={ __( 'Order' ) }
151
- value={ view.sort?.direction || 'desc' }
152
- disabled={ ! view?.sort?.field }
168
+ value={ value }
153
169
  onChange={ ( newDirection ) => {
154
- if ( ! view?.sort?.field ) {
155
- return;
156
- }
157
170
  if ( newDirection === 'asc' || newDirection === 'desc' ) {
158
171
  onChangeView( {
159
172
  ...view,
160
173
  sort: {
161
174
  direction: newDirection,
162
- field: view.sort.field,
175
+ field:
176
+ view.sort?.field ||
177
+ // If there is no field assigned as the sorting field assign the first sortable field.
178
+ fields.find(
179
+ ( field ) => field.enableSorting !== false
180
+ )?.id ||
181
+ '',
163
182
  },
164
183
  } );
165
184
  return;
@@ -218,51 +237,238 @@ function ItemsPerPageControl() {
218
237
  );
219
238
  }
220
239
 
221
- function FieldControl() {
222
- const { view, fields, onChangeView } = useContext( DataViewsContext );
223
- const mandatoryFields = getMandatoryFields( view );
224
- const hidableFields = fields.filter(
225
- ( field ) =>
226
- field.enableHiding !== false &&
227
- ! mandatoryFields.includes( field.id )
228
- );
229
- const viewFields = view.fields || fields.map( ( field ) => field.id );
230
- if ( ! hidableFields?.length ) {
231
- return null;
232
- }
240
+ interface FieldItemProps {
241
+ id: any;
242
+ label: string;
243
+ index: number;
244
+ isVisible: boolean;
245
+ isHidable: boolean;
246
+ }
247
+
248
+ function FieldItem( {
249
+ field: { id, label, index, isVisible, isHidable },
250
+ fields,
251
+ view,
252
+ onChangeView,
253
+ }: {
254
+ field: FieldItemProps;
255
+ fields: Field< any >[];
256
+ view: View;
257
+ onChangeView: ( view: View ) => void;
258
+ } ) {
259
+ const visibleFieldIds = getVisibleFieldIds( view, fields );
260
+
233
261
  return (
234
- <ItemGroup isBordered isSeparated>
235
- { hidableFields?.map( ( field ) => {
236
- const isVisible = viewFields.includes( field.id );
237
- return (
238
- <Item key={ field.id }>
239
- <HStack expanded>
240
- <span>{ field.label }</span>
262
+ <Item key={ id }>
263
+ <HStack
264
+ expanded
265
+ className={ `dataviews-field-control__field dataviews-field-control__field-${ id }` }
266
+ >
267
+ <span>{ label }</span>
268
+ <HStack
269
+ justify="flex-end"
270
+ expanded={ false }
271
+ className="dataviews-field-control__actions"
272
+ >
273
+ { view.type === LAYOUT_TABLE && isVisible && (
274
+ <>
241
275
  <Button
242
- className="'dataviews-view-config__field-control-button"
276
+ disabled={ index < 1 }
277
+ accessibleWhenDisabled
243
278
  size="compact"
244
- onClick={ () =>
279
+ onClick={ () => {
245
280
  onChangeView( {
246
281
  ...view,
247
- fields: isVisible
248
- ? viewFields.filter(
249
- ( id ) => id !== field.id
250
- )
251
- : [ ...viewFields, field.id ],
252
- } )
253
- }
254
- icon={ isVisible ? seen : unseen }
255
- label={
256
- isVisible
257
- ? __( 'Hide field' )
258
- : __( 'Show field' )
259
- }
282
+ fields: [
283
+ ...( visibleFieldIds.slice(
284
+ 0,
285
+ index - 1
286
+ ) ?? [] ),
287
+ id,
288
+ visibleFieldIds[ index - 1 ],
289
+ ...visibleFieldIds.slice(
290
+ index + 1
291
+ ),
292
+ ],
293
+ } );
294
+ } }
295
+ icon={ chevronUp }
296
+ label={ sprintf(
297
+ /* translators: %s: field label */
298
+ __( 'Move %s up' ),
299
+ label
300
+ ) }
260
301
  />
261
- </HStack>
262
- </Item>
263
- );
264
- } ) }
265
- </ItemGroup>
302
+ <Button
303
+ disabled={ index >= visibleFieldIds.length - 1 }
304
+ accessibleWhenDisabled
305
+ size="compact"
306
+ onClick={ () => {
307
+ onChangeView( {
308
+ ...view,
309
+ fields: [
310
+ ...( visibleFieldIds.slice(
311
+ 0,
312
+ index
313
+ ) ?? [] ),
314
+ visibleFieldIds[ index + 1 ],
315
+ id,
316
+ ...visibleFieldIds.slice(
317
+ index + 2
318
+ ),
319
+ ],
320
+ } );
321
+ } }
322
+ icon={ chevronDown }
323
+ label={ sprintf(
324
+ /* translators: %s: field label */
325
+ __( 'Move %s down' ),
326
+ label
327
+ ) }
328
+ />{ ' ' }
329
+ </>
330
+ ) }
331
+ <Button
332
+ className="dataviews-field-control__field-visibility-button"
333
+ disabled={ ! isHidable }
334
+ accessibleWhenDisabled
335
+ size="compact"
336
+ onClick={ () => {
337
+ onChangeView( {
338
+ ...view,
339
+ fields: isVisible
340
+ ? visibleFieldIds.filter(
341
+ ( fieldId ) => fieldId !== id
342
+ )
343
+ : [ ...visibleFieldIds, id ],
344
+ } );
345
+ // Focus the visibility button to avoid focus loss.
346
+ // Our code is safe against the component being unmounted, so we don't need to worry about cleaning the timeout.
347
+ // eslint-disable-next-line @wordpress/react-no-unsafe-timeout
348
+ setTimeout( () => {
349
+ const element = document.querySelector(
350
+ `.dataviews-field-control__field-${ id } .dataviews-field-control__field-visibility-button`
351
+ );
352
+ if ( element instanceof HTMLElement ) {
353
+ element.focus();
354
+ }
355
+ }, 50 );
356
+ } }
357
+ icon={ isVisible ? seen : unseen }
358
+ label={
359
+ isVisible
360
+ ? sprintf(
361
+ /* translators: %s: field label */
362
+ __( 'Hide %s' ),
363
+ label
364
+ )
365
+ : sprintf(
366
+ /* translators: %s: field label */
367
+ __( 'Show %s' ),
368
+ label
369
+ )
370
+ }
371
+ />
372
+ </HStack>
373
+ </HStack>
374
+ </Item>
375
+ );
376
+ }
377
+
378
+ function FieldControl() {
379
+ const { view, fields, onChangeView } = useContext( DataViewsContext );
380
+
381
+ const visibleFieldIds = useMemo(
382
+ () => getVisibleFieldIds( view, fields ),
383
+ [ view, fields ]
384
+ );
385
+ const hiddenFieldIds = useMemo(
386
+ () => getHiddenFieldIds( view, fields ),
387
+ [ view, fields ]
388
+ );
389
+ const notHidableFieldIds = useMemo(
390
+ () => getNotHidableFieldIds( view ),
391
+ [ view ]
392
+ );
393
+
394
+ const visibleFields = fields
395
+ .filter( ( { id } ) => visibleFieldIds.includes( id ) )
396
+ .map( ( { id, label, enableHiding } ) => {
397
+ return {
398
+ id,
399
+ label,
400
+ index: visibleFieldIds.indexOf( id ),
401
+ isVisible: true,
402
+ isHidable: notHidableFieldIds.includes( id )
403
+ ? false
404
+ : enableHiding,
405
+ };
406
+ } );
407
+ if ( view.type === LAYOUT_TABLE && view.layout?.combinedFields ) {
408
+ view.layout.combinedFields.forEach( ( { id, label } ) => {
409
+ visibleFields.push( {
410
+ id,
411
+ label,
412
+ index: visibleFieldIds.indexOf( id ),
413
+ isVisible: true,
414
+ isHidable: notHidableFieldIds.includes( id ),
415
+ } );
416
+ } );
417
+ }
418
+ visibleFields.sort( ( a, b ) => a.index - b.index );
419
+
420
+ const hiddenFields = fields
421
+ .filter( ( { id } ) => hiddenFieldIds.includes( id ) )
422
+ .map( ( { id, label, enableHiding }, index ) => {
423
+ return {
424
+ id,
425
+ label,
426
+ index,
427
+ isVisible: false,
428
+ isHidable: enableHiding,
429
+ };
430
+ } );
431
+
432
+ if ( ! visibleFields?.length && ! hiddenFields?.length ) {
433
+ return null;
434
+ }
435
+
436
+ return (
437
+ <VStack spacing={ 6 } className="dataviews-field-control">
438
+ { !! visibleFields?.length && (
439
+ <ItemGroup isBordered isSeparated>
440
+ { visibleFields.map( ( field ) => (
441
+ <FieldItem
442
+ key={ field.id }
443
+ field={ field }
444
+ fields={ fields }
445
+ view={ view }
446
+ onChangeView={ onChangeView }
447
+ />
448
+ ) ) }
449
+ </ItemGroup>
450
+ ) }
451
+ { !! hiddenFields?.length && (
452
+ <>
453
+ <VStack spacing={ 4 }>
454
+ <BaseControl.VisualLabel style={ { margin: 0 } }>
455
+ { __( 'Hidden' ) }
456
+ </BaseControl.VisualLabel>
457
+ <ItemGroup isBordered isSeparated>
458
+ { hiddenFields.map( ( field ) => (
459
+ <FieldItem
460
+ key={ field.id }
461
+ field={ field }
462
+ fields={ fields }
463
+ view={ view }
464
+ onChangeView={ onChangeView }
465
+ />
466
+ ) ) }
467
+ </ItemGroup>
468
+ </VStack>
469
+ </>
470
+ ) }
471
+ </VStack>
266
472
  );
267
473
  }
268
474
 
@@ -304,7 +510,14 @@ function SettingsSection( {
304
510
  );
305
511
  }
306
512
 
307
- function DataviewsViewConfigContent() {
513
+ function DataviewsViewConfigContent( {
514
+ density,
515
+ setDensity,
516
+ }: {
517
+ density: number;
518
+ setDensity: React.Dispatch< React.SetStateAction< number > >;
519
+ } ) {
520
+ const { view } = useContext( DataViewsContext );
308
521
  return (
309
522
  <VStack className="dataviews-view-config" spacing={ 6 }>
310
523
  <SettingsSection title={ __( 'Appearance' ) }>
@@ -312,6 +525,12 @@ function DataviewsViewConfigContent() {
312
525
  <SortFieldControl />
313
526
  <SortDirectionControl />
314
527
  </HStack>
528
+ { view.type === LAYOUT_GRID && (
529
+ <DensityPicker
530
+ density={ density }
531
+ setDensity={ setDensity }
532
+ />
533
+ ) }
315
534
  <ItemsPerPageControl />
316
535
  </SettingsSection>
317
536
  <SettingsSection title={ __( 'Properties' ) }>
@@ -322,8 +541,14 @@ function DataviewsViewConfigContent() {
322
541
  }
323
542
 
324
543
  function _DataViewsViewConfig( {
544
+ density,
545
+ setDensity,
325
546
  defaultLayouts = { list: {}, grid: {}, table: {} },
326
- }: ViewActionsProps ) {
547
+ }: {
548
+ density: number;
549
+ setDensity: React.Dispatch< React.SetStateAction< number > >;
550
+ defaultLayouts?: SupportedLayouts;
551
+ } ) {
327
552
  const [ isShowingViewPopover, setIsShowingViewPopover ] =
328
553
  useState< boolean >( false );
329
554
 
@@ -345,7 +570,10 @@ function _DataViewsViewConfig( {
345
570
  } }
346
571
  focusOnMount
347
572
  >
348
- <DataviewsViewConfigContent />
573
+ <DataviewsViewConfigContent
574
+ density={ density }
575
+ setDensity={ setDensity }
576
+ />
349
577
  </Popover>
350
578
  ) }
351
579
  </div>
@@ -3,6 +3,8 @@
3
3
  /* stylelint-disable-next-line property-no-unknown -- the linter needs to be updated to accepted the container-type property */
4
4
  container-type: inline-size;
5
5
  padding: $grid-unit-20;
6
+ font-size: $default-font-size;
7
+ line-height: $default-line-height;
6
8
  }
7
9
  .dataviews-view-config__sort-direction .components-toggle-group-control-option-base {
8
10
  text-transform: uppercase;
@@ -29,6 +31,10 @@
29
31
  }
30
32
  }
31
33
 
34
+ .dataviews-settings-section:has(.dataviews-settings-section__content:empty) {
35
+ display: none;
36
+ }
37
+
32
38
  /* stylelint-disable-next-line scss/at-rule-no-unknown -- '@container' not globally permitted */
33
39
  @container (max-width: 500px) {
34
40
  .dataviews-settings-section.dataviews-settings-section {
@@ -42,3 +48,22 @@
42
48
  }
43
49
  }
44
50
  }
51
+ .dataviews-field-control__field {
52
+ height: $grid-unit-40;
53
+ }
54
+
55
+ .dataviews-field-control__actions {
56
+ position: absolute;
57
+ top: -9999em;
58
+ }
59
+ .dataviews-field-control__actions.dataviews-field-control__actions {
60
+ gap: $grid-unit-05;
61
+ }
62
+
63
+ .dataviews-field-control__field:hover,
64
+ .dataviews-field-control__field:focus-within {
65
+ .dataviews-field-control__actions {
66
+ position: unset;
67
+ top: unset;
68
+ }
69
+ }
@@ -0,0 +1,43 @@
1
+ /**
2
+ * WordPress dependencies
3
+ */
4
+ import { BaseControl, TimePicker, VisuallyHidden } from '@wordpress/components';
5
+ import { useCallback } from '@wordpress/element';
6
+
7
+ /**
8
+ * Internal dependencies
9
+ */
10
+ import type { DataFormControlProps } from '../types';
11
+
12
+ export default function DateTime< Item >( {
13
+ data,
14
+ field,
15
+ onChange,
16
+ hideLabelFromVision,
17
+ }: DataFormControlProps< Item > ) {
18
+ const { id, label } = field;
19
+ const value = field.getValue( { item: data } );
20
+
21
+ const onChangeControl = useCallback(
22
+ ( newValue: string | null ) => onChange( { [ id ]: newValue } ),
23
+ [ id, onChange ]
24
+ );
25
+
26
+ return (
27
+ <fieldset className="dataviews-controls__datetime">
28
+ { ! hideLabelFromVision && (
29
+ <BaseControl.VisualLabel as="legend">
30
+ { label }
31
+ </BaseControl.VisualLabel>
32
+ ) }
33
+ { hideLabelFromVision && (
34
+ <VisuallyHidden as="legend">{ label }</VisuallyHidden>
35
+ ) }
36
+ <TimePicker
37
+ currentTime={ value }
38
+ onChange={ onChangeControl }
39
+ hideLabelFromVision
40
+ />
41
+ </fieldset>
42
+ );
43
+ }
@@ -0,0 +1,61 @@
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import type { ComponentType } from 'react';
5
+
6
+ /**
7
+ * Internal dependencies
8
+ */
9
+ import type {
10
+ DataFormControlProps,
11
+ Field,
12
+ FieldTypeDefinition,
13
+ } from '../types';
14
+ import datetime from './datetime';
15
+ import integer from './integer';
16
+ import radio from './radio';
17
+ import select from './select';
18
+ import text from './text';
19
+
20
+ interface FormControls {
21
+ [ key: string ]: ComponentType< DataFormControlProps< any > >;
22
+ }
23
+
24
+ const FORM_CONTROLS: FormControls = {
25
+ datetime,
26
+ integer,
27
+ radio,
28
+ select,
29
+ text,
30
+ };
31
+
32
+ export function getControl< Item >(
33
+ field: Field< Item >,
34
+ fieldTypeDefinition: FieldTypeDefinition< Item >
35
+ ) {
36
+ if ( typeof field.Edit === 'function' ) {
37
+ return field.Edit;
38
+ }
39
+
40
+ if ( typeof field.Edit === 'string' ) {
41
+ return getControlByType( field.Edit );
42
+ }
43
+
44
+ if ( field.elements ) {
45
+ return getControlByType( 'select' );
46
+ }
47
+
48
+ if ( typeof fieldTypeDefinition.Edit === 'string' ) {
49
+ return getControlByType( fieldTypeDefinition.Edit );
50
+ }
51
+
52
+ return fieldTypeDefinition.Edit;
53
+ }
54
+
55
+ export function getControlByType( type: string ) {
56
+ if ( Object.keys( FORM_CONTROLS ).includes( type ) ) {
57
+ return FORM_CONTROLS[ type ];
58
+ }
59
+
60
+ throw 'Control ' + type + ' not found';
61
+ }
@@ -0,0 +1,38 @@
1
+ /**
2
+ * WordPress dependencies
3
+ */
4
+ import { __experimentalNumberControl as NumberControl } from '@wordpress/components';
5
+ import { useCallback } from '@wordpress/element';
6
+
7
+ /**
8
+ * Internal dependencies
9
+ */
10
+ import type { DataFormControlProps } from '../types';
11
+
12
+ export default function Integer< Item >( {
13
+ data,
14
+ field,
15
+ onChange,
16
+ hideLabelFromVision,
17
+ }: DataFormControlProps< Item > ) {
18
+ const { id, label, description } = field;
19
+ const value = field.getValue( { item: data } ) ?? '';
20
+ const onChangeControl = useCallback(
21
+ ( newValue: string | undefined ) =>
22
+ onChange( {
23
+ [ id ]: Number( newValue ),
24
+ } ),
25
+ [ id, onChange ]
26
+ );
27
+
28
+ return (
29
+ <NumberControl
30
+ label={ label }
31
+ help={ description }
32
+ value={ value }
33
+ onChange={ onChangeControl }
34
+ __next40pxDefaultSize
35
+ hideLabelFromVision={ hideLabelFromVision }
36
+ />
37
+ );
38
+ }
@@ -0,0 +1,42 @@
1
+ /**
2
+ * WordPress dependencies
3
+ */
4
+ import { RadioControl } from '@wordpress/components';
5
+ import { useCallback } from '@wordpress/element';
6
+
7
+ /**
8
+ * Internal dependencies
9
+ */
10
+ import type { DataFormControlProps } from '../types';
11
+
12
+ export default function Radio< Item >( {
13
+ data,
14
+ field,
15
+ onChange,
16
+ hideLabelFromVision,
17
+ }: DataFormControlProps< Item > ) {
18
+ const { id, label } = field;
19
+ const value = field.getValue( { item: data } );
20
+
21
+ const onChangeControl = useCallback(
22
+ ( newValue: string ) =>
23
+ onChange( {
24
+ [ id ]: newValue,
25
+ } ),
26
+ [ id, onChange ]
27
+ );
28
+
29
+ if ( field.elements ) {
30
+ return (
31
+ <RadioControl
32
+ label={ label }
33
+ onChange={ onChangeControl }
34
+ options={ field.elements }
35
+ selected={ value }
36
+ hideLabelFromVision={ hideLabelFromVision }
37
+ />
38
+ );
39
+ }
40
+
41
+ return null;
42
+ }