@wordpress/dataviews 2.2.0 → 4.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 (298) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/README.md +23 -8
  3. package/build/components/dataform/index.js +78 -0
  4. package/build/components/dataform/index.js.map +1 -0
  5. package/build/components/dataviews/index.js +115 -0
  6. package/build/components/dataviews/index.js.map +1 -0
  7. package/build/{bulk-actions.js → components/dataviews-bulk-actions/index.js} +39 -16
  8. package/build/components/dataviews-bulk-actions/index.js.map +1 -0
  9. package/build/{bulk-actions-toolbar.js → components/dataviews-bulk-actions-toolbar/index.js} +36 -20
  10. package/build/components/dataviews-bulk-actions-toolbar/index.js.map +1 -0
  11. package/build/components/dataviews-context/index.js +36 -0
  12. package/build/components/dataviews-context/index.js.map +1 -0
  13. package/build/{add-filter.js → components/dataviews-filters/add-filter.js} +3 -3
  14. package/build/components/dataviews-filters/add-filter.js.map +1 -0
  15. package/build/{filter-summary.js → components/dataviews-filters/filter-summary.js} +15 -14
  16. package/build/components/dataviews-filters/filter-summary.js.map +1 -0
  17. package/build/{filters.js → components/dataviews-filters/index.js} +15 -16
  18. package/build/components/dataviews-filters/index.js.map +1 -0
  19. package/build/{reset-filters.js → components/dataviews-filters/reset-filters.js} +1 -1
  20. package/build/components/dataviews-filters/reset-filters.js.map +1 -0
  21. package/build/{search-widget.js → components/dataviews-filters/search-widget.js} +21 -19
  22. package/build/components/dataviews-filters/search-widget.js.map +1 -0
  23. package/build/{item-actions.js → components/dataviews-item-actions/index.js} +3 -3
  24. package/build/components/dataviews-item-actions/index.js.map +1 -0
  25. package/build/components/dataviews-layout/index.js +53 -0
  26. package/build/components/dataviews-layout/index.js.map +1 -0
  27. package/build/{pagination.js → components/dataviews-pagination/index.js} +18 -15
  28. package/build/components/dataviews-pagination/index.js.map +1 -0
  29. package/build/{search.js → components/dataviews-search/index.js} +10 -6
  30. package/build/components/dataviews-search/index.js.map +1 -0
  31. package/build/components/dataviews-selection-checkbox/index.js +52 -0
  32. package/build/components/dataviews-selection-checkbox/index.js.map +1 -0
  33. package/build/{view-actions.js → components/dataviews-view-config/index.js} +94 -80
  34. package/build/components/dataviews-view-config/index.js.map +1 -0
  35. package/build/filter-and-sort-data-view.js +4 -1
  36. package/build/filter-and-sort-data-view.js.map +1 -1
  37. package/build/index.js +8 -1
  38. package/build/index.js.map +1 -1
  39. package/build/layouts/grid/density-picker.js +143 -0
  40. package/build/layouts/grid/density-picker.js.map +1 -0
  41. package/build/{view-grid.js → layouts/grid/index.js} +40 -53
  42. package/build/layouts/grid/index.js.map +1 -0
  43. package/build/layouts/index.js +52 -0
  44. package/build/layouts/index.js.map +1 -0
  45. package/build/{view-list.js → layouts/list/index.js} +31 -27
  46. package/build/layouts/list/index.js.map +1 -0
  47. package/build/layouts/table/column-header-menu.js +196 -0
  48. package/build/layouts/table/column-header-menu.js.map +1 -0
  49. package/build/layouts/table/index.js +350 -0
  50. package/build/layouts/table/index.js.map +1 -0
  51. package/build/normalize-fields.js +1 -1
  52. package/build/normalize-fields.js.map +1 -1
  53. package/build/private-types.js +6 -0
  54. package/build/private-types.js.map +1 -0
  55. package/build/types.js.map +1 -1
  56. package/build/utils.js.map +1 -1
  57. package/build-module/components/dataform/index.js +72 -0
  58. package/build-module/components/dataform/index.js.map +1 -0
  59. package/build-module/components/dataviews/index.js +108 -0
  60. package/build-module/components/dataviews/index.js.map +1 -0
  61. package/build-module/{bulk-actions.js → components/dataviews-bulk-actions/index.js} +39 -17
  62. package/build-module/components/dataviews-bulk-actions/index.js.map +1 -0
  63. package/build-module/{bulk-actions-toolbar.js → components/dataviews-bulk-actions-toolbar/index.js} +35 -20
  64. package/build-module/components/dataviews-bulk-actions-toolbar/index.js.map +1 -0
  65. package/build-module/components/dataviews-context/index.js +30 -0
  66. package/build-module/components/dataviews-context/index.js.map +1 -0
  67. package/build-module/{add-filter.js → components/dataviews-filters/add-filter.js} +3 -3
  68. package/build-module/components/dataviews-filters/add-filter.js.map +1 -0
  69. package/build-module/{filter-summary.js → components/dataviews-filters/filter-summary.js} +15 -14
  70. package/build-module/components/dataviews-filters/filter-summary.js.map +1 -0
  71. package/build-module/{filters.js → components/dataviews-filters/index.js} +16 -17
  72. package/build-module/components/dataviews-filters/index.js.map +1 -0
  73. package/build-module/{reset-filters.js → components/dataviews-filters/reset-filters.js} +1 -1
  74. package/build-module/components/dataviews-filters/reset-filters.js.map +1 -0
  75. package/build-module/{search-widget.js → components/dataviews-filters/search-widget.js} +21 -19
  76. package/build-module/components/dataviews-filters/search-widget.js.map +1 -0
  77. package/build-module/{item-actions.js → components/dataviews-item-actions/index.js} +3 -3
  78. package/build-module/components/dataviews-item-actions/index.js.map +1 -0
  79. package/build-module/components/dataviews-layout/index.js +45 -0
  80. package/build-module/components/dataviews-layout/index.js.map +1 -0
  81. package/build-module/{pagination.js → components/dataviews-pagination/index.js} +19 -17
  82. package/build-module/components/dataviews-pagination/index.js.map +1 -0
  83. package/build-module/{search.js → components/dataviews-search/index.js} +10 -7
  84. package/build-module/components/dataviews-search/index.js.map +1 -0
  85. package/build-module/components/dataviews-selection-checkbox/index.js +45 -0
  86. package/build-module/components/dataviews-selection-checkbox/index.js.map +1 -0
  87. package/build-module/{view-actions.js → components/dataviews-view-config/index.js} +98 -84
  88. package/build-module/components/dataviews-view-config/index.js.map +1 -0
  89. package/build-module/filter-and-sort-data-view.js +4 -1
  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/layouts/grid/density-picker.js +138 -0
  94. package/build-module/layouts/grid/density-picker.js.map +1 -0
  95. package/build-module/{view-grid.js → layouts/grid/index.js} +37 -50
  96. package/build-module/layouts/grid/index.js.map +1 -0
  97. package/build-module/layouts/index.js +43 -0
  98. package/build-module/layouts/index.js.map +1 -0
  99. package/build-module/{view-list.js → layouts/list/index.js} +29 -25
  100. package/build-module/layouts/list/index.js.map +1 -0
  101. package/build-module/layouts/table/column-header-menu.js +190 -0
  102. package/build-module/layouts/table/column-header-menu.js.map +1 -0
  103. package/build-module/layouts/table/index.js +344 -0
  104. package/build-module/layouts/table/index.js.map +1 -0
  105. package/build-module/normalize-fields.js +1 -1
  106. package/build-module/normalize-fields.js.map +1 -1
  107. package/build-module/private-types.js +2 -0
  108. package/build-module/private-types.js.map +1 -0
  109. package/build-module/types.js.map +1 -1
  110. package/build-module/utils.js.map +1 -1
  111. package/build-style/style-rtl.css +607 -561
  112. package/build-style/style.css +607 -561
  113. package/build-types/components/dataform/index.d.ts +17 -0
  114. package/build-types/components/dataform/index.d.ts.map +1 -0
  115. package/build-types/components/dataform/stories/index.story.d.ts +11 -0
  116. package/build-types/components/dataform/stories/index.story.d.ts.map +1 -0
  117. package/build-types/components/dataviews/index.d.ts +33 -0
  118. package/build-types/components/dataviews/index.d.ts.map +1 -0
  119. package/build-types/{stories → components/dataviews/stories}/fixtures.d.ts +18 -17
  120. package/build-types/components/dataviews/stories/fixtures.d.ts.map +1 -0
  121. package/build-types/components/dataviews/stories/index.story.d.ts +46 -0
  122. package/build-types/components/dataviews/stories/index.story.d.ts.map +1 -0
  123. package/build-types/components/dataviews-bulk-actions/index.d.ts +5 -0
  124. package/build-types/components/dataviews-bulk-actions/index.d.ts.map +1 -0
  125. package/build-types/components/dataviews-bulk-actions-toolbar/index.d.ts +2 -0
  126. package/build-types/components/dataviews-bulk-actions-toolbar/index.d.ts.map +1 -0
  127. package/build-types/components/dataviews-context/index.d.ts +26 -0
  128. package/build-types/components/dataviews-context/index.d.ts.map +1 -0
  129. package/build-types/{add-filter.d.ts → components/dataviews-filters/add-filter.d.ts} +1 -2
  130. package/build-types/components/dataviews-filters/add-filter.d.ts.map +1 -0
  131. package/build-types/{filter-summary.d.ts → components/dataviews-filters/filter-summary.d.ts} +1 -1
  132. package/build-types/components/dataviews-filters/filter-summary.d.ts.map +1 -0
  133. package/build-types/components/dataviews-filters/index.d.ts +4 -0
  134. package/build-types/components/dataviews-filters/index.d.ts.map +1 -0
  135. package/build-types/{reset-filters.d.ts → components/dataviews-filters/reset-filters.d.ts} +1 -2
  136. package/build-types/components/dataviews-filters/reset-filters.d.ts.map +1 -0
  137. package/build-types/{search-widget.d.ts → components/dataviews-filters/search-widget.d.ts} +1 -2
  138. package/build-types/components/dataviews-filters/search-widget.d.ts.map +1 -0
  139. package/build-types/components/dataviews-item-actions/index.d.ts +35 -0
  140. package/build-types/components/dataviews-item-actions/index.d.ts.map +1 -0
  141. package/build-types/components/dataviews-layout/index.d.ts +2 -0
  142. package/build-types/components/dataviews-layout/index.d.ts.map +1 -0
  143. package/build-types/components/dataviews-pagination/index.d.ts +4 -0
  144. package/build-types/components/dataviews-pagination/index.d.ts.map +1 -0
  145. package/build-types/components/dataviews-search/index.d.ts +6 -0
  146. package/build-types/components/dataviews-search/index.d.ts.map +1 -0
  147. package/build-types/components/dataviews-selection-checkbox/index.d.ts +16 -0
  148. package/build-types/components/dataviews-selection-checkbox/index.d.ts.map +1 -0
  149. package/build-types/components/dataviews-view-config/index.d.ts +8 -0
  150. package/build-types/components/dataviews-view-config/index.d.ts.map +1 -0
  151. package/build-types/filter-and-sort-data-view.d.ts +2 -2
  152. package/build-types/filter-and-sort-data-view.d.ts.map +1 -1
  153. package/build-types/index.d.ts +2 -1
  154. package/build-types/index.d.ts.map +1 -1
  155. package/build-types/layouts/grid/density-picker.d.ts +5 -0
  156. package/build-types/layouts/grid/density-picker.d.ts.map +1 -0
  157. package/build-types/layouts/grid/index.d.ts +3 -0
  158. package/build-types/layouts/grid/index.d.ts.map +1 -0
  159. package/build-types/{layouts.d.ts → layouts/index.d.ts} +6 -5
  160. package/build-types/layouts/index.d.ts.map +1 -0
  161. package/build-types/layouts/list/index.d.ts +3 -0
  162. package/build-types/layouts/list/index.d.ts.map +1 -0
  163. package/build-types/layouts/table/column-header-menu.d.ts +17 -0
  164. package/build-types/layouts/table/column-header-menu.d.ts.map +1 -0
  165. package/build-types/layouts/table/index.d.ts +4 -0
  166. package/build-types/layouts/table/index.d.ts.map +1 -0
  167. package/build-types/normalize-fields.d.ts +2 -2
  168. package/build-types/normalize-fields.d.ts.map +1 -1
  169. package/build-types/private-types.d.ts +3 -0
  170. package/build-types/private-types.d.ts.map +1 -0
  171. package/build-types/types.d.ts +106 -46
  172. package/build-types/types.d.ts.map +1 -1
  173. package/build-types/utils.d.ts +2 -2
  174. package/build-types/utils.d.ts.map +1 -1
  175. package/package.json +10 -10
  176. package/src/components/dataform/index.tsx +106 -0
  177. package/src/components/dataform/stories/index.story.tsx +42 -0
  178. package/src/components/dataviews/index.tsx +149 -0
  179. package/src/{stories → components/dataviews/stories}/fixtures.js +23 -11
  180. package/src/components/dataviews/stories/index.story.js +65 -0
  181. package/src/components/dataviews/style.scss +97 -0
  182. package/src/{bulk-actions.tsx → components/dataviews-bulk-actions/index.tsx} +58 -36
  183. package/src/components/dataviews-bulk-actions/style.scss +7 -0
  184. package/src/{bulk-actions-toolbar.tsx → components/dataviews-bulk-actions-toolbar/index.tsx} +48 -36
  185. package/src/components/dataviews-bulk-actions-toolbar/style.scss +45 -0
  186. package/src/components/dataviews-context/index.ts +49 -0
  187. package/src/{add-filter.tsx → components/dataviews-filters/add-filter.tsx} +4 -4
  188. package/src/{filter-summary.tsx → components/dataviews-filters/filter-summary.tsx} +36 -22
  189. package/src/{filters.tsx → components/dataviews-filters/index.tsx} +11 -25
  190. package/src/{reset-filters.tsx → components/dataviews-filters/reset-filters.tsx} +2 -2
  191. package/src/{search-widget.tsx → components/dataviews-filters/search-widget.tsx} +20 -20
  192. package/src/components/dataviews-filters/style.scss +252 -0
  193. package/src/{item-actions.tsx → components/dataviews-item-actions/index.tsx} +16 -17
  194. package/src/components/dataviews-item-actions/style.scss +3 -0
  195. package/src/components/dataviews-layout/index.tsx +51 -0
  196. package/src/{pagination.tsx → components/dataviews-pagination/index.tsx} +15 -23
  197. package/src/components/dataviews-pagination/style.scss +26 -0
  198. package/src/{search.tsx → components/dataviews-search/index.tsx} +5 -10
  199. package/src/components/dataviews-selection-checkbox/index.tsx +65 -0
  200. package/src/components/dataviews-selection-checkbox/style.scss +14 -0
  201. package/src/{view-actions.tsx → components/dataviews-view-config/index.tsx} +116 -119
  202. package/src/filter-and-sort-data-view.ts +13 -3
  203. package/src/index.ts +2 -1
  204. package/src/layouts/grid/density-picker.tsx +136 -0
  205. package/src/{view-grid.tsx → layouts/grid/index.tsx} +45 -63
  206. package/src/layouts/grid/style.scss +140 -0
  207. package/src/layouts/index.ts +66 -0
  208. package/src/{view-list.tsx → layouts/list/index.tsx} +40 -30
  209. package/src/layouts/list/style.scss +189 -0
  210. package/src/layouts/table/column-header-menu.tsx +268 -0
  211. package/src/layouts/table/index.tsx +471 -0
  212. package/src/layouts/table/style.scss +201 -0
  213. package/src/normalize-fields.ts +6 -4
  214. package/src/private-types.tsx +2 -0
  215. package/src/style.scss +11 -919
  216. package/src/test/filter-and-sort-data-view.js +17 -2
  217. package/src/types.ts +113 -55
  218. package/src/utils.ts +2 -4
  219. package/tsconfig.tsbuildinfo +1 -1
  220. package/build/add-filter.js.map +0 -1
  221. package/build/bulk-actions-toolbar.js.map +0 -1
  222. package/build/bulk-actions.js.map +0 -1
  223. package/build/dataviews.js +0 -136
  224. package/build/dataviews.js.map +0 -1
  225. package/build/filter-summary.js.map +0 -1
  226. package/build/filters.js.map +0 -1
  227. package/build/item-actions.js.map +0 -1
  228. package/build/layouts.js +0 -38
  229. package/build/layouts.js.map +0 -1
  230. package/build/pagination.js.map +0 -1
  231. package/build/reset-filters.js.map +0 -1
  232. package/build/search-widget.js.map +0 -1
  233. package/build/search.js.map +0 -1
  234. package/build/single-selection-checkbox.js +0 -63
  235. package/build/single-selection-checkbox.js.map +0 -1
  236. package/build/view-actions.js.map +0 -1
  237. package/build/view-grid.js.map +0 -1
  238. package/build/view-list.js.map +0 -1
  239. package/build/view-table.js +0 -409
  240. package/build/view-table.js.map +0 -1
  241. package/build-module/add-filter.js.map +0 -1
  242. package/build-module/bulk-actions-toolbar.js.map +0 -1
  243. package/build-module/bulk-actions.js.map +0 -1
  244. package/build-module/dataviews.js +0 -129
  245. package/build-module/dataviews.js.map +0 -1
  246. package/build-module/filter-summary.js.map +0 -1
  247. package/build-module/filters.js.map +0 -1
  248. package/build-module/item-actions.js.map +0 -1
  249. package/build-module/layouts.js +0 -30
  250. package/build-module/layouts.js.map +0 -1
  251. package/build-module/pagination.js.map +0 -1
  252. package/build-module/reset-filters.js.map +0 -1
  253. package/build-module/search-widget.js.map +0 -1
  254. package/build-module/search.js.map +0 -1
  255. package/build-module/single-selection-checkbox.js +0 -56
  256. package/build-module/single-selection-checkbox.js.map +0 -1
  257. package/build-module/view-actions.js.map +0 -1
  258. package/build-module/view-grid.js.map +0 -1
  259. package/build-module/view-list.js.map +0 -1
  260. package/build-module/view-table.js +0 -402
  261. package/build-module/view-table.js.map +0 -1
  262. package/build-types/add-filter.d.ts.map +0 -1
  263. package/build-types/bulk-actions-toolbar.d.ts +0 -12
  264. package/build-types/bulk-actions-toolbar.d.ts.map +0 -1
  265. package/build-types/bulk-actions.d.ts +0 -14
  266. package/build-types/bulk-actions.d.ts.map +0 -1
  267. package/build-types/dataviews.d.ts +0 -24
  268. package/build-types/dataviews.d.ts.map +0 -1
  269. package/build-types/filter-summary.d.ts.map +0 -1
  270. package/build-types/filters.d.ts +0 -13
  271. package/build-types/filters.d.ts.map +0 -1
  272. package/build-types/item-actions.d.ts +0 -35
  273. package/build-types/item-actions.d.ts.map +0 -1
  274. package/build-types/layouts.d.ts.map +0 -1
  275. package/build-types/pagination.d.ts +0 -16
  276. package/build-types/pagination.d.ts.map +0 -1
  277. package/build-types/reset-filters.d.ts.map +0 -1
  278. package/build-types/search-widget.d.ts.map +0 -1
  279. package/build-types/search.d.ts +0 -13
  280. package/build-types/search.d.ts.map +0 -1
  281. package/build-types/single-selection-checkbox.d.ts +0 -17
  282. package/build-types/single-selection-checkbox.d.ts.map +0 -1
  283. package/build-types/stories/fixtures.d.ts.map +0 -1
  284. package/build-types/stories/index.story.d.ts +0 -15
  285. package/build-types/stories/index.story.d.ts.map +0 -1
  286. package/build-types/view-actions.d.ts +0 -12
  287. package/build-types/view-actions.d.ts.map +0 -1
  288. package/build-types/view-grid.d.ts +0 -4
  289. package/build-types/view-grid.d.ts.map +0 -1
  290. package/build-types/view-list.d.ts +0 -4
  291. package/build-types/view-list.d.ts.map +0 -1
  292. package/build-types/view-table.d.ts +0 -5
  293. package/build-types/view-table.d.ts.map +0 -1
  294. package/src/dataviews.tsx +0 -189
  295. package/src/layouts.ts +0 -39
  296. package/src/single-selection-checkbox.tsx +0 -80
  297. package/src/stories/index.story.js +0 -64
  298. package/src/view-table.tsx +0 -603
@@ -0,0 +1,471 @@
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import clsx from 'clsx';
5
+
6
+ /**
7
+ * WordPress dependencies
8
+ */
9
+ import { __ } from '@wordpress/i18n';
10
+ import {
11
+ CheckboxControl,
12
+ Spinner,
13
+ __experimentalHStack as HStack,
14
+ __experimentalVStack as VStack,
15
+ } from '@wordpress/components';
16
+ import {
17
+ useEffect,
18
+ useId,
19
+ useRef,
20
+ useState,
21
+ useMemo,
22
+ } from '@wordpress/element';
23
+
24
+ /**
25
+ * Internal dependencies
26
+ */
27
+ import SingleSelectionCheckbox from '../../components/dataviews-selection-checkbox';
28
+ import ItemActions from '../../components/dataviews-item-actions';
29
+ import { sortValues } from '../../constants';
30
+ import {
31
+ useSomeItemHasAPossibleBulkAction,
32
+ useHasAPossibleBulkAction,
33
+ } from '../../components/dataviews-bulk-actions';
34
+ import type {
35
+ Action,
36
+ NormalizedField,
37
+ ViewTable as ViewTableType,
38
+ ViewTableProps,
39
+ CombinedField,
40
+ } from '../../types';
41
+ import type { SetSelection } from '../../private-types';
42
+ import ColumnHeaderMenu from './column-header-menu';
43
+
44
+ interface BulkSelectionCheckboxProps< Item > {
45
+ selection: string[];
46
+ onChangeSelection: SetSelection;
47
+ data: Item[];
48
+ actions: Action< Item >[];
49
+ getItemId: ( item: Item ) => string;
50
+ }
51
+
52
+ interface TableColumnFieldProps< Item > {
53
+ primaryField?: NormalizedField< Item >;
54
+ field: NormalizedField< Item >;
55
+ item: Item;
56
+ }
57
+
58
+ interface TableColumnCombinedProps< Item > {
59
+ primaryField?: NormalizedField< Item >;
60
+ fields: NormalizedField< Item >[];
61
+ field: CombinedField;
62
+ item: Item;
63
+ view: ViewTableType;
64
+ }
65
+
66
+ interface TableColumnProps< Item > {
67
+ primaryField?: NormalizedField< Item >;
68
+ fields: NormalizedField< Item >[];
69
+ item: Item;
70
+ column: string;
71
+ view: ViewTableType;
72
+ }
73
+
74
+ interface TableRowProps< Item > {
75
+ hasBulkActions: boolean;
76
+ item: Item;
77
+ actions: Action< Item >[];
78
+ fields: NormalizedField< Item >[];
79
+ id: string;
80
+ view: ViewTableType;
81
+ primaryField?: NormalizedField< Item >;
82
+ selection: string[];
83
+ getItemId: ( item: Item ) => string;
84
+ onChangeSelection: SetSelection;
85
+ }
86
+
87
+ function BulkSelectionCheckbox< Item >( {
88
+ selection,
89
+ onChangeSelection,
90
+ data,
91
+ actions,
92
+ getItemId,
93
+ }: BulkSelectionCheckboxProps< Item > ) {
94
+ const selectableItems = useMemo( () => {
95
+ return data.filter( ( item ) => {
96
+ return actions.some(
97
+ ( action ) =>
98
+ action.supportsBulk &&
99
+ ( ! action.isEligible || action.isEligible( item ) )
100
+ );
101
+ } );
102
+ }, [ data, actions ] );
103
+ const selectedItems = data.filter(
104
+ ( item ) =>
105
+ selection.includes( getItemId( item ) ) &&
106
+ selectableItems.includes( item )
107
+ );
108
+ const areAllSelected = selectedItems.length === selectableItems.length;
109
+ return (
110
+ <CheckboxControl
111
+ className="dataviews-view-table-selection-checkbox"
112
+ __nextHasNoMarginBottom
113
+ checked={ areAllSelected }
114
+ indeterminate={ ! areAllSelected && !! selectedItems.length }
115
+ onChange={ () => {
116
+ if ( areAllSelected ) {
117
+ onChangeSelection( [] );
118
+ } else {
119
+ onChangeSelection(
120
+ selectableItems.map( ( item ) => getItemId( item ) )
121
+ );
122
+ }
123
+ } }
124
+ aria-label={
125
+ areAllSelected ? __( 'Deselect all' ) : __( 'Select all' )
126
+ }
127
+ />
128
+ );
129
+ }
130
+
131
+ function TableColumn< Item >( {
132
+ column,
133
+ fields,
134
+ view,
135
+ ...props
136
+ }: TableColumnProps< Item > ) {
137
+ const field = fields.find( ( f ) => f.id === column );
138
+ if ( !! field ) {
139
+ return <TableColumnField { ...props } field={ field } />;
140
+ }
141
+ const combinedField = view.layout?.combinedFields?.find(
142
+ ( f ) => f.id === column
143
+ );
144
+ if ( !! combinedField ) {
145
+ return (
146
+ <TableColumnCombined
147
+ { ...props }
148
+ fields={ fields }
149
+ view={ view }
150
+ field={ combinedField }
151
+ />
152
+ );
153
+ }
154
+
155
+ return null;
156
+ }
157
+
158
+ function TableColumnField< Item >( {
159
+ primaryField,
160
+ item,
161
+ field,
162
+ }: TableColumnFieldProps< Item > ) {
163
+ return (
164
+ <div
165
+ className={ clsx( 'dataviews-view-table__cell-content-wrapper', {
166
+ 'dataviews-view-table__primary-field':
167
+ primaryField?.id === field.id,
168
+ } ) }
169
+ >
170
+ <field.render { ...{ item } } />
171
+ </div>
172
+ );
173
+ }
174
+
175
+ function TableColumnCombined< Item >( {
176
+ field,
177
+ ...props
178
+ }: TableColumnCombinedProps< Item > ) {
179
+ const children = field.children.map( ( child ) => (
180
+ <TableColumn key={ child } { ...props } column={ child } />
181
+ ) );
182
+
183
+ if ( field.direction === 'horizontal' ) {
184
+ return <HStack spacing={ 3 }>{ children }</HStack>;
185
+ }
186
+ return <VStack spacing={ 0 }>{ children }</VStack>;
187
+ }
188
+
189
+ function TableRow< Item >( {
190
+ hasBulkActions,
191
+ item,
192
+ actions,
193
+ fields,
194
+ id,
195
+ view,
196
+ primaryField,
197
+ selection,
198
+ getItemId,
199
+ onChangeSelection,
200
+ }: TableRowProps< Item > ) {
201
+ const hasPossibleBulkAction = useHasAPossibleBulkAction( actions, item );
202
+ const isSelected = hasPossibleBulkAction && selection.includes( id );
203
+ const [ isHovered, setIsHovered ] = useState( false );
204
+
205
+ const handleMouseEnter = () => {
206
+ setIsHovered( true );
207
+ };
208
+ const handleMouseLeave = () => {
209
+ setIsHovered( false );
210
+ };
211
+
212
+ // Will be set to true if `onTouchStart` fires. This happens before
213
+ // `onClick` and can be used to exclude touchscreen devices from certain
214
+ // behaviours.
215
+ const isTouchDevice = useRef( false );
216
+ const columns = view.fields || fields.map( ( f ) => f.id );
217
+
218
+ return (
219
+ <tr
220
+ className={ clsx( 'dataviews-view-table__row', {
221
+ 'is-selected': hasPossibleBulkAction && isSelected,
222
+ 'is-hovered': isHovered,
223
+ 'has-bulk-actions': hasPossibleBulkAction,
224
+ } ) }
225
+ onMouseEnter={ handleMouseEnter }
226
+ onMouseLeave={ handleMouseLeave }
227
+ onTouchStart={ () => {
228
+ isTouchDevice.current = true;
229
+ } }
230
+ onClick={ () => {
231
+ if ( ! hasPossibleBulkAction ) {
232
+ return;
233
+ }
234
+ if (
235
+ ! isTouchDevice.current &&
236
+ document.getSelection()?.type !== 'Range'
237
+ ) {
238
+ onChangeSelection(
239
+ selection.includes( id )
240
+ ? selection.filter( ( itemId ) => id !== itemId )
241
+ : [ id ]
242
+ );
243
+ }
244
+ } }
245
+ >
246
+ { hasBulkActions && (
247
+ <td
248
+ className="dataviews-view-table__checkbox-column"
249
+ style={ {
250
+ width: '1%',
251
+ } }
252
+ >
253
+ <div className="dataviews-view-table__cell-content-wrapper">
254
+ <SingleSelectionCheckbox
255
+ item={ item }
256
+ selection={ selection }
257
+ onChangeSelection={ onChangeSelection }
258
+ getItemId={ getItemId }
259
+ primaryField={ primaryField }
260
+ disabled={ ! hasPossibleBulkAction }
261
+ />
262
+ </div>
263
+ </td>
264
+ ) }
265
+ { columns.map( ( column: string ) => {
266
+ // Explicits picks the supported styles.
267
+ const { width, maxWidth, minWidth } =
268
+ view.layout?.styles?.[ column ] ?? {};
269
+
270
+ return (
271
+ <td key={ column } style={ { width, maxWidth, minWidth } }>
272
+ <TableColumn
273
+ primaryField={ primaryField }
274
+ fields={ fields }
275
+ item={ item }
276
+ column={ column }
277
+ view={ view }
278
+ />
279
+ </td>
280
+ );
281
+ } ) }
282
+ { !! actions?.length && (
283
+ // Disable reason: we are not making the element interactive,
284
+ // but preventing any click events from bubbling up to the
285
+ // table row. This allows us to add a click handler to the row
286
+ // itself (to toggle row selection) without erroneously
287
+ // intercepting click events from ItemActions.
288
+
289
+ /* eslint-disable jsx-a11y/no-noninteractive-element-interactions, jsx-a11y/click-events-have-key-events */
290
+ <td
291
+ className="dataviews-view-table__actions-column"
292
+ onClick={ ( e ) => e.stopPropagation() }
293
+ >
294
+ <ItemActions item={ item } actions={ actions } />
295
+ </td>
296
+ /* eslint-enable jsx-a11y/no-noninteractive-element-interactions, jsx-a11y/click-events-have-key-events */
297
+ ) }
298
+ </tr>
299
+ );
300
+ }
301
+
302
+ function ViewTable< Item >( {
303
+ actions,
304
+ data,
305
+ fields,
306
+ getItemId,
307
+ isLoading = false,
308
+ onChangeView,
309
+ onChangeSelection,
310
+ selection,
311
+ setOpenedFilter,
312
+ view,
313
+ }: ViewTableProps< Item > ) {
314
+ const headerMenuRefs = useRef<
315
+ Map< string, { node: HTMLButtonElement; fallback: string } >
316
+ >( new Map() );
317
+ const headerMenuToFocusRef = useRef< HTMLButtonElement >();
318
+ const [ nextHeaderMenuToFocus, setNextHeaderMenuToFocus ] =
319
+ useState< HTMLButtonElement >();
320
+ const hasBulkActions = useSomeItemHasAPossibleBulkAction( actions, data );
321
+
322
+ useEffect( () => {
323
+ if ( headerMenuToFocusRef.current ) {
324
+ headerMenuToFocusRef.current.focus();
325
+ headerMenuToFocusRef.current = undefined;
326
+ }
327
+ } );
328
+
329
+ const tableNoticeId = useId();
330
+
331
+ if ( nextHeaderMenuToFocus ) {
332
+ // If we need to force focus, we short-circuit rendering here
333
+ // to prevent any additional work while we handle that.
334
+ // Clearing out the focus directive is necessary to make sure
335
+ // future renders don't cause unexpected focus jumps.
336
+ headerMenuToFocusRef.current = nextHeaderMenuToFocus;
337
+ setNextHeaderMenuToFocus( undefined );
338
+ return;
339
+ }
340
+
341
+ const onHide = ( field: NormalizedField< Item > ) => {
342
+ const hidden = headerMenuRefs.current.get( field.id );
343
+ const fallback = hidden
344
+ ? headerMenuRefs.current.get( hidden.fallback )
345
+ : undefined;
346
+ setNextHeaderMenuToFocus( fallback?.node );
347
+ };
348
+
349
+ const columns = view.fields || fields.map( ( f ) => f.id );
350
+ const hasData = !! data?.length;
351
+
352
+ const primaryField = fields.find(
353
+ ( field ) => field.id === view.layout?.primaryField
354
+ );
355
+
356
+ return (
357
+ <>
358
+ <table
359
+ className="dataviews-view-table"
360
+ aria-busy={ isLoading }
361
+ aria-describedby={ tableNoticeId }
362
+ >
363
+ <thead>
364
+ <tr className="dataviews-view-table__row">
365
+ { hasBulkActions && (
366
+ <th
367
+ className="dataviews-view-table__checkbox-column"
368
+ style={ {
369
+ width: '1%',
370
+ } }
371
+ scope="col"
372
+ >
373
+ <BulkSelectionCheckbox
374
+ selection={ selection }
375
+ onChangeSelection={ onChangeSelection }
376
+ data={ data }
377
+ actions={ actions }
378
+ getItemId={ getItemId }
379
+ />
380
+ </th>
381
+ ) }
382
+ { columns.map( ( column, index ) => {
383
+ // Explicits picks the supported styles.
384
+ const { width, maxWidth, minWidth } =
385
+ view.layout?.styles?.[ column ] ?? {};
386
+ return (
387
+ <th
388
+ key={ column }
389
+ style={ { width, maxWidth, minWidth } }
390
+ aria-sort={
391
+ view.sort?.field === column
392
+ ? sortValues[ view.sort.direction ]
393
+ : undefined
394
+ }
395
+ scope="col"
396
+ >
397
+ <ColumnHeaderMenu
398
+ ref={ ( node ) => {
399
+ if ( node ) {
400
+ headerMenuRefs.current.set(
401
+ column,
402
+ {
403
+ node,
404
+ fallback:
405
+ columns[
406
+ index > 0
407
+ ? index - 1
408
+ : 1
409
+ ],
410
+ }
411
+ );
412
+ } else {
413
+ headerMenuRefs.current.delete(
414
+ column
415
+ );
416
+ }
417
+ } }
418
+ fieldId={ column }
419
+ view={ view }
420
+ fields={ fields }
421
+ onChangeView={ onChangeView }
422
+ onHide={ onHide }
423
+ setOpenedFilter={ setOpenedFilter }
424
+ />
425
+ </th>
426
+ );
427
+ } ) }
428
+ { !! actions?.length && (
429
+ <th className="dataviews-view-table__actions-column">
430
+ <span className="dataviews-view-table-header">
431
+ { __( 'Actions' ) }
432
+ </span>
433
+ </th>
434
+ ) }
435
+ </tr>
436
+ </thead>
437
+ <tbody>
438
+ { hasData &&
439
+ data.map( ( item, index ) => (
440
+ <TableRow
441
+ key={ getItemId( item ) }
442
+ item={ item }
443
+ hasBulkActions={ hasBulkActions }
444
+ actions={ actions }
445
+ fields={ fields }
446
+ id={ getItemId( item ) || index.toString() }
447
+ view={ view }
448
+ primaryField={ primaryField }
449
+ selection={ selection }
450
+ getItemId={ getItemId }
451
+ onChangeSelection={ onChangeSelection }
452
+ />
453
+ ) ) }
454
+ </tbody>
455
+ </table>
456
+ <div
457
+ className={ clsx( {
458
+ 'dataviews-loading': isLoading,
459
+ 'dataviews-no-results': ! hasData && ! isLoading,
460
+ } ) }
461
+ id={ tableNoticeId }
462
+ >
463
+ { ! hasData && (
464
+ <p>{ isLoading ? <Spinner /> : __( 'No results' ) }</p>
465
+ ) }
466
+ </div>
467
+ </>
468
+ );
469
+ }
470
+
471
+ export default ViewTable;
@@ -0,0 +1,201 @@
1
+ .dataviews-view-table {
2
+ width: 100%;
3
+ text-indent: 0;
4
+ border-color: inherit;
5
+ border-collapse: collapse;
6
+ position: relative;
7
+ color: $gray-700;
8
+ margin-bottom: auto;
9
+
10
+ a {
11
+ text-decoration: none;
12
+ color: $gray-900;
13
+ font-weight: 500;
14
+ }
15
+ th {
16
+ text-align: left;
17
+ color: $gray-900;
18
+ font-weight: normal;
19
+ font-size: $default-font-size;
20
+ }
21
+ td,
22
+ th {
23
+ padding: $grid-unit-15;
24
+ white-space: nowrap;
25
+
26
+ &.dataviews-view-table__actions-column {
27
+ text-align: right;
28
+ }
29
+
30
+ &.dataviews-view-table__checkbox-column {
31
+ padding-right: 0;
32
+ }
33
+ }
34
+ tr {
35
+ border-top: 1px solid $gray-100;
36
+
37
+ .dataviews-view-table-header-button {
38
+ gap: $grid-unit-05;
39
+ }
40
+
41
+ td:first-child,
42
+ th:first-child {
43
+ padding-left: $grid-unit-60;
44
+
45
+ .dataviews-view-table-header-button,
46
+ .dataviews-view-table-header {
47
+ margin-left: - #{$grid-unit-10};
48
+ }
49
+ }
50
+
51
+ td:last-child,
52
+ th:last-child {
53
+ padding-right: $grid-unit-60;
54
+ }
55
+
56
+ &:last-child {
57
+ border-bottom: 0;
58
+ }
59
+
60
+ &.is-hovered {
61
+ background-color: #f8f8f8;
62
+ }
63
+
64
+ .components-checkbox-control__input.components-checkbox-control__input {
65
+ opacity: 0;
66
+
67
+ &:checked,
68
+ &:indeterminate,
69
+ &:focus {
70
+ opacity: 1;
71
+ }
72
+ }
73
+
74
+ .dataviews-item-actions .components-button:not(.dataviews-all-actions-button) {
75
+ opacity: 0;
76
+ }
77
+
78
+ &:focus-within,
79
+ &.is-hovered,
80
+ &:hover {
81
+ .components-checkbox-control__input,
82
+ .dataviews-item-actions .components-button:not(.dataviews-all-actions-button) {
83
+ opacity: 1;
84
+ }
85
+ }
86
+
87
+ @media (hover: none) {
88
+ // Show checkboxes and quick-actions on devices that do not support hover.
89
+ .components-checkbox-control__input.components-checkbox-control__input,
90
+ .dataviews-item-actions .components-button:not(.dataviews-all-actions-button) {
91
+ opacity: 1;
92
+ }
93
+ }
94
+
95
+ &.is-selected {
96
+ background-color: rgba(var(--wp-admin-theme-color--rgb), 0.04);
97
+ color: $gray-700;
98
+
99
+ &,
100
+ & + tr {
101
+ border-top: 1px solid rgba(var(--wp-admin-theme-color--rgb), 0.12);
102
+ }
103
+
104
+ &:hover {
105
+ background-color: rgba(var(--wp-admin-theme-color--rgb), 0.08);
106
+ }
107
+ }
108
+ }
109
+ thead {
110
+ position: sticky;
111
+ inset-block-start: 0;
112
+ z-index: z-index(".dataviews-view-table thead");
113
+
114
+ tr {
115
+ border: 0;
116
+ }
117
+ th {
118
+ background-color: $white;
119
+ padding-top: $grid-unit-10;
120
+ padding-bottom: $grid-unit-10;
121
+ padding-left: $grid-unit-15;
122
+ font-size: 11px;
123
+ text-transform: uppercase;
124
+ font-weight: 500;
125
+
126
+ &:has(.dataviews-view-table-header-button):not(:first-child) {
127
+ padding-left: $grid-unit-05;
128
+ }
129
+ }
130
+ }
131
+ tbody {
132
+ td {
133
+ vertical-align: top;
134
+ }
135
+ .dataviews-view-table__cell-content-wrapper {
136
+ min-height: $grid-unit-40;
137
+ display: flex;
138
+ align-items: center;
139
+ }
140
+
141
+ .components-v-stack > .dataviews-view-table__cell-content-wrapper:not(:first-child) {
142
+ min-height: 0;
143
+ }
144
+ }
145
+ .dataviews-view-table-header-button {
146
+ padding: $grid-unit-05 $grid-unit-10;
147
+ font-size: 11px;
148
+ text-transform: uppercase;
149
+ font-weight: 500;
150
+
151
+ &:not(:hover) {
152
+ color: $gray-900;
153
+ }
154
+
155
+ span {
156
+ speak: none;
157
+
158
+ &:empty {
159
+ display: none;
160
+ }
161
+ }
162
+ }
163
+
164
+ .dataviews-view-table-header {
165
+ padding-left: $grid-unit-05;
166
+ }
167
+
168
+ .dataviews-view-table__actions-column {
169
+ width: 1%;
170
+ }
171
+
172
+ &:has(tr.is-selected) {
173
+ .components-checkbox-control__input {
174
+ opacity: 1;
175
+ }
176
+ }
177
+ }
178
+
179
+ .dataviews-view-table__cell-content-wrapper:empty {
180
+ display: none;
181
+ }
182
+
183
+ /* stylelint-disable-next-line scss/at-rule-no-unknown -- '@container' not globally permitted */
184
+ @container (max-width: 430px) {
185
+ .dataviews-view-table tr td:first-child,
186
+ .dataviews-view-table tr th:first-child {
187
+ padding-left: $grid-unit-30;
188
+ }
189
+
190
+ .dataviews-view-table tr td:last-child,
191
+ .dataviews-view-table tr th:last-child {
192
+ padding-right: $grid-unit-30;
193
+ }
194
+ }
195
+
196
+ .dataviews-view-table-selection-checkbox {
197
+ --checkbox-input-size: 24px;
198
+ @include break-small() {
199
+ --checkbox-input-size: 16px;
200
+ }
201
+ }
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Internal dependencies
3
3
  */
4
- import type { Field, AnyItem, NormalizedField } from './types';
4
+ import type { Field, NormalizedField, ItemRecord } from './types';
5
5
 
6
6
  /**
7
7
  * Apply default values and normalize the fields config.
@@ -9,15 +9,17 @@ import type { Field, AnyItem, NormalizedField } from './types';
9
9
  * @param fields Fields config.
10
10
  * @return Normalized fields config.
11
11
  */
12
- export function normalizeFields< Item extends AnyItem >(
12
+ export function normalizeFields< Item >(
13
13
  fields: Field< Item >[]
14
14
  ): NormalizedField< Item >[] {
15
15
  return fields.map( ( field ) => {
16
- const getValue = field.getValue || ( ( { item } ) => item[ field.id ] );
16
+ const getValue =
17
+ field.getValue ||
18
+ ( ( { item }: { item: ItemRecord } ) => item[ field.id ] );
17
19
 
18
20
  return {
19
21
  ...field,
20
- header: field.header || field.id,
22
+ label: field.label || field.id,
21
23
  getValue,
22
24
  render: field.render || getValue,
23
25
  };
@@ -0,0 +1,2 @@
1
+ export type SelectionOrUpdater = string[] | ( ( prev: string[] ) => string[] );
2
+ export type SetSelection = ( selection: SelectionOrUpdater ) => void;