@wordpress/dataviews 4.2.0 → 4.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 (123) hide show
  1. package/CHANGELOG.md +2 -0
  2. package/README.md +47 -7
  3. package/build/components/dataviews/index.js +3 -5
  4. package/build/components/dataviews/index.js.map +1 -1
  5. package/build/components/dataviews-bulk-actions/index.js +145 -141
  6. package/build/components/dataviews-bulk-actions/index.js.map +1 -1
  7. package/build/components/dataviews-filters/add-filter.js +4 -6
  8. package/build/components/dataviews-filters/add-filter.js.map +1 -1
  9. package/build/components/dataviews-filters/search-widget.js +28 -18
  10. package/build/components/dataviews-filters/search-widget.js.map +1 -1
  11. package/build/components/dataviews-footer/index.js +45 -0
  12. package/build/components/dataviews-footer/index.js.map +1 -0
  13. package/build/components/dataviews-item-actions/index.js +5 -8
  14. package/build/components/dataviews-item-actions/index.js.map +1 -1
  15. package/build/components/dataviews-pagination/index.js +4 -4
  16. package/build/components/dataviews-pagination/index.js.map +1 -1
  17. package/build/components/dataviews-view-config/index.js +171 -32
  18. package/build/components/dataviews-view-config/index.js.map +1 -1
  19. package/build/dataforms-layouts/panel/index.js +4 -1
  20. package/build/dataforms-layouts/panel/index.js.map +1 -1
  21. package/build/dataviews-layouts/index.js +48 -2
  22. package/build/dataviews-layouts/index.js.map +1 -1
  23. package/build/dataviews-layouts/list/index.js +124 -84
  24. package/build/dataviews-layouts/list/index.js.map +1 -1
  25. package/build/dataviews-layouts/table/column-header-menu.js +52 -54
  26. package/build/dataviews-layouts/table/column-header-menu.js.map +1 -1
  27. package/build/dataviews-layouts/table/index.js +7 -35
  28. package/build/dataviews-layouts/table/index.js.map +1 -1
  29. package/build/normalize-fields.js +4 -2
  30. package/build/normalize-fields.js.map +1 -1
  31. package/build/types.js.map +1 -1
  32. package/build-module/components/dataviews/index.js +3 -5
  33. package/build-module/components/dataviews/index.js.map +1 -1
  34. package/build-module/components/dataviews-bulk-actions/index.js +145 -143
  35. package/build-module/components/dataviews-bulk-actions/index.js.map +1 -1
  36. package/build-module/components/dataviews-filters/add-filter.js +4 -6
  37. package/build-module/components/dataviews-filters/add-filter.js.map +1 -1
  38. package/build-module/components/dataviews-filters/search-widget.js +28 -18
  39. package/build-module/components/dataviews-filters/search-widget.js.map +1 -1
  40. package/build-module/components/dataviews-footer/index.js +38 -0
  41. package/build-module/components/dataviews-footer/index.js.map +1 -0
  42. package/build-module/components/dataviews-item-actions/index.js +5 -8
  43. package/build-module/components/dataviews-item-actions/index.js.map +1 -1
  44. package/build-module/components/dataviews-pagination/index.js +5 -5
  45. package/build-module/components/dataviews-pagination/index.js.map +1 -1
  46. package/build-module/components/dataviews-view-config/index.js +177 -38
  47. package/build-module/components/dataviews-view-config/index.js.map +1 -1
  48. package/build-module/dataforms-layouts/panel/index.js +4 -1
  49. package/build-module/dataforms-layouts/panel/index.js.map +1 -1
  50. package/build-module/dataviews-layouts/index.js +45 -1
  51. package/build-module/dataviews-layouts/index.js.map +1 -1
  52. package/build-module/dataviews-layouts/list/index.js +125 -83
  53. package/build-module/dataviews-layouts/list/index.js.map +1 -1
  54. package/build-module/dataviews-layouts/table/column-header-menu.js +52 -54
  55. package/build-module/dataviews-layouts/table/column-header-menu.js.map +1 -1
  56. package/build-module/dataviews-layouts/table/index.js +9 -37
  57. package/build-module/dataviews-layouts/table/index.js.map +1 -1
  58. package/build-module/normalize-fields.js +4 -2
  59. package/build-module/normalize-fields.js.map +1 -1
  60. package/build-module/types.js.map +1 -1
  61. package/build-style/style-rtl.css +79 -63
  62. package/build-style/style.css +79 -63
  63. package/build-types/components/dataviews/index.d.ts.map +1 -1
  64. package/build-types/components/dataviews/stories/fixtures.d.ts +27 -131
  65. package/build-types/components/dataviews/stories/fixtures.d.ts.map +1 -1
  66. package/build-types/components/dataviews/stories/index.story.d.ts +12 -53
  67. package/build-types/components/dataviews/stories/index.story.d.ts.map +1 -1
  68. package/build-types/components/dataviews-bulk-actions/index.d.ts +11 -1
  69. package/build-types/components/dataviews-bulk-actions/index.d.ts.map +1 -1
  70. package/build-types/components/dataviews-filters/add-filter.d.ts.map +1 -1
  71. package/build-types/components/dataviews-filters/search-widget.d.ts.map +1 -1
  72. package/build-types/components/dataviews-footer/index.d.ts +2 -0
  73. package/build-types/components/dataviews-footer/index.d.ts.map +1 -0
  74. package/build-types/components/dataviews-item-actions/index.d.ts.map +1 -1
  75. package/build-types/components/dataviews-view-config/index.d.ts.map +1 -1
  76. package/build-types/dataforms-layouts/panel/index.d.ts.map +1 -1
  77. package/build-types/dataviews-layouts/index.d.ts +4 -2
  78. package/build-types/dataviews-layouts/index.d.ts.map +1 -1
  79. package/build-types/dataviews-layouts/list/index.d.ts.map +1 -1
  80. package/build-types/dataviews-layouts/table/column-header-menu.d.ts.map +1 -1
  81. package/build-types/dataviews-layouts/table/index.d.ts.map +1 -1
  82. package/build-types/normalize-fields.d.ts.map +1 -1
  83. package/build-types/types.d.ts +2 -0
  84. package/build-types/types.d.ts.map +1 -1
  85. package/package.json +11 -11
  86. package/src/components/dataviews/index.tsx +2 -6
  87. package/src/components/dataviews/stories/fixtures.tsx +690 -0
  88. package/src/components/dataviews/stories/index.story.tsx +164 -0
  89. package/src/components/dataviews/style.scss +2 -0
  90. package/src/components/dataviews-bulk-actions/index.tsx +264 -213
  91. package/src/components/dataviews-bulk-actions/style.scss +9 -4
  92. package/src/components/dataviews-filters/add-filter.tsx +7 -11
  93. package/src/components/dataviews-filters/search-widget.tsx +45 -17
  94. package/src/components/dataviews-filters/style.scss +12 -2
  95. package/src/components/dataviews-footer/index.tsx +50 -0
  96. package/src/components/dataviews-footer/style.scss +40 -0
  97. package/src/components/dataviews-item-actions/index.tsx +8 -14
  98. package/src/components/dataviews-pagination/index.tsx +5 -5
  99. package/src/components/dataviews-pagination/style.scss +0 -19
  100. package/src/components/dataviews-view-config/index.tsx +252 -53
  101. package/src/components/dataviews-view-config/style.scss +25 -0
  102. package/src/dataforms-layouts/panel/index.tsx +2 -0
  103. package/src/dataviews-layouts/grid/style.scss +1 -1
  104. package/src/dataviews-layouts/index.ts +63 -2
  105. package/src/dataviews-layouts/list/index.tsx +199 -127
  106. package/src/dataviews-layouts/list/style.scss +10 -4
  107. package/src/dataviews-layouts/table/column-header-menu.tsx +85 -87
  108. package/src/dataviews-layouts/table/index.tsx +8 -65
  109. package/src/dataviews-layouts/table/style.scss +0 -5
  110. package/src/normalize-fields.ts +2 -0
  111. package/src/style.scss +1 -1
  112. package/src/types.ts +2 -0
  113. package/tsconfig.tsbuildinfo +1 -1
  114. package/build/components/dataviews-bulk-actions-toolbar/index.js +0 -207
  115. package/build/components/dataviews-bulk-actions-toolbar/index.js.map +0 -1
  116. package/build-module/components/dataviews-bulk-actions-toolbar/index.js +0 -201
  117. package/build-module/components/dataviews-bulk-actions-toolbar/index.js.map +0 -1
  118. package/build-types/components/dataviews-bulk-actions-toolbar/index.d.ts +0 -2
  119. package/build-types/components/dataviews-bulk-actions-toolbar/index.d.ts.map +0 -1
  120. package/src/components/dataviews/stories/fixtures.js +0 -250
  121. package/src/components/dataviews/stories/index.story.js +0 -71
  122. package/src/components/dataviews-bulk-actions-toolbar/index.tsx +0 -288
  123. package/src/components/dataviews-bulk-actions-toolbar/style.scss +0 -45
@@ -21,10 +21,11 @@ import {
21
21
  __experimentalHeading as Heading,
22
22
  __experimentalText as Text,
23
23
  privateApis as componentsPrivateApis,
24
+ BaseControl,
24
25
  } from '@wordpress/components';
25
- import { __, _x } from '@wordpress/i18n';
26
+ import { __, _x, sprintf } from '@wordpress/i18n';
26
27
  import { memo, useContext, useState, useMemo } from '@wordpress/element';
27
- import { cog, seen, unseen } from '@wordpress/icons';
28
+ import { chevronDown, chevronUp, cog, seen, unseen } from '@wordpress/icons';
28
29
  import warning from '@wordpress/warning';
29
30
 
30
31
  /**
@@ -33,20 +34,22 @@ import warning from '@wordpress/warning';
33
34
  import {
34
35
  SORTING_DIRECTIONS,
35
36
  LAYOUT_GRID,
37
+ LAYOUT_TABLE,
36
38
  sortIcons,
37
39
  sortLabels,
38
40
  } from '../../constants';
39
- import { VIEW_LAYOUTS, getMandatoryFields } from '../../dataviews-layouts';
40
- import type { SupportedLayouts } from '../../types';
41
+ import {
42
+ VIEW_LAYOUTS,
43
+ getNotHidableFieldIds,
44
+ getVisibleFieldIds,
45
+ getHiddenFieldIds,
46
+ } from '../../dataviews-layouts';
47
+ import type { SupportedLayouts, View, Field } from '../../types';
41
48
  import DataViewsContext from '../dataviews-context';
42
49
  import { unlock } from '../../lock-unlock';
43
50
  import DensityPicker from '../../dataviews-layouts/grid/density-picker';
44
51
 
45
- const {
46
- DropdownMenuV2: DropdownMenu,
47
- DropdownMenuRadioItemV2: DropdownMenuRadioItem,
48
- DropdownMenuItemLabelV2: DropdownMenuItemLabel,
49
- } = unlock( componentsPrivateApis );
52
+ const { DropdownMenuV2 } = unlock( componentsPrivateApis );
50
53
 
51
54
  interface ViewTypeMenuProps {
52
55
  defaultLayouts?: SupportedLayouts;
@@ -62,7 +65,7 @@ function ViewTypeMenu( {
62
65
  }
63
66
  const activeView = VIEW_LAYOUTS.find( ( v ) => view.type === v.type );
64
67
  return (
65
- <DropdownMenu
68
+ <DropdownMenuV2
66
69
  trigger={
67
70
  <Button
68
71
  size="compact"
@@ -77,7 +80,7 @@ function ViewTypeMenu( {
77
80
  return null;
78
81
  }
79
82
  return (
80
- <DropdownMenuRadioItem
83
+ <DropdownMenuV2.RadioItem
81
84
  key={ layout }
82
85
  value={ layout }
83
86
  name="view-actions-available-view"
@@ -97,13 +100,13 @@ function ViewTypeMenu( {
97
100
  warning( 'Invalid dataview' );
98
101
  } }
99
102
  >
100
- <DropdownMenuItemLabel>
103
+ <DropdownMenuV2.ItemLabel>
101
104
  { config.label }
102
- </DropdownMenuItemLabel>
103
- </DropdownMenuRadioItem>
105
+ </DropdownMenuV2.ItemLabel>
106
+ </DropdownMenuV2.RadioItem>
104
107
  );
105
108
  } ) }
106
- </DropdownMenu>
109
+ </DropdownMenuV2>
107
110
  );
108
111
  }
109
112
 
@@ -143,6 +146,14 @@ function SortFieldControl() {
143
146
 
144
147
  function SortDirectionControl() {
145
148
  const { view, fields, onChangeView } = useContext( DataViewsContext );
149
+
150
+ const sortableFields = fields.filter(
151
+ ( field ) => field.enableSorting !== false
152
+ );
153
+ if ( sortableFields.length === 0 ) {
154
+ return null;
155
+ }
156
+
146
157
  let value = view.sort?.direction;
147
158
  if ( ! value && view.sort?.field ) {
148
159
  value = 'desc';
@@ -226,50 +237,238 @@ function ItemsPerPageControl() {
226
237
  );
227
238
  }
228
239
 
229
- function FieldControl() {
230
- const { view, fields, onChangeView } = useContext( DataViewsContext );
231
- const mandatoryFields = getMandatoryFields( view );
232
- const hidableFields = fields.filter(
233
- ( field ) =>
234
- field.enableHiding !== false &&
235
- ! mandatoryFields.includes( field.id )
236
- );
237
- const viewFields = view.fields || fields.map( ( field ) => field.id );
238
- if ( ! hidableFields?.length ) {
239
- return null;
240
- }
240
+ interface FieldItemProps {
241
+ id: any;
242
+ label: string;
243
+ index: number;
244
+ isVisible: boolean;
245
+ isHidable: boolean;
246
+ }
247
+
248
+ function FieldItem( {
249
+ field: { id, label, index, isVisible, isHidable },
250
+ fields,
251
+ view,
252
+ onChangeView,
253
+ }: {
254
+ field: FieldItemProps;
255
+ fields: Field< any >[];
256
+ view: View;
257
+ onChangeView: ( view: View ) => void;
258
+ } ) {
259
+ const visibleFieldIds = getVisibleFieldIds( view, fields );
260
+
241
261
  return (
242
- <ItemGroup isBordered isSeparated>
243
- { hidableFields?.map( ( field ) => {
244
- const isVisible = viewFields.includes( field.id );
245
- return (
246
- <Item key={ field.id }>
247
- <HStack expanded>
248
- <span>{ field.label }</span>
262
+ <Item key={ id }>
263
+ <HStack
264
+ expanded
265
+ className={ `dataviews-field-control__field dataviews-field-control__field-${ id }` }
266
+ >
267
+ <span>{ label }</span>
268
+ <HStack
269
+ justify="flex-end"
270
+ expanded={ false }
271
+ className="dataviews-field-control__actions"
272
+ >
273
+ { view.type === LAYOUT_TABLE && isVisible && (
274
+ <>
249
275
  <Button
276
+ disabled={ index < 1 }
277
+ accessibleWhenDisabled
250
278
  size="compact"
251
- onClick={ () =>
279
+ onClick={ () => {
252
280
  onChangeView( {
253
281
  ...view,
254
- fields: isVisible
255
- ? viewFields.filter(
256
- ( id ) => id !== field.id
257
- )
258
- : [ ...viewFields, field.id ],
259
- } )
260
- }
261
- icon={ isVisible ? seen : unseen }
262
- label={
263
- isVisible
264
- ? __( 'Hide field' )
265
- : __( 'Show field' )
266
- }
282
+ fields: [
283
+ ...( visibleFieldIds.slice(
284
+ 0,
285
+ index - 1
286
+ ) ?? [] ),
287
+ id,
288
+ visibleFieldIds[ index - 1 ],
289
+ ...visibleFieldIds.slice(
290
+ index + 1
291
+ ),
292
+ ],
293
+ } );
294
+ } }
295
+ icon={ chevronUp }
296
+ label={ sprintf(
297
+ /* translators: %s: field label */
298
+ __( 'Move %s up' ),
299
+ label
300
+ ) }
267
301
  />
268
- </HStack>
269
- </Item>
270
- );
271
- } ) }
272
- </ItemGroup>
302
+ <Button
303
+ disabled={ index >= visibleFieldIds.length - 1 }
304
+ accessibleWhenDisabled
305
+ size="compact"
306
+ onClick={ () => {
307
+ onChangeView( {
308
+ ...view,
309
+ fields: [
310
+ ...( visibleFieldIds.slice(
311
+ 0,
312
+ index
313
+ ) ?? [] ),
314
+ visibleFieldIds[ index + 1 ],
315
+ id,
316
+ ...visibleFieldIds.slice(
317
+ index + 2
318
+ ),
319
+ ],
320
+ } );
321
+ } }
322
+ icon={ chevronDown }
323
+ label={ sprintf(
324
+ /* translators: %s: field label */
325
+ __( 'Move %s down' ),
326
+ label
327
+ ) }
328
+ />{ ' ' }
329
+ </>
330
+ ) }
331
+ <Button
332
+ className="dataviews-field-control__field-visibility-button"
333
+ disabled={ ! isHidable }
334
+ accessibleWhenDisabled
335
+ size="compact"
336
+ onClick={ () => {
337
+ onChangeView( {
338
+ ...view,
339
+ fields: isVisible
340
+ ? visibleFieldIds.filter(
341
+ ( fieldId ) => fieldId !== id
342
+ )
343
+ : [ ...visibleFieldIds, id ],
344
+ } );
345
+ // Focus the visibility button to avoid focus loss.
346
+ // Our code is safe against the component being unmounted, so we don't need to worry about cleaning the timeout.
347
+ // eslint-disable-next-line @wordpress/react-no-unsafe-timeout
348
+ setTimeout( () => {
349
+ const element = document.querySelector(
350
+ `.dataviews-field-control__field-${ id } .dataviews-field-control__field-visibility-button`
351
+ );
352
+ if ( element instanceof HTMLElement ) {
353
+ element.focus();
354
+ }
355
+ }, 50 );
356
+ } }
357
+ icon={ isVisible ? seen : unseen }
358
+ label={
359
+ isVisible
360
+ ? sprintf(
361
+ /* translators: %s: field label */
362
+ __( 'Hide %s' ),
363
+ label
364
+ )
365
+ : sprintf(
366
+ /* translators: %s: field label */
367
+ __( 'Show %s' ),
368
+ label
369
+ )
370
+ }
371
+ />
372
+ </HStack>
373
+ </HStack>
374
+ </Item>
375
+ );
376
+ }
377
+
378
+ function FieldControl() {
379
+ const { view, fields, onChangeView } = useContext( DataViewsContext );
380
+
381
+ const visibleFieldIds = useMemo(
382
+ () => getVisibleFieldIds( view, fields ),
383
+ [ view, fields ]
384
+ );
385
+ const hiddenFieldIds = useMemo(
386
+ () => getHiddenFieldIds( view, fields ),
387
+ [ view, fields ]
388
+ );
389
+ const notHidableFieldIds = useMemo(
390
+ () => getNotHidableFieldIds( view ),
391
+ [ view ]
392
+ );
393
+
394
+ const visibleFields = fields
395
+ .filter( ( { id } ) => visibleFieldIds.includes( id ) )
396
+ .map( ( { id, label, enableHiding } ) => {
397
+ return {
398
+ id,
399
+ label,
400
+ index: visibleFieldIds.indexOf( id ),
401
+ isVisible: true,
402
+ isHidable: notHidableFieldIds.includes( id )
403
+ ? false
404
+ : enableHiding,
405
+ };
406
+ } );
407
+ if ( view.type === LAYOUT_TABLE && view.layout?.combinedFields ) {
408
+ view.layout.combinedFields.forEach( ( { id, label } ) => {
409
+ visibleFields.push( {
410
+ id,
411
+ label,
412
+ index: visibleFieldIds.indexOf( id ),
413
+ isVisible: true,
414
+ isHidable: notHidableFieldIds.includes( id ),
415
+ } );
416
+ } );
417
+ }
418
+ visibleFields.sort( ( a, b ) => a.index - b.index );
419
+
420
+ const hiddenFields = fields
421
+ .filter( ( { id } ) => hiddenFieldIds.includes( id ) )
422
+ .map( ( { id, label, enableHiding }, index ) => {
423
+ return {
424
+ id,
425
+ label,
426
+ index,
427
+ isVisible: false,
428
+ isHidable: enableHiding,
429
+ };
430
+ } );
431
+
432
+ if ( ! visibleFields?.length && ! hiddenFields?.length ) {
433
+ return null;
434
+ }
435
+
436
+ return (
437
+ <VStack spacing={ 6 } className="dataviews-field-control">
438
+ { !! visibleFields?.length && (
439
+ <ItemGroup isBordered isSeparated>
440
+ { visibleFields.map( ( field ) => (
441
+ <FieldItem
442
+ key={ field.id }
443
+ field={ field }
444
+ fields={ fields }
445
+ view={ view }
446
+ onChangeView={ onChangeView }
447
+ />
448
+ ) ) }
449
+ </ItemGroup>
450
+ ) }
451
+ { !! hiddenFields?.length && (
452
+ <>
453
+ <VStack spacing={ 4 }>
454
+ <BaseControl.VisualLabel style={ { margin: 0 } }>
455
+ { __( 'Hidden' ) }
456
+ </BaseControl.VisualLabel>
457
+ <ItemGroup isBordered isSeparated>
458
+ { hiddenFields.map( ( field ) => (
459
+ <FieldItem
460
+ key={ field.id }
461
+ field={ field }
462
+ fields={ fields }
463
+ view={ view }
464
+ onChangeView={ onChangeView }
465
+ />
466
+ ) ) }
467
+ </ItemGroup>
468
+ </VStack>
469
+ </>
470
+ ) }
471
+ </VStack>
273
472
  );
274
473
  }
275
474
 
@@ -3,6 +3,8 @@
3
3
  /* stylelint-disable-next-line property-no-unknown -- the linter needs to be updated to accepted the container-type property */
4
4
  container-type: inline-size;
5
5
  padding: $grid-unit-20;
6
+ font-size: $default-font-size;
7
+ line-height: $default-line-height;
6
8
  }
7
9
  .dataviews-view-config__sort-direction .components-toggle-group-control-option-base {
8
10
  text-transform: uppercase;
@@ -29,6 +31,10 @@
29
31
  }
30
32
  }
31
33
 
34
+ .dataviews-settings-section:has(.dataviews-settings-section__content:empty) {
35
+ display: none;
36
+ }
37
+
32
38
  /* stylelint-disable-next-line scss/at-rule-no-unknown -- '@container' not globally permitted */
33
39
  @container (max-width: 500px) {
34
40
  .dataviews-settings-section.dataviews-settings-section {
@@ -42,3 +48,22 @@
42
48
  }
43
49
  }
44
50
  }
51
+ .dataviews-field-control__field {
52
+ height: $grid-unit-40;
53
+ }
54
+
55
+ .dataviews-field-control__actions {
56
+ position: absolute;
57
+ top: -9999em;
58
+ }
59
+ .dataviews-field-control__actions.dataviews-field-control__actions {
60
+ gap: $grid-unit-05;
61
+ }
62
+
63
+ .dataviews-field-control__field:hover,
64
+ .dataviews-field-control__field:focus-within {
65
+ .dataviews-field-control__actions {
66
+ position: unset;
67
+ top: unset;
68
+ }
69
+ }
@@ -44,6 +44,8 @@ function DropdownHeader( {
44
44
  <Spacer />
45
45
  { onClose && (
46
46
  <Button
47
+ // TODO: Switch to `true` (40px size) if possible
48
+ __next40pxDefaultSize={ false }
47
49
  className="dataforms-layouts-panel__dropdown-header-action"
48
50
  label={ __( 'Close' ) }
49
51
  icon={ closeSmall }
@@ -118,7 +118,7 @@
118
118
  background: $gray-100;
119
119
  padding: 0 $grid-unit-10;
120
120
  min-height: $grid-unit-30;
121
- border-radius: $radius-block-ui;
121
+ border-radius: $radius-small;
122
122
  display: flex;
123
123
  align-items: center;
124
124
  font-size: 12px;
@@ -16,7 +16,7 @@ import ViewTable from './table';
16
16
  import ViewGrid from './grid';
17
17
  import ViewList from './list';
18
18
  import { LAYOUT_GRID, LAYOUT_LIST, LAYOUT_TABLE } from '../constants';
19
- import type { View } from '../types';
19
+ import type { View, Field } from '../types';
20
20
 
21
21
  export const VIEW_LAYOUTS = [
22
22
  {
@@ -39,7 +39,7 @@ export const VIEW_LAYOUTS = [
39
39
  },
40
40
  ];
41
41
 
42
- export function getMandatoryFields( view: View ): string[] {
42
+ export function getNotHidableFieldIds( view: View ): string[] {
43
43
  if ( view.type === 'table' ) {
44
44
  return [ view.layout?.primaryField ]
45
45
  .concat(
@@ -64,3 +64,64 @@ export function getMandatoryFields( view: View ): string[] {
64
64
 
65
65
  return [];
66
66
  }
67
+
68
+ function getCombinedFieldIds( view: View ): string[] {
69
+ const combinedFields: string[] = [];
70
+ if ( view.type === LAYOUT_TABLE && view.layout?.combinedFields ) {
71
+ view.layout.combinedFields.forEach( ( combination ) => {
72
+ combinedFields.push( ...combination.children );
73
+ } );
74
+ }
75
+ return combinedFields;
76
+ }
77
+
78
+ export function getVisibleFieldIds(
79
+ view: View,
80
+ fields: Field< any >[]
81
+ ): string[] {
82
+ const fieldsToExclude = getCombinedFieldIds( view );
83
+
84
+ if ( view.fields ) {
85
+ return view.fields.filter( ( id ) => ! fieldsToExclude.includes( id ) );
86
+ }
87
+
88
+ const visibleFields = [];
89
+ if ( view.type === LAYOUT_TABLE && view.layout?.combinedFields ) {
90
+ visibleFields.push(
91
+ ...view.layout.combinedFields.map( ( { id } ) => id )
92
+ );
93
+ }
94
+ visibleFields.push(
95
+ ...fields
96
+ .filter( ( { id } ) => ! fieldsToExclude.includes( id ) )
97
+ .map( ( { id } ) => id )
98
+ );
99
+
100
+ return visibleFields;
101
+ }
102
+
103
+ export function getHiddenFieldIds(
104
+ view: View,
105
+ fields: Field< any >[]
106
+ ): string[] {
107
+ const fieldsToExclude = [
108
+ ...getCombinedFieldIds( view ),
109
+ ...getVisibleFieldIds( view, fields ),
110
+ ];
111
+
112
+ // The media field does not need to be in the view.fields to be displayed.
113
+ if ( view.type === LAYOUT_GRID && view.layout?.mediaField ) {
114
+ fieldsToExclude.push( view.layout?.mediaField );
115
+ }
116
+
117
+ if ( view.type === LAYOUT_LIST && view.layout?.mediaField ) {
118
+ fieldsToExclude.push( view.layout?.mediaField );
119
+ }
120
+
121
+ return fields
122
+ .filter(
123
+ ( { id, enableHiding } ) =>
124
+ ! fieldsToExclude.includes( id ) && enableHiding
125
+ )
126
+ .map( ( { id } ) => id );
127
+ }