@wordpress/dataviews 4.0.0 → 4.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 (214) hide show
  1. package/CHANGELOG.md +16 -5
  2. package/build/components/dataform/index.js +10 -61
  3. package/build/components/dataform/index.js.map +1 -1
  4. package/build/components/dataviews/index.js +16 -5
  5. package/build/components/dataviews/index.js.map +1 -1
  6. package/build/components/dataviews-bulk-actions/index.js +3 -0
  7. package/build/components/dataviews-bulk-actions/index.js.map +1 -1
  8. package/build/components/dataviews-filters/add-filter.js +34 -17
  9. package/build/components/dataviews-filters/add-filter.js.map +1 -1
  10. package/build/components/dataviews-filters/index.js +106 -43
  11. package/build/components/dataviews-filters/index.js.map +1 -1
  12. package/build/components/dataviews-layout/index.js +2 -2
  13. package/build/components/dataviews-layout/index.js.map +1 -1
  14. package/build/components/dataviews-search/index.js +8 -5
  15. package/build/components/dataviews-search/index.js.map +1 -1
  16. package/build/components/dataviews-view-config/index.js +225 -190
  17. package/build/components/dataviews-view-config/index.js.map +1 -1
  18. package/build/constants.js +6 -1
  19. package/build/constants.js.map +1 -1
  20. package/build/dataforms-layouts/index.js +24 -0
  21. package/build/dataforms-layouts/index.js.map +1 -0
  22. package/build/dataforms-layouts/panel/index.js +129 -0
  23. package/build/dataforms-layouts/panel/index.js.map +1 -0
  24. package/build/dataforms-layouts/regular/index.js +39 -0
  25. package/build/dataforms-layouts/regular/index.js.map +1 -0
  26. package/build/{layouts → dataviews-layouts}/grid/density-picker.js +1 -1
  27. package/build/dataviews-layouts/grid/density-picker.js.map +1 -0
  28. package/build/{layouts → dataviews-layouts}/grid/index.js +8 -8
  29. package/build/dataviews-layouts/grid/index.js.map +1 -0
  30. package/build/dataviews-layouts/index.js.map +1 -0
  31. package/build/dataviews-layouts/list/index.js.map +1 -0
  32. package/build/{layouts → dataviews-layouts}/table/column-header-menu.js +1 -1
  33. package/build/dataviews-layouts/table/column-header-menu.js.map +1 -0
  34. package/build/dataviews-layouts/table/index.js.map +1 -0
  35. package/build/field-types/index.js +46 -0
  36. package/build/field-types/index.js.map +1 -0
  37. package/build/field-types/integer.js +94 -0
  38. package/build/field-types/integer.js.map +1 -0
  39. package/build/field-types/text.js +87 -0
  40. package/build/field-types/text.js.map +1 -0
  41. package/build/filter-and-sort-data-view.js +2 -11
  42. package/build/filter-and-sort-data-view.js.map +1 -1
  43. package/build/index.js +9 -2
  44. package/build/index.js.map +1 -1
  45. package/build/normalize-fields.js +35 -1
  46. package/build/normalize-fields.js.map +1 -1
  47. package/build/types.js.map +1 -1
  48. package/build/validation.js +22 -0
  49. package/build/validation.js.map +1 -0
  50. package/build-module/components/dataform/index.js +10 -61
  51. package/build-module/components/dataform/index.js.map +1 -1
  52. package/build-module/components/dataviews/index.js +14 -5
  53. package/build-module/components/dataviews/index.js.map +1 -1
  54. package/build-module/components/dataviews-bulk-actions/index.js +3 -0
  55. package/build-module/components/dataviews-bulk-actions/index.js.map +1 -1
  56. package/build-module/components/dataviews-filters/add-filter.js +33 -17
  57. package/build-module/components/dataviews-filters/add-filter.js.map +1 -1
  58. package/build-module/components/dataviews-filters/index.js +105 -45
  59. package/build-module/components/dataviews-filters/index.js.map +1 -1
  60. package/build-module/components/dataviews-layout/index.js +1 -1
  61. package/build-module/components/dataviews-layout/index.js.map +1 -1
  62. package/build-module/components/dataviews-search/index.js +8 -5
  63. package/build-module/components/dataviews-search/index.js.map +1 -1
  64. package/build-module/components/dataviews-view-config/index.js +228 -193
  65. package/build-module/components/dataviews-view-config/index.js.map +1 -1
  66. package/build-module/constants.js +5 -0
  67. package/build-module/constants.js.map +1 -1
  68. package/build-module/dataforms-layouts/index.js +16 -0
  69. package/build-module/dataforms-layouts/index.js.map +1 -0
  70. package/build-module/dataforms-layouts/panel/index.js +124 -0
  71. package/build-module/dataforms-layouts/panel/index.js.map +1 -0
  72. package/build-module/dataforms-layouts/regular/index.js +32 -0
  73. package/build-module/dataforms-layouts/regular/index.js.map +1 -0
  74. package/build-module/{layouts → dataviews-layouts}/grid/density-picker.js +2 -2
  75. package/build-module/dataviews-layouts/grid/density-picker.js.map +1 -0
  76. package/build-module/{layouts → dataviews-layouts}/grid/index.js +8 -8
  77. package/build-module/dataviews-layouts/grid/index.js.map +1 -0
  78. package/build-module/dataviews-layouts/index.js.map +1 -0
  79. package/build-module/dataviews-layouts/list/index.js.map +1 -0
  80. package/build-module/{layouts → dataviews-layouts}/table/column-header-menu.js +1 -1
  81. package/build-module/dataviews-layouts/table/column-header-menu.js.map +1 -0
  82. package/build-module/dataviews-layouts/table/index.js.map +1 -0
  83. package/build-module/field-types/index.js +40 -0
  84. package/build-module/field-types/index.js.map +1 -0
  85. package/build-module/field-types/integer.js +87 -0
  86. package/build-module/field-types/integer.js.map +1 -0
  87. package/build-module/field-types/text.js +80 -0
  88. package/build-module/field-types/text.js.map +1 -0
  89. package/build-module/filter-and-sort-data-view.js +2 -11
  90. package/build-module/filter-and-sort-data-view.js.map +1 -1
  91. package/build-module/index.js +2 -1
  92. package/build-module/index.js.map +1 -1
  93. package/build-module/normalize-fields.js +34 -2
  94. package/build-module/normalize-fields.js.map +1 -1
  95. package/build-module/types.js.map +1 -1
  96. package/build-module/validation.js +15 -0
  97. package/build-module/validation.js.map +1 -0
  98. package/build-style/style-rtl.css +175 -6
  99. package/build-style/style.css +175 -6
  100. package/build-types/components/dataform/index.d.ts +2 -13
  101. package/build-types/components/dataform/index.d.ts.map +1 -1
  102. package/build-types/components/dataform/stories/index.story.d.ts +12 -1
  103. package/build-types/components/dataform/stories/index.story.d.ts.map +1 -1
  104. package/build-types/components/dataviews/index.d.ts.map +1 -1
  105. package/build-types/components/dataviews/stories/fixtures.d.ts +6 -0
  106. package/build-types/components/dataviews/stories/fixtures.d.ts.map +1 -1
  107. package/build-types/components/dataviews-bulk-actions/index.d.ts.map +1 -1
  108. package/build-types/components/dataviews-filters/add-filter.d.ts +3 -0
  109. package/build-types/components/dataviews-filters/add-filter.d.ts.map +1 -1
  110. package/build-types/components/dataviews-filters/index.d.ts +11 -1
  111. package/build-types/components/dataviews-filters/index.d.ts.map +1 -1
  112. package/build-types/components/dataviews-search/index.d.ts.map +1 -1
  113. package/build-types/components/dataviews-view-config/index.d.ts +1 -1
  114. package/build-types/components/dataviews-view-config/index.d.ts.map +1 -1
  115. package/build-types/constants.d.ts +4 -0
  116. package/build-types/constants.d.ts.map +1 -1
  117. package/build-types/dataforms-layouts/index.d.ts +9 -0
  118. package/build-types/dataforms-layouts/index.d.ts.map +1 -0
  119. package/build-types/dataforms-layouts/panel/index.d.ts +3 -0
  120. package/build-types/dataforms-layouts/panel/index.d.ts.map +1 -0
  121. package/build-types/dataforms-layouts/regular/index.d.ts +3 -0
  122. package/build-types/dataforms-layouts/regular/index.d.ts.map +1 -0
  123. package/build-types/dataviews-layouts/grid/density-picker.d.ts.map +1 -0
  124. package/build-types/dataviews-layouts/grid/index.d.ts.map +1 -0
  125. package/build-types/dataviews-layouts/index.d.ts.map +1 -0
  126. package/build-types/dataviews-layouts/list/index.d.ts.map +1 -0
  127. package/build-types/dataviews-layouts/table/column-header-menu.d.ts.map +1 -0
  128. package/build-types/dataviews-layouts/table/index.d.ts.map +1 -0
  129. package/build-types/field-types/index.d.ts +20 -0
  130. package/build-types/field-types/index.d.ts.map +1 -0
  131. package/build-types/field-types/integer.d.ts +14 -0
  132. package/build-types/field-types/integer.d.ts.map +1 -0
  133. package/build-types/field-types/text.d.ts +14 -0
  134. package/build-types/field-types/text.d.ts.map +1 -0
  135. package/build-types/filter-and-sort-data-view.d.ts.map +1 -1
  136. package/build-types/index.d.ts +2 -1
  137. package/build-types/index.d.ts.map +1 -1
  138. package/build-types/normalize-fields.d.ts +0 -3
  139. package/build-types/normalize-fields.d.ts.map +1 -1
  140. package/build-types/types.d.ts +38 -3
  141. package/build-types/types.d.ts.map +1 -1
  142. package/build-types/validation.d.ts +3 -0
  143. package/build-types/validation.d.ts.map +1 -0
  144. package/package.json +12 -11
  145. package/src/components/dataform/index.tsx +8 -97
  146. package/src/components/dataform/stories/index.story.tsx +40 -3
  147. package/src/components/dataviews/index.tsx +20 -8
  148. package/src/components/dataviews/stories/fixtures.js +1 -0
  149. package/src/components/dataviews/style.scss +5 -2
  150. package/src/components/dataviews-bulk-actions/index.tsx +5 -0
  151. package/src/components/dataviews-filters/add-filter.tsx +37 -21
  152. package/src/components/dataviews-filters/index.tsx +149 -61
  153. package/src/components/dataviews-filters/style.scss +30 -0
  154. package/src/components/dataviews-layout/index.tsx +1 -1
  155. package/src/components/dataviews-search/index.tsx +8 -5
  156. package/src/components/dataviews-view-config/index.tsx +272 -258
  157. package/src/components/dataviews-view-config/style.scss +44 -0
  158. package/src/constants.ts +5 -0
  159. package/src/dataforms-layouts/index.tsx +20 -0
  160. package/src/dataforms-layouts/panel/index.tsx +164 -0
  161. package/src/dataforms-layouts/panel/style.scss +59 -0
  162. package/src/dataforms-layouts/regular/index.tsx +41 -0
  163. package/src/{layouts → dataviews-layouts}/grid/density-picker.tsx +2 -2
  164. package/src/{layouts → dataviews-layouts}/grid/index.tsx +8 -8
  165. package/src/{layouts → dataviews-layouts}/grid/style.scss +29 -0
  166. package/src/{layouts → dataviews-layouts}/list/style.scss +4 -1
  167. package/src/{layouts → dataviews-layouts}/table/column-header-menu.tsx +1 -1
  168. package/src/field-types/index.tsx +45 -0
  169. package/src/field-types/integer.tsx +103 -0
  170. package/src/field-types/text.tsx +95 -0
  171. package/src/filter-and-sort-data-view.ts +1 -15
  172. package/src/index.ts +2 -1
  173. package/src/normalize-fields.ts +44 -3
  174. package/src/style.scss +6 -3
  175. package/src/test/filter-and-sort-data-view.js +46 -3
  176. package/src/test/validation.ts +131 -0
  177. package/src/types.ts +50 -3
  178. package/src/validation.ts +18 -0
  179. package/tsconfig.json +2 -1
  180. package/tsconfig.tsbuildinfo +1 -1
  181. package/build/layouts/grid/density-picker.js.map +0 -1
  182. package/build/layouts/grid/index.js.map +0 -1
  183. package/build/layouts/index.js.map +0 -1
  184. package/build/layouts/list/index.js.map +0 -1
  185. package/build/layouts/table/column-header-menu.js.map +0 -1
  186. package/build/layouts/table/index.js.map +0 -1
  187. package/build-module/layouts/grid/density-picker.js.map +0 -1
  188. package/build-module/layouts/grid/index.js.map +0 -1
  189. package/build-module/layouts/index.js.map +0 -1
  190. package/build-module/layouts/list/index.js.map +0 -1
  191. package/build-module/layouts/table/column-header-menu.js.map +0 -1
  192. package/build-module/layouts/table/index.js.map +0 -1
  193. package/build-types/layouts/grid/density-picker.d.ts.map +0 -1
  194. package/build-types/layouts/grid/index.d.ts.map +0 -1
  195. package/build-types/layouts/index.d.ts.map +0 -1
  196. package/build-types/layouts/list/index.d.ts.map +0 -1
  197. package/build-types/layouts/table/column-header-menu.d.ts.map +0 -1
  198. package/build-types/layouts/table/index.d.ts.map +0 -1
  199. /package/build/{layouts → dataviews-layouts}/index.js +0 -0
  200. /package/build/{layouts → dataviews-layouts}/list/index.js +0 -0
  201. /package/build/{layouts → dataviews-layouts}/table/index.js +0 -0
  202. /package/build-module/{layouts → dataviews-layouts}/index.js +0 -0
  203. /package/build-module/{layouts → dataviews-layouts}/list/index.js +0 -0
  204. /package/build-module/{layouts → dataviews-layouts}/table/index.js +0 -0
  205. /package/build-types/{layouts → dataviews-layouts}/grid/density-picker.d.ts +0 -0
  206. /package/build-types/{layouts → dataviews-layouts}/grid/index.d.ts +0 -0
  207. /package/build-types/{layouts → dataviews-layouts}/index.d.ts +0 -0
  208. /package/build-types/{layouts → dataviews-layouts}/list/index.d.ts +0 -0
  209. /package/build-types/{layouts → dataviews-layouts}/table/column-header-menu.d.ts +0 -0
  210. /package/build-types/{layouts → dataviews-layouts}/table/index.d.ts +0 -0
  211. /package/src/{layouts → dataviews-layouts}/index.ts +0 -0
  212. /package/src/{layouts → dataviews-layouts}/list/index.tsx +0 -0
  213. /package/src/{layouts → dataviews-layouts}/table/index.tsx +0 -0
  214. /package/src/{layouts → dataviews-layouts}/table/style.scss +0 -0
@@ -8,131 +8,92 @@ import type { ChangeEvent } from 'react';
8
8
  */
9
9
  import {
10
10
  Button,
11
- privateApis as componentsPrivateApis,
11
+ Popover,
12
+ __experimentalToggleGroupControl as ToggleGroupControl,
13
+ __experimentalToggleGroupControlOption as ToggleGroupControlOption,
14
+ __experimentalToggleGroupControlOptionIcon as ToggleGroupControlOptionIcon,
15
+ SelectControl,
16
+ __experimentalItemGroup as ItemGroup,
17
+ __experimentalItem as Item,
18
+ __experimentalGrid as Grid,
19
+ __experimentalVStack as VStack,
12
20
  __experimentalHStack as HStack,
21
+ __experimentalHeading as Heading,
22
+ __experimentalText as Text,
23
+ privateApis as componentsPrivateApis,
13
24
  } from '@wordpress/components';
14
25
  import { __, _x } from '@wordpress/i18n';
15
- import { memo, useContext } from '@wordpress/element';
16
- import { cog } from '@wordpress/icons';
26
+ import { memo, useContext, useState, useMemo } from '@wordpress/element';
27
+ import { cog, seen, unseen } from '@wordpress/icons';
28
+ import warning from '@wordpress/warning';
17
29
 
18
30
  /**
19
31
  * Internal dependencies
20
32
  */
21
- import { unlock } from '../../lock-unlock';
22
- import { SORTING_DIRECTIONS, sortLabels } from '../../constants';
23
- import { VIEW_LAYOUTS, getMandatoryFields } from '../../layouts';
24
- import type { NormalizedField, View, SupportedLayouts } from '../../types';
33
+ import { SORTING_DIRECTIONS, sortIcons, sortLabels } from '../../constants';
34
+ import { VIEW_LAYOUTS, getMandatoryFields } from '../../dataviews-layouts';
35
+ import type { SupportedLayouts } from '../../types';
25
36
  import DataViewsContext from '../dataviews-context';
37
+ import { unlock } from '../../lock-unlock';
26
38
 
27
39
  const {
28
40
  DropdownMenuV2: DropdownMenu,
29
- DropdownMenuGroupV2: DropdownMenuGroup,
30
- DropdownMenuItemV2: DropdownMenuItem,
31
41
  DropdownMenuRadioItemV2: DropdownMenuRadioItem,
32
- DropdownMenuCheckboxItemV2: DropdownMenuCheckboxItem,
33
42
  DropdownMenuItemLabelV2: DropdownMenuItemLabel,
34
43
  } = unlock( componentsPrivateApis );
35
44
 
36
45
  interface ViewTypeMenuProps {
37
- view: View;
38
- onChangeView: ( view: View ) => void;
39
- defaultLayouts?: SupportedLayouts;
40
- }
41
-
42
- interface PageSizeMenuProps {
43
- view: View;
44
- onChangeView: ( view: View ) => void;
45
- }
46
-
47
- interface FieldsVisibilityMenuProps< Item > {
48
- view: View;
49
- onChangeView: ( view: View ) => void;
50
- fields: NormalizedField< Item >[];
51
- }
52
-
53
- interface SortMenuProps< Item > {
54
- fields: NormalizedField< Item >[];
55
- view: View;
56
- onChangeView: ( view: View ) => void;
57
- }
58
-
59
- interface ViewActionsProps {
60
46
  defaultLayouts?: SupportedLayouts;
61
47
  }
62
48
 
63
49
  function ViewTypeMenu( {
64
- view,
65
- onChangeView,
66
50
  defaultLayouts = { list: {}, grid: {}, table: {} },
67
51
  }: ViewTypeMenuProps ) {
52
+ const { view, onChangeView } = useContext( DataViewsContext );
68
53
  const availableLayouts = Object.keys( defaultLayouts );
69
54
  if ( availableLayouts.length <= 1 ) {
70
55
  return null;
71
56
  }
72
- return availableLayouts.map( ( layout ) => {
73
- const config = VIEW_LAYOUTS.find( ( v ) => v.type === layout );
74
- if ( ! config ) {
75
- return null;
76
- }
77
- return (
78
- <DropdownMenuRadioItem
79
- key={ layout }
80
- value={ layout }
81
- name="view-actions-available-view"
82
- checked={ layout === view.type }
83
- hideOnClick
84
- onChange={ ( e: ChangeEvent< HTMLInputElement > ) => {
85
- switch ( e.target.value ) {
86
- case 'list':
87
- case 'grid':
88
- case 'table':
89
- return onChangeView( {
90
- ...view,
91
- type: e.target.value,
92
- ...defaultLayouts[ e.target.value ],
93
- } );
94
- }
95
- throw new Error( 'Invalid dataview' );
96
- } }
97
- >
98
- <DropdownMenuItemLabel>{ config.label }</DropdownMenuItemLabel>
99
- </DropdownMenuRadioItem>
100
- );
101
- } );
102
- }
103
-
104
- const PAGE_SIZE_VALUES = [ 10, 20, 50, 100 ];
105
- function PageSizeMenu( { view, onChangeView }: PageSizeMenuProps ) {
57
+ const activeView = VIEW_LAYOUTS.find( ( v ) => view.type === v.type );
106
58
  return (
107
59
  <DropdownMenu
108
60
  trigger={
109
- <DropdownMenuItem
110
- suffix={ <span aria-hidden="true">{ view.perPage }</span> }
111
- >
112
- <DropdownMenuItemLabel>
113
- { __( 'Items per page' ) }
114
- </DropdownMenuItemLabel>
115
- </DropdownMenuItem>
61
+ <Button
62
+ size="compact"
63
+ icon={ activeView?.icon }
64
+ label={ __( 'Layout' ) }
65
+ />
116
66
  }
117
67
  >
118
- { PAGE_SIZE_VALUES.map( ( size ) => {
68
+ { availableLayouts.map( ( layout ) => {
69
+ const config = VIEW_LAYOUTS.find( ( v ) => v.type === layout );
70
+ if ( ! config ) {
71
+ return null;
72
+ }
119
73
  return (
120
74
  <DropdownMenuRadioItem
121
- key={ size }
122
- value={ size }
123
- name="view-actions-page-size"
124
- checked={ view.perPage === size }
125
- onChange={ () => {
126
- onChangeView( {
127
- ...view,
128
- // `e.target.value` holds the same value as `size` but as a string,
129
- // so we use `size` directly to avoid parsing to int.
130
- perPage: size,
131
- page: 1,
132
- } );
75
+ key={ layout }
76
+ value={ layout }
77
+ name="view-actions-available-view"
78
+ checked={ layout === view.type }
79
+ hideOnClick
80
+ onChange={ ( e: ChangeEvent< HTMLInputElement > ) => {
81
+ switch ( e.target.value ) {
82
+ case 'list':
83
+ case 'grid':
84
+ case 'table':
85
+ return onChangeView( {
86
+ ...view,
87
+ type: e.target.value,
88
+ ...defaultLayouts[ e.target.value ],
89
+ } );
90
+ }
91
+ warning( 'Invalid dataview' );
133
92
  } }
134
93
  >
135
- <DropdownMenuItemLabel>{ size }</DropdownMenuItemLabel>
94
+ <DropdownMenuItemLabel>
95
+ { config.label }
96
+ </DropdownMenuItemLabel>
136
97
  </DropdownMenuRadioItem>
137
98
  );
138
99
  } ) }
@@ -140,11 +101,125 @@ function PageSizeMenu( { view, onChangeView }: PageSizeMenuProps ) {
140
101
  );
141
102
  }
142
103
 
143
- function FieldsVisibilityMenu< Item >( {
144
- view,
145
- onChangeView,
146
- fields,
147
- }: FieldsVisibilityMenuProps< Item > ) {
104
+ interface ViewActionsProps {
105
+ defaultLayouts?: SupportedLayouts;
106
+ }
107
+
108
+ function SortFieldControl() {
109
+ const { view, fields, onChangeView } = useContext( DataViewsContext );
110
+ const orderOptions = useMemo( () => {
111
+ const sortableFields = fields.filter(
112
+ ( field ) => field.enableSorting !== false
113
+ );
114
+ return sortableFields.map( ( field ) => {
115
+ return {
116
+ label: field.label,
117
+ value: field.id,
118
+ };
119
+ } );
120
+ }, [ fields ] );
121
+
122
+ return (
123
+ <SelectControl
124
+ __nextHasNoMarginBottom
125
+ __next40pxDefaultSize
126
+ label={ __( 'Sort by' ) }
127
+ value={ view.sort?.field }
128
+ options={ orderOptions }
129
+ onChange={ ( value: string ) => {
130
+ onChangeView( {
131
+ ...view,
132
+ sort: {
133
+ direction: view?.sort?.direction || 'desc',
134
+ field: value,
135
+ },
136
+ } );
137
+ } }
138
+ />
139
+ );
140
+ }
141
+
142
+ function SortDirectionControl() {
143
+ const { view, onChangeView } = useContext( DataViewsContext );
144
+ return (
145
+ <ToggleGroupControl
146
+ className="dataviews-view-config__sort-direction"
147
+ __nextHasNoMarginBottom
148
+ __next40pxDefaultSize
149
+ isBlock
150
+ label={ __( 'Order' ) }
151
+ value={ view.sort?.direction || 'desc' }
152
+ disabled={ ! view?.sort?.field }
153
+ onChange={ ( newDirection ) => {
154
+ if ( ! view?.sort?.field ) {
155
+ return;
156
+ }
157
+ if ( newDirection === 'asc' || newDirection === 'desc' ) {
158
+ onChangeView( {
159
+ ...view,
160
+ sort: {
161
+ direction: newDirection,
162
+ field: view.sort.field,
163
+ },
164
+ } );
165
+ return;
166
+ }
167
+ warning( 'Invalid direction' );
168
+ } }
169
+ >
170
+ { SORTING_DIRECTIONS.map( ( direction ) => {
171
+ return (
172
+ <ToggleGroupControlOptionIcon
173
+ key={ direction }
174
+ value={ direction }
175
+ icon={ sortIcons[ direction ] }
176
+ label={ sortLabels[ direction ] }
177
+ />
178
+ );
179
+ } ) }
180
+ </ToggleGroupControl>
181
+ );
182
+ }
183
+
184
+ const PAGE_SIZE_VALUES = [ 10, 20, 50, 100 ];
185
+ function ItemsPerPageControl() {
186
+ const { view, onChangeView } = useContext( DataViewsContext );
187
+ return (
188
+ <ToggleGroupControl
189
+ __nextHasNoMarginBottom
190
+ __next40pxDefaultSize
191
+ isBlock
192
+ label={ __( 'Items per page' ) }
193
+ value={ view.perPage || 10 }
194
+ disabled={ ! view?.sort?.field }
195
+ onChange={ ( newItemsPerPage ) => {
196
+ const newItemsPerPageNumber =
197
+ typeof newItemsPerPage === 'number' ||
198
+ newItemsPerPage === undefined
199
+ ? newItemsPerPage
200
+ : parseInt( newItemsPerPage, 10 );
201
+ onChangeView( {
202
+ ...view,
203
+ perPage: newItemsPerPageNumber,
204
+ page: 1,
205
+ } );
206
+ } }
207
+ >
208
+ { PAGE_SIZE_VALUES.map( ( value ) => {
209
+ return (
210
+ <ToggleGroupControlOption
211
+ key={ value }
212
+ value={ value }
213
+ label={ value.toString() }
214
+ />
215
+ );
216
+ } ) }
217
+ </ToggleGroupControl>
218
+ );
219
+ }
220
+
221
+ function FieldControl() {
222
+ const { view, fields, onChangeView } = useContext( DataViewsContext );
148
223
  const mandatoryFields = getMandatoryFields( view );
149
224
  const hidableFields = fields.filter(
150
225
  ( field ) =>
@@ -156,185 +231,124 @@ function FieldsVisibilityMenu< Item >( {
156
231
  return null;
157
232
  }
158
233
  return (
159
- <DropdownMenu
160
- trigger={
161
- <DropdownMenuItem>
162
- <DropdownMenuItemLabel>
163
- { __( 'Fields' ) }
164
- </DropdownMenuItemLabel>
165
- </DropdownMenuItem>
166
- }
167
- >
234
+ <ItemGroup isBordered isSeparated>
168
235
  { hidableFields?.map( ( field ) => {
236
+ const isVisible = viewFields.includes( field.id );
169
237
  return (
170
- <DropdownMenuCheckboxItem
171
- key={ field.id }
172
- value={ field.id }
173
- checked={ viewFields.includes( field.id ) }
174
- onChange={ () => {
175
- onChangeView( {
176
- ...view,
177
- fields: viewFields.includes( field.id )
178
- ? viewFields.filter(
179
- ( id ) => id !== field.id
180
- )
181
- : [ ...viewFields, field.id ],
182
- } );
183
- } }
184
- >
185
- <DropdownMenuItemLabel>
186
- { field.label }
187
- </DropdownMenuItemLabel>
188
- </DropdownMenuCheckboxItem>
238
+ <Item key={ field.id }>
239
+ <HStack expanded>
240
+ <span>{ field.label }</span>
241
+ <Button
242
+ className="'dataviews-view-config__field-control-button"
243
+ size="compact"
244
+ onClick={ () =>
245
+ onChangeView( {
246
+ ...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
+ }
260
+ />
261
+ </HStack>
262
+ </Item>
189
263
  );
190
264
  } ) }
191
- </DropdownMenu>
265
+ </ItemGroup>
192
266
  );
193
267
  }
194
268
 
195
- function SortMenu< Item >( {
196
- fields,
197
- view,
198
- onChangeView,
199
- }: SortMenuProps< Item > ) {
200
- const sortableFields = fields.filter(
201
- ( field ) => field.enableSorting !== false
202
- );
203
- if ( ! sortableFields?.length ) {
204
- return null;
205
- }
206
- const currentSortedField = fields.find(
207
- ( field ) => field.id === view.sort?.field
208
- );
269
+ function SettingsSection( {
270
+ title,
271
+ description,
272
+ children,
273
+ }: {
274
+ title: string;
275
+ description?: string;
276
+ children: React.ReactNode;
277
+ } ) {
209
278
  return (
210
- <DropdownMenu
211
- trigger={
212
- <DropdownMenuItem
213
- suffix={
214
- <span aria-hidden="true">
215
- { currentSortedField?.label }
216
- </span>
217
- }
279
+ <Grid columns={ 12 } className="dataviews-settings-section" gap={ 4 }>
280
+ <div className="dataviews-settings-section__sidebar">
281
+ <Heading
282
+ level={ 2 }
283
+ className="dataviews-settings-section__title"
218
284
  >
219
- <DropdownMenuItemLabel>
220
- { __( 'Sort by' ) }
221
- </DropdownMenuItemLabel>
222
- </DropdownMenuItem>
223
- }
224
- >
225
- { sortableFields?.map( ( field ) => {
226
- const sortedDirection = view.sort?.direction;
227
- return (
228
- <DropdownMenu
229
- key={ field.id }
230
- trigger={
231
- <DropdownMenuItem>
232
- <DropdownMenuItemLabel>
233
- { field.label }
234
- </DropdownMenuItemLabel>
235
- </DropdownMenuItem>
236
- }
237
- style={ {
238
- minWidth: '220px',
239
- } }
285
+ { title }
286
+ </Heading>
287
+ { description && (
288
+ <Text
289
+ variant="muted"
290
+ className="dataviews-settings-section__description"
240
291
  >
241
- { SORTING_DIRECTIONS.map( ( direction ) => {
242
- const isChecked =
243
- currentSortedField !== undefined &&
244
- sortedDirection === direction &&
245
- field.id === currentSortedField.id;
246
-
247
- const value = `${ field.id }-${ direction }`;
292
+ { description }
293
+ </Text>
294
+ ) }
295
+ </div>
296
+ <Grid
297
+ columns={ 8 }
298
+ gap={ 4 }
299
+ className="dataviews-settings-section__content"
300
+ >
301
+ { children }
302
+ </Grid>
303
+ </Grid>
304
+ );
305
+ }
248
306
 
249
- return (
250
- <DropdownMenuRadioItem
251
- key={ value }
252
- // All sorting radio items share the same name, so that
253
- // selecting a sorting option automatically deselects the
254
- // previously selected one, even if it is displayed in
255
- // another submenu. The field and direction are passed via
256
- // the `value` prop.
257
- name="view-actions-sorting"
258
- value={ value }
259
- checked={ isChecked }
260
- onChange={ () => {
261
- onChangeView( {
262
- ...view,
263
- sort: {
264
- field: field.id,
265
- direction,
266
- },
267
- } );
268
- } }
269
- >
270
- <DropdownMenuItemLabel>
271
- { sortLabels[ direction ] }
272
- </DropdownMenuItemLabel>
273
- </DropdownMenuRadioItem>
274
- );
275
- } ) }
276
- </DropdownMenu>
277
- );
278
- } ) }
279
- </DropdownMenu>
307
+ function DataviewsViewConfigContent() {
308
+ return (
309
+ <VStack className="dataviews-view-config" spacing={ 6 }>
310
+ <SettingsSection title={ __( 'Appearance' ) }>
311
+ <HStack expanded className="is-divided-in-two">
312
+ <SortFieldControl />
313
+ <SortDirectionControl />
314
+ </HStack>
315
+ <ItemsPerPageControl />
316
+ </SettingsSection>
317
+ <SettingsSection title={ __( 'Properties' ) }>
318
+ <FieldControl />
319
+ </SettingsSection>
320
+ </VStack>
280
321
  );
281
322
  }
282
323
 
283
- function _DataViewsViewConfig( { defaultLayouts }: ViewActionsProps ) {
284
- const { view, fields, onChangeView } = useContext( DataViewsContext );
285
- const activeView = VIEW_LAYOUTS.find( ( v ) => view.type === v.type );
324
+ function _DataViewsViewConfig( {
325
+ defaultLayouts = { list: {}, grid: {}, table: {} },
326
+ }: ViewActionsProps ) {
327
+ const [ isShowingViewPopover, setIsShowingViewPopover ] =
328
+ useState< boolean >( false );
329
+
286
330
  return (
287
331
  <>
288
- <HStack
289
- spacing={ 1 }
290
- expanded={ false }
291
- style={ { flexShrink: 0 } }
292
- >
293
- <DropdownMenu
294
- trigger={
295
- <Button
296
- size="compact"
297
- icon={ activeView?.icon }
298
- label={ __( 'Layout' ) }
299
- />
300
- }
301
- >
302
- <ViewTypeMenu
303
- view={ view }
304
- onChangeView={ onChangeView }
305
- defaultLayouts={ defaultLayouts }
306
- />
307
- </DropdownMenu>
308
- <DropdownMenu
309
- trigger={
310
- <Button
311
- size="compact"
312
- icon={ cog }
313
- label={ _x(
314
- 'View options',
315
- 'View is used as a noun'
316
- ) }
317
- />
318
- }
319
- >
320
- <DropdownMenuGroup>
321
- <SortMenu
322
- fields={ fields }
323
- view={ view }
324
- onChangeView={ onChangeView }
325
- />
326
- <FieldsVisibilityMenu
327
- fields={ fields }
328
- view={ view }
329
- onChangeView={ onChangeView }
330
- />
331
- <PageSizeMenu
332
- view={ view }
333
- onChangeView={ onChangeView }
334
- />
335
- </DropdownMenuGroup>
336
- </DropdownMenu>
337
- </HStack>
332
+ <ViewTypeMenu defaultLayouts={ defaultLayouts } />
333
+ <div>
334
+ <Button
335
+ size="compact"
336
+ icon={ cog }
337
+ label={ _x( 'View options', 'View is used as a noun' ) }
338
+ onClick={ () => setIsShowingViewPopover( true ) }
339
+ />
340
+ { isShowingViewPopover && (
341
+ <Popover
342
+ placement="bottom-end"
343
+ onClose={ () => {
344
+ setIsShowingViewPopover( false );
345
+ } }
346
+ focusOnMount
347
+ >
348
+ <DataviewsViewConfigContent />
349
+ </Popover>
350
+ ) }
351
+ </div>
338
352
  </>
339
353
  );
340
354
  }
@@ -0,0 +1,44 @@
1
+ .dataviews-view-config {
2
+ width: 320px;
3
+ /* stylelint-disable-next-line property-no-unknown -- the linter needs to be updated to accepted the container-type property */
4
+ container-type: inline-size;
5
+ padding: $grid-unit-20;
6
+ }
7
+ .dataviews-view-config__sort-direction .components-toggle-group-control-option-base {
8
+ text-transform: uppercase;
9
+ }
10
+
11
+ .dataviews-settings-section__title.dataviews-settings-section__title {
12
+ line-height: $grid-unit-30;
13
+ font-size: 15px;
14
+ }
15
+
16
+ .dataviews-settings-section__sidebar {
17
+ grid-column: span 4;
18
+ }
19
+
20
+ .dataviews-settings-section__content,
21
+ .dataviews-settings-section__content > * {
22
+ grid-column: span 8;
23
+ }
24
+
25
+ .dataviews-settings-section__content .is-divided-in-two {
26
+ display: contents;
27
+ & > * {
28
+ grid-column: span 4;
29
+ }
30
+ }
31
+
32
+ /* stylelint-disable-next-line scss/at-rule-no-unknown -- '@container' not globally permitted */
33
+ @container (max-width: 500px) {
34
+ .dataviews-settings-section.dataviews-settings-section {
35
+ grid-template-columns: repeat(2, 1fr);
36
+ .dataviews-settings-section__sidebar {
37
+ grid-column: span 2;
38
+ }
39
+
40
+ .dataviews-settings-section__content {
41
+ grid-column: span 2;
42
+ }
43
+ }
44
+ }
package/src/constants.ts CHANGED
@@ -2,6 +2,7 @@
2
2
  * WordPress dependencies
3
3
  */
4
4
  import { __ } from '@wordpress/i18n';
5
+ import { arrowDown, arrowUp } from '@wordpress/icons';
5
6
 
6
7
  /**
7
8
  * Internal dependencies
@@ -58,6 +59,10 @@ export const sortLabels = {
58
59
  asc: __( 'Sort ascending' ),
59
60
  desc: __( 'Sort descending' ),
60
61
  };
62
+ export const sortIcons = {
63
+ asc: arrowUp,
64
+ desc: arrowDown,
65
+ };
61
66
 
62
67
  // View layouts.
63
68
  export const LAYOUT_TABLE = 'table';
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Internal dependencies
3
+ */
4
+ import FormRegular from './regular';
5
+ import FormPanel from './panel';
6
+
7
+ const FORM_LAYOUTS = [
8
+ {
9
+ type: 'regular',
10
+ component: FormRegular,
11
+ },
12
+ {
13
+ type: 'panel',
14
+ component: FormPanel,
15
+ },
16
+ ];
17
+
18
+ export function getFormLayout( type: string ) {
19
+ return FORM_LAYOUTS.find( ( layout ) => layout.type === type );
20
+ }