@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
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wordpress/dataviews",
3
- "version": "4.10.0",
3
+ "version": "4.11.1",
4
4
  "description": "DataViews is a component that provides an API to render datasets using different types of layouts (table, grid, list, etc.).",
5
5
  "author": "The WordPress Contributors",
6
6
  "license": "GPL-2.0-or-later",
@@ -27,7 +27,8 @@
27
27
  "exports": {
28
28
  ".": {
29
29
  "types": "./build-types/index.d.ts",
30
- "import": "./build-module/index.js"
30
+ "import": "./build-module/index.js",
31
+ "default": "./build/index.js"
31
32
  },
32
33
  "./wp": {
33
34
  "types": "./build-types/index.d.ts",
@@ -45,15 +46,15 @@
45
46
  "dependencies": {
46
47
  "@ariakit/react": "^0.4.15",
47
48
  "@babel/runtime": "7.25.7",
48
- "@wordpress/components": "*",
49
- "@wordpress/compose": "*",
50
- "@wordpress/data": "*",
51
- "@wordpress/element": "*",
52
- "@wordpress/i18n": "*",
53
- "@wordpress/icons": "*",
54
- "@wordpress/primitives": "*",
55
- "@wordpress/private-apis": "*",
56
- "@wordpress/warning": "*",
49
+ "@wordpress/components": "^29.1.1",
50
+ "@wordpress/compose": "^7.15.1",
51
+ "@wordpress/data": "^10.15.1",
52
+ "@wordpress/element": "^6.15.1",
53
+ "@wordpress/i18n": "^5.15.1",
54
+ "@wordpress/icons": "^10.15.1",
55
+ "@wordpress/primitives": "^4.15.1",
56
+ "@wordpress/private-apis": "^1.15.0",
57
+ "@wordpress/warning": "^3.15.0",
57
58
  "clsx": "^2.1.1",
58
59
  "remove-accents": "^0.5.0"
59
60
  },
@@ -66,5 +67,5 @@
66
67
  "scripts": {
67
68
  "build:wp": "node build"
68
69
  },
69
- "gitHead": "b432c18934c9db866b6dba7d37517a4e97d642e3"
70
+ "gitHead": "0d4503ecc364cf4ca16024673baf5a84dacf212f"
70
71
  }
@@ -8,6 +8,7 @@ import type { ReactNode } from 'react';
8
8
  */
9
9
  import { __experimentalHStack as HStack } from '@wordpress/components';
10
10
  import { useMemo, useState } from '@wordpress/element';
11
+ import { useResizeObserver } from '@wordpress/compose';
11
12
 
12
13
  /**
13
14
  * Internal dependencies
@@ -47,6 +48,7 @@ type DataViewsProps< Item > = {
47
48
  onClickItem?: ( item: Item ) => void;
48
49
  isItemClickable?: ( item: Item ) => boolean;
49
50
  header?: ReactNode;
51
+ getItemLevel?: ( item: Item ) => number;
50
52
  } & ( Item extends ItemWithId
51
53
  ? { getItemId?: ( item: Item ) => string }
52
54
  : { getItemId: ( item: Item ) => string } );
@@ -64,6 +66,7 @@ export default function DataViews< Item >( {
64
66
  actions = EMPTY_ARRAY,
65
67
  data,
66
68
  getItemId = defaultGetItemId,
69
+ getItemLevel,
67
70
  isLoading = false,
68
71
  paginationInfo,
69
72
  defaultLayouts,
@@ -73,6 +76,15 @@ export default function DataViews< Item >( {
73
76
  isItemClickable = defaultIsItemClickable,
74
77
  header,
75
78
  }: DataViewsProps< Item > ) {
79
+ const [ containerWidth, setContainerWidth ] = useState( 0 );
80
+ const containerRef = useResizeObserver(
81
+ ( resizeObserverEntries: any ) => {
82
+ setContainerWidth(
83
+ resizeObserverEntries[ 0 ].borderBoxSize[ 0 ].inlineSize
84
+ );
85
+ },
86
+ { box: 'border-box' }
87
+ );
76
88
  const [ selectionState, setSelectionState ] = useState< string[] >( [] );
77
89
  const isUncontrolled =
78
90
  selectionProperty === undefined || onChangeSelection === undefined;
@@ -115,11 +127,13 @@ export default function DataViews< Item >( {
115
127
  openedFilter,
116
128
  setOpenedFilter,
117
129
  getItemId,
130
+ getItemLevel,
118
131
  isItemClickable,
119
132
  onClickItem,
133
+ containerWidth,
120
134
  } }
121
135
  >
122
- <div className="dataviews-wrapper">
136
+ <div className="dataviews-wrapper" ref={ containerRef }>
123
137
  <HStack
124
138
  alignment="top"
125
139
  justify="space-between"
@@ -33,7 +33,6 @@
33
33
  @include reduce-motion( "transition" );
34
34
  }
35
35
 
36
- /* stylelint-disable-next-line scss/at-rule-no-unknown -- '@container' not globally permitted */
37
36
  @container (max-width: 430px) {
38
37
  .dataviews__view-actions,
39
38
  .dataviews-filters__container {
@@ -26,8 +26,10 @@ type DataViewsContextType< Item > = {
26
26
  openedFilter: string | null;
27
27
  setOpenedFilter: ( openedFilter: string | null ) => void;
28
28
  getItemId: ( item: Item ) => string;
29
+ getItemLevel?: ( item: Item ) => number;
29
30
  onClickItem?: ( item: Item ) => void;
30
31
  isItemClickable: ( item: Item ) => boolean;
32
+ containerWidth: number;
31
33
  };
32
34
 
33
35
  const DataViewsContext = createContext< DataViewsContextType< any > >( {
@@ -45,6 +47,7 @@ const DataViewsContext = createContext< DataViewsContextType< any > >( {
45
47
  openedFilter: null,
46
48
  getItemId: ( item ) => item.id,
47
49
  isItemClickable: () => true,
50
+ containerWidth: 0,
48
51
  } );
49
52
 
50
53
  export default DataViewsContext;
@@ -33,37 +33,40 @@ export function AddFilterMenu( {
33
33
  view,
34
34
  onChangeView,
35
35
  setOpenedFilter,
36
- trigger,
36
+ triggerProps,
37
37
  }: AddFilterProps & {
38
- trigger: React.ReactNode;
38
+ triggerProps: React.ComponentProps< typeof Menu.TriggerButton >;
39
39
  } ) {
40
40
  const inactiveFilters = filters.filter( ( filter ) => ! filter.isVisible );
41
41
  return (
42
- <Menu trigger={ trigger }>
43
- { inactiveFilters.map( ( filter ) => {
44
- return (
45
- <Menu.Item
46
- key={ filter.field }
47
- onClick={ () => {
48
- setOpenedFilter( filter.field );
49
- onChangeView( {
50
- ...view,
51
- page: 1,
52
- filters: [
53
- ...( view.filters || [] ),
54
- {
55
- field: filter.field,
56
- value: undefined,
57
- operator: filter.operators[ 0 ],
58
- },
59
- ],
60
- } );
61
- } }
62
- >
63
- <Menu.ItemLabel>{ filter.name }</Menu.ItemLabel>
64
- </Menu.Item>
65
- );
66
- } ) }
42
+ <Menu>
43
+ <Menu.TriggerButton { ...triggerProps } />
44
+ <Menu.Popover>
45
+ { inactiveFilters.map( ( filter ) => {
46
+ return (
47
+ <Menu.Item
48
+ key={ filter.field }
49
+ onClick={ () => {
50
+ setOpenedFilter( filter.field );
51
+ onChangeView( {
52
+ ...view,
53
+ page: 1,
54
+ filters: [
55
+ ...( view.filters || [] ),
56
+ {
57
+ field: filter.field,
58
+ value: undefined,
59
+ operator: filter.operators[ 0 ],
60
+ },
61
+ ],
62
+ } );
63
+ } }
64
+ >
65
+ <Menu.ItemLabel>{ filter.name }</Menu.ItemLabel>
66
+ </Menu.Item>
67
+ );
68
+ } ) }
69
+ </Menu.Popover>
67
70
  </Menu>
68
71
  );
69
72
  }
@@ -78,18 +81,19 @@ function AddFilter(
78
81
  const inactiveFilters = filters.filter( ( filter ) => ! filter.isVisible );
79
82
  return (
80
83
  <AddFilterMenu
81
- trigger={
82
- <Button
83
- accessibleWhenDisabled
84
- size="compact"
85
- className="dataviews-filters-button"
86
- variant="tertiary"
87
- disabled={ ! inactiveFilters.length }
88
- ref={ ref }
89
- >
90
- { __( 'Add filter' ) }
91
- </Button>
92
- }
84
+ triggerProps={ {
85
+ render: (
86
+ <Button
87
+ accessibleWhenDisabled
88
+ size="compact"
89
+ className="dataviews-filters-button"
90
+ variant="tertiary"
91
+ disabled={ ! inactiveFilters.length }
92
+ ref={ ref }
93
+ />
94
+ ),
95
+ children: __( 'Add filter' ),
96
+ } }
93
97
  { ...{ filters, view, onChangeView, setOpenedFilter } }
94
98
  />
95
99
  );
@@ -136,7 +136,7 @@ export function FiltersToggle( {
136
136
  view={ view }
137
137
  onChangeView={ onChangeViewWithFilterVisibility }
138
138
  setOpenedFilter={ setOpenedFilter }
139
- trigger={ buttonComponent }
139
+ triggerProps={ { render: buttonComponent } }
140
140
  />
141
141
  ) : (
142
142
  <FilterVisibilityToggle
@@ -11,15 +11,12 @@
11
11
  z-index: z-index(".dataviews-footer");
12
12
  }
13
13
 
14
-
15
- /* stylelint-disable-next-line scss/at-rule-no-unknown -- '@container' not globally permitted */
16
14
  @container (max-width: 430px) {
17
15
  .dataviews-footer {
18
16
  padding: $grid-unit-15 $grid-unit-30;
19
17
  }
20
18
  }
21
19
 
22
- /* stylelint-disable-next-line scss/at-rule-no-unknown -- '@container' not globally permitted */
23
20
  @container (max-width: 560px) {
24
21
  .dataviews-footer {
25
22
  flex-direction: column !important;
@@ -75,6 +75,8 @@ function ButtonTrigger< Item >( {
75
75
  <Button
76
76
  label={ label }
77
77
  icon={ action.icon }
78
+ disabled={ !! action.disabled }
79
+ accessibleWhenDisabled
78
80
  isDestructive={ action.isDestructive }
79
81
  size="compact"
80
82
  onClick={ onClick }
@@ -90,7 +92,7 @@ function MenuItemTrigger< Item >( {
90
92
  const label =
91
93
  typeof action.label === 'string' ? action.label : action.label( items );
92
94
  return (
93
- <Menu.Item onClick={ onClick }>
95
+ <Menu.Item disabled={ action.disabled } onClick={ onClick }>
94
96
  <Menu.ItemLabel>{ label }</Menu.ItemLabel>
95
97
  </Menu.Item>
96
98
  );
@@ -145,13 +147,6 @@ export function ActionsMenuGroup< Item >( {
145
147
  );
146
148
  }
147
149
 
148
- function hasOnlyOneActionAndIsPrimary< Item >(
149
- primaryActions: Action< Item >[],
150
- actions: Action< Item >[]
151
- ) {
152
- return primaryActions.length === 1 && actions.length === 1;
153
- }
154
-
155
150
  export default function ItemActions< Item >( {
156
151
  item,
157
152
  actions,
@@ -184,7 +179,8 @@ export default function ItemActions< Item >( {
184
179
  );
185
180
  }
186
181
 
187
- if ( hasOnlyOneActionAndIsPrimary( primaryActions, actions ) ) {
182
+ // If all actions are primary, there is no need to render the dropdown.
183
+ if ( primaryActions.length === eligibleActions.length ) {
188
184
  return (
189
185
  <PrimaryActions
190
186
  item={ item }
@@ -229,25 +225,27 @@ function CompactItemActions< Item >( {
229
225
  );
230
226
  return (
231
227
  <>
232
- <Menu
233
- trigger={
234
- <Button
235
- size={ isSmall ? 'small' : 'compact' }
236
- icon={ moreVertical }
237
- label={ __( 'Actions' ) }
238
- accessibleWhenDisabled
239
- disabled={ ! actions.length }
240
- className="dataviews-all-actions-button"
241
- />
242
- }
243
- placement="bottom-end"
244
- >
245
- <ActionsMenuGroup
246
- actions={ actions }
247
- item={ item }
248
- registry={ registry }
249
- setActiveModalAction={ setActiveModalAction }
228
+ <Menu placement="bottom-end">
229
+ <Menu.TriggerButton
230
+ render={
231
+ <Button
232
+ size={ isSmall ? 'small' : 'compact' }
233
+ icon={ moreVertical }
234
+ label={ __( 'Actions' ) }
235
+ accessibleWhenDisabled
236
+ disabled={ ! actions.length }
237
+ className="dataviews-all-actions-button"
238
+ />
239
+ }
250
240
  />
241
+ <Menu.Popover>
242
+ <ActionsMenuGroup
243
+ actions={ actions }
244
+ item={ item }
245
+ registry={ registry }
246
+ setActiveModalAction={ setActiveModalAction }
247
+ />
248
+ </Menu.Popover>
251
249
  </Menu>
252
250
  { !! activeModalAction && (
253
251
  <ActionModal
@@ -21,6 +21,7 @@ export default function DataViewsLayout() {
21
21
  data,
22
22
  fields,
23
23
  getItemId,
24
+ getItemLevel,
24
25
  isLoading,
25
26
  view,
26
27
  onChangeView,
@@ -40,6 +41,7 @@ export default function DataViewsLayout() {
40
41
  data={ data }
41
42
  fields={ fields }
42
43
  getItemId={ getItemId }
44
+ getItemLevel={ getItemLevel }
43
45
  isLoading={ isLoading }
44
46
  onChangeView={ onChangeView }
45
47
  onChangeSelection={ onChangeSelection }
@@ -69,50 +69,57 @@ function ViewTypeMenu( {
69
69
  }
70
70
  const activeView = VIEW_LAYOUTS.find( ( v ) => view.type === v.type );
71
71
  return (
72
- <Menu
73
- trigger={
74
- <Button
75
- size="compact"
76
- icon={ activeView?.icon }
77
- label={ __( 'Layout' ) }
78
- />
79
- }
80
- >
81
- { availableLayouts.map( ( layout ) => {
82
- const config = VIEW_LAYOUTS.find( ( v ) => v.type === layout );
83
- if ( ! config ) {
84
- return null;
72
+ <Menu>
73
+ <Menu.TriggerButton
74
+ render={
75
+ <Button
76
+ size="compact"
77
+ icon={ activeView?.icon }
78
+ label={ __( 'Layout' ) }
79
+ />
85
80
  }
86
- return (
87
- <Menu.RadioItem
88
- key={ layout }
89
- value={ layout }
90
- name="view-actions-available-view"
91
- checked={ layout === view.type }
92
- hideOnClick
93
- onChange={ ( e: ChangeEvent< HTMLInputElement > ) => {
94
- switch ( e.target.value ) {
95
- case 'list':
96
- case 'grid':
97
- case 'table':
98
- const viewWithoutLayout = { ...view };
99
- if ( 'layout' in viewWithoutLayout ) {
100
- delete viewWithoutLayout.layout;
101
- }
102
- // @ts-expect-error
103
- return onChangeView( {
104
- ...viewWithoutLayout,
105
- type: e.target.value,
106
- ...defaultLayouts[ e.target.value ],
107
- } );
108
- }
109
- warning( 'Invalid dataview' );
110
- } }
111
- >
112
- <Menu.ItemLabel>{ config.label }</Menu.ItemLabel>
113
- </Menu.RadioItem>
114
- );
115
- } ) }
81
+ />
82
+ <Menu.Popover>
83
+ { availableLayouts.map( ( layout ) => {
84
+ const config = VIEW_LAYOUTS.find(
85
+ ( v ) => v.type === layout
86
+ );
87
+ if ( ! config ) {
88
+ return null;
89
+ }
90
+ return (
91
+ <Menu.RadioItem
92
+ key={ layout }
93
+ value={ layout }
94
+ name="view-actions-available-view"
95
+ checked={ layout === view.type }
96
+ hideOnClick
97
+ onChange={ (
98
+ e: ChangeEvent< HTMLInputElement >
99
+ ) => {
100
+ switch ( e.target.value ) {
101
+ case 'list':
102
+ case 'grid':
103
+ case 'table':
104
+ const viewWithoutLayout = { ...view };
105
+ if ( 'layout' in viewWithoutLayout ) {
106
+ delete viewWithoutLayout.layout;
107
+ }
108
+ // @ts-expect-error
109
+ return onChangeView( {
110
+ ...viewWithoutLayout,
111
+ type: e.target.value,
112
+ ...defaultLayouts[ e.target.value ],
113
+ } );
114
+ }
115
+ warning( 'Invalid dataview' );
116
+ } }
117
+ >
118
+ <Menu.ItemLabel>{ config.label }</Menu.ItemLabel>
119
+ </Menu.RadioItem>
120
+ );
121
+ } ) }
122
+ </Menu.Popover>
116
123
  </Menu>
117
124
  );
118
125
  }
@@ -145,6 +152,7 @@ function SortFieldControl() {
145
152
  direction: view?.sort?.direction || 'desc',
146
153
  field: value,
147
154
  },
155
+ showLevels: false,
148
156
  } );
149
157
  } }
150
158
  />
@@ -187,6 +195,7 @@ function SortDirectionControl() {
187
195
  )?.id ||
188
196
  '',
189
197
  },
198
+ showLevels: false,
190
199
  } );
191
200
  return;
192
201
  }
@@ -43,7 +43,6 @@
43
43
  display: none;
44
44
  }
45
45
 
46
- /* stylelint-disable-next-line scss/at-rule-no-unknown -- '@container' not globally permitted */
47
46
  @container (max-width: 500px) {
48
47
  .dataviews-settings-section.dataviews-settings-section {
49
48
  grid-template-columns: repeat(2, 1fr);
@@ -13,6 +13,7 @@ import {
13
13
  Spinner,
14
14
  Flex,
15
15
  FlexItem,
16
+ privateApis as componentsPrivateApis,
16
17
  } from '@wordpress/components';
17
18
  import { __ } from '@wordpress/i18n';
18
19
  import { useInstanceId } from '@wordpress/compose';
@@ -20,9 +21,13 @@ import { useInstanceId } from '@wordpress/compose';
20
21
  /**
21
22
  * Internal dependencies
22
23
  */
24
+ import { unlock } from '../../lock-unlock';
23
25
  import ItemActions from '../../components/dataviews-item-actions';
24
26
  import DataViewsSelectionCheckbox from '../../components/dataviews-selection-checkbox';
25
- import { useHasAPossibleBulkAction } from '../../components/dataviews-bulk-actions';
27
+ import {
28
+ useHasAPossibleBulkAction,
29
+ useSomeItemHasAPossibleBulkAction,
30
+ } from '../../components/dataviews-bulk-actions';
26
31
  import type {
27
32
  Action,
28
33
  NormalizedField,
@@ -32,6 +37,7 @@ import type {
32
37
  import type { SetSelection } from '../../private-types';
33
38
  import getClickableItemProps from '../utils/get-clickable-item-props';
34
39
  import { useUpdatedPreviewSizeOnViewportChange } from './preview-size-picker';
40
+ const { Badge } = unlock( componentsPrivateApis );
35
41
 
36
42
  interface GridItemProps< Item > {
37
43
  view: ViewGridType;
@@ -47,6 +53,7 @@ interface GridItemProps< Item > {
47
53
  descriptionField?: NormalizedField< Item >;
48
54
  regularFields: NormalizedField< Item >[];
49
55
  badgeFields: NormalizedField< Item >[];
56
+ hasBulkActions: boolean;
50
57
  }
51
58
 
52
59
  function GridItem< Item >( {
@@ -63,6 +70,7 @@ function GridItem< Item >( {
63
70
  descriptionField,
64
71
  regularFields,
65
72
  badgeFields,
73
+ hasBulkActions,
66
74
  }: GridItemProps< Item > ) {
67
75
  const { showTitle = true, showMedia = true, showDescription = true } = view;
68
76
  const hasBulkAction = useHasAPossibleBulkAction( actions, item );
@@ -135,7 +143,7 @@ function GridItem< Item >( {
135
143
  { renderedMediaField }
136
144
  </div>
137
145
  ) }
138
- { showMedia && renderedMediaField && (
146
+ { hasBulkActions && showMedia && renderedMediaField && (
139
147
  <DataViewsSelectionCheckbox
140
148
  item={ item }
141
149
  selection={ selection }
@@ -152,7 +160,9 @@ function GridItem< Item >( {
152
160
  <div { ...clickableTitleItemProps } { ...titleA11yProps }>
153
161
  { renderedTitleField }
154
162
  </div>
155
- <ItemActions item={ item } actions={ actions } isCompact />
163
+ { !! actions?.length && (
164
+ <ItemActions item={ item } actions={ actions } isCompact />
165
+ ) }
156
166
  </HStack>
157
167
  <VStack spacing={ 1 }>
158
168
  { showDescription && descriptionField?.render && (
@@ -168,12 +178,12 @@ function GridItem< Item >( {
168
178
  >
169
179
  { badgeFields.map( ( field ) => {
170
180
  return (
171
- <FlexItem
181
+ <Badge
172
182
  key={ field.id }
173
183
  className="dataviews-view-grid__field-value"
174
184
  >
175
185
  <field.render item={ item } />
176
- </FlexItem>
186
+ </Badge>
177
187
  );
178
188
  } ) }
179
189
  </HStack>
@@ -258,6 +268,7 @@ export default function ViewGrid< Item >( {
258
268
  );
259
269
  const hasData = !! data?.length;
260
270
  const updatedPreviewSize = useUpdatedPreviewSizeOnViewportChange();
271
+ const hasBulkActions = useSomeItemHasAPossibleBulkAction( actions, data );
261
272
  const usedPreviewSize = updatedPreviewSize || view.layout?.previewSize;
262
273
  const gridStyle = usedPreviewSize
263
274
  ? {
@@ -292,6 +303,7 @@ export default function ViewGrid< Item >( {
292
303
  descriptionField={ descriptionField }
293
304
  regularFields={ regularFields }
294
305
  badgeFields={ badgeFields }
306
+ hasBulkActions={ hasBulkActions }
295
307
  />
296
308
  );
297
309
  } ) }