@wordpress/dataviews 0.2.0 → 0.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 (87) hide show
  1. package/CHANGELOG.md +2 -0
  2. package/LICENSE.md +1 -1
  3. package/README.md +30 -6
  4. package/build/add-filter.js +109 -49
  5. package/build/add-filter.js.map +1 -1
  6. package/build/constants.js +24 -2
  7. package/build/constants.js.map +1 -1
  8. package/build/dataviews.js +12 -9
  9. package/build/dataviews.js.map +1 -1
  10. package/build/dropdown-menu-helper.js +72 -0
  11. package/build/dropdown-menu-helper.js.map +1 -0
  12. package/build/filter-summary.js +43 -54
  13. package/build/filter-summary.js.map +1 -1
  14. package/build/filters.js +27 -17
  15. package/build/filters.js.map +1 -1
  16. package/build/index.js +13 -0
  17. package/build/index.js.map +1 -1
  18. package/build/item-actions.js +12 -12
  19. package/build/item-actions.js.map +1 -1
  20. package/build/pagination.js +31 -65
  21. package/build/pagination.js.map +1 -1
  22. package/build/reset-filters.js +8 -8
  23. package/build/reset-filters.js.map +1 -1
  24. package/build/search.js +8 -6
  25. package/build/search.js.map +1 -1
  26. package/build/utils.js +71 -0
  27. package/build/utils.js.map +1 -0
  28. package/build/view-actions.js +72 -95
  29. package/build/view-actions.js.map +1 -1
  30. package/build/view-grid.js +4 -6
  31. package/build/view-grid.js.map +1 -1
  32. package/build/view-list.js +26 -13
  33. package/build/view-list.js.map +1 -1
  34. package/build/view-table.js +153 -154
  35. package/build/view-table.js.map +1 -1
  36. package/build-module/add-filter.js +113 -53
  37. package/build-module/add-filter.js.map +1 -1
  38. package/build-module/constants.js +20 -0
  39. package/build-module/constants.js.map +1 -1
  40. package/build-module/dataviews.js +13 -10
  41. package/build-module/dataviews.js.map +1 -1
  42. package/build-module/dropdown-menu-helper.js +64 -0
  43. package/build-module/dropdown-menu-helper.js.map +1 -0
  44. package/build-module/filter-summary.js +45 -56
  45. package/build-module/filter-summary.js.map +1 -1
  46. package/build-module/filters.js +26 -17
  47. package/build-module/filters.js.map +1 -1
  48. package/build-module/index.js +1 -0
  49. package/build-module/index.js.map +1 -1
  50. package/build-module/item-actions.js +12 -12
  51. package/build-module/item-actions.js.map +1 -1
  52. package/build-module/pagination.js +35 -69
  53. package/build-module/pagination.js.map +1 -1
  54. package/build-module/reset-filters.js +6 -6
  55. package/build-module/reset-filters.js.map +1 -1
  56. package/build-module/search.js +7 -6
  57. package/build-module/search.js.map +1 -1
  58. package/build-module/utils.js +63 -0
  59. package/build-module/utils.js.map +1 -0
  60. package/build-module/view-actions.js +73 -97
  61. package/build-module/view-actions.js.map +1 -1
  62. package/build-module/view-grid.js +4 -6
  63. package/build-module/view-grid.js.map +1 -1
  64. package/build-module/view-list.js +27 -14
  65. package/build-module/view-list.js.map +1 -1
  66. package/build-module/view-table.js +156 -157
  67. package/build-module/view-table.js.map +1 -1
  68. package/build-style/style-rtl.css +180 -70
  69. package/build-style/style.css +180 -70
  70. package/package.json +11 -10
  71. package/src/add-filter.js +227 -68
  72. package/src/constants.js +16 -0
  73. package/src/dataviews.js +19 -12
  74. package/src/dropdown-menu-helper.js +61 -0
  75. package/src/filter-summary.js +70 -103
  76. package/src/filters.js +41 -24
  77. package/src/index.js +1 -0
  78. package/src/item-actions.js +30 -25
  79. package/src/pagination.js +75 -123
  80. package/src/reset-filters.js +5 -5
  81. package/src/search.js +8 -6
  82. package/src/style.scss +182 -48
  83. package/src/utils.js +51 -0
  84. package/src/view-actions.js +113 -114
  85. package/src/view-grid.js +4 -4
  86. package/src/view-list.js +42 -28
  87. package/src/view-table.js +280 -238
@@ -8,14 +8,17 @@ import classNames from 'classnames';
8
8
  * WordPress dependencies
9
9
  */
10
10
  import { useAsyncList } from '@wordpress/compose';
11
- import { __experimentalHStack as HStack, __experimentalVStack as VStack } from '@wordpress/components';
11
+ import { __experimentalHStack as HStack, __experimentalVStack as VStack, Button } from '@wordpress/components';
12
12
  import { ENTER, SPACE } from '@wordpress/keycodes';
13
+ import { info } from '@wordpress/icons';
14
+ import { __ } from '@wordpress/i18n';
13
15
  export default function ViewList({
14
16
  view,
15
17
  fields,
16
18
  data,
17
19
  getItemId,
18
20
  onSelectionChange,
21
+ onDetailsChange,
19
22
  selection,
20
23
  deferredRendering
21
24
  }) {
@@ -35,41 +38,51 @@ export default function ViewList({
35
38
  }
36
39
  };
37
40
  return createElement("ul", {
38
- className: "dataviews-list-view"
39
- }, usedData.map((item, index) => {
41
+ className: "dataviews-view-list"
42
+ }, usedData.map(item => {
40
43
  return createElement("li", {
41
- key: getItemId?.(item) || index
44
+ key: getItemId(item),
45
+ className: classNames({
46
+ 'is-selected': selection.includes(item.id)
47
+ })
48
+ }, createElement(HStack, {
49
+ className: "dataviews-view-list__item-wrapper"
42
50
  }, createElement("div", {
43
51
  role: "button",
44
52
  tabIndex: 0,
45
53
  "aria-pressed": selection.includes(item.id),
46
54
  onKeyDown: onEnter(item),
47
- className: classNames('dataviews-list-view__item', {
48
- 'dataviews-list-view__item-selected': selection.includes(item.id)
49
- }),
55
+ className: "dataviews-view-list__item",
50
56
  onClick: () => onSelectionChange([item])
51
57
  }, createElement(HStack, {
52
- spacing: 3
58
+ spacing: 3,
59
+ justify: "start"
53
60
  }, createElement("div", {
54
- className: "dataviews-list-view__media-wrapper"
61
+ className: "dataviews-view-list__media-wrapper"
55
62
  }, mediaField?.render({
56
63
  item
57
64
  }) || createElement("div", {
58
- className: "dataviews-list-view__media-placeholder"
59
- })), createElement(HStack, null, createElement(VStack, {
65
+ className: "dataviews-view-list__media-placeholder"
66
+ })), createElement(VStack, {
60
67
  spacing: 1
61
68
  }, primaryField?.render({
62
69
  item
63
70
  }), createElement("div", {
64
- className: "dataviews-list-view__fields"
71
+ className: "dataviews-view-list__fields"
65
72
  }, visibleFields.map(field => {
66
73
  return createElement("span", {
67
74
  key: field.id,
68
- className: "dataviews-list-view__field"
75
+ className: "dataviews-view-list__field"
69
76
  }, field.render({
70
77
  item
71
78
  }));
72
- })))))));
79
+ }))))), onDetailsChange && createElement(Button, {
80
+ className: "dataviews-view-list__details-button",
81
+ onClick: () => onDetailsChange([item]),
82
+ icon: info,
83
+ label: __('View details'),
84
+ size: "compact"
85
+ })));
73
86
  }));
74
87
  }
75
88
  //# sourceMappingURL=view-list.js.map
@@ -1 +1 @@
1
- {"version":3,"names":["classNames","useAsyncList","__experimentalHStack","HStack","__experimentalVStack","VStack","ENTER","SPACE","ViewList","view","fields","data","getItemId","onSelectionChange","selection","deferredRendering","shownData","step","usedData","mediaField","find","field","id","layout","primaryField","visibleFields","filter","hiddenFields","includes","onEnter","item","event","keyCode","createElement","className","map","index","key","role","tabIndex","onKeyDown","onClick","spacing","render"],"sources":["@wordpress/dataviews/src/view-list.js"],"sourcesContent":["/**\n * External dependencies\n */\nimport classNames from 'classnames';\n\n/**\n * WordPress dependencies\n */\nimport { useAsyncList } from '@wordpress/compose';\nimport {\n\t__experimentalHStack as HStack,\n\t__experimentalVStack as VStack,\n} from '@wordpress/components';\nimport { ENTER, SPACE } from '@wordpress/keycodes';\n\nexport default function ViewList( {\n\tview,\n\tfields,\n\tdata,\n\tgetItemId,\n\tonSelectionChange,\n\tselection,\n\tdeferredRendering,\n} ) {\n\tconst shownData = useAsyncList( data, { step: 3 } );\n\tconst usedData = deferredRendering ? shownData : data;\n\tconst mediaField = fields.find(\n\t\t( field ) => field.id === view.layout.mediaField\n\t);\n\tconst primaryField = fields.find(\n\t\t( field ) => field.id === view.layout.primaryField\n\t);\n\tconst visibleFields = fields.filter(\n\t\t( field ) =>\n\t\t\t! view.hiddenFields.includes( field.id ) &&\n\t\t\t! [ view.layout.primaryField, view.layout.mediaField ].includes(\n\t\t\t\tfield.id\n\t\t\t)\n\t);\n\n\tconst onEnter = ( item ) => ( event ) => {\n\t\tconst { keyCode } = event;\n\t\tif ( [ ENTER, SPACE ].includes( keyCode ) ) {\n\t\t\tonSelectionChange( [ item ] );\n\t\t}\n\t};\n\n\treturn (\n\t\t<ul className=\"dataviews-list-view\">\n\t\t\t{ usedData.map( ( item, index ) => {\n\t\t\t\treturn (\n\t\t\t\t\t<li key={ getItemId?.( item ) || index }>\n\t\t\t\t\t\t<div\n\t\t\t\t\t\t\trole=\"button\"\n\t\t\t\t\t\t\ttabIndex={ 0 }\n\t\t\t\t\t\t\taria-pressed={ selection.includes( item.id ) }\n\t\t\t\t\t\t\tonKeyDown={ onEnter( item ) }\n\t\t\t\t\t\t\tclassName={ classNames(\n\t\t\t\t\t\t\t\t'dataviews-list-view__item',\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t'dataviews-list-view__item-selected':\n\t\t\t\t\t\t\t\t\t\tselection.includes( item.id ),\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t) }\n\t\t\t\t\t\t\tonClick={ () => onSelectionChange( [ item ] ) }\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<HStack spacing={ 3 }>\n\t\t\t\t\t\t\t\t<div className=\"dataviews-list-view__media-wrapper\">\n\t\t\t\t\t\t\t\t\t{ mediaField?.render( { item } ) || (\n\t\t\t\t\t\t\t\t\t\t<div className=\"dataviews-list-view__media-placeholder\"></div>\n\t\t\t\t\t\t\t\t\t) }\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t<HStack>\n\t\t\t\t\t\t\t\t\t<VStack spacing={ 1 }>\n\t\t\t\t\t\t\t\t\t\t{ primaryField?.render( { item } ) }\n\t\t\t\t\t\t\t\t\t\t<div className=\"dataviews-list-view__fields\">\n\t\t\t\t\t\t\t\t\t\t\t{ visibleFields.map( ( field ) => {\n\t\t\t\t\t\t\t\t\t\t\t\treturn (\n\t\t\t\t\t\t\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tkey={ field.id }\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tclassName=\"dataviews-list-view__field\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t{ field.render( {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\titem,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t} ) }\n\t\t\t\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t\t\t\t} ) }\n\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t</VStack>\n\t\t\t\t\t\t\t\t</HStack>\n\t\t\t\t\t\t\t</HStack>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</li>\n\t\t\t\t);\n\t\t\t} ) }\n\t\t</ul>\n\t);\n}\n"],"mappings":"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"}
1
+ {"version":3,"names":["classNames","useAsyncList","__experimentalHStack","HStack","__experimentalVStack","VStack","Button","ENTER","SPACE","info","__","ViewList","view","fields","data","getItemId","onSelectionChange","onDetailsChange","selection","deferredRendering","shownData","step","usedData","mediaField","find","field","id","layout","primaryField","visibleFields","filter","hiddenFields","includes","onEnter","item","event","keyCode","createElement","className","map","key","role","tabIndex","onKeyDown","onClick","spacing","justify","render","icon","label","size"],"sources":["@wordpress/dataviews/src/view-list.js"],"sourcesContent":["/**\n * External dependencies\n */\nimport classNames from 'classnames';\n\n/**\n * WordPress dependencies\n */\nimport { useAsyncList } from '@wordpress/compose';\nimport {\n\t__experimentalHStack as HStack,\n\t__experimentalVStack as VStack,\n\tButton,\n} from '@wordpress/components';\nimport { ENTER, SPACE } from '@wordpress/keycodes';\nimport { info } from '@wordpress/icons';\nimport { __ } from '@wordpress/i18n';\n\nexport default function ViewList( {\n\tview,\n\tfields,\n\tdata,\n\tgetItemId,\n\tonSelectionChange,\n\tonDetailsChange,\n\tselection,\n\tdeferredRendering,\n} ) {\n\tconst shownData = useAsyncList( data, { step: 3 } );\n\tconst usedData = deferredRendering ? shownData : data;\n\tconst mediaField = fields.find(\n\t\t( field ) => field.id === view.layout.mediaField\n\t);\n\tconst primaryField = fields.find(\n\t\t( field ) => field.id === view.layout.primaryField\n\t);\n\tconst visibleFields = fields.filter(\n\t\t( field ) =>\n\t\t\t! view.hiddenFields.includes( field.id ) &&\n\t\t\t! [ view.layout.primaryField, view.layout.mediaField ].includes(\n\t\t\t\tfield.id\n\t\t\t)\n\t);\n\n\tconst onEnter = ( item ) => ( event ) => {\n\t\tconst { keyCode } = event;\n\t\tif ( [ ENTER, SPACE ].includes( keyCode ) ) {\n\t\t\tonSelectionChange( [ item ] );\n\t\t}\n\t};\n\n\treturn (\n\t\t<ul className=\"dataviews-view-list\">\n\t\t\t{ usedData.map( ( item ) => {\n\t\t\t\treturn (\n\t\t\t\t\t<li\n\t\t\t\t\t\tkey={ getItemId( item ) }\n\t\t\t\t\t\tclassName={ classNames( {\n\t\t\t\t\t\t\t'is-selected': selection.includes( item.id ),\n\t\t\t\t\t\t} ) }\n\t\t\t\t\t>\n\t\t\t\t\t\t<HStack className=\"dataviews-view-list__item-wrapper\">\n\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\trole=\"button\"\n\t\t\t\t\t\t\t\ttabIndex={ 0 }\n\t\t\t\t\t\t\t\taria-pressed={ selection.includes( item.id ) }\n\t\t\t\t\t\t\t\tonKeyDown={ onEnter( item ) }\n\t\t\t\t\t\t\t\tclassName=\"dataviews-view-list__item\"\n\t\t\t\t\t\t\t\tonClick={ () => onSelectionChange( [ item ] ) }\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t<HStack spacing={ 3 } justify=\"start\">\n\t\t\t\t\t\t\t\t\t<div className=\"dataviews-view-list__media-wrapper\">\n\t\t\t\t\t\t\t\t\t\t{ mediaField?.render( { item } ) || (\n\t\t\t\t\t\t\t\t\t\t\t<div className=\"dataviews-view-list__media-placeholder\"></div>\n\t\t\t\t\t\t\t\t\t\t) }\n\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t<VStack spacing={ 1 }>\n\t\t\t\t\t\t\t\t\t\t{ primaryField?.render( { item } ) }\n\t\t\t\t\t\t\t\t\t\t<div className=\"dataviews-view-list__fields\">\n\t\t\t\t\t\t\t\t\t\t\t{ visibleFields.map( ( field ) => {\n\t\t\t\t\t\t\t\t\t\t\t\treturn (\n\t\t\t\t\t\t\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tkey={ field.id }\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tclassName=\"dataviews-view-list__field\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t{ field.render( {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\titem,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t} ) }\n\t\t\t\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t\t\t\t} ) }\n\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t</VStack>\n\t\t\t\t\t\t\t\t</HStack>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t{ onDetailsChange && (\n\t\t\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\t\t\tclassName=\"dataviews-view-list__details-button\"\n\t\t\t\t\t\t\t\t\tonClick={ () =>\n\t\t\t\t\t\t\t\t\t\tonDetailsChange( [ item ] )\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\ticon={ info }\n\t\t\t\t\t\t\t\t\tlabel={ __( 'View details' ) }\n\t\t\t\t\t\t\t\t\tsize=\"compact\"\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t) }\n\t\t\t\t\t\t</HStack>\n\t\t\t\t\t</li>\n\t\t\t\t);\n\t\t\t} ) }\n\t\t</ul>\n\t);\n}\n"],"mappings":"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"}
@@ -1,111 +1,99 @@
1
1
  import { createElement } from "react";
2
+ /**
3
+ * External dependencies
4
+ */
5
+ import classNames from 'classnames';
6
+
2
7
  /**
3
8
  * WordPress dependencies
4
9
  */
5
10
  import { __ } from '@wordpress/i18n';
6
11
  import { useAsyncList } from '@wordpress/compose';
7
- import { chevronDown, chevronUp, unseen, check, arrowUp, arrowDown, chevronRightSmall, funnel } from '@wordpress/icons';
12
+ import { unseen, funnel } from '@wordpress/icons';
8
13
  import { Button, Icon, privateApis as componentsPrivateApis } from '@wordpress/components';
9
- import { Children, Fragment } from '@wordpress/element';
14
+ import { Children, Fragment, forwardRef, useEffect, useId, useRef, useState } from '@wordpress/element';
10
15
 
11
16
  /**
12
17
  * Internal dependencies
13
18
  */
14
19
  import { unlock } from './lock-unlock';
15
20
  import ItemActions from './item-actions';
16
- import { ENUMERATION_TYPE, OPERATOR_IN, OPERATOR_NOT_IN } from './constants';
21
+ import { ENUMERATION_TYPE, OPERATORS, SORTING_DIRECTIONS } from './constants';
22
+ import { DropdownMenuRadioItemCustom } from './dropdown-menu-helper';
17
23
  const {
18
24
  DropdownMenuV2: DropdownMenu,
19
25
  DropdownMenuGroupV2: DropdownMenuGroup,
20
26
  DropdownMenuItemV2: DropdownMenuItem,
27
+ DropdownMenuRadioItemV2: DropdownMenuRadioItem,
21
28
  DropdownMenuSeparatorV2: DropdownMenuSeparator,
22
- DropdownSubMenuV2: DropdownSubMenu,
23
- DropdownSubMenuTriggerV2: DropdownSubMenuTrigger
29
+ DropdownMenuItemLabelV2: DropdownMenuItemLabel,
30
+ DropdownMenuItemHelpTextV2: DropdownMenuItemHelpText
24
31
  } = unlock(componentsPrivateApis);
25
- const sortingItemsInfo = {
26
- asc: {
27
- icon: arrowUp,
28
- label: __('Sort ascending')
29
- },
30
- desc: {
31
- icon: arrowDown,
32
- label: __('Sort descending')
33
- }
32
+ const sortArrows = {
33
+ asc: '↑',
34
+ desc: '↓'
34
35
  };
35
- const sortIcons = {
36
- asc: chevronUp,
37
- desc: chevronDown
36
+ const sanitizeOperators = field => {
37
+ let operators = field.filterBy?.operators;
38
+ if (!operators || !Array.isArray(operators)) {
39
+ operators = Object.keys(OPERATORS);
40
+ }
41
+ return operators.filter(operator => Object.keys(OPERATORS).includes(operator));
38
42
  };
39
- function HeaderMenu({
43
+ const HeaderMenu = forwardRef(function HeaderMenu({
40
44
  field,
41
45
  view,
42
- onChangeView
43
- }) {
44
- const isSortable = field.enableSorting !== false;
46
+ onChangeView,
47
+ onHide
48
+ }, ref) {
45
49
  const isHidable = field.enableHiding !== false;
46
- if (!isSortable && !isHidable) {
47
- return field.header;
48
- }
50
+ const isSortable = field.enableSorting !== false;
49
51
  const isSorted = view.sort?.field === field.id;
50
- let filter, filterInView;
51
- const otherFilters = [];
52
- if (field.type === ENUMERATION_TYPE) {
53
- let columnOperators = field.filterBy?.operators;
54
- if (!columnOperators || !Array.isArray(columnOperators)) {
55
- columnOperators = [OPERATOR_IN, OPERATOR_NOT_IN];
56
- }
57
- const operators = columnOperators.filter(operator => [OPERATOR_IN, OPERATOR_NOT_IN].includes(operator));
58
- if (operators.length >= 0) {
59
- filter = {
60
- field: field.id,
61
- operators,
62
- elements: field.elements || []
63
- };
64
- filterInView = {
65
- field: filter.field,
66
- operator: filter.operators[0],
67
- value: undefined
68
- };
69
- }
52
+ let filter, filterInView, activeElement, activeOperator, otherFilters;
53
+ const operators = sanitizeOperators(field);
54
+ if (field.type === ENUMERATION_TYPE && operators.length > 0) {
55
+ filter = {
56
+ field: field.id,
57
+ operators,
58
+ elements: field.elements || []
59
+ };
60
+ filterInView = view.filters.find(f => f.field === filter.field);
61
+ otherFilters = view.filters.filter(f => f.field !== filter.field);
62
+ activeElement = filter.elements.find(element => element.value === filterInView?.value);
63
+ activeOperator = filterInView?.operator || filter.operators[0];
70
64
  }
71
65
  const isFilterable = !!filter;
72
- if (isFilterable) {
73
- const columnFilters = view.filters;
74
- columnFilters.forEach(columnFilter => {
75
- if (columnFilter.field === filter.field) {
76
- filterInView = {
77
- ...columnFilter
78
- };
79
- } else {
80
- otherFilters.push(columnFilter);
81
- }
82
- });
66
+ if (!isSortable && !isHidable && !isFilterable) {
67
+ return field.header;
83
68
  }
84
69
  return createElement(DropdownMenu, {
85
70
  align: "start",
86
71
  trigger: createElement(Button, {
87
- icon: isSorted && sortIcons[view.sort.direction],
88
- iconPosition: "right",
89
- text: field.header,
90
- style: {
91
- padding: 0
92
- },
93
- size: "compact"
94
- })
95
- }, createElement(WithSeparators, null, isSortable && createElement(DropdownMenuGroup, null, Object.entries(sortingItemsInfo).map(([direction, info]) => {
96
- const isActive = isSorted && view.sort.direction === direction;
97
- return createElement(DropdownMenuItem, {
98
- key: direction,
99
- role: "menuitemradio",
100
- "aria-checked": isActive,
101
- prefix: createElement(Icon, {
102
- icon: info.icon
103
- }),
104
- suffix: isActive && createElement(Icon, {
105
- icon: check
106
- }),
107
- onSelect: event => {
108
- event.preventDefault();
72
+ size: "compact",
73
+ className: "dataviews-view-table-header-button",
74
+ ref: ref,
75
+ variant: "tertiary"
76
+ }, field.header, isSorted && createElement("span", {
77
+ "aria-hidden": "true"
78
+ }, isSorted && sortArrows[view.sort.direction])),
79
+ style: {
80
+ minWidth: '240px'
81
+ }
82
+ }, createElement(WithSeparators, null, isSortable && createElement(DropdownMenuGroup, null, Object.entries(SORTING_DIRECTIONS).map(([direction, info]) => {
83
+ const isChecked = isSorted && view.sort.direction === direction;
84
+ const value = `${field.id}-${direction}`;
85
+ return createElement(DropdownMenuRadioItem, {
86
+ key: value
87
+ // All sorting radio items share the same name, so that
88
+ // selecting a sorting option automatically deselects the
89
+ // previously selected one, even if it is displayed in
90
+ // another submenu. The field and direction are passed via
91
+ // the `value` prop.
92
+ ,
93
+ name: "view-table-sorting",
94
+ value: value,
95
+ checked: isChecked,
96
+ onChange: () => {
109
97
  onChangeView({
110
98
  ...view,
111
99
  sort: {
@@ -114,96 +102,72 @@ function HeaderMenu({
114
102
  }
115
103
  });
116
104
  }
117
- }, info.label);
105
+ }, createElement(DropdownMenuItemLabel, null, info.label));
118
106
  })), isHidable && createElement(DropdownMenuItem, {
119
- role: "menuitemradio",
120
- "aria-checked": false,
121
107
  prefix: createElement(Icon, {
122
108
  icon: unseen
123
109
  }),
124
- onSelect: event => {
125
- event.preventDefault();
110
+ onClick: () => {
111
+ onHide(field);
126
112
  onChangeView({
127
113
  ...view,
128
114
  hiddenFields: view.hiddenFields.concat(field.id)
129
115
  });
130
116
  }
131
- }, __('Hide')), isFilterable && createElement(DropdownMenuGroup, null, createElement(DropdownSubMenu, {
117
+ }, createElement(DropdownMenuItemLabel, null, __('Hide'))), isFilterable && createElement(DropdownMenuGroup, null, createElement(DropdownMenu, {
132
118
  key: filter.field,
133
- trigger: createElement(DropdownSubMenuTrigger, {
119
+ trigger: createElement(DropdownMenuItem, {
134
120
  prefix: createElement(Icon, {
135
121
  icon: funnel
136
122
  }),
137
- suffix: createElement(Icon, {
138
- icon: chevronRightSmall
139
- })
140
- }, __('Filter by'))
123
+ suffix: activeElement && createElement("span", {
124
+ "aria-hidden": "true"
125
+ }, activeOperator in OPERATORS && `${OPERATORS[activeOperator].label} `, activeElement?.label)
126
+ }, createElement(DropdownMenuItemLabel, null, __('Filter by')))
141
127
  }, createElement(WithSeparators, null, createElement(DropdownMenuGroup, null, filter.elements.map(element => {
142
- let isActive = false;
143
- if (filterInView) {
144
- // Intentionally use loose comparison, so it does type conversion.
145
- // This covers the case where a top-level filter for the same field converts a number into a string.
146
- /* eslint-disable eqeqeq */
147
- isActive = element.value == filterInView.value;
148
- /* eslint-enable eqeqeq */
149
- }
150
-
151
- return createElement(DropdownMenuItem, {
128
+ const isActive = activeElement?.value === element.value;
129
+ return createElement(DropdownMenuRadioItemCustom, {
152
130
  key: element.value,
153
- role: "menuitemradio",
154
- "aria-checked": isActive,
155
- prefix: isActive && createElement(Icon, {
156
- icon: check
157
- }),
158
- onSelect: () => {
131
+ name: `view-table-${filter.field}`,
132
+ value: element.value,
133
+ checked: isActive,
134
+ onClick: () => {
159
135
  onChangeView({
160
136
  ...view,
137
+ page: 1,
161
138
  filters: [...otherFilters, {
162
139
  field: filter.field,
163
- operator: filterInView?.operator,
140
+ operator: activeOperator,
164
141
  value: isActive ? undefined : element.value
165
142
  }]
166
143
  });
167
144
  }
168
- }, element.label);
169
- })), filter.operators.length > 1 && createElement(DropdownSubMenu, {
170
- trigger: createElement(DropdownSubMenuTrigger, {
171
- suffix: createElement(Fragment, null, filterInView.operator === OPERATOR_IN ? __('Is') : __('Is not'), createElement(Icon, {
172
- icon: chevronRightSmall
173
- }), ' ')
174
- }, __('Conditions'))
175
- }, createElement(DropdownMenuItem, {
176
- key: "in-filter",
177
- role: "menuitemradio",
178
- "aria-checked": filterInView?.operator === OPERATOR_IN,
179
- prefix: filterInView?.operator === OPERATOR_IN && createElement(Icon, {
180
- icon: check
181
- }),
182
- onSelect: () => onChangeView({
183
- ...view,
184
- filters: [...otherFilters, {
185
- field: filter.field,
186
- operator: OPERATOR_IN,
187
- value: filterInView?.value
188
- }]
189
- })
190
- }, __('Is')), createElement(DropdownMenuItem, {
191
- key: "not-in-filter",
192
- role: "menuitemradio",
193
- "aria-checked": filterInView?.operator === OPERATOR_NOT_IN,
194
- prefix: filterInView?.operator === OPERATOR_NOT_IN && createElement(Icon, {
195
- icon: check
196
- }),
197
- onSelect: () => onChangeView({
145
+ }, createElement(DropdownMenuItemLabel, null, element.label), !!element.description && createElement(DropdownMenuItemHelpText, null, element.description));
146
+ })), filter.operators.length > 1 && createElement(DropdownMenu, {
147
+ trigger: createElement(DropdownMenuItem, {
148
+ suffix: createElement("span", {
149
+ "aria-hidden": "true"
150
+ }, OPERATORS[activeOperator]?.label)
151
+ }, createElement(DropdownMenuItemLabel, null, __('Conditions')))
152
+ }, Object.entries(OPERATORS).map(([operator, {
153
+ label,
154
+ key
155
+ }]) => createElement(DropdownMenuRadioItem, {
156
+ key: key,
157
+ name: `view-table-${filter.field}-conditions`,
158
+ value: operator,
159
+ checked: activeOperator === operator,
160
+ onChange: e => onChangeView({
198
161
  ...view,
162
+ page: 1,
199
163
  filters: [...otherFilters, {
200
164
  field: filter.field,
201
- operator: OPERATOR_NOT_IN,
165
+ operator: e.target.value,
202
166
  value: filterInView?.value
203
167
  }]
204
168
  })
205
- }, __('Is not'))))))));
206
- }
169
+ }, createElement(DropdownMenuItemLabel, null, label)))))))));
170
+ });
207
171
  function WithSeparators({
208
172
  children
209
173
  }) {
@@ -221,25 +185,43 @@ function ViewTable({
221
185
  isLoading = false,
222
186
  deferredRendering
223
187
  }) {
188
+ const headerMenuRefs = useRef(new Map());
189
+ const headerMenuToFocusRef = useRef();
190
+ const [nextHeaderMenuToFocus, setNextHeaderMenuToFocus] = useState();
191
+ useEffect(() => {
192
+ if (headerMenuToFocusRef.current) {
193
+ headerMenuToFocusRef.current.focus();
194
+ headerMenuToFocusRef.current = undefined;
195
+ }
196
+ });
197
+ const asyncData = useAsyncList(data);
198
+ const tableNoticeId = useId();
199
+ if (nextHeaderMenuToFocus) {
200
+ // If we need to force focus, we short-circuit rendering here
201
+ // to prevent any additional work while we handle that.
202
+ // Clearing out the focus directive is necessary to make sure
203
+ // future renders don't cause unexpected focus jumps.
204
+ headerMenuToFocusRef.current = nextHeaderMenuToFocus;
205
+ setNextHeaderMenuToFocus();
206
+ return;
207
+ }
208
+ const onHide = field => {
209
+ const hidden = headerMenuRefs.current.get(field.id);
210
+ const fallback = headerMenuRefs.current.get(hidden.fallback);
211
+ setNextHeaderMenuToFocus(fallback?.node);
212
+ };
224
213
  const visibleFields = fields.filter(field => !view.hiddenFields.includes(field.id) && ![view.layout.mediaField, view.layout.primaryField].includes(field.id));
225
- const shownData = useAsyncList(data);
226
- const usedData = deferredRendering ? shownData : data;
214
+ const usedData = deferredRendering ? asyncData : data;
227
215
  const hasData = !!usedData?.length;
228
- if (isLoading) {
229
- // TODO:Add spinner or progress bar..
230
- return createElement("div", {
231
- className: "dataviews-loading"
232
- }, createElement("h3", null, __('Loading')));
233
- }
234
216
  const sortValues = {
235
217
  asc: 'ascending',
236
218
  desc: 'descending'
237
219
  };
238
- return createElement("div", {
239
- className: "dataviews-table-view-wrapper"
240
- }, hasData && createElement("table", {
241
- className: "dataviews-table-view"
242
- }, createElement("thead", null, createElement("tr", null, visibleFields.map(field => createElement("th", {
220
+ return createElement("div", null, createElement("table", {
221
+ className: "dataviews-view-table",
222
+ "aria-busy": isLoading,
223
+ "aria-describedby": tableNoticeId
224
+ }, createElement("thead", null, createElement("tr", null, visibleFields.map((field, index) => createElement("th", {
243
225
  key: field.id,
244
226
  style: {
245
227
  width: field.width || undefined,
@@ -250,13 +232,26 @@ function ViewTable({
250
232
  "aria-sort": view.sort?.field === field.id && sortValues[view.sort.direction],
251
233
  scope: "col"
252
234
  }, createElement(HeaderMenu, {
235
+ ref: node => {
236
+ if (node) {
237
+ headerMenuRefs.current.set(field.id, {
238
+ node,
239
+ fallback: visibleFields[index > 0 ? index - 1 : 1]?.id
240
+ });
241
+ } else {
242
+ headerMenuRefs.current.delete(field.id);
243
+ }
244
+ },
253
245
  field: field,
254
246
  view: view,
255
- onChangeView: onChangeView
247
+ onChangeView: onChangeView,
248
+ onHide: onHide
256
249
  }))), !!actions?.length && createElement("th", {
257
250
  "data-field-id": "actions"
258
- }, __('Actions')))), createElement("tbody", null, usedData.map((item, index) => createElement("tr", {
259
- key: getItemId?.(item) || index
251
+ }, createElement("span", {
252
+ className: "dataviews-view-table-header"
253
+ }, __('Actions'))))), createElement("tbody", null, hasData && usedData.map(item => createElement("tr", {
254
+ key: getItemId(item)
260
255
  }, visibleFields.map(field => createElement("td", {
261
256
  key: field.id,
262
257
  style: {
@@ -269,9 +264,13 @@ function ViewTable({
269
264
  }))), !!actions?.length && createElement("td", null, createElement(ItemActions, {
270
265
  item: item,
271
266
  actions: actions
272
- })))))), !hasData && createElement("div", {
273
- className: "dataviews-no-results"
274
- }, createElement("p", null, __('No results'))));
267
+ })))))), createElement("div", {
268
+ className: classNames({
269
+ 'dataviews-loading': isLoading,
270
+ 'dataviews-no-results': !hasData && !isLoading
271
+ }),
272
+ id: tableNoticeId
273
+ }, !hasData && createElement("p", null, isLoading ? __('Loading…') : __('No results'))));
275
274
  }
276
275
  export default ViewTable;
277
276
  //# sourceMappingURL=view-table.js.map