@wordpress/block-editor 15.10.1-next.ba3aee3a2.0 → 15.10.1-next.v.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 (159) hide show
  1. package/build/components/block-bindings/attribute-control.cjs +1 -1
  2. package/build/components/block-bindings/attribute-control.cjs.map +1 -1
  3. package/build/components/block-bindings/source-fields-list.cjs +1 -1
  4. package/build/components/block-bindings/source-fields-list.cjs.map +1 -1
  5. package/build/components/block-tools/index.cjs +82 -70
  6. package/build/components/block-tools/index.cjs.map +2 -2
  7. package/build/components/block-visibility/block-visibility-info.cjs +0 -59
  8. package/build/components/block-visibility/block-visibility-info.cjs.map +3 -3
  9. package/build/components/block-visibility/constants.cjs +10 -5
  10. package/build/components/block-visibility/constants.cjs.map +2 -2
  11. package/build/components/block-visibility/index.cjs +13 -5
  12. package/build/components/block-visibility/index.cjs.map +3 -3
  13. package/build/components/block-visibility/modal.cjs +397 -0
  14. package/build/components/block-visibility/modal.cjs.map +7 -0
  15. package/build/components/block-visibility/toolbar.cjs +1 -1
  16. package/build/components/block-visibility/toolbar.cjs.map +2 -2
  17. package/build/components/block-visibility/use-block-visibility.cjs +13 -17
  18. package/build/components/block-visibility/use-block-visibility.cjs.map +2 -2
  19. package/build/components/block-visibility/utils.cjs +81 -0
  20. package/build/components/block-visibility/utils.cjs.map +7 -0
  21. package/build/components/block-visibility/viewport-menu-item.cjs +61 -0
  22. package/build/components/block-visibility/viewport-menu-item.cjs.map +7 -0
  23. package/build/components/block-visibility/viewport-toolbar.cjs +89 -0
  24. package/build/components/block-visibility/viewport-toolbar.cjs.map +7 -0
  25. package/build/components/inner-blocks/use-inner-block-template-sync.cjs +1 -1
  26. package/build/components/inner-blocks/use-inner-block-template-sync.cjs.map +1 -1
  27. package/build/components/inserter/menu.cjs +6 -2
  28. package/build/components/inserter/menu.cjs.map +2 -2
  29. package/build/components/list-view/block-select-button.cjs +2 -2
  30. package/build/components/list-view/block-select-button.cjs.map +2 -2
  31. package/build/components/list-view/block.cjs +39 -22
  32. package/build/components/list-view/block.cjs.map +2 -2
  33. package/build/components/rich-text/index.cjs +1 -1
  34. package/build/components/rich-text/index.cjs.map +1 -1
  35. package/build/components/url-input/index.cjs +2 -0
  36. package/build/components/url-input/index.cjs.map +2 -2
  37. package/build/components/use-block-commands/index.cjs +1 -1
  38. package/build/components/use-block-commands/index.cjs.map +2 -2
  39. package/build/hooks/block-fields/index.cjs +75 -166
  40. package/build/hooks/block-fields/index.cjs.map +2 -2
  41. package/build/hooks/block-fields/link/index.cjs +13 -23
  42. package/build/hooks/block-fields/link/index.cjs.map +2 -2
  43. package/build/hooks/block-fields/media/index.cjs +32 -58
  44. package/build/hooks/block-fields/media/index.cjs.map +2 -2
  45. package/build/hooks/block-fields/rich-text/index.cjs +1 -5
  46. package/build/hooks/block-fields/rich-text/index.cjs.map +2 -2
  47. package/build/hooks/cross-origin-isolation.cjs +102 -0
  48. package/build/hooks/cross-origin-isolation.cjs.map +7 -0
  49. package/build/hooks/index.cjs +1 -0
  50. package/build/hooks/index.cjs.map +2 -2
  51. package/build/layouts/flex.cjs +6 -2
  52. package/build/layouts/flex.cjs.map +2 -2
  53. package/build/store/private-selectors.cjs +33 -1
  54. package/build/store/private-selectors.cjs.map +3 -3
  55. package/build/store/reducer.cjs +1 -1
  56. package/build/store/reducer.cjs.map +1 -1
  57. package/build/store/selectors.cjs +7 -8
  58. package/build/store/selectors.cjs.map +2 -2
  59. package/build-module/components/block-bindings/attribute-control.mjs +1 -1
  60. package/build-module/components/block-bindings/attribute-control.mjs.map +1 -1
  61. package/build-module/components/block-bindings/source-fields-list.mjs +1 -1
  62. package/build-module/components/block-bindings/source-fields-list.mjs.map +1 -1
  63. package/build-module/components/block-tools/index.mjs +85 -73
  64. package/build-module/components/block-tools/index.mjs.map +2 -2
  65. package/build-module/components/block-visibility/block-visibility-info.mjs +0 -59
  66. package/build-module/components/block-visibility/block-visibility-info.mjs.map +3 -3
  67. package/build-module/components/block-visibility/constants.mjs +8 -4
  68. package/build-module/components/block-visibility/constants.mjs.map +2 -2
  69. package/build-module/components/block-visibility/index.mjs +13 -6
  70. package/build-module/components/block-visibility/index.mjs.map +2 -2
  71. package/build-module/components/block-visibility/modal.mjs +384 -0
  72. package/build-module/components/block-visibility/modal.mjs.map +7 -0
  73. package/build-module/components/block-visibility/toolbar.mjs +1 -1
  74. package/build-module/components/block-visibility/toolbar.mjs.map +2 -2
  75. package/build-module/components/block-visibility/use-block-visibility.mjs +13 -13
  76. package/build-module/components/block-visibility/use-block-visibility.mjs.map +2 -2
  77. package/build-module/components/block-visibility/utils.mjs +55 -0
  78. package/build-module/components/block-visibility/utils.mjs.map +7 -0
  79. package/build-module/components/block-visibility/viewport-menu-item.mjs +40 -0
  80. package/build-module/components/block-visibility/viewport-menu-item.mjs.map +7 -0
  81. package/build-module/components/block-visibility/viewport-toolbar.mjs +68 -0
  82. package/build-module/components/block-visibility/viewport-toolbar.mjs.map +7 -0
  83. package/build-module/components/inner-blocks/use-inner-block-template-sync.mjs +1 -1
  84. package/build-module/components/inner-blocks/use-inner-block-template-sync.mjs.map +1 -1
  85. package/build-module/components/inserter/menu.mjs +6 -2
  86. package/build-module/components/inserter/menu.mjs.map +2 -2
  87. package/build-module/components/list-view/block-select-button.mjs +2 -2
  88. package/build-module/components/list-view/block-select-button.mjs.map +2 -2
  89. package/build-module/components/list-view/block.mjs +39 -22
  90. package/build-module/components/list-view/block.mjs.map +2 -2
  91. package/build-module/components/rich-text/index.mjs +1 -1
  92. package/build-module/components/rich-text/index.mjs.map +1 -1
  93. package/build-module/components/url-input/index.mjs +2 -0
  94. package/build-module/components/url-input/index.mjs.map +2 -2
  95. package/build-module/components/use-block-commands/index.mjs +1 -1
  96. package/build-module/components/use-block-commands/index.mjs.map +2 -2
  97. package/build-module/hooks/block-fields/index.mjs +75 -166
  98. package/build-module/hooks/block-fields/index.mjs.map +2 -2
  99. package/build-module/hooks/block-fields/link/index.mjs +13 -23
  100. package/build-module/hooks/block-fields/link/index.mjs.map +2 -2
  101. package/build-module/hooks/block-fields/media/index.mjs +32 -58
  102. package/build-module/hooks/block-fields/media/index.mjs.map +2 -2
  103. package/build-module/hooks/block-fields/rich-text/index.mjs +1 -5
  104. package/build-module/hooks/block-fields/rich-text/index.mjs.map +2 -2
  105. package/build-module/hooks/cross-origin-isolation.mjs +100 -0
  106. package/build-module/hooks/cross-origin-isolation.mjs.map +7 -0
  107. package/build-module/hooks/index.mjs +1 -0
  108. package/build-module/hooks/index.mjs.map +2 -2
  109. package/build-module/layouts/flex.mjs +6 -2
  110. package/build-module/layouts/flex.mjs.map +2 -2
  111. package/build-module/store/private-selectors.mjs +34 -1
  112. package/build-module/store/private-selectors.mjs.map +2 -2
  113. package/build-module/store/reducer.mjs +1 -1
  114. package/build-module/store/reducer.mjs.map +1 -1
  115. package/build-module/store/selectors.mjs +7 -8
  116. package/build-module/store/selectors.mjs.map +2 -2
  117. package/build-style/content-rtl.css +4 -1
  118. package/build-style/content.css +4 -1
  119. package/build-style/style-rtl.css +48 -0
  120. package/build-style/style.css +48 -0
  121. package/package.json +39 -39
  122. package/src/components/block-bindings/attribute-control.js +1 -1
  123. package/src/components/block-bindings/source-fields-list.js +1 -1
  124. package/src/components/block-list/content.scss +4 -1
  125. package/src/components/block-tools/index.js +45 -33
  126. package/src/components/block-visibility/block-visibility-info.js +0 -1
  127. package/src/components/block-visibility/constants.js +7 -3
  128. package/src/components/block-visibility/index.js +21 -3
  129. package/src/components/block-visibility/modal.js +358 -0
  130. package/src/components/block-visibility/style.scss +58 -0
  131. package/src/components/block-visibility/test/use-block-visibility.js +12 -56
  132. package/src/components/block-visibility/test/utils.js +266 -0
  133. package/src/components/block-visibility/toolbar.js +1 -1
  134. package/src/components/block-visibility/use-block-visibility.js +18 -21
  135. package/src/components/block-visibility/utils.js +95 -0
  136. package/src/components/block-visibility/viewport-menu-item.js +42 -0
  137. package/src/components/block-visibility/viewport-toolbar.js +88 -0
  138. package/src/components/inner-blocks/use-inner-block-template-sync.js +1 -1
  139. package/src/components/inserter/menu.js +6 -2
  140. package/src/components/list-view/block-select-button.js +2 -2
  141. package/src/components/list-view/block.js +47 -25
  142. package/src/components/rich-text/index.js +1 -1
  143. package/src/components/url-input/index.js +2 -0
  144. package/src/components/use-block-commands/index.js +4 -3
  145. package/src/hooks/block-fields/index.js +104 -224
  146. package/src/hooks/block-fields/link/index.js +13 -39
  147. package/src/hooks/block-fields/media/index.js +31 -90
  148. package/src/hooks/block-fields/rich-text/index.js +1 -5
  149. package/src/hooks/block-fields/styles.scss +2 -0
  150. package/src/hooks/cross-origin-isolation.js +143 -0
  151. package/src/hooks/index.js +1 -0
  152. package/src/layouts/flex.js +8 -3
  153. package/src/layouts/test/flex.js +53 -0
  154. package/src/store/private-selectors.js +64 -1
  155. package/src/store/reducer.js +1 -1
  156. package/src/store/selectors.js +7 -9
  157. package/src/store/test/private-selectors.js +80 -0
  158. package/src/style.scss +1 -0
  159. package/src/components/block-visibility/styles.scss +0 -10
@@ -0,0 +1,266 @@
1
+ /**
2
+ * Internal dependencies
3
+ */
4
+ import {
5
+ getViewportCheckboxState,
6
+ getHideEverywhereCheckboxState,
7
+ } from '../utils';
8
+
9
+ describe( 'block-visibility utils', () => {
10
+ describe( 'getViewportCheckboxState', () => {
11
+ it( 'should return false for empty or invalid input', () => {
12
+ expect( getViewportCheckboxState( [], 'mobile' ) ).toBe( false );
13
+ expect( getViewportCheckboxState( null, 'mobile' ) ).toBe( false );
14
+ expect( getViewportCheckboxState( undefined, 'mobile' ) ).toBe(
15
+ false
16
+ );
17
+ } );
18
+
19
+ it( 'should return false when no blocks are hidden for viewport', () => {
20
+ const blocks = [
21
+ {
22
+ attributes: {
23
+ metadata: {
24
+ blockVisibility: {},
25
+ },
26
+ },
27
+ },
28
+ {
29
+ attributes: {},
30
+ },
31
+ ];
32
+ expect( getViewportCheckboxState( blocks, 'mobile' ) ).toBe(
33
+ false
34
+ );
35
+ } );
36
+
37
+ it( 'should return false when all blocks are hidden everywhere (blockVisibility=false not handled)', () => {
38
+ const blocks = [
39
+ {
40
+ attributes: {
41
+ metadata: {
42
+ blockVisibility: false,
43
+ },
44
+ },
45
+ },
46
+ {
47
+ attributes: {
48
+ metadata: {
49
+ blockVisibility: false,
50
+ },
51
+ },
52
+ },
53
+ ];
54
+ // Note: isBlockHiddenForViewport doesn't check for blockVisibility === false,
55
+ // so it treats false as a non-object and returns false (not hidden)
56
+ expect( getViewportCheckboxState( blocks, 'mobile' ) ).toBe(
57
+ false
58
+ );
59
+ expect( getViewportCheckboxState( blocks, 'tablet' ) ).toBe(
60
+ false
61
+ );
62
+ expect( getViewportCheckboxState( blocks, 'desktop' ) ).toBe(
63
+ false
64
+ );
65
+ } );
66
+
67
+ it( 'should return null when some blocks are hidden for viewport', () => {
68
+ // Suppress console.log from getViewportCheckboxState
69
+ const consoleSpy = jest
70
+ .spyOn( console, 'log' )
71
+ .mockImplementation( () => {} );
72
+
73
+ const blocks = [
74
+ {
75
+ attributes: {
76
+ metadata: {
77
+ blockVisibility: {
78
+ mobile: false,
79
+ },
80
+ },
81
+ },
82
+ },
83
+ {
84
+ attributes: {
85
+ metadata: {
86
+ blockVisibility: {},
87
+ },
88
+ },
89
+ },
90
+ ];
91
+ expect( getViewportCheckboxState( blocks, 'mobile' ) ).toBe( null );
92
+
93
+ consoleSpy.mockRestore();
94
+ } );
95
+
96
+ it( 'should return false when some blocks have blockVisibility=false (not handled)', () => {
97
+ const blocks = [
98
+ {
99
+ attributes: {
100
+ metadata: {
101
+ blockVisibility: false,
102
+ },
103
+ },
104
+ },
105
+ {
106
+ attributes: {
107
+ metadata: {
108
+ blockVisibility: {},
109
+ },
110
+ },
111
+ },
112
+ ];
113
+ // Note: isBlockHiddenForViewport doesn't check for blockVisibility === false,
114
+ // so both blocks are treated as not hidden, resulting in false
115
+ expect( getViewportCheckboxState( blocks, 'mobile' ) ).toBe(
116
+ false
117
+ );
118
+ } );
119
+
120
+ it( 'should return false for invalid viewport', () => {
121
+ const block = {
122
+ attributes: {
123
+ metadata: {
124
+ blockVisibility: {
125
+ mobile: false,
126
+ },
127
+ },
128
+ },
129
+ };
130
+ expect( getViewportCheckboxState( [ block ], 'invalid' ) ).toBe(
131
+ false
132
+ );
133
+ } );
134
+
135
+ it( 'should return false when blockVisibility === true exists', () => {
136
+ // Test with blockVisibility=false (not handled by isBlockHiddenForViewport)
137
+ const blocks1 = [
138
+ {
139
+ attributes: {
140
+ metadata: {
141
+ blockVisibility: true,
142
+ },
143
+ },
144
+ },
145
+ {
146
+ attributes: {
147
+ metadata: {
148
+ blockVisibility: false,
149
+ },
150
+ },
151
+ },
152
+ ];
153
+ // First block is explicitly visible (true), second has blockVisibility=false
154
+ // which is not handled, so both are treated as not hidden
155
+ expect( getViewportCheckboxState( blocks1, 'mobile' ) ).toBe(
156
+ false
157
+ );
158
+
159
+ // Test with no metadata
160
+ const blocks2 = [
161
+ {
162
+ attributes: {
163
+ metadata: {
164
+ blockVisibility: true,
165
+ },
166
+ },
167
+ },
168
+ {
169
+ attributes: {},
170
+ },
171
+ ];
172
+ // Both blocks are not hidden (first is explicitly visible, second has no visibility set)
173
+ expect( getViewportCheckboxState( blocks2, 'mobile' ) ).toBe(
174
+ false
175
+ );
176
+ } );
177
+ } );
178
+
179
+ describe( 'getHideEverywhereCheckboxState', () => {
180
+ it( 'should return false for empty or invalid input', () => {
181
+ expect( getHideEverywhereCheckboxState( [] ) ).toBe( false );
182
+ expect( getHideEverywhereCheckboxState( null ) ).toBe( false );
183
+ expect( getHideEverywhereCheckboxState( undefined ) ).toBe( false );
184
+ } );
185
+
186
+ it( 'should return false when no blocks are hidden everywhere', () => {
187
+ const blocks = [
188
+ {
189
+ attributes: {
190
+ metadata: {
191
+ blockVisibility: {},
192
+ },
193
+ },
194
+ },
195
+ {
196
+ attributes: {},
197
+ },
198
+ ];
199
+ expect( getHideEverywhereCheckboxState( blocks ) ).toBe( false );
200
+ } );
201
+
202
+ it( 'should return true when all blocks are hidden everywhere', () => {
203
+ const blocks = [
204
+ {
205
+ attributes: {
206
+ metadata: {
207
+ blockVisibility: false,
208
+ },
209
+ },
210
+ },
211
+ {
212
+ attributes: {
213
+ metadata: {
214
+ blockVisibility: false,
215
+ },
216
+ },
217
+ },
218
+ ];
219
+ expect( getHideEverywhereCheckboxState( blocks ) ).toBe( true );
220
+ } );
221
+
222
+ it( 'should return null when some blocks are hidden everywhere', () => {
223
+ const blocks = [
224
+ {
225
+ attributes: {
226
+ metadata: {
227
+ blockVisibility: false,
228
+ },
229
+ },
230
+ },
231
+ {
232
+ attributes: {
233
+ metadata: {
234
+ blockVisibility: {},
235
+ },
236
+ },
237
+ },
238
+ ];
239
+ expect( getHideEverywhereCheckboxState( blocks ) ).toBe( null );
240
+ } );
241
+
242
+ it( 'should return false when blocks have viewport-specific visibility', () => {
243
+ const blocks = [
244
+ {
245
+ attributes: {
246
+ metadata: {
247
+ blockVisibility: {
248
+ mobile: false,
249
+ },
250
+ },
251
+ },
252
+ },
253
+ {
254
+ attributes: {
255
+ metadata: {
256
+ blockVisibility: {
257
+ tablet: false,
258
+ },
259
+ },
260
+ },
261
+ },
262
+ ];
263
+ expect( getHideEverywhereCheckboxState( blocks ) ).toBe( false );
264
+ } );
265
+ } );
266
+ } );
@@ -75,7 +75,7 @@ export default function BlockVisibilityToolbar( { clientIds } ) {
75
75
 
76
76
  return (
77
77
  <>
78
- <ToolbarGroup className="block-editor-block-lock-toolbar">
78
+ <ToolbarGroup>
79
79
  <ToolbarButton
80
80
  disabled={ ! canToggleBlockVisibility }
81
81
  icon={ hasHiddenBlock ? unseen : seen }
@@ -10,44 +10,41 @@ import { useMemo } from '@wordpress/element';
10
10
  import { BLOCK_VISIBILITY_VIEWPORTS } from './constants';
11
11
 
12
12
  /**
13
- * Determines if a block should be hidden based on visibility settings.
14
- *
15
- * Priority:
16
- * 1. Device type override (Mobile/Tablet) - uses device type to determine viewport
17
- * 2. Actual window size (Desktop mode) - uses viewport detection
13
+ * Returns information about the current block visibility state.
18
14
  *
19
15
  * @param {Object} options Parameters to avoid extra store subscriptions.
20
16
  * @param {Object|boolean} options.blockVisibility Block visibility metadata.
21
17
  * @param {string} options.deviceType Current device type ('desktop', 'tablet', 'mobile').
22
- * @return {Object} Object with `isBlockCurrentlyHidden` boolean property.
18
+ * @return {Object} Object with `isBlockCurrentlyHidden` and `currentViewport` boolean properties.
23
19
  */
24
- export function useBlockVisibility( options = {} ) {
20
+ export default function useBlockVisibility( options = {} ) {
25
21
  const {
26
22
  blockVisibility = undefined,
27
- deviceType = BLOCK_VISIBILITY_VIEWPORTS.desktop.value,
23
+ deviceType = BLOCK_VISIBILITY_VIEWPORTS.desktop.key,
28
24
  } = options;
29
25
 
30
26
  const isLargerThanMobile = useViewportMatch( 'mobile', '>=' ); // >= 480px
31
27
  const isLargerThanTablet = useViewportMatch( 'medium', '>=' ); // >= 782px
32
28
 
33
29
  /*
34
- * When Desktop is selected, use actual viewport detection.
35
- * When Mobile/Tablet is selected, override with device type.
30
+ * Priority:
31
+ * 1. Device type override (Mobile/Tablet) - uses device type to determine viewport
32
+ * 2. Actual window size (Desktop mode) - uses viewport detection
36
33
  */
37
34
  const currentViewport = useMemo( () => {
38
- if ( deviceType === BLOCK_VISIBILITY_VIEWPORTS.mobile.value ) {
39
- return BLOCK_VISIBILITY_VIEWPORTS.mobile.value;
35
+ if ( deviceType === BLOCK_VISIBILITY_VIEWPORTS.mobile.key ) {
36
+ return BLOCK_VISIBILITY_VIEWPORTS.mobile.key;
40
37
  }
41
- if ( deviceType === BLOCK_VISIBILITY_VIEWPORTS.tablet.value ) {
42
- return BLOCK_VISIBILITY_VIEWPORTS.tablet.value;
38
+ if ( deviceType === BLOCK_VISIBILITY_VIEWPORTS.tablet.key ) {
39
+ return BLOCK_VISIBILITY_VIEWPORTS.tablet.key;
43
40
  }
44
41
  if ( ! isLargerThanMobile ) {
45
- return BLOCK_VISIBILITY_VIEWPORTS.mobile.value;
42
+ return BLOCK_VISIBILITY_VIEWPORTS.mobile.key;
46
43
  }
47
44
  if ( isLargerThanMobile && ! isLargerThanTablet ) {
48
- return BLOCK_VISIBILITY_VIEWPORTS.tablet.value;
45
+ return BLOCK_VISIBILITY_VIEWPORTS.tablet.key;
49
46
  }
50
- return BLOCK_VISIBILITY_VIEWPORTS.desktop.value;
47
+ return BLOCK_VISIBILITY_VIEWPORTS.desktop.key;
51
48
  }, [ deviceType, isLargerThanMobile, isLargerThanTablet ] );
52
49
 
53
50
  // Determine if block is currently hidden.
@@ -66,8 +63,8 @@ export function useBlockVisibility( options = {} ) {
66
63
  return false;
67
64
  }, [ blockVisibility, currentViewport ] );
68
65
 
69
- return useMemo(
70
- () => ( { isBlockCurrentlyHidden, currentViewport } ),
71
- [ isBlockCurrentlyHidden, currentViewport ]
72
- );
66
+ return {
67
+ isBlockCurrentlyHidden,
68
+ currentViewport,
69
+ };
73
70
  }
@@ -0,0 +1,95 @@
1
+ /**
2
+ * Internal dependencies
3
+ */
4
+ import { BLOCK_VISIBILITY_VIEWPORT_ENTRIES } from './constants';
5
+
6
+ /**
7
+ * Checks if a block is hidden for a specific viewport.
8
+ *
9
+ * @param {Object} block The block to check.
10
+ * @param {string} viewport The viewport to check (e.g., 'mobile', 'tablet', 'desktop').
11
+ * @return {boolean} Whether the block is hidden for the viewport.
12
+ */
13
+ function isBlockHiddenForViewport( block, viewport ) {
14
+ if ( ! block ) {
15
+ return false;
16
+ }
17
+
18
+ const blockVisibility = block.attributes?.metadata?.blockVisibility;
19
+
20
+ // If explicitly visible everywhere (true), return false for all viewports.
21
+ if ( blockVisibility === true ) {
22
+ return false;
23
+ }
24
+
25
+ // If null or not an object, block is not hidden for any specific viewport.
26
+ if ( 'object' !== typeof blockVisibility ) {
27
+ return false;
28
+ }
29
+
30
+ // Check if the viewport is valid.
31
+ if (
32
+ ! BLOCK_VISIBILITY_VIEWPORT_ENTRIES.some(
33
+ ( [ , { key } ] ) => key === viewport
34
+ )
35
+ ) {
36
+ return false;
37
+ }
38
+
39
+ // Check if the specific viewport is hidden.
40
+ return blockVisibility[ viewport ] === false;
41
+ }
42
+
43
+ /**
44
+ * Gets the checkbox state for a viewport across multiple blocks.
45
+ * Returns `true` if all blocks are hidden, `null` if some are hidden, `false` if none are hidden.
46
+ *
47
+ * @param {Array} blocks Array of blocks to check.
48
+ * @param {string} viewport The viewport to check (e.g., 'mobile', 'tablet', 'desktop').
49
+ * @return {boolean|null} `true` if all hidden, `null` if some hidden, `false` if none hidden.
50
+ */
51
+ export function getViewportCheckboxState( blocks, viewport ) {
52
+ if ( ! blocks?.length ) {
53
+ return false;
54
+ }
55
+
56
+ const hiddenCount = blocks.filter( ( block ) =>
57
+ isBlockHiddenForViewport( block, viewport )
58
+ ).length;
59
+
60
+ if ( hiddenCount === 0 ) {
61
+ return false;
62
+ }
63
+ if ( hiddenCount === blocks.length ) {
64
+ return true;
65
+ }
66
+
67
+ return null; // Indeterminate: some hidden, some visible (normal mixed state)
68
+ }
69
+
70
+ /**
71
+ * Gets the checkbox state for "hide everywhere" across multiple blocks.
72
+ * Returns `true` if all blocks are hidden everywhere, `null` if some are hidden everywhere, `false` if none are.
73
+ *
74
+ * @param {Array} blocks Array of blocks to check.
75
+ * @return {boolean|null} `true` if all hidden everywhere, `null` if some hidden everywhere, `false` if none.
76
+ */
77
+ export function getHideEverywhereCheckboxState( blocks ) {
78
+ if ( ! blocks?.length ) {
79
+ return false;
80
+ }
81
+
82
+ const hiddenEverywhereCount = blocks.filter(
83
+ ( block ) =>
84
+ block && block.attributes?.metadata?.blockVisibility === false
85
+ ).length;
86
+
87
+ if ( hiddenEverywhereCount === 0 ) {
88
+ return false;
89
+ }
90
+ if ( hiddenEverywhereCount === blocks.length ) {
91
+ return true;
92
+ }
93
+
94
+ return null; // Indeterminate: some but not all
95
+ }
@@ -0,0 +1,42 @@
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 { useState } from '@wordpress/element';
8
+ import { useSelect } from '@wordpress/data';
9
+
10
+ /**
11
+ * Internal dependencies
12
+ */
13
+ import { BlockVisibilityModal } from './';
14
+ import { store as blockEditorStore } from '../../store';
15
+ import { unlock } from '../../lock-unlock';
16
+
17
+ export default function BlockVisibilityViewportMenuItem( { clientIds } ) {
18
+ const [ isModalOpen, setIsModalOpen ] = useState( false );
19
+ const areBlocksHiddenAnywhere = useSelect(
20
+ ( select ) =>
21
+ unlock( select( blockEditorStore ) ).areBlocksHiddenAnywhere(
22
+ clientIds
23
+ ),
24
+ [ clientIds ]
25
+ );
26
+ return (
27
+ <>
28
+ <MenuItem
29
+ icon={ areBlocksHiddenAnywhere ? unseen : seen }
30
+ onClick={ () => setIsModalOpen( true ) }
31
+ >
32
+ { areBlocksHiddenAnywhere ? __( 'Show' ) : __( 'Hide' ) }
33
+ </MenuItem>
34
+ { isModalOpen && (
35
+ <BlockVisibilityModal
36
+ clientIds={ clientIds }
37
+ onClose={ () => setIsModalOpen( false ) }
38
+ />
39
+ ) }
40
+ </>
41
+ );
42
+ }
@@ -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, useState } from '@wordpress/element';
7
+ import { seen, unseen } from '@wordpress/icons';
8
+ import { hasBlockSupport } from '@wordpress/blocks';
9
+ import { useSelect } from '@wordpress/data';
10
+
11
+ /**
12
+ * Internal dependencies
13
+ */
14
+ import { store as blockEditorStore } from '../../store';
15
+ import { BlockVisibilityModal } from './';
16
+ import { unlock } from '../../lock-unlock';
17
+
18
+ export default function BlockVisibilityViewportToolbar( { clientIds } ) {
19
+ const hasBlockVisibilityButtonShownRef = useRef( false );
20
+ const [ isModalOpen, setIsModalOpen ] = useState( false );
21
+ const { canToggleBlockVisibility, areBlocksHiddenAnywhere } = useSelect(
22
+ ( select ) => {
23
+ const {
24
+ getBlocksByClientId,
25
+ getBlockName,
26
+ areBlocksHiddenAnywhere: _areBlocksHiddenAnywhere,
27
+ } = unlock( select( blockEditorStore ) );
28
+ const _blocks = getBlocksByClientId( clientIds );
29
+ return {
30
+ canToggleBlockVisibility: _blocks.every( ( { clientId } ) =>
31
+ hasBlockSupport(
32
+ getBlockName( clientId ),
33
+ 'visibility',
34
+ true
35
+ )
36
+ ),
37
+ areBlocksHiddenAnywhere: _areBlocksHiddenAnywhere( clientIds ),
38
+ };
39
+ },
40
+
41
+ [ clientIds ]
42
+ );
43
+
44
+ /*
45
+ * If the block visibility button has been shown, we don't want to
46
+ * remove it from the toolbar until the toolbar is rendered again
47
+ * without it. Removing it beforehand can cause focus loss issues.
48
+ * It needs to return focus from whence it came, and to do that,
49
+ * we need to leave the button in the toolbar.
50
+ */
51
+ useEffect( () => {
52
+ if ( areBlocksHiddenAnywhere ) {
53
+ hasBlockVisibilityButtonShownRef.current = true;
54
+ }
55
+ }, [ areBlocksHiddenAnywhere ] );
56
+
57
+ if (
58
+ ! areBlocksHiddenAnywhere &&
59
+ ! hasBlockVisibilityButtonShownRef.current
60
+ ) {
61
+ return null;
62
+ }
63
+
64
+ return (
65
+ <>
66
+ <ToolbarGroup className="block-editor-block-visibility-toolbar">
67
+ <ToolbarButton
68
+ disabled={ ! canToggleBlockVisibility }
69
+ icon={ areBlocksHiddenAnywhere ? unseen : seen }
70
+ label={
71
+ areBlocksHiddenAnywhere
72
+ ? __( 'Hidden' )
73
+ : __( 'Visible' )
74
+ }
75
+ onClick={ () => setIsModalOpen( true ) }
76
+ aria-expanded={ isModalOpen }
77
+ aria-haspopup={ ! isModalOpen ? 'dialog' : undefined }
78
+ />
79
+ </ToolbarGroup>
80
+ { isModalOpen && (
81
+ <BlockVisibilityModal
82
+ clientIds={ clientIds }
83
+ onClose={ () => setIsModalOpen( false ) }
84
+ />
85
+ ) }
86
+ </>
87
+ );
88
+ }
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * External dependencies
3
3
  */
4
- import fastDeepEqual from 'fast-deep-equal/es6';
4
+ import fastDeepEqual from 'fast-deep-equal/es6/index.js';
5
5
 
6
6
  /**
7
7
  * WordPress dependencies
@@ -75,6 +75,8 @@ function InserterMenu(
75
75
  const [ selectedMediaCategory, setSelectedMediaCategory ] =
76
76
  useState( null );
77
77
  const isLargeViewport = useViewportMatch( 'large' );
78
+ const isMobileViewport = useViewportMatch( 'medium', '<' );
79
+ const maybeCloseInserter = isMobileViewport ? onClose : NOOP;
78
80
 
79
81
  function getInitialTab() {
80
82
  if ( __experimentalInitialTab ) {
@@ -114,6 +116,7 @@ function InserterMenu(
114
116
  _rootClientId
115
117
  );
116
118
  onSelect( blocks );
119
+ maybeCloseInserter();
117
120
 
118
121
  // Check for focus loss due to filtering blocks by selected block type
119
122
  window.requestAnimationFrame( () => {
@@ -128,7 +131,7 @@ function InserterMenu(
128
131
  }
129
132
  } );
130
133
  },
131
- [ onInsertBlocks, onSelect, shouldFocusBlock ]
134
+ [ onInsertBlocks, maybeCloseInserter, onSelect, ref, shouldFocusBlock ]
132
135
  );
133
136
 
134
137
  const onInsertPattern = useCallback(
@@ -136,8 +139,9 @@ function InserterMenu(
136
139
  onToggleInsertionPoint( false );
137
140
  onInsertBlocks( blocks, { patternName }, ...args );
138
141
  onSelect();
142
+ maybeCloseInserter();
139
143
  },
140
- [ onInsertBlocks, onSelect ]
144
+ [ onInsertBlocks, maybeCloseInserter, onSelect, onToggleInsertionPoint ]
141
145
  );
142
146
 
143
147
  const onHover = useCallback(
@@ -66,7 +66,7 @@ function ListViewBlockSelectButton(
66
66
  ( select ) => {
67
67
  const { getBlockName, getBlockAttributes } =
68
68
  select( blockEditorStore );
69
- const { isBlockHidden: _isBlockHidden } = unlock(
69
+ const { areBlocksHiddenAnywhere } = unlock(
70
70
  select( blockEditorStore )
71
71
  );
72
72
  const blockAttributes = getBlockAttributes( clientId );
@@ -76,7 +76,7 @@ function ListViewBlockSelectButton(
76
76
  'visibility',
77
77
  true
78
78
  ),
79
- isBlockHidden: _isBlockHidden( clientId ),
79
+ isBlockHidden: areBlocksHiddenAnywhere( [ clientId ] ),
80
80
  hasPatternName: !! blockAttributes?.metadata?.patternName,
81
81
  };
82
82
  },