@wordpress/block-editor 15.4.1-next.f56bd8138.0 → 15.5.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 (106) hide show
  1. package/CHANGELOG.md +2 -0
  2. package/build/components/background-image-control/index.js +51 -24
  3. package/build/components/background-image-control/index.js.map +1 -1
  4. package/build/components/block-edit/edit.js +13 -5
  5. package/build/components/block-edit/edit.js.map +1 -1
  6. package/build/components/block-list/block.js +14 -4
  7. package/build/components/block-list/block.js.map +1 -1
  8. package/build/components/block-list/use-block-props/index.js +5 -4
  9. package/build/components/block-list/use-block-props/index.js.map +1 -1
  10. package/build/components/block-settings-menu-controls/index.js +10 -2
  11. package/build/components/block-settings-menu-controls/index.js.map +1 -1
  12. package/build/components/block-toolbar/index.js +5 -0
  13. package/build/components/block-toolbar/index.js.map +1 -1
  14. package/build/components/block-visibility/index.js +21 -0
  15. package/build/components/block-visibility/index.js.map +1 -0
  16. package/build/components/block-visibility/menu-item.js +52 -0
  17. package/build/components/block-visibility/menu-item.js.map +1 -0
  18. package/build/components/block-visibility/toolbar.js +88 -0
  19. package/build/components/block-visibility/toolbar.js.map +1 -0
  20. package/build/components/inspector-controls-tabs/index.js +23 -0
  21. package/build/components/inspector-controls-tabs/index.js.map +1 -1
  22. package/build/components/link-control/index.js +1 -1
  23. package/build/components/link-control/index.js.map +1 -1
  24. package/build/components/list-view/block-select-button.js +23 -4
  25. package/build/components/list-view/block-select-button.js.map +1 -1
  26. package/build/components/list-view/block.js +9 -3
  27. package/build/components/list-view/block.js.map +1 -1
  28. package/build/components/rich-text/index.js +4 -2
  29. package/build/components/rich-text/index.js.map +1 -1
  30. package/build/components/use-block-drop-zone/index.js +5 -1
  31. package/build/components/use-block-drop-zone/index.js.map +1 -1
  32. package/build/hooks/block-bindings.js +9 -4
  33. package/build/hooks/block-bindings.js.map +1 -1
  34. package/build/store/private-selectors.js +19 -1
  35. package/build/store/private-selectors.js.map +1 -1
  36. package/build/store/reducer.js +1 -5
  37. package/build/store/reducer.js.map +1 -1
  38. package/build/utils/block-bindings.js +3 -48
  39. package/build/utils/block-bindings.js.map +1 -1
  40. package/build-module/components/background-image-control/index.js +52 -25
  41. package/build-module/components/background-image-control/index.js.map +1 -1
  42. package/build-module/components/block-edit/edit.js +14 -6
  43. package/build-module/components/block-edit/edit.js.map +1 -1
  44. package/build-module/components/block-list/block.js +14 -4
  45. package/build-module/components/block-list/block.js.map +1 -1
  46. package/build-module/components/block-list/use-block-props/index.js +5 -4
  47. package/build-module/components/block-list/use-block-props/index.js.map +1 -1
  48. package/build-module/components/block-settings-menu-controls/index.js +10 -2
  49. package/build-module/components/block-settings-menu-controls/index.js.map +1 -1
  50. package/build-module/components/block-toolbar/index.js +5 -0
  51. package/build-module/components/block-toolbar/index.js.map +1 -1
  52. package/build-module/components/block-visibility/index.js +3 -0
  53. package/build-module/components/block-visibility/index.js.map +1 -0
  54. package/build-module/components/block-visibility/menu-item.js +45 -0
  55. package/build-module/components/block-visibility/menu-item.js.map +1 -0
  56. package/build-module/components/block-visibility/toolbar.js +81 -0
  57. package/build-module/components/block-visibility/toolbar.js.map +1 -0
  58. package/build-module/components/inspector-controls-tabs/index.js +23 -0
  59. package/build-module/components/inspector-controls-tabs/index.js.map +1 -1
  60. package/build-module/components/link-control/index.js +1 -1
  61. package/build-module/components/link-control/index.js.map +1 -1
  62. package/build-module/components/list-view/block-select-button.js +24 -5
  63. package/build-module/components/list-view/block-select-button.js.map +1 -1
  64. package/build-module/components/list-view/block.js +9 -3
  65. package/build-module/components/list-view/block.js.map +1 -1
  66. package/build-module/components/rich-text/index.js +4 -2
  67. package/build-module/components/rich-text/index.js.map +1 -1
  68. package/build-module/components/use-block-drop-zone/index.js +6 -2
  69. package/build-module/components/use-block-drop-zone/index.js.map +1 -1
  70. package/build-module/hooks/block-bindings.js +10 -5
  71. package/build-module/hooks/block-bindings.js.map +1 -1
  72. package/build-module/store/private-selectors.js +18 -1
  73. package/build-module/store/private-selectors.js.map +1 -1
  74. package/build-module/store/reducer.js +1 -5
  75. package/build-module/store/reducer.js.map +1 -1
  76. package/build-module/utils/block-bindings.js +3 -45
  77. package/build-module/utils/block-bindings.js.map +1 -1
  78. package/build-style/content-rtl.css +13 -0
  79. package/build-style/content.css +13 -0
  80. package/build-style/style-rtl.css +27 -0
  81. package/build-style/style.css +27 -0
  82. package/package.json +34 -34
  83. package/src/components/background-image-control/index.js +54 -27
  84. package/src/components/background-image-control/style.scss +28 -0
  85. package/src/components/block-edit/edit.js +23 -5
  86. package/src/components/block-list/block.js +16 -0
  87. package/src/components/block-list/content.scss +19 -0
  88. package/src/components/block-list/use-block-props/index.js +9 -9
  89. package/src/components/block-settings-menu-controls/index.js +21 -1
  90. package/src/components/block-toolbar/index.js +9 -0
  91. package/src/components/block-visibility/index.js +2 -0
  92. package/src/components/block-visibility/menu-item.js +53 -0
  93. package/src/components/block-visibility/toolbar.js +88 -0
  94. package/src/components/inspector-controls-tabs/index.js +33 -1
  95. package/src/components/link-control/index.js +1 -1
  96. package/src/components/link-control/test/index.js +4 -4
  97. package/src/components/list-view/block-select-button.js +32 -9
  98. package/src/components/list-view/block.js +24 -14
  99. package/src/components/list-view/style.scss +1 -0
  100. package/src/components/media-replace-flow/test/index.js +1 -1
  101. package/src/components/rich-text/index.js +6 -2
  102. package/src/components/use-block-drop-zone/index.js +14 -1
  103. package/src/hooks/block-bindings.js +49 -43
  104. package/src/store/private-selectors.js +21 -1
  105. package/src/store/reducer.js +1 -6
  106. package/src/utils/block-bindings.js +6 -48
@@ -624,6 +624,7 @@ function BlockListBlockProvider( props ) {
624
624
  ? getBlockDefaultClassName( blockName )
625
625
  : undefined,
626
626
  blockTitle: blockType?.title,
627
+ isBlockHidden: attributes?.metadata?.blockVisibility === false,
627
628
  };
628
629
 
629
630
  // When in preview mode, we can avoid a lot of selection and
@@ -632,6 +633,9 @@ function BlockListBlockProvider( props ) {
632
633
  return previewContext;
633
634
  }
634
635
 
636
+ const { isBlockHidden: _isBlockHidden } = unlock(
637
+ select( blockEditorStore )
638
+ );
635
639
  const _isSelected = isBlockSelected( clientId );
636
640
  const canRemove = canRemoveBlock( clientId );
637
641
  const canMove = canMoveBlock( clientId );
@@ -705,6 +709,7 @@ function BlockListBlockProvider( props ) {
705
709
  originalBlockClientId: isInvalid
706
710
  ? blocksWithSameName[ 0 ]
707
711
  : false,
712
+ isBlockHidden: _isBlockHidden( clientId ),
708
713
  };
709
714
  },
710
715
  [ clientId, rootClientId ]
@@ -747,6 +752,7 @@ function BlockListBlockProvider( props ) {
747
752
  className,
748
753
  defaultClassName,
749
754
  originalBlockClientId,
755
+ isBlockHidden,
750
756
  } = selectedProps;
751
757
 
752
758
  // Users of the editor.BlockListBlock filter used to be able to
@@ -795,8 +801,18 @@ function BlockListBlockProvider( props ) {
795
801
  originalBlockClientId,
796
802
  themeSupportsLayout,
797
803
  canMove,
804
+ isBlockHidden,
798
805
  };
799
806
 
807
+ if (
808
+ isBlockHidden &&
809
+ ! isSelected &&
810
+ ! isMultiSelected &&
811
+ ! hasChildSelected
812
+ ) {
813
+ return null;
814
+ }
815
+
800
816
  // Here we separate between the props passed to BlockListBlock and any other
801
817
  // information we selected for internal use. BlockListBlock is a filtered
802
818
  // component and thus ALL the props are PUBLIC API.
@@ -93,6 +93,25 @@ _::-webkit-full-page-media, _:future, :root [data-has-multi-selection="true"] .b
93
93
  }
94
94
  }
95
95
 
96
+
97
+ // Hidden blocks.
98
+ // In order for the block toolbar to render correctly, blocks cannot be hidden.
99
+ // Instead, use styles to visually hide blocks.
100
+ .block-editor-block-list__block.is-block-hidden {
101
+ visibility: hidden;
102
+ overflow: hidden;
103
+ height: 0;
104
+ border: none !important;
105
+ padding: 0 !important;
106
+ }
107
+
108
+ &.is-layout-flex:not(.is-vertical) > .is-block-hidden {
109
+ width: 0;
110
+ height: auto;
111
+ align-self: stretch;
112
+ white-space: nowrap !important;
113
+ }
114
+
96
115
  // Re-enable it on components inside.
97
116
  [class^="components-"] {
98
117
  user-select: text;
@@ -29,7 +29,6 @@ import { useBlockRefProvider } from './use-block-refs';
29
29
  import { useIntersectionObserver } from './use-intersection-observer';
30
30
  import { useScrollIntoView } from './use-scroll-into-view';
31
31
  import { useFlashEditableBlocks } from '../../use-flash-editable-blocks';
32
- import { canBindBlock } from '../../../utils/block-bindings';
33
32
  import { useFirefoxDraggableCompatibility } from './use-firefox-draggable-compatibility';
34
33
 
35
34
  /**
@@ -102,6 +101,7 @@ export function useBlockProps( props = {}, { __unstableIsHtml } = {} ) {
102
101
  defaultClassName,
103
102
  isSectionBlock,
104
103
  canMove,
104
+ isBlockHidden,
105
105
  } = useContext( PrivateBlockContext );
106
106
 
107
107
  // translators: %s: Type of block (i.e. Text, Image etc)
@@ -128,14 +128,13 @@ export function useBlockProps( props = {}, { __unstableIsHtml } = {} ) {
128
128
 
129
129
  const blockEditContext = useBlockEditContext();
130
130
  const hasBlockBindings = !! blockEditContext[ blockBindingsKey ];
131
- const bindingsStyle =
132
- hasBlockBindings && canBindBlock( name )
133
- ? {
134
- '--wp-admin-theme-color': 'var(--wp-block-synced-color)',
135
- '--wp-admin-theme-color--rgb':
136
- 'var(--wp-block-synced-color--rgb)',
137
- }
138
- : {};
131
+ const bindingsStyle = hasBlockBindings
132
+ ? {
133
+ '--wp-admin-theme-color': 'var(--wp-block-synced-color)',
134
+ '--wp-admin-theme-color--rgb':
135
+ 'var(--wp-block-synced-color--rgb)',
136
+ }
137
+ : {};
139
138
 
140
139
  // Ensures it warns only inside the `edit` implementation for the block.
141
140
  if ( blockApiVersion < 2 && clientId === blockEditContext.clientId ) {
@@ -185,6 +184,7 @@ export function useBlockProps( props = {}, { __unstableIsHtml } = {} ) {
185
184
  'has-negative-margin': hasNegativeMargin,
186
185
  'is-content-locked-temporarily-editing-as-blocks':
187
186
  isTemporarilyEditingAsBlocks,
187
+ 'is-block-hidden': isBlockHidden,
188
188
  },
189
189
  className,
190
190
  props.className,
@@ -6,6 +6,7 @@ import {
6
6
  MenuGroup,
7
7
  __experimentalStyleProvider as StyleProvider,
8
8
  } from '@wordpress/components';
9
+ import { hasBlockSupport } from '@wordpress/blocks';
9
10
  import { useSelect } from '@wordpress/data';
10
11
 
11
12
  /**
@@ -20,13 +21,20 @@ import { store as blockEditorStore } from '../../store';
20
21
  import BlockModeToggle from '../block-settings-menu/block-mode-toggle';
21
22
  import { ModifyContentLockMenuItem } from '../content-lock';
22
23
  import { BlockRenameControl, useBlockRename } from '../block-rename';
24
+ import { BlockVisibilityMenuItem } from '../block-visibility';
23
25
 
24
26
  const { Fill, Slot } = createSlotFill( 'BlockSettingsMenuControls' );
25
27
 
26
28
  const BlockSettingsMenuControlsSlot = ( { fillProps, clientIds = null } ) => {
27
- const { selectedBlocks, selectedClientIds, isContentOnly } = useSelect(
29
+ const {
30
+ selectedBlocks,
31
+ selectedClientIds,
32
+ isContentOnly,
33
+ canToggleSelectedBlocksVisibility,
34
+ } = useSelect(
28
35
  ( select ) => {
29
36
  const {
37
+ getBlocksByClientId,
30
38
  getBlockNamesByClientId,
31
39
  getSelectedBlockClientIds,
32
40
  getBlockEditingMode,
@@ -38,6 +46,11 @@ const BlockSettingsMenuControlsSlot = ( { fillProps, clientIds = null } ) => {
38
46
  selectedClientIds: ids,
39
47
  isContentOnly:
40
48
  getBlockEditingMode( ids[ 0 ] ) === 'contentOnly',
49
+ canToggleSelectedBlocksVisibility: getBlocksByClientId(
50
+ ids
51
+ ).every( ( block ) =>
52
+ hasBlockSupport( block.name, 'blockVisibility', true )
53
+ ),
41
54
  };
42
55
  },
43
56
  [ clientIds ]
@@ -49,6 +62,8 @@ const BlockSettingsMenuControlsSlot = ( { fillProps, clientIds = null } ) => {
49
62
  selectedClientIds.length === 1 && canLock && ! isContentOnly;
50
63
  const showRenameButton =
51
64
  selectedClientIds.length === 1 && canRename && ! isContentOnly;
65
+ const showVisibilityButton =
66
+ canToggleSelectedBlocksVisibility && ! isContentOnly;
52
67
 
53
68
  // Check if current selection of blocks is Groupable or Ungroupable
54
69
  // and pass this props down to ConvertToGroupButton.
@@ -93,6 +108,11 @@ const BlockSettingsMenuControlsSlot = ( { fillProps, clientIds = null } ) => {
93
108
  clientId={ selectedClientIds[ 0 ] }
94
109
  />
95
110
  ) }
111
+ { showVisibilityButton && (
112
+ <BlockVisibilityMenuItem
113
+ clientIds={ selectedClientIds }
114
+ />
115
+ ) }
96
116
  { fills }
97
117
  { selectedClientIds.length === 1 && (
98
118
  <ModifyContentLockMenuItem
@@ -28,6 +28,7 @@ import BlockControls from '../block-controls';
28
28
  import __unstableBlockToolbarLastItem from './block-toolbar-last-item';
29
29
  import BlockSettingsMenu from '../block-settings-menu';
30
30
  import { BlockLockToolbar } from '../block-lock';
31
+ import { BlockVisibilityToolbar } from '../block-visibility';
31
32
  import { BlockGroupToolbar } from '../convert-to-group-buttons';
32
33
  import BlockEditVisuallyButton from '../block-edit-visually-button';
33
34
  import { useShowHoveredOrFocusedGestures } from './utils';
@@ -73,6 +74,7 @@ export function PrivateBlockToolbar( {
73
74
  showSlots,
74
75
  showGroupButtons,
75
76
  showLockButtons,
77
+ showBlockVisibilityButton,
76
78
  showSwitchSectionStyleButton,
77
79
  hasFixedToolbar,
78
80
  isNavigationMode,
@@ -163,6 +165,7 @@ export function PrivateBlockToolbar( {
163
165
  showSlots: ! _isZoomOut,
164
166
  showGroupButtons: ! _isZoomOut,
165
167
  showLockButtons: ! _isZoomOut,
168
+ showBlockVisibilityButton: ! _isZoomOut,
166
169
  showSwitchSectionStyleButton: _showSwitchSectionStyleButton,
167
170
  hasFixedToolbar: getSettings().hasFixedToolbar,
168
171
  isNavigationMode: isNavigationModeEnabled,
@@ -227,6 +230,12 @@ export function PrivateBlockToolbar( {
227
230
  >
228
231
  <ToolbarGroup className="block-editor-block-toolbar__block-controls">
229
232
  <BlockSwitcher clientIds={ blockClientIds } />
233
+ { isDefaultEditingMode &&
234
+ showBlockVisibilityButton && (
235
+ <BlockVisibilityToolbar
236
+ clientIds={ blockClientIds }
237
+ />
238
+ ) }
230
239
  { ! isMultiToolbar &&
231
240
  isDefaultEditingMode &&
232
241
  showLockButtons && (
@@ -0,0 +1,2 @@
1
+ export { default as BlockVisibilityMenuItem } from './menu-item';
2
+ export { default as BlockVisibilityToolbar } from './toolbar';
@@ -0,0 +1,53 @@
1
+ /**
2
+ * WordPress dependencies
3
+ */
4
+ import { __ } from '@wordpress/i18n';
5
+ import { MenuItem } from '@wordpress/components';
6
+ import { seen, unseen } from '@wordpress/icons';
7
+ import { useSelect, useDispatch } from '@wordpress/data';
8
+
9
+ /**
10
+ * Internal dependencies
11
+ */
12
+ import { cleanEmptyObject } from '../../hooks/utils';
13
+ import { store as blockEditorStore } from '../../store';
14
+
15
+ export default function BlockVisibilityMenuItem( { clientIds } ) {
16
+ const { updateBlockAttributes } = useDispatch( blockEditorStore );
17
+ const blocks = useSelect(
18
+ ( select ) => {
19
+ return select( blockEditorStore ).getBlocksByClientId( clientIds );
20
+ },
21
+ [ clientIds ]
22
+ );
23
+
24
+ const hasHiddenBlock = blocks.some(
25
+ ( block ) => block.attributes.metadata?.blockVisibility === false
26
+ );
27
+
28
+ const toggleBlockVisibility = () => {
29
+ const attributesByClientId = Object.fromEntries(
30
+ blocks?.map( ( { clientId, attributes } ) => [
31
+ clientId,
32
+ {
33
+ metadata: cleanEmptyObject( {
34
+ ...attributes?.metadata,
35
+ blockVisibility: hasHiddenBlock ? undefined : false,
36
+ } ),
37
+ },
38
+ ] )
39
+ );
40
+ updateBlockAttributes( clientIds, attributesByClientId, {
41
+ uniqueByBlock: true,
42
+ } );
43
+ };
44
+
45
+ return (
46
+ <MenuItem
47
+ icon={ hasHiddenBlock ? seen : unseen }
48
+ onClick={ toggleBlockVisibility }
49
+ >
50
+ { hasHiddenBlock ? __( 'Show' ) : __( 'Hide' ) }
51
+ </MenuItem>
52
+ );
53
+ }
@@ -0,0 +1,88 @@
1
+ /**
2
+ * WordPress dependencies
3
+ */
4
+ import { __ } from '@wordpress/i18n';
5
+ import { ToolbarButton, ToolbarGroup } from '@wordpress/components';
6
+ import { useRef, useEffect } from '@wordpress/element';
7
+ import { seen, unseen } from '@wordpress/icons';
8
+ import { useSelect, useDispatch } from '@wordpress/data';
9
+ import { hasBlockSupport } from '@wordpress/blocks';
10
+
11
+ /**
12
+ * Internal dependencies
13
+ */
14
+ import { store as blockEditorStore } from '../../store';
15
+ import { cleanEmptyObject } from '../../hooks/utils';
16
+
17
+ export default function BlockVisibilityToolbar( { clientIds } ) {
18
+ const { blocks, canToggleBlockVisibility } = useSelect(
19
+ ( select ) => {
20
+ const { getBlockName, getBlocksByClientId } =
21
+ select( blockEditorStore );
22
+ const _blocks = getBlocksByClientId( clientIds );
23
+ return {
24
+ blocks: _blocks,
25
+ canToggleBlockVisibility: _blocks.every( ( { clientId } ) =>
26
+ hasBlockSupport(
27
+ getBlockName( clientId ),
28
+ 'blockVisibility',
29
+ true
30
+ )
31
+ ),
32
+ };
33
+ },
34
+ [ clientIds ]
35
+ );
36
+
37
+ const hasHiddenBlock = blocks.some(
38
+ ( block ) => block.attributes.metadata?.blockVisibility === false
39
+ );
40
+
41
+ const hasBlockVisibilityButtonShownRef = useRef( false );
42
+ const { updateBlockAttributes } = useDispatch( blockEditorStore );
43
+
44
+ // If the block visibility button has been shown, we don't want to
45
+ // remove it from the toolbar until the toolbar is rendered again
46
+ // without it. Removing it beforehand can cause focus loss issues.
47
+ // It needs to return focus from whence it came, and to do that,
48
+ // we need to leave the button in the toolbar.
49
+ useEffect( () => {
50
+ if ( hasHiddenBlock ) {
51
+ hasBlockVisibilityButtonShownRef.current = true;
52
+ }
53
+ }, [ hasHiddenBlock ] );
54
+
55
+ if ( ! hasHiddenBlock && ! hasBlockVisibilityButtonShownRef.current ) {
56
+ return null;
57
+ }
58
+
59
+ const toggleBlockVisibility = () => {
60
+ const attributesByClientId = Object.fromEntries(
61
+ blocks?.map( ( { clientId, attributes } ) => [
62
+ clientId,
63
+ {
64
+ metadata: cleanEmptyObject( {
65
+ ...attributes?.metadata,
66
+ blockVisibility: hasHiddenBlock ? undefined : false,
67
+ } ),
68
+ },
69
+ ] )
70
+ );
71
+ updateBlockAttributes( clientIds, attributesByClientId, {
72
+ uniqueByBlock: true,
73
+ } );
74
+ };
75
+
76
+ return (
77
+ <>
78
+ <ToolbarGroup className="block-editor-block-lock-toolbar">
79
+ <ToolbarButton
80
+ disabled={ ! canToggleBlockVisibility }
81
+ icon={ hasHiddenBlock ? unseen : seen }
82
+ label={ hasHiddenBlock ? __( 'Hidden' ) : __( 'Visible' ) }
83
+ onClick={ toggleBlockVisibility }
84
+ />
85
+ </ToolbarGroup>
86
+ </>
87
+ );
88
+ }
@@ -6,6 +6,7 @@ import {
6
6
  Tooltip,
7
7
  privateApis as componentsPrivateApis,
8
8
  } from '@wordpress/components';
9
+ import { useEffect, useState } from '@wordpress/element';
9
10
  import { store as preferencesStore } from '@wordpress/preferences';
10
11
  import { useSelect } from '@wordpress/data';
11
12
 
@@ -43,9 +44,40 @@ export default function InspectorControlsTabs( {
43
44
  ? TAB_LIST_VIEW.name
44
45
  : undefined;
45
46
 
47
+ const [ selectedTabId, setSelectedTabId ] = useState(
48
+ initialTabName ?? tabs[ 0 ]?.name
49
+ );
50
+
51
+ // When the active tab is not amongst the available `tabs`, it indicates
52
+ // the list of tabs was changed dynamically with the active one being
53
+ // removed. Set the active tab back to the first tab.
54
+ useEffect( () => {
55
+ // Skip this behavior if `initialTabName` is supplied. In the navigation
56
+ // block, the list view tab isn't present in `tabs` initially. The early
57
+ // return here prevents the dynamic behavior that follows from overriding
58
+ // `initialTabName`.
59
+ if ( initialTabName ) {
60
+ return;
61
+ }
62
+
63
+ if ( tabs?.length && selectedTabId ) {
64
+ const activeTab = tabs.find(
65
+ ( tab ) => tab.name === selectedTabId
66
+ );
67
+ if ( ! activeTab ) {
68
+ setSelectedTabId( tabs[ 0 ].name );
69
+ }
70
+ }
71
+ }, [ tabs, selectedTabId, initialTabName ] );
72
+
46
73
  return (
47
74
  <div className="block-editor-block-inspector__tabs">
48
- <Tabs defaultTabId={ initialTabName } key={ clientId }>
75
+ <Tabs
76
+ defaultTabId={ initialTabName }
77
+ selectedTabId={ selectedTabId }
78
+ onSelect={ setSelectedTabId }
79
+ key={ clientId }
80
+ >
49
81
  <Tabs.TabList>
50
82
  { tabs.map( ( tab ) =>
51
83
  showIconLabels ? (
@@ -488,7 +488,7 @@ function LinkControl( {
488
488
  className="block-editor-link-control__search-submit"
489
489
  aria-disabled={ isDisabled }
490
490
  >
491
- { __( 'Save' ) }
491
+ { __( 'Apply' ) }
492
492
  </Button>
493
493
  </HStack>
494
494
  ) }
@@ -795,7 +795,7 @@ describe( 'Manual link entry', () => {
795
795
  }
796
796
 
797
797
  const submitButton = screen.queryByRole( 'button', {
798
- name: 'Save',
798
+ name: 'Apply',
799
799
  } );
800
800
 
801
801
  // Verify the submission UI is disabled.
@@ -1035,7 +1035,7 @@ describe( 'Link submission', () => {
1035
1035
  expect( createSubmitButton ).not.toBeInTheDocument();
1036
1036
 
1037
1037
  const editSubmitButton = screen.getByRole( 'button', {
1038
- name: 'Save',
1038
+ name: 'Apply',
1039
1039
  } );
1040
1040
 
1041
1041
  expect( editSubmitButton ).toBeVisible();
@@ -2033,7 +2033,7 @@ describe( 'Addition Settings UI', () => {
2033
2033
 
2034
2034
  // check that the "Apply" button is disabled by default.
2035
2035
  const submitButton = screen.queryByRole( 'button', {
2036
- name: 'Save',
2036
+ name: 'Apply',
2037
2037
  } );
2038
2038
 
2039
2039
  expect( submitButton ).toHaveAttribute( 'aria-disabled', 'true' );
@@ -2489,7 +2489,7 @@ describe( 'Controlling link title text', () => {
2489
2489
  expect( textInput ).toHaveValue( textValue );
2490
2490
 
2491
2491
  const submitButton = screen.queryByRole( 'button', {
2492
- name: 'Save',
2492
+ name: 'Apply',
2493
2493
  } );
2494
2494
 
2495
2495
  await user.click( submitButton );
@@ -12,9 +12,10 @@ import {
12
12
  privateApis as componentsPrivateApis,
13
13
  } from '@wordpress/components';
14
14
  import { forwardRef } from '@wordpress/element';
15
- import { Icon, lockSmall as lock, pinSmall } from '@wordpress/icons';
15
+ import { Icon, lockSmall as lock, pinSmall, unseen } from '@wordpress/icons';
16
16
  import { SPACE, ENTER } from '@wordpress/keycodes';
17
17
  import { useSelect } from '@wordpress/data';
18
+ import { hasBlockSupport } from '@wordpress/blocks';
18
19
 
19
20
  /**
20
21
  * Internal dependencies
@@ -27,6 +28,7 @@ import { useBlockLock } from '../block-lock';
27
28
  import useListViewImages from './use-list-view-images';
28
29
  import { store as blockEditorStore } from '../../store';
29
30
  import { unlock } from '../../lock-unlock';
31
+
30
32
  const { Badge } = unlock( componentsPrivateApis );
31
33
 
32
34
  function ListViewBlockSelectButton(
@@ -53,15 +55,31 @@ function ListViewBlockSelectButton(
53
55
  context: 'list-view',
54
56
  } );
55
57
  const { isLocked } = useBlockLock( clientId );
56
- const { isContentOnly } = useSelect(
57
- ( select ) => ( {
58
- isContentOnly:
59
- select( blockEditorStore ).getBlockEditingMode( clientId ) ===
60
- 'contentOnly',
61
- } ),
62
- [ clientId ]
63
- );
58
+ const { canToggleBlockVisibility, isBlockHidden, isContentOnly } =
59
+ useSelect(
60
+ ( select ) => {
61
+ const { getBlockName } = select( blockEditorStore );
62
+ const { isBlockHidden: _isBlockHidden } = unlock(
63
+ select( blockEditorStore )
64
+ );
65
+ return {
66
+ canToggleBlockVisibility: hasBlockSupport(
67
+ getBlockName( clientId ),
68
+ 'blockVisibility',
69
+ true
70
+ ),
71
+ isBlockHidden: _isBlockHidden( clientId ),
72
+ isContentOnly:
73
+ select( blockEditorStore ).getBlockEditingMode(
74
+ clientId
75
+ ) === 'contentOnly',
76
+ };
77
+ },
78
+ [ clientId ]
79
+ );
64
80
  const shouldShowLockIcon = isLocked && ! isContentOnly;
81
+ const shouldShowBlockVisibilityIcon =
82
+ canToggleBlockVisibility && isBlockHidden;
65
83
  const isSticky = blockInformation?.positionType === 'sticky';
66
84
  const images = useListViewImages( { clientId, isExpanded } );
67
85
 
@@ -147,6 +165,11 @@ function ListViewBlockSelectButton(
147
165
  ) ) }
148
166
  </span>
149
167
  ) : null }
168
+ { shouldShowBlockVisibilityIcon && (
169
+ <span className="block-editor-list-view-block-select-button__block-visibility">
170
+ <Icon icon={ unseen } />
171
+ </span>
172
+ ) }
150
173
  { shouldShowLockIcon && (
151
174
  <span className="block-editor-list-view-block-select-button__lock">
152
175
  <Icon icon={ lock } />
@@ -119,20 +119,25 @@ function ListViewBlock( {
119
119
 
120
120
  const pasteStyles = usePasteStyles();
121
121
 
122
- const { block, blockName, allowRightClickOverrides } = useSelect(
123
- ( select ) => {
124
- const { getBlock, getBlockName, getSettings } =
125
- select( blockEditorStore );
126
-
127
- return {
128
- block: getBlock( clientId ),
129
- blockName: getBlockName( clientId ),
130
- allowRightClickOverrides:
131
- getSettings().allowRightClickOverrides,
132
- };
133
- },
134
- [ clientId ]
135
- );
122
+ const { block, blockName, allowRightClickOverrides, isBlockHidden } =
123
+ useSelect(
124
+ ( select ) => {
125
+ const { getBlock, getBlockName, getSettings } =
126
+ select( blockEditorStore );
127
+ const { isBlockHidden: _isBlockHidden } = unlock(
128
+ select( blockEditorStore )
129
+ );
130
+
131
+ return {
132
+ block: getBlock( clientId ),
133
+ blockName: getBlockName( clientId ),
134
+ allowRightClickOverrides:
135
+ getSettings().allowRightClickOverrides,
136
+ isBlockHidden: _isBlockHidden( clientId ),
137
+ };
138
+ },
139
+ [ clientId ]
140
+ );
136
141
 
137
142
  const showBlockActions =
138
143
  // When a block hides its toolbar it also hides the block settings menu,
@@ -484,6 +489,10 @@ function ListViewBlock( {
484
489
  isLocked
485
490
  );
486
491
 
492
+ const blockVisibilityDescription = isBlockHidden
493
+ ? __( 'Block is hidden.' )
494
+ : null;
495
+
487
496
  const hasSiblings = siblingBlockCount > 0;
488
497
  const hasRenderedMovers = showBlockMovers && hasSiblings;
489
498
  const moverCellClassName = clsx(
@@ -581,6 +590,7 @@ function ListViewBlock( {
581
590
  { [
582
591
  blockPositionDescription,
583
592
  blockPropertiesDescription,
593
+ blockVisibilityDescription,
584
594
  ]
585
595
  .filter( Boolean )
586
596
  .join( ' ' ) }
@@ -412,6 +412,7 @@
412
412
  color: $white;
413
413
  }
414
414
 
415
+ .block-editor-list-view-block-select-button__block-visibility,
415
416
  .block-editor-list-view-block-select-button__lock,
416
417
  .block-editor-list-view-block-select-button__sticky {
417
418
  line-height: 0;
@@ -119,7 +119,7 @@ describe( 'General media replace flow', () => {
119
119
 
120
120
  await user.click(
121
121
  screen.getByRole( 'button', {
122
- name: 'Save',
122
+ name: 'Apply',
123
123
  } )
124
124
  );
125
125
 
@@ -39,7 +39,6 @@ import FormatEdit from './format-edit';
39
39
  import { getAllowedFormats } from './utils';
40
40
  import { Content, valueToHTMLString } from './content';
41
41
  import { withDeprecations } from './with-deprecations';
42
- import { canBindBlock } from '../../utils/block-bindings';
43
42
  import BlockContext from '../block-context';
44
43
 
45
44
  export const keyboardShortcutContext = createContext();
@@ -177,9 +176,14 @@ export function RichTextWrapper(
177
176
 
178
177
  const { disableBoundBlock, bindingsPlaceholder, bindingsLabel } = useSelect(
179
178
  ( select ) => {
179
+ const { __experimentalBlockBindingsSupportedAttributes } =
180
+ select( blockEditorStore ).getSettings();
181
+
180
182
  if (
181
183
  ! blockBindings?.[ identifier ] ||
182
- ! canBindBlock( blockName )
184
+ ! (
185
+ blockName in __experimentalBlockBindingsSupportedAttributes
186
+ )
183
187
  ) {
184
188
  return {};
185
189
  }
@@ -9,6 +9,7 @@ import {
9
9
  } from '@wordpress/compose';
10
10
  import { isRTL } from '@wordpress/i18n';
11
11
  import {
12
+ hasBlockSupport,
12
13
  isUnmodifiedDefaultBlock as getIsUnmodifiedDefaultBlock,
13
14
  store as blocksStore,
14
15
  } from '@wordpress/blocks';
@@ -410,7 +411,19 @@ export default function useBlockDropZone( {
410
411
  return;
411
412
  }
412
413
 
413
- const blocks = getBlocks( targetRootClientId );
414
+ const blocks = getBlocks( targetRootClientId )
415
+ // Filter out blocks that are hidden
416
+ .filter( ( block ) => {
417
+ return ! (
418
+ hasBlockSupport(
419
+ block.name,
420
+ 'blockVisibility',
421
+ true
422
+ ) &&
423
+ block.attributes?.metadata?.blockVisibility ===
424
+ false
425
+ );
426
+ } );
414
427
 
415
428
  // The block list is empty, don't show the insertion point but still allow dropping.
416
429
  if ( blocks.length === 0 ) {