@wordpress/dataviews 4.10.0 → 4.11.1

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 (119) hide show
  1. package/CHANGELOG.md +19 -9
  2. package/LICENSE.md +1 -1
  3. package/README.md +309 -175
  4. package/build/components/dataviews/index.js +12 -1
  5. package/build/components/dataviews/index.js.map +1 -1
  6. package/build/components/dataviews-context/index.js +2 -1
  7. package/build/components/dataviews-context/index.js.map +1 -1
  8. package/build/components/dataviews-filters/add-filter.js +35 -30
  9. package/build/components/dataviews-filters/add-filter.js.map +1 -1
  10. package/build/components/dataviews-filters/index.js +3 -1
  11. package/build/components/dataviews-filters/index.js.map +1 -1
  12. package/build/components/dataviews-item-actions/index.js +24 -19
  13. package/build/components/dataviews-item-actions/index.js.map +1 -1
  14. package/build/components/dataviews-layout/index.js +2 -0
  15. package/build/components/dataviews-layout/index.js.map +1 -1
  16. package/build/components/dataviews-view-config/index.js +48 -43
  17. package/build/components/dataviews-view-config/index.js.map +1 -1
  18. package/build/dataviews-layouts/grid/index.js +12 -5
  19. package/build/dataviews-layouts/grid/index.js.map +1 -1
  20. package/build/dataviews-layouts/grid/preview-size-picker.js +22 -25
  21. package/build/dataviews-layouts/grid/preview-size-picker.js.map +1 -1
  22. package/build/dataviews-layouts/list/index.js +27 -20
  23. package/build/dataviews-layouts/list/index.js.map +1 -1
  24. package/build/dataviews-layouts/table/column-header-menu.js +102 -99
  25. package/build/dataviews-layouts/table/column-header-menu.js.map +1 -1
  26. package/build/dataviews-layouts/table/column-primary.js +7 -3
  27. package/build/dataviews-layouts/table/column-primary.js.map +1 -1
  28. package/build/dataviews-layouts/table/index.js +4 -0
  29. package/build/dataviews-layouts/table/index.js.map +1 -1
  30. package/build/types.js.map +1 -1
  31. package/build-module/components/dataviews/index.js +12 -1
  32. package/build-module/components/dataviews/index.js.map +1 -1
  33. package/build-module/components/dataviews-context/index.js +2 -1
  34. package/build-module/components/dataviews-context/index.js.map +1 -1
  35. package/build-module/components/dataviews-filters/add-filter.js +36 -31
  36. package/build-module/components/dataviews-filters/add-filter.js.map +1 -1
  37. package/build-module/components/dataviews-filters/index.js +3 -1
  38. package/build-module/components/dataviews-filters/index.js.map +1 -1
  39. package/build-module/components/dataviews-item-actions/index.js +24 -19
  40. package/build-module/components/dataviews-item-actions/index.js.map +1 -1
  41. package/build-module/components/dataviews-layout/index.js +2 -0
  42. package/build-module/components/dataviews-layout/index.js.map +1 -1
  43. package/build-module/components/dataviews-view-config/index.js +49 -44
  44. package/build-module/components/dataviews-view-config/index.js.map +1 -1
  45. package/build-module/dataviews-layouts/grid/index.js +14 -7
  46. package/build-module/dataviews-layouts/grid/index.js.map +1 -1
  47. package/build-module/dataviews-layouts/grid/preview-size-picker.js +22 -25
  48. package/build-module/dataviews-layouts/grid/preview-size-picker.js.map +1 -1
  49. package/build-module/dataviews-layouts/list/index.js +27 -20
  50. package/build-module/dataviews-layouts/list/index.js.map +1 -1
  51. package/build-module/dataviews-layouts/table/column-header-menu.js +102 -99
  52. package/build-module/dataviews-layouts/table/column-header-menu.js.map +1 -1
  53. package/build-module/dataviews-layouts/table/column-primary.js +7 -3
  54. package/build-module/dataviews-layouts/table/column-primary.js.map +1 -1
  55. package/build-module/dataviews-layouts/table/index.js +4 -0
  56. package/build-module/dataviews-layouts/table/index.js.map +1 -1
  57. package/build-module/types.js.map +1 -1
  58. package/build-style/style-rtl.css +45 -55
  59. package/build-style/style.css +45 -55
  60. package/build-types/components/dataviews/index.d.ts +2 -1
  61. package/build-types/components/dataviews/index.d.ts.map +1 -1
  62. package/build-types/components/dataviews-context/index.d.ts +2 -0
  63. package/build-types/components/dataviews-context/index.d.ts.map +1 -1
  64. package/build-types/components/dataviews-filters/add-filter.d.ts +3 -2
  65. package/build-types/components/dataviews-filters/add-filter.d.ts.map +1 -1
  66. package/build-types/components/dataviews-item-actions/index.d.ts.map +1 -1
  67. package/build-types/components/dataviews-layout/index.d.ts.map +1 -1
  68. package/build-types/components/dataviews-view-config/index.d.ts.map +1 -1
  69. package/build-types/dataviews-layouts/grid/index.d.ts.map +1 -1
  70. package/build-types/dataviews-layouts/grid/preview-size-picker.d.ts.map +1 -1
  71. package/build-types/dataviews-layouts/list/index.d.ts.map +1 -1
  72. package/build-types/dataviews-layouts/table/column-header-menu.d.ts.map +1 -1
  73. package/build-types/dataviews-layouts/table/column-primary.d.ts +2 -1
  74. package/build-types/dataviews-layouts/table/column-primary.d.ts.map +1 -1
  75. package/build-types/dataviews-layouts/table/index.d.ts +1 -1
  76. package/build-types/dataviews-layouts/table/index.d.ts.map +1 -1
  77. package/build-types/test/dataform.d.ts +2 -0
  78. package/build-types/test/dataform.d.ts.map +1 -0
  79. package/build-types/test/dataviews.d.ts +2 -0
  80. package/build-types/test/dataviews.d.ts.map +1 -0
  81. package/build-types/test/normalize-fields.d.ts +2 -0
  82. package/build-types/test/normalize-fields.d.ts.map +1 -0
  83. package/build-types/test/validation.d.ts +2 -0
  84. package/build-types/test/validation.d.ts.map +1 -0
  85. package/build-types/types.d.ts +5 -0
  86. package/build-types/types.d.ts.map +1 -1
  87. package/build-wp/index.js +1209 -999
  88. package/package.json +13 -12
  89. package/src/components/dataviews/index.tsx +15 -1
  90. package/src/components/dataviews/style.scss +0 -1
  91. package/src/components/dataviews-context/index.ts +3 -0
  92. package/src/components/dataviews-filters/add-filter.tsx +43 -39
  93. package/src/components/dataviews-filters/index.tsx +1 -1
  94. package/src/components/dataviews-footer/style.scss +0 -3
  95. package/src/components/dataviews-item-actions/index.tsx +25 -27
  96. package/src/components/dataviews-layout/index.tsx +2 -0
  97. package/src/components/dataviews-view-config/index.tsx +52 -43
  98. package/src/components/dataviews-view-config/style.scss +0 -1
  99. package/src/dataviews-layouts/grid/index.tsx +17 -5
  100. package/src/dataviews-layouts/grid/preview-size-picker.tsx +25 -30
  101. package/src/dataviews-layouts/grid/style.scss +24 -33
  102. package/src/dataviews-layouts/list/index.tsx +35 -27
  103. package/src/dataviews-layouts/list/style.scss +5 -5
  104. package/src/dataviews-layouts/table/column-header-menu.tsx +152 -148
  105. package/src/dataviews-layouts/table/column-primary.tsx +7 -0
  106. package/src/dataviews-layouts/table/index.tsx +10 -0
  107. package/src/dataviews-layouts/table/style.scss +0 -1
  108. package/src/test/dataform.tsx +348 -0
  109. package/src/test/dataviews.tsx +380 -0
  110. package/src/types.ts +6 -0
  111. package/tsconfig.json +14 -4
  112. package/tsconfig.tsbuildinfo +1 -1
  113. package/build/components/form-field-visibility/index.js +0 -32
  114. package/build/components/form-field-visibility/index.js.map +0 -1
  115. package/build-module/components/form-field-visibility/index.js +0 -26
  116. package/build-module/components/form-field-visibility/index.js.map +0 -1
  117. package/build-types/components/form-field-visibility/index.d.ts +0 -11
  118. package/build-types/components/form-field-visibility/index.d.ts.map +0 -1
  119. package/src/components/form-field-visibility/index.tsx +0 -32
@@ -3,7 +3,6 @@
3
3
  */
4
4
  import { RangeControl } from '@wordpress/components';
5
5
  import { __ } from '@wordpress/i18n';
6
- import { useViewportMatch } from '@wordpress/compose';
7
6
  import { useMemo, useContext } from '@wordpress/element';
8
7
 
9
8
  /**
@@ -12,7 +11,9 @@ import { useMemo, useContext } from '@wordpress/element';
12
11
  import DataViewsContext from '../../components/dataviews-context';
13
12
  import type { ViewGrid } from '../../types';
14
13
 
15
- const viewportBreaks = {
14
+ const viewportBreaks: {
15
+ [ key: string ]: { min: number; max: number; default: number };
16
+ } = {
16
17
  xhuge: { min: 3, max: 6, default: 5 },
17
18
  huge: { min: 2, max: 4, default: 4 },
18
19
  xlarge: { min: 2, max: 3, default: 3 },
@@ -20,38 +21,35 @@ const viewportBreaks = {
20
21
  mobile: { min: 1, max: 2, default: 2 },
21
22
  };
22
23
 
23
- function useViewPortBreakpoint() {
24
- const isXHuge = useViewportMatch( 'xhuge', '>=' );
25
- const isHuge = useViewportMatch( 'huge', '>=' );
26
- const isXlarge = useViewportMatch( 'xlarge', '>=' );
27
- const isLarge = useViewportMatch( 'large', '>=' );
28
- const isMobile = useViewportMatch( 'mobile', '>=' );
24
+ /**
25
+ * Breakpoints were adjusted from media queries breakpoints to account for
26
+ * the sidebar width. This was done to match the existing styles we had.
27
+ */
28
+ const BREAKPOINTS = {
29
+ xhuge: 1520,
30
+ huge: 1140,
31
+ xlarge: 780,
32
+ large: 480,
33
+ mobile: 0,
34
+ };
29
35
 
30
- if ( isXHuge ) {
31
- return 'xhuge';
32
- }
33
- if ( isHuge ) {
34
- return 'huge';
35
- }
36
- if ( isXlarge ) {
37
- return 'xlarge';
38
- }
39
- if ( isLarge ) {
40
- return 'large';
41
- }
42
- if ( isMobile ) {
43
- return 'mobile';
36
+ function useViewPortBreakpoint() {
37
+ const containerWidth = useContext( DataViewsContext ).containerWidth;
38
+ for ( const [ key, value ] of Object.entries( BREAKPOINTS ) ) {
39
+ if ( containerWidth >= value ) {
40
+ return key;
41
+ }
44
42
  }
45
- return null;
43
+ return 'mobile';
46
44
  }
47
45
 
48
46
  export function useUpdatedPreviewSizeOnViewportChange() {
49
- const viewport = useViewPortBreakpoint();
50
47
  const view = useContext( DataViewsContext ).view as ViewGrid;
48
+ const viewport = useViewPortBreakpoint();
51
49
  return useMemo( () => {
52
50
  const previewSize = view.layout?.previewSize;
53
51
  let newPreviewSize;
54
- if ( ! viewport || ! previewSize ) {
52
+ if ( ! previewSize ) {
55
53
  return;
56
54
  }
57
55
  const breakValues = viewportBreaks[ viewport ];
@@ -69,9 +67,8 @@ export default function PreviewSizePicker() {
69
67
  const viewport = useViewPortBreakpoint();
70
68
  const context = useContext( DataViewsContext );
71
69
  const view = context.view as ViewGrid;
72
- const breakValues = viewportBreaks[ viewport || 'mobile' ];
70
+ const breakValues = viewportBreaks[ viewport ];
73
71
  const previewSizeToUse = view.layout?.previewSize || breakValues.default;
74
-
75
72
  const marks = useMemo(
76
73
  () =>
77
74
  Array.from(
@@ -84,11 +81,9 @@ export default function PreviewSizePicker() {
84
81
  ),
85
82
  [ breakValues ]
86
83
  );
87
-
88
- if ( ! viewport ) {
84
+ if ( viewport === 'mobile' ) {
89
85
  return null;
90
86
  }
91
-
92
87
  return (
93
88
  <RangeControl
94
89
  __nextHasNoMarginBottom
@@ -3,6 +3,7 @@
3
3
  grid-template-rows: max-content;
4
4
  padding: 0 $grid-unit-60 $grid-unit-30;
5
5
  transition: padding ease-out 0.1s;
6
+ container-type: inline-size;
6
7
  @include reduce-motion("transition");
7
8
 
8
9
 
@@ -30,11 +31,16 @@
30
31
  .dataviews-view-grid__fields .dataviews-view-grid__field .dataviews-view-grid__field-value {
31
32
  color: $gray-900;
32
33
  }
33
-
34
- .dataviews-view-grid__media::after {
35
- background-color: rgba(var(--wp-admin-theme-color--rgb), 0.08);
36
- box-shadow: inset 0 0 0 $border-width var(--wp-admin-theme-color);
37
- }
34
+ }
35
+ &.is-selected .dataviews-view-grid__media::after,
36
+ .dataviews-view-grid__media:focus::after {
37
+ background-color: rgba(var(--wp-admin-theme-color--rgb), 0.08);
38
+ }
39
+ &.is-selected .dataviews-view-grid__media::after {
40
+ box-shadow: inset 0 0 0 $border-width var(--wp-admin-theme-color);
41
+ }
42
+ .dataviews-view-grid__media:focus::after {
43
+ box-shadow: inset 0 0 0 var(--wp-admin-border-width-focus) var(--wp-admin-theme-color);
38
44
  }
39
45
  }
40
46
 
@@ -106,36 +112,29 @@
106
112
  &:not(:empty) {
107
113
  padding-bottom: $grid-unit-15;
108
114
  }
109
-
110
- .dataviews-view-grid__field-value {
111
- width: fit-content;
112
- background: $gray-100;
113
- padding: 0 $grid-unit-10;
114
- min-height: $grid-unit-30;
115
- border-radius: $radius-small;
116
- display: flex;
117
- align-items: center;
118
- font-size: 12px;
119
- }
120
115
  }
121
116
  }
122
117
 
123
118
  .dataviews-view-grid.dataviews-view-grid {
124
- grid-template-columns: repeat(1, minmax(0, 1fr));
125
-
126
- @include break-mobile() {
119
+ /**
120
+ * Breakpoints were adjusted from media queries breakpoints to account for
121
+ * the sidebar width. This was done to match the existing styles we had.
122
+ */
123
+ @container (max-width: 480px) {
124
+ grid-template-columns: repeat(1, minmax(0, 1fr));
125
+ padding-left: $grid-unit-30;
126
+ padding-right: $grid-unit-30;
127
+ }
128
+ @container (min-width: 480px) {
127
129
  grid-template-columns: repeat(2, minmax(0, 1fr));
128
130
  }
129
-
130
- @include break-xlarge() {
131
+ @container (min-width: 780px) {
131
132
  grid-template-columns: repeat(3, minmax(0, 1fr));
132
133
  }
133
-
134
- @include break-huge() {
134
+ @container (min-width: 1140px) {
135
135
  grid-template-columns: repeat(4, minmax(0, 1fr));
136
136
  }
137
-
138
- @include break-xhuge() {
137
+ @container (min-width: 1520px) {
139
138
  grid-template-columns: repeat(5, minmax(0, 1fr));
140
139
  }
141
140
  }
@@ -158,14 +157,6 @@
158
157
  top: $grid-unit-10;
159
158
  }
160
159
 
161
- /* stylelint-disable-next-line scss/at-rule-no-unknown -- '@container' not globally permitted */
162
- @container (max-width: 430px) {
163
- .dataviews-view-grid {
164
- padding-left: $grid-unit-30;
165
- padding-right: $grid-unit-30;
166
- }
167
- }
168
-
169
160
  .dataviews-view-grid__media--clickable {
170
161
  cursor: pointer;
171
162
  }
@@ -101,6 +101,8 @@ function PrimaryActionGridCell< Item >( {
101
101
  render={
102
102
  <Button
103
103
  label={ label }
104
+ disabled={ !! primaryAction.disabled }
105
+ accessibleWhenDisabled
104
106
  icon={ primaryAction.icon }
105
107
  isDestructive={ primaryAction.isDestructive }
106
108
  size="small"
@@ -124,6 +126,8 @@ function PrimaryActionGridCell< Item >( {
124
126
  render={
125
127
  <Button
126
128
  label={ label }
129
+ disabled={ !! primaryAction.disabled }
130
+ accessibleWhenDisabled
127
131
  icon={ primaryAction.icon }
128
132
  isDestructive={ primaryAction.isDestructive }
129
133
  size="small"
@@ -215,32 +219,36 @@ function ListItem< Item >( {
215
219
  ) }
216
220
  { ! hasOnlyOnePrimaryAction && (
217
221
  <div role="gridcell">
218
- <Menu
219
- trigger={
220
- <Composite.Item
221
- id={ generateDropdownTriggerCompositeId(
222
- idPrefix
223
- ) }
224
- render={
225
- <Button
226
- size="small"
227
- icon={ moreVertical }
228
- label={ __( 'Actions' ) }
229
- accessibleWhenDisabled
230
- disabled={ ! actions.length }
231
- onKeyDown={ onDropdownTriggerKeyDown }
232
- />
233
- }
234
- />
235
- }
236
- placement="bottom-end"
237
- >
238
- <ActionsMenuGroup
239
- actions={ eligibleActions }
240
- item={ item }
241
- registry={ registry }
242
- setActiveModalAction={ setActiveModalAction }
222
+ <Menu placement="bottom-end">
223
+ <Menu.TriggerButton
224
+ render={
225
+ <Composite.Item
226
+ id={ generateDropdownTriggerCompositeId(
227
+ idPrefix
228
+ ) }
229
+ render={
230
+ <Button
231
+ size="small"
232
+ icon={ moreVertical }
233
+ label={ __( 'Actions' ) }
234
+ accessibleWhenDisabled
235
+ disabled={ ! actions.length }
236
+ onKeyDown={
237
+ onDropdownTriggerKeyDown
238
+ }
239
+ />
240
+ }
241
+ />
242
+ }
243
243
  />
244
+ <Menu.Popover>
245
+ <ActionsMenuGroup
246
+ actions={ eligibleActions }
247
+ item={ item }
248
+ registry={ registry }
249
+ setActiveModalAction={ setActiveModalAction }
250
+ />
251
+ </Menu.Popover>
244
252
  </Menu>
245
253
  { !! activeModalAction && (
246
254
  <ActionModal
@@ -257,7 +265,7 @@ function ListItem< Item >( {
257
265
  return (
258
266
  <Composite.Row
259
267
  ref={ itemRef }
260
- render={ <li /> }
268
+ render={ <div /> }
261
269
  role="row"
262
270
  className={ clsx( {
263
271
  'is-selected': isSelected,
@@ -482,7 +490,7 @@ export default function ViewList< Item >( props: ViewListProps< Item > ) {
482
490
  return (
483
491
  <Composite
484
492
  id={ baseId }
485
- render={ <ul /> }
493
+ render={ <div /> }
486
494
  className="dataviews-view-list"
487
495
  role="grid"
488
496
  activeId={ activeCompositeId }
@@ -1,11 +1,11 @@
1
- ul.dataviews-view-list {
1
+ div.dataviews-view-list {
2
2
  list-style-type: none;
3
3
  }
4
4
 
5
5
  .dataviews-view-list {
6
6
  margin: 0 0 auto;
7
7
 
8
- li {
8
+ div[role="row"] {
9
9
  margin: 0;
10
10
  border-top: 1px solid $gray-100;
11
11
 
@@ -45,7 +45,7 @@ ul.dataviews-view-list {
45
45
  &.is-selected.is-selected {
46
46
  border-top: 1px solid rgba(var(--wp-admin-theme-color--rgb), 0.12);
47
47
 
48
- & + li {
48
+ & + div[role="row"] {
49
49
  border-top: 1px solid rgba(var(--wp-admin-theme-color--rgb), 0.12);
50
50
  }
51
51
  }
@@ -69,8 +69,8 @@ ul.dataviews-view-list {
69
69
 
70
70
  }
71
71
 
72
- li.is-selected,
73
- li.is-selected:focus-within {
72
+ div[role="row"].is-selected,
73
+ div[role="row"].is-selected:focus-within {
74
74
  .dataviews-view-list__item-wrapper {
75
75
  background-color: rgba(var(--wp-admin-theme-color--rgb), 0.04);
76
76
  color: $gray-900;
@@ -93,168 +93,172 @@ const _HeaderMenu = forwardRef( function HeaderMenu< Item >(
93
93
  ! field.filterBy?.isPrimary;
94
94
 
95
95
  return (
96
- <Menu
97
- align="start"
98
- trigger={
99
- <Button
100
- size="compact"
101
- className="dataviews-view-table-header-button"
102
- ref={ ref }
103
- variant="tertiary"
104
- >
105
- { header }
106
- { view.sort && isSorted && (
107
- <span aria-hidden="true">
108
- { sortArrows[ view.sort.direction ] }
109
- </span>
110
- ) }
111
- </Button>
112
- }
113
- style={ { minWidth: '240px' } }
114
- >
115
- <WithMenuSeparators>
116
- { isSortable && (
117
- <Menu.Group>
118
- { SORTING_DIRECTIONS.map(
119
- ( direction: SortDirection ) => {
120
- const isChecked =
121
- view.sort &&
122
- isSorted &&
123
- view.sort.direction === direction;
96
+ <Menu>
97
+ <Menu.TriggerButton
98
+ render={
99
+ <Button
100
+ size="compact"
101
+ className="dataviews-view-table-header-button"
102
+ ref={ ref }
103
+ variant="tertiary"
104
+ />
105
+ }
106
+ >
107
+ { header }
108
+ { view.sort && isSorted && (
109
+ <span aria-hidden="true">
110
+ { sortArrows[ view.sort.direction ] }
111
+ </span>
112
+ ) }
113
+ </Menu.TriggerButton>
114
+ <Menu.Popover style={ { minWidth: '240px' } }>
115
+ <WithMenuSeparators>
116
+ { isSortable && (
117
+ <Menu.Group>
118
+ { SORTING_DIRECTIONS.map(
119
+ ( direction: SortDirection ) => {
120
+ const isChecked =
121
+ view.sort &&
122
+ isSorted &&
123
+ view.sort.direction === direction;
124
124
 
125
- const value = `${ fieldId }-${ direction }`;
125
+ const value = `${ fieldId }-${ direction }`;
126
126
 
127
- return (
128
- <Menu.RadioItem
129
- key={ value }
130
- // All sorting radio items share the same name, so that
131
- // selecting a sorting option automatically deselects the
132
- // previously selected one, even if it is displayed in
133
- // another submenu. The field and direction are passed via
134
- // the `value` prop.
135
- name="view-table-sorting"
136
- value={ value }
137
- checked={ isChecked }
138
- onChange={ () => {
139
- onChangeView( {
140
- ...view,
141
- sort: {
142
- field: fieldId,
143
- direction,
144
- },
145
- } );
146
- } }
147
- >
148
- <Menu.ItemLabel>
149
- { sortLabels[ direction ] }
150
- </Menu.ItemLabel>
151
- </Menu.RadioItem>
152
- );
153
- }
154
- ) }
155
- </Menu.Group>
156
- ) }
157
- { canAddFilter && (
158
- <Menu.Group>
159
- <Menu.Item
160
- prefix={ <Icon icon={ funnel } /> }
161
- onClick={ () => {
162
- setOpenedFilter( fieldId );
163
- onChangeView( {
164
- ...view,
165
- page: 1,
166
- filters: [
167
- ...( view.filters || [] ),
168
- {
169
- field: fieldId,
170
- value: undefined,
171
- operator: operators[ 0 ],
172
- },
173
- ],
174
- } );
175
- } }
176
- >
177
- <Menu.ItemLabel>
178
- { __( 'Add filter' ) }
179
- </Menu.ItemLabel>
180
- </Menu.Item>
181
- </Menu.Group>
182
- ) }
183
- { ( canMove || isHidable ) && field && (
184
- <Menu.Group>
185
- { canMove && (
127
+ return (
128
+ <Menu.RadioItem
129
+ key={ value }
130
+ // All sorting radio items share the same name, so that
131
+ // selecting a sorting option automatically deselects the
132
+ // previously selected one, even if it is displayed in
133
+ // another submenu. The field and direction are passed via
134
+ // the `value` prop.
135
+ name="view-table-sorting"
136
+ value={ value }
137
+ checked={ isChecked }
138
+ onChange={ () => {
139
+ onChangeView( {
140
+ ...view,
141
+ sort: {
142
+ field: fieldId,
143
+ direction,
144
+ },
145
+ showLevels: false,
146
+ } );
147
+ } }
148
+ >
149
+ <Menu.ItemLabel>
150
+ { sortLabels[ direction ] }
151
+ </Menu.ItemLabel>
152
+ </Menu.RadioItem>
153
+ );
154
+ }
155
+ ) }
156
+ </Menu.Group>
157
+ ) }
158
+ { canAddFilter && (
159
+ <Menu.Group>
186
160
  <Menu.Item
187
- prefix={ <Icon icon={ arrowLeft } /> }
188
- disabled={ index < 1 }
161
+ prefix={ <Icon icon={ funnel } /> }
189
162
  onClick={ () => {
163
+ setOpenedFilter( fieldId );
190
164
  onChangeView( {
191
165
  ...view,
192
- fields: [
193
- ...( visibleFieldIds.slice(
194
- 0,
195
- index - 1
196
- ) ?? [] ),
197
- fieldId,
198
- visibleFieldIds[ index - 1 ],
199
- ...visibleFieldIds.slice(
200
- index + 1
201
- ),
166
+ page: 1,
167
+ filters: [
168
+ ...( view.filters || [] ),
169
+ {
170
+ field: fieldId,
171
+ value: undefined,
172
+ operator: operators[ 0 ],
173
+ },
202
174
  ],
203
175
  } );
204
176
  } }
205
177
  >
206
178
  <Menu.ItemLabel>
207
- { __( 'Move left' ) }
179
+ { __( 'Add filter' ) }
208
180
  </Menu.ItemLabel>
209
181
  </Menu.Item>
210
- ) }
211
- { canMove && (
212
- <Menu.Item
213
- prefix={ <Icon icon={ arrowRight } /> }
214
- disabled={ index >= visibleFieldIds.length - 1 }
215
- onClick={ () => {
216
- onChangeView( {
217
- ...view,
218
- fields: [
219
- ...( visibleFieldIds.slice(
220
- 0,
221
- index
222
- ) ?? [] ),
223
- visibleFieldIds[ index + 1 ],
224
- fieldId,
225
- ...visibleFieldIds.slice(
226
- index + 2
182
+ </Menu.Group>
183
+ ) }
184
+ { ( canMove || isHidable ) && field && (
185
+ <Menu.Group>
186
+ { canMove && (
187
+ <Menu.Item
188
+ prefix={ <Icon icon={ arrowLeft } /> }
189
+ disabled={ index < 1 }
190
+ onClick={ () => {
191
+ onChangeView( {
192
+ ...view,
193
+ fields: [
194
+ ...( visibleFieldIds.slice(
195
+ 0,
196
+ index - 1
197
+ ) ?? [] ),
198
+ fieldId,
199
+ visibleFieldIds[ index - 1 ],
200
+ ...visibleFieldIds.slice(
201
+ index + 1
202
+ ),
203
+ ],
204
+ } );
205
+ } }
206
+ >
207
+ <Menu.ItemLabel>
208
+ { __( 'Move left' ) }
209
+ </Menu.ItemLabel>
210
+ </Menu.Item>
211
+ ) }
212
+ { canMove && (
213
+ <Menu.Item
214
+ prefix={ <Icon icon={ arrowRight } /> }
215
+ disabled={
216
+ index >= visibleFieldIds.length - 1
217
+ }
218
+ onClick={ () => {
219
+ onChangeView( {
220
+ ...view,
221
+ fields: [
222
+ ...( visibleFieldIds.slice(
223
+ 0,
224
+ index
225
+ ) ?? [] ),
226
+ visibleFieldIds[ index + 1 ],
227
+ fieldId,
228
+ ...visibleFieldIds.slice(
229
+ index + 2
230
+ ),
231
+ ],
232
+ } );
233
+ } }
234
+ >
235
+ <Menu.ItemLabel>
236
+ { __( 'Move right' ) }
237
+ </Menu.ItemLabel>
238
+ </Menu.Item>
239
+ ) }
240
+ { isHidable && field && (
241
+ <Menu.Item
242
+ prefix={ <Icon icon={ unseen } /> }
243
+ onClick={ () => {
244
+ onHide( field );
245
+ onChangeView( {
246
+ ...view,
247
+ fields: visibleFieldIds.filter(
248
+ ( id ) => id !== fieldId
227
249
  ),
228
- ],
229
- } );
230
- } }
231
- >
232
- <Menu.ItemLabel>
233
- { __( 'Move right' ) }
234
- </Menu.ItemLabel>
235
- </Menu.Item>
236
- ) }
237
- { isHidable && field && (
238
- <Menu.Item
239
- prefix={ <Icon icon={ unseen } /> }
240
- onClick={ () => {
241
- onHide( field );
242
- onChangeView( {
243
- ...view,
244
- fields: visibleFieldIds.filter(
245
- ( id ) => id !== fieldId
246
- ),
247
- } );
248
- } }
249
- >
250
- <Menu.ItemLabel>
251
- { __( 'Hide column' ) }
252
- </Menu.ItemLabel>
253
- </Menu.Item>
254
- ) }
255
- </Menu.Group>
256
- ) }
257
- </WithMenuSeparators>
250
+ } );
251
+ } }
252
+ >
253
+ <Menu.ItemLabel>
254
+ { __( 'Hide column' ) }
255
+ </Menu.ItemLabel>
256
+ </Menu.Item>
257
+ ) }
258
+ </Menu.Group>
259
+ ) }
260
+ </WithMenuSeparators>
261
+ </Menu.Popover>
258
262
  </Menu>
259
263
  );
260
264
  } );
@@ -14,6 +14,7 @@ import getClickableItemProps from '../utils/get-clickable-item-props';
14
14
 
15
15
  function ColumnPrimary< Item >( {
16
16
  item,
17
+ level,
17
18
  titleField,
18
19
  mediaField,
19
20
  descriptionField,
@@ -21,6 +22,7 @@ function ColumnPrimary< Item >( {
21
22
  isItemClickable,
22
23
  }: {
23
24
  item: Item;
25
+ level?: number;
24
26
  titleField?: NormalizedField< Item >;
25
27
  mediaField?: NormalizedField< Item >;
26
28
  descriptionField?: NormalizedField< Item >;
@@ -44,6 +46,11 @@ function ColumnPrimary< Item >( {
44
46
  <VStack spacing={ 0 }>
45
47
  { titleField && (
46
48
  <div { ...clickableProps }>
49
+ { level !== undefined && (
50
+ <span className="dataviews-view-table__level">
51
+ { '—'.repeat( level ) }&nbsp;
52
+ </span>
53
+ ) }
47
54
  <titleField.render item={ item } />
48
55
  </div>
49
56
  ) }