@wordpress/block-editor 15.10.1-next.ba3aee3a2.0 → 15.11.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 (214) hide show
  1. package/CHANGELOG.md +2 -0
  2. package/build/components/block-bindings/attribute-control.cjs +1 -1
  3. package/build/components/block-bindings/attribute-control.cjs.map +1 -1
  4. package/build/components/block-bindings/source-fields-list.cjs +1 -1
  5. package/build/components/block-bindings/source-fields-list.cjs.map +1 -1
  6. package/build/components/block-edit/context.cjs +5 -0
  7. package/build/components/block-edit/context.cjs.map +2 -2
  8. package/build/components/block-edit/index.cjs +3 -0
  9. package/build/components/block-edit/index.cjs.map +2 -2
  10. package/build/components/block-inspector/index.cjs +2 -9
  11. package/build/components/block-inspector/index.cjs.map +2 -2
  12. package/build/components/block-list/block.cjs +3 -0
  13. package/build/components/block-list/block.cjs.map +2 -2
  14. package/build/components/block-tools/index.cjs +82 -70
  15. package/build/components/block-tools/index.cjs.map +2 -2
  16. package/build/components/block-visibility/block-visibility-info.cjs +0 -59
  17. package/build/components/block-visibility/block-visibility-info.cjs.map +3 -3
  18. package/build/components/block-visibility/constants.cjs +10 -5
  19. package/build/components/block-visibility/constants.cjs.map +2 -2
  20. package/build/components/block-visibility/index.cjs +13 -5
  21. package/build/components/block-visibility/index.cjs.map +3 -3
  22. package/build/components/block-visibility/modal.cjs +397 -0
  23. package/build/components/block-visibility/modal.cjs.map +7 -0
  24. package/build/components/block-visibility/toolbar.cjs +1 -1
  25. package/build/components/block-visibility/toolbar.cjs.map +2 -2
  26. package/build/components/block-visibility/use-block-visibility.cjs +13 -17
  27. package/build/components/block-visibility/use-block-visibility.cjs.map +2 -2
  28. package/build/components/block-visibility/utils.cjs +81 -0
  29. package/build/components/block-visibility/utils.cjs.map +7 -0
  30. package/build/components/block-visibility/viewport-menu-item.cjs +61 -0
  31. package/build/components/block-visibility/viewport-menu-item.cjs.map +7 -0
  32. package/build/components/block-visibility/viewport-toolbar.cjs +89 -0
  33. package/build/components/block-visibility/viewport-toolbar.cjs.map +7 -0
  34. package/build/components/button-block-appender/index.cjs +23 -19
  35. package/build/components/button-block-appender/index.cjs.map +2 -2
  36. package/build/components/font-sizes/font-size-picker.cjs +2 -1
  37. package/build/components/font-sizes/font-size-picker.cjs.map +2 -2
  38. package/build/components/inner-blocks/use-inner-block-template-sync.cjs +1 -1
  39. package/build/components/inner-blocks/use-inner-block-template-sync.cjs.map +1 -1
  40. package/build/components/inserter/menu.cjs +6 -2
  41. package/build/components/inserter/menu.cjs.map +2 -2
  42. package/build/components/inspector-controls/fill.cjs +4 -25
  43. package/build/components/inspector-controls/fill.cjs.map +2 -2
  44. package/build/components/inspector-controls-tabs/use-inspector-controls-tabs.cjs +6 -6
  45. package/build/components/inspector-controls-tabs/use-inspector-controls-tabs.cjs.map +2 -2
  46. package/build/components/list-view/block-select-button.cjs +2 -2
  47. package/build/components/list-view/block-select-button.cjs.map +2 -2
  48. package/build/components/list-view/block.cjs +39 -22
  49. package/build/components/list-view/block.cjs.map +2 -2
  50. package/build/components/rich-text/index.cjs +1 -1
  51. package/build/components/rich-text/index.cjs.map +1 -1
  52. package/build/components/url-input/index.cjs +2 -0
  53. package/build/components/url-input/index.cjs.map +2 -2
  54. package/build/components/use-block-commands/index.cjs +1 -1
  55. package/build/components/use-block-commands/index.cjs.map +2 -2
  56. package/build/hooks/block-fields/index.cjs +92 -217
  57. package/build/hooks/block-fields/index.cjs.map +3 -3
  58. package/build/hooks/block-fields/link/index.cjs +13 -32
  59. package/build/hooks/block-fields/link/index.cjs.map +2 -2
  60. package/build/hooks/block-fields/media/index.cjs +36 -67
  61. package/build/hooks/block-fields/media/index.cjs.map +2 -2
  62. package/build/hooks/block-fields/rich-text/index.cjs +1 -5
  63. package/build/hooks/block-fields/rich-text/index.cjs.map +2 -2
  64. package/build/hooks/cross-origin-isolation.cjs +102 -0
  65. package/build/hooks/cross-origin-isolation.cjs.map +7 -0
  66. package/build/hooks/index.cjs +3 -1
  67. package/build/hooks/index.cjs.map +3 -3
  68. package/build/hooks/list-view.cjs +27 -10
  69. package/build/hooks/list-view.cjs.map +2 -2
  70. package/build/hooks/utils.cjs +3 -2
  71. package/build/hooks/utils.cjs.map +2 -2
  72. package/build/layouts/flex.cjs +6 -2
  73. package/build/layouts/flex.cjs.map +2 -2
  74. package/build/store/private-selectors.cjs +33 -1
  75. package/build/store/private-selectors.cjs.map +3 -3
  76. package/build/store/reducer.cjs +1 -1
  77. package/build/store/reducer.cjs.map +1 -1
  78. package/build/store/selectors.cjs +14 -9
  79. package/build/store/selectors.cjs.map +2 -2
  80. package/build-module/components/block-bindings/attribute-control.mjs +1 -1
  81. package/build-module/components/block-bindings/attribute-control.mjs.map +1 -1
  82. package/build-module/components/block-bindings/source-fields-list.mjs +1 -1
  83. package/build-module/components/block-bindings/source-fields-list.mjs.map +1 -1
  84. package/build-module/components/block-edit/context.mjs +4 -0
  85. package/build-module/components/block-edit/context.mjs.map +2 -2
  86. package/build-module/components/block-edit/index.mjs +4 -0
  87. package/build-module/components/block-edit/index.mjs.map +2 -2
  88. package/build-module/components/block-inspector/index.mjs +2 -9
  89. package/build-module/components/block-inspector/index.mjs.map +2 -2
  90. package/build-module/components/block-list/block.mjs +3 -0
  91. package/build-module/components/block-list/block.mjs.map +2 -2
  92. package/build-module/components/block-tools/index.mjs +85 -73
  93. package/build-module/components/block-tools/index.mjs.map +2 -2
  94. package/build-module/components/block-visibility/block-visibility-info.mjs +0 -59
  95. package/build-module/components/block-visibility/block-visibility-info.mjs.map +3 -3
  96. package/build-module/components/block-visibility/constants.mjs +8 -4
  97. package/build-module/components/block-visibility/constants.mjs.map +2 -2
  98. package/build-module/components/block-visibility/index.mjs +13 -6
  99. package/build-module/components/block-visibility/index.mjs.map +2 -2
  100. package/build-module/components/block-visibility/modal.mjs +384 -0
  101. package/build-module/components/block-visibility/modal.mjs.map +7 -0
  102. package/build-module/components/block-visibility/toolbar.mjs +1 -1
  103. package/build-module/components/block-visibility/toolbar.mjs.map +2 -2
  104. package/build-module/components/block-visibility/use-block-visibility.mjs +13 -13
  105. package/build-module/components/block-visibility/use-block-visibility.mjs.map +2 -2
  106. package/build-module/components/block-visibility/utils.mjs +55 -0
  107. package/build-module/components/block-visibility/utils.mjs.map +7 -0
  108. package/build-module/components/block-visibility/viewport-menu-item.mjs +40 -0
  109. package/build-module/components/block-visibility/viewport-menu-item.mjs.map +7 -0
  110. package/build-module/components/block-visibility/viewport-toolbar.mjs +68 -0
  111. package/build-module/components/block-visibility/viewport-toolbar.mjs.map +7 -0
  112. package/build-module/components/button-block-appender/index.mjs +23 -19
  113. package/build-module/components/button-block-appender/index.mjs.map +2 -2
  114. package/build-module/components/font-sizes/font-size-picker.mjs +2 -1
  115. package/build-module/components/font-sizes/font-size-picker.mjs.map +2 -2
  116. package/build-module/components/inner-blocks/use-inner-block-template-sync.mjs +1 -1
  117. package/build-module/components/inner-blocks/use-inner-block-template-sync.mjs.map +1 -1
  118. package/build-module/components/inserter/menu.mjs +6 -2
  119. package/build-module/components/inserter/menu.mjs.map +2 -2
  120. package/build-module/components/inspector-controls/fill.mjs +6 -22
  121. package/build-module/components/inspector-controls/fill.mjs.map +2 -2
  122. package/build-module/components/inspector-controls-tabs/use-inspector-controls-tabs.mjs +6 -6
  123. package/build-module/components/inspector-controls-tabs/use-inspector-controls-tabs.mjs.map +2 -2
  124. package/build-module/components/list-view/block-select-button.mjs +2 -2
  125. package/build-module/components/list-view/block-select-button.mjs.map +2 -2
  126. package/build-module/components/list-view/block.mjs +39 -22
  127. package/build-module/components/list-view/block.mjs.map +2 -2
  128. package/build-module/components/rich-text/index.mjs +1 -1
  129. package/build-module/components/rich-text/index.mjs.map +1 -1
  130. package/build-module/components/url-input/index.mjs +2 -0
  131. package/build-module/components/url-input/index.mjs.map +2 -2
  132. package/build-module/components/use-block-commands/index.mjs +1 -1
  133. package/build-module/components/use-block-commands/index.mjs.map +2 -2
  134. package/build-module/hooks/block-fields/index.mjs +85 -218
  135. package/build-module/hooks/block-fields/index.mjs.map +2 -2
  136. package/build-module/hooks/block-fields/link/index.mjs +13 -32
  137. package/build-module/hooks/block-fields/link/index.mjs.map +2 -2
  138. package/build-module/hooks/block-fields/media/index.mjs +36 -67
  139. package/build-module/hooks/block-fields/media/index.mjs.map +2 -2
  140. package/build-module/hooks/block-fields/rich-text/index.mjs +1 -5
  141. package/build-module/hooks/block-fields/rich-text/index.mjs.map +2 -2
  142. package/build-module/hooks/cross-origin-isolation.mjs +100 -0
  143. package/build-module/hooks/cross-origin-isolation.mjs.map +7 -0
  144. package/build-module/hooks/index.mjs +3 -1
  145. package/build-module/hooks/index.mjs.map +2 -2
  146. package/build-module/hooks/list-view.mjs +27 -10
  147. package/build-module/hooks/list-view.mjs.map +2 -2
  148. package/build-module/hooks/utils.mjs +5 -3
  149. package/build-module/hooks/utils.mjs.map +2 -2
  150. package/build-module/layouts/flex.mjs +6 -2
  151. package/build-module/layouts/flex.mjs.map +2 -2
  152. package/build-module/store/private-selectors.mjs +34 -1
  153. package/build-module/store/private-selectors.mjs.map +2 -2
  154. package/build-module/store/reducer.mjs +1 -1
  155. package/build-module/store/reducer.mjs.map +1 -1
  156. package/build-module/store/selectors.mjs +14 -9
  157. package/build-module/store/selectors.mjs.map +2 -2
  158. package/build-style/content-rtl.css +4 -1
  159. package/build-style/content.css +4 -1
  160. package/build-style/style-rtl.css +48 -0
  161. package/build-style/style.css +48 -0
  162. package/package.json +39 -39
  163. package/src/components/block-bindings/attribute-control.js +1 -1
  164. package/src/components/block-bindings/source-fields-list.js +1 -1
  165. package/src/components/block-edit/context.js +3 -0
  166. package/src/components/block-edit/index.js +6 -0
  167. package/src/components/block-inspector/index.js +2 -6
  168. package/src/components/block-list/block.js +3 -0
  169. package/src/components/block-list/block.native.js +5 -0
  170. package/src/components/block-list/content.scss +4 -1
  171. package/src/components/block-patterns-list/stories/index.story.jsx +1 -1
  172. package/src/components/block-tools/index.js +45 -33
  173. package/src/components/block-visibility/block-visibility-info.js +0 -1
  174. package/src/components/block-visibility/constants.js +7 -3
  175. package/src/components/block-visibility/index.js +21 -3
  176. package/src/components/block-visibility/modal.js +358 -0
  177. package/src/components/block-visibility/style.scss +58 -0
  178. package/src/components/block-visibility/test/use-block-visibility.js +12 -56
  179. package/src/components/block-visibility/test/utils.js +266 -0
  180. package/src/components/block-visibility/toolbar.js +1 -1
  181. package/src/components/block-visibility/use-block-visibility.js +18 -21
  182. package/src/components/block-visibility/utils.js +95 -0
  183. package/src/components/block-visibility/viewport-menu-item.js +42 -0
  184. package/src/components/block-visibility/viewport-toolbar.js +88 -0
  185. package/src/components/button-block-appender/index.js +2 -2
  186. package/src/components/font-sizes/font-size-picker.js +1 -0
  187. package/src/components/inner-blocks/use-inner-block-template-sync.js +1 -1
  188. package/src/components/inserter/menu.js +6 -2
  189. package/src/components/inspector-controls/fill.js +10 -20
  190. package/src/components/inspector-controls-tabs/use-inspector-controls-tabs.js +11 -8
  191. package/src/components/list-view/block-select-button.js +2 -2
  192. package/src/components/list-view/block.js +47 -25
  193. package/src/components/rich-text/index.js +1 -1
  194. package/src/components/url-input/index.js +2 -0
  195. package/src/components/use-block-commands/index.js +4 -3
  196. package/src/hooks/block-fields/index.js +127 -292
  197. package/src/hooks/block-fields/link/index.js +13 -51
  198. package/src/hooks/block-fields/media/index.js +35 -106
  199. package/src/hooks/block-fields/rich-text/index.js +1 -5
  200. package/src/hooks/block-fields/styles.scss +2 -0
  201. package/src/hooks/cross-origin-isolation.js +143 -0
  202. package/src/hooks/index.js +3 -1
  203. package/src/hooks/list-view.js +40 -10
  204. package/src/hooks/utils.js +4 -0
  205. package/src/layouts/flex.js +8 -3
  206. package/src/layouts/test/flex.js +53 -0
  207. package/src/store/private-selectors.js +64 -1
  208. package/src/store/reducer.js +1 -1
  209. package/src/store/selectors.js +21 -15
  210. package/src/store/test/private-selectors.js +80 -0
  211. package/src/style.scss +1 -0
  212. package/src/components/block-visibility/styles.scss +0 -10
  213. /package/src/components/block-icon/stories/{index.story.js → index.story.ts} +0 -0
  214. /package/src/components/contrast-checker/stories/{index.story.js → index.story.ts} +0 -0
@@ -24,9 +24,8 @@ import { useInspectorPopoverPlacement } from '../use-inspector-popover-placement
24
24
  import { getMediaSelectKey } from '../../../store/private-keys';
25
25
  import { store as blockEditorStore } from '../../../store';
26
26
 
27
- function MediaThumbnail( { data, field, attachment } ) {
28
- const config = field.config || {};
29
- const { allowedTypes = [], multiple = false } = config;
27
+ function MediaThumbnail( { data, field, attachment, config } ) {
28
+ const { allowedTypes = [], multiple = false } = config || {};
30
29
 
31
30
  if ( multiple ) {
32
31
  return 'todo multiple';
@@ -53,7 +52,7 @@ function MediaThumbnail( { data, field, attachment } ) {
53
52
  const value = field.getValue( { item: data } );
54
53
  const url = value?.url;
55
54
 
56
- if ( url ) {
55
+ if ( allowedTypes[ 0 ] === 'image' && url ) {
57
56
  return (
58
57
  <div className="block-editor-content-only-controls__media-thumbnail">
59
58
  <img alt="" width={ 24 } height={ 24 } src={ url } />
@@ -85,19 +84,11 @@ export default function Media( { data, field, onChange, config = {} } ) {
85
84
  isControl: true,
86
85
  } );
87
86
  const value = field.getValue( { item: data } );
88
- const { allowedTypes = [], multiple = false } = field.config || {};
89
- const { fieldDef } = config;
90
- const updateAttributes = ( newFieldValue ) => {
91
- const mappedChanges = field.setValue( {
92
- item: data,
93
- value: newFieldValue,
94
- } );
95
- onChange( mappedChanges );
96
- };
97
-
98
- // Check if featured image is supported by checking if it's in the mapping
99
- const hasFeaturedImageSupport =
100
- fieldDef?.mapping && 'featuredImage' in fieldDef.mapping;
87
+ const {
88
+ allowedTypes = [],
89
+ multiple = false,
90
+ useFeaturedImage = false,
91
+ } = config;
101
92
 
102
93
  const id = value?.id;
103
94
  const url = value?.url;
@@ -147,107 +138,44 @@ export default function Media( { data, field, onChange, config = {} } ) {
147
138
  multiple={ multiple }
148
139
  popoverProps={ popoverProps }
149
140
  onReset={ () => {
150
- // Build reset value dynamically based on mapping
151
- const resetValue = {};
152
-
153
- if ( fieldDef?.mapping ) {
154
- Object.keys( fieldDef.mapping ).forEach( ( key ) => {
155
- if (
156
- key === 'id' ||
157
- key === 'src' ||
158
- key === 'url'
159
- ) {
160
- resetValue[ key ] = undefined;
161
- } else if ( key === 'caption' || key === 'alt' ) {
162
- resetValue[ key ] = '';
163
- }
164
- } );
165
- }
166
-
167
- // Turn off featured image when resetting (only if it's in the mapping)
168
- if ( hasFeaturedImageSupport ) {
169
- resetValue.featuredImage = false;
170
- }
171
-
172
- // Merge with existing value to preserve other field properties
173
- updateAttributes( { ...value, ...resetValue } );
141
+ onChange(
142
+ field.setValue( {
143
+ item: data,
144
+ value: {},
145
+ } )
146
+ );
174
147
  } }
175
- { ...( hasFeaturedImageSupport && {
148
+ { ...( useFeaturedImage && {
176
149
  useFeaturedImage: !! value?.featuredImage,
177
150
  onToggleFeaturedImage: () => {
178
- updateAttributes( {
179
- ...value,
180
- featuredImage: ! value?.featuredImage,
181
- } );
151
+ onChange(
152
+ field.setValue( {
153
+ item: data,
154
+ value: {
155
+ featuredImage: ! value?.featuredImage,
156
+ },
157
+ } )
158
+ );
182
159
  },
183
160
  } ) }
184
161
  onSelect={ ( selectedMedia ) => {
185
162
  if ( selectedMedia.id && selectedMedia.url ) {
186
- // Determine mediaType from MIME type, not from object type
187
- let mediaType = 'image'; // default
188
- if ( selectedMedia.mime_type ) {
189
- if (
190
- selectedMedia.mime_type.startsWith( 'video/' )
191
- ) {
192
- mediaType = 'video';
193
- } else if (
194
- selectedMedia.mime_type.startsWith( 'audio/' )
195
- ) {
196
- mediaType = 'audio';
197
- }
198
- }
199
-
200
- // Build new value dynamically based on what's in the mapping
201
- const newValue = {};
202
-
203
- // Iterate over mapping keys and set values for supported properties
204
- if ( fieldDef?.mapping ) {
205
- Object.keys( fieldDef.mapping ).forEach(
206
- ( key ) => {
207
- if ( key === 'id' ) {
208
- newValue[ key ] = selectedMedia.id;
209
- } else if (
210
- key === 'src' ||
211
- key === 'url'
212
- ) {
213
- newValue[ key ] = selectedMedia.url;
214
- } else if ( key === 'type' ) {
215
- newValue[ key ] = mediaType;
216
- } else if (
217
- key === 'link' &&
218
- selectedMedia.link
219
- ) {
220
- newValue[ key ] = selectedMedia.link;
221
- } else if (
222
- key === 'caption' &&
223
- ! value?.caption &&
224
- selectedMedia.caption
225
- ) {
226
- newValue[ key ] = selectedMedia.caption;
227
- } else if (
228
- key === 'alt' &&
229
- ! value?.alt &&
230
- selectedMedia.alt
231
- ) {
232
- newValue[ key ] = selectedMedia.alt;
233
- } else if (
234
- key === 'poster' &&
235
- selectedMedia.poster
236
- ) {
237
- newValue[ key ] = selectedMedia.poster;
238
- }
239
- }
240
- );
241
- }
163
+ const newValue = {
164
+ ...selectedMedia,
165
+ mediaType: selectedMedia.media_type,
166
+ };
242
167
 
243
168
  // Turn off featured image when manually selecting media
244
- if ( hasFeaturedImageSupport ) {
169
+ if ( useFeaturedImage ) {
245
170
  newValue.featuredImage = false;
246
171
  }
247
172
 
248
- // Merge with existing value to preserve other field properties
249
- const finalValue = { ...value, ...newValue };
250
- updateAttributes( finalValue );
173
+ onChange(
174
+ field.setValue( {
175
+ item: data,
176
+ value: newValue,
177
+ } )
178
+ );
251
179
  }
252
180
  } }
253
181
  renderToggle={ ( buttonProps ) => (
@@ -268,6 +196,7 @@ export default function Media( { data, field, onChange, config = {} } ) {
268
196
  attachment={ attachment }
269
197
  field={ field }
270
198
  data={ data }
199
+ config={ config }
271
200
  />
272
201
  <span className="block-editor-content-only-controls__media-title">
273
202
  {
@@ -33,10 +33,6 @@ export default function RichTextControl( {
33
33
  const attrValue = field.getValue( { item: data } );
34
34
  const fieldConfig = field.config || {};
35
35
  const { clientId } = config;
36
- const updateAttributes = ( html ) => {
37
- const mappedChanges = field.setValue( { item: data, value: html } );
38
- onChange( mappedChanges );
39
- };
40
36
  const [ selection, setSelection ] = useState( {
41
37
  start: undefined,
42
38
  end: undefined,
@@ -107,7 +103,7 @@ export default function RichTextControl( {
107
103
  } = useRichText( {
108
104
  value: attrValue,
109
105
  onChange( html, { __unstableFormats, __unstableText } ) {
110
- updateAttributes( html );
106
+ onChange( field.setValue( { item: data, value: html } ) );
111
107
  Object.values( changeHandlers ).forEach( ( changeHandler ) => {
112
108
  changeHandler( __unstableFormats, __unstableText );
113
109
  } );
@@ -30,4 +30,6 @@
30
30
 
31
31
  .block-editor-block-fields__header-title {
32
32
  flex: 1;
33
+ // Override the default margin on a h2 element.
34
+ margin: 0 !important;
33
35
  }
@@ -0,0 +1,143 @@
1
+ /**
2
+ * WordPress dependencies
3
+ */
4
+ import { addFilter } from '@wordpress/hooks';
5
+ import { createHigherOrderComponent } from '@wordpress/compose';
6
+
7
+ /**
8
+ * Adds crossorigin and credentialless attributes to elements as needed.
9
+ *
10
+ * @param {Element} el The element to modify.
11
+ */
12
+ function addCrossOriginAttributes( el ) {
13
+ // Add the crossorigin attribute if missing.
14
+ if ( ! el.hasAttribute( 'crossorigin' ) ) {
15
+ el.setAttribute( 'crossorigin', 'anonymous' );
16
+ }
17
+
18
+ // For iframes, add the credentialless attribute.
19
+ if ( el.nodeName === 'IFRAME' && ! el.hasAttribute( 'credentialless' ) ) {
20
+ // Do not modify the iframed editor canvas.
21
+ if ( el.getAttribute( 'src' )?.startsWith( 'blob:' ) ) {
22
+ return;
23
+ }
24
+
25
+ el.setAttribute( 'credentialless', '' );
26
+
27
+ // Reload the iframe to ensure the new attribute is taken into account.
28
+ const origSrc = el.getAttribute( 'src' ) || '';
29
+ el.setAttribute( 'src', '' );
30
+ el.setAttribute( 'src', origSrc );
31
+ }
32
+ }
33
+
34
+ // Only add the mutation observer if the site is cross-origin isolated.
35
+ if ( window.crossOriginIsolated ) {
36
+ /*
37
+ * Detects dynamically added DOM nodes that are missing the `crossorigin` attribute.
38
+ */
39
+ const observer = new window.MutationObserver( ( mutations ) => {
40
+ mutations.forEach( ( mutation ) => {
41
+ [ mutation.addedNodes, mutation.target ].forEach( ( value ) => {
42
+ const nodes =
43
+ value instanceof window.NodeList ? value : [ value ];
44
+ nodes.forEach( ( node ) => {
45
+ const el = node;
46
+
47
+ if ( ! el.querySelectorAll ) {
48
+ // Most likely a text node.
49
+ return;
50
+ }
51
+
52
+ el.querySelectorAll(
53
+ 'img,source,script,video,link,iframe'
54
+ ).forEach( ( v ) => {
55
+ addCrossOriginAttributes( v );
56
+ } );
57
+
58
+ if ( el.nodeName === 'IFRAME' ) {
59
+ const iframeNode = el;
60
+
61
+ /*
62
+ * Sandboxed iframes should not get modified. For example embedding a tweet served in a sandboxed
63
+ * iframe, the tweet itself would not be modified.
64
+ */
65
+ const isEmbedSandboxIframe =
66
+ iframeNode.classList.contains(
67
+ 'components-sandbox'
68
+ );
69
+
70
+ if ( ! isEmbedSandboxIframe ) {
71
+ iframeNode.addEventListener( 'load', () => {
72
+ if ( iframeNode.contentDocument ) {
73
+ observer.observe(
74
+ iframeNode.contentDocument,
75
+ {
76
+ childList: true,
77
+ attributes: true,
78
+ subtree: true,
79
+ }
80
+ );
81
+ }
82
+ } );
83
+ }
84
+ }
85
+
86
+ if (
87
+ [
88
+ 'IMG',
89
+ 'SOURCE',
90
+ 'SCRIPT',
91
+ 'VIDEO',
92
+ 'LINK',
93
+ 'IFRAME',
94
+ ].includes( el.nodeName )
95
+ ) {
96
+ addCrossOriginAttributes( el );
97
+ }
98
+ } );
99
+ } );
100
+ } );
101
+ } );
102
+
103
+ observer.observe( document.body, {
104
+ childList: true,
105
+ attributes: true,
106
+ subtree: true,
107
+ } );
108
+ }
109
+
110
+ // Only apply the embed preview filter when cross-origin isolated.
111
+ if ( window.crossOriginIsolated ) {
112
+ const supportsCredentialless =
113
+ 'credentialless' in window.HTMLIFrameElement.prototype;
114
+
115
+ const disableEmbedPreviews = createHigherOrderComponent(
116
+ ( BlockEdit ) => ( props ) => {
117
+ if ( 'core/embed' !== props.name ) {
118
+ return <BlockEdit { ...props } />;
119
+ }
120
+
121
+ // List of embeds that do not support a preview is from packages/block-library/src/embed/variations.js.
122
+ const previewable =
123
+ supportsCredentialless &&
124
+ ! [ 'facebook', 'smugmug' ].includes(
125
+ props.attributes.providerNameSlug
126
+ );
127
+
128
+ return (
129
+ <BlockEdit
130
+ { ...props }
131
+ attributes={ { ...props.attributes, previewable } }
132
+ />
133
+ );
134
+ },
135
+ 'withDisabledEmbedPreview'
136
+ );
137
+
138
+ addFilter(
139
+ 'editor.BlockEdit',
140
+ 'media-experiments/disable-embed-previews',
141
+ disableEmbedPreviews
142
+ );
143
+ }
@@ -7,13 +7,14 @@ import {
7
7
  createBlockSaveFilter,
8
8
  } from './utils';
9
9
  import './compat';
10
+ import './cross-origin-isolation';
10
11
  import align from './align';
11
12
  import background from './background';
12
13
  import './lock';
13
14
  import allowedBlocks from './allowed-blocks';
14
15
  import anchor from './anchor';
15
16
  import ariaLabel from './aria-label';
16
- import './block-fields';
17
+ import blockFields from './block-fields';
17
18
  import customClassName from './custom-class-name';
18
19
  import './generated-class-name';
19
20
  import style from './style';
@@ -55,6 +56,7 @@ createBlockEditFilter(
55
56
  blockBindingsPanel,
56
57
  childLayout,
57
58
  allowedBlocks,
59
+ blockFields,
58
60
  listView,
59
61
  autoInspectorControls,
60
62
  ].filter( Boolean )
@@ -5,13 +5,15 @@ import { __ } from '@wordpress/i18n';
5
5
  import { PanelBody } from '@wordpress/components';
6
6
  import { useSelect } from '@wordpress/data';
7
7
  import { store as blocksStore, hasBlockSupport } from '@wordpress/blocks';
8
+ import { useContext } from '@wordpress/element';
8
9
 
9
10
  /**
10
11
  * Internal dependencies
11
12
  */
12
- import InspectorControls from '../components/inspector-controls';
13
13
  import { store as blockEditorStore } from '../store';
14
14
  import { PrivateListView } from '../components/list-view';
15
+ import InspectorControls from '../components/inspector-controls/fill';
16
+ import { PrivateBlockContext } from '../components/block-list/private-block-context';
15
17
 
16
18
  export const LIST_VIEW_SUPPORT_KEY = 'listView';
17
19
 
@@ -34,23 +36,50 @@ export function hasListViewSupport( nameOrType ) {
34
36
  * @return {Element|null} List view inspector controls or null.
35
37
  */
36
38
  export function ListViewPanel( { clientId, name } ) {
39
+ const { isSelectionWithinCurrentSection } =
40
+ useContext( PrivateBlockContext );
37
41
  const isEnabled = hasListViewSupport( name );
38
- const { hasChildren, blockTitle } = useSelect(
39
- ( select ) => ( {
40
- hasChildren:
41
- !! select( blockEditorStore ).getBlockCount( clientId ),
42
- blockTitle: select( blocksStore ).getBlockType( name )?.title,
43
- } ),
44
- [ clientId, name ]
42
+ const { hasChildren, blockTitle, isNestedListView } = useSelect(
43
+ ( select ) => {
44
+ const { getBlockCount, getBlockParents, getBlockName } =
45
+ select( blockEditorStore );
46
+
47
+ // When the ListView is shown in a section, avoid showing List Views
48
+ // for both parent and child blocks that have support. In this situation
49
+ // the parent will show the child anyway in its List.
50
+ // Search parents to see if there's one that also has support, and if so
51
+ // skip rendering.
52
+ // This matches closely the logic in the `BlockCard` component.
53
+ let _isNestedListView = false;
54
+ if ( isSelectionWithinCurrentSection ) {
55
+ const parents = getBlockParents( clientId, true );
56
+ _isNestedListView = parents.find( ( parentId ) => {
57
+ const parentName = getBlockName( parentId );
58
+ return (
59
+ parentName === 'core/navigation' ||
60
+ hasBlockSupport( parentName, 'listView' )
61
+ );
62
+ } );
63
+ }
64
+
65
+ return {
66
+ hasChildren: !! getBlockCount( clientId ),
67
+ blockTitle: select( blocksStore ).getBlockType( name )?.title,
68
+ isNestedListView: _isNestedListView,
69
+ };
70
+ },
71
+ [ clientId, name, isSelectionWithinCurrentSection ]
45
72
  );
46
73
 
47
- if ( ! isEnabled ) {
74
+ if ( ! isEnabled || isNestedListView ) {
48
75
  return null;
49
76
  }
50
77
 
78
+ const showBlockTitle = isSelectionWithinCurrentSection;
79
+
51
80
  return (
52
81
  <InspectorControls group="list">
53
- <PanelBody title={ null }>
82
+ <PanelBody title={ showBlockTitle ? blockTitle : undefined }>
54
83
  { ! hasChildren && (
55
84
  <p className="block-editor-block-inspector__no-blocks">
56
85
  { __( 'No items yet.' ) }
@@ -74,4 +103,5 @@ export default {
74
103
  edit: ListViewPanel,
75
104
  hasSupport: hasListViewSupport,
76
105
  attributeKeys: [],
106
+ supportsPatternEditing: true,
77
107
  };
@@ -19,6 +19,7 @@ import {
19
19
  useBlockEditContext,
20
20
  mayDisplayControlsKey,
21
21
  mayDisplayParentControlsKey,
22
+ mayDisplayPatternEditingControlsKey,
22
23
  } from '../components/block-edit/context';
23
24
  import { useSettings } from '../components';
24
25
  import { useSettingsForBlockElement } from '../components/global-styles/hooks';
@@ -534,8 +535,11 @@ export function createBlockEditFilter( features ) {
534
535
  hasSupport,
535
536
  attributeKeys = [],
536
537
  shareWithChildBlocks,
538
+ supportsPatternEditing,
537
539
  } = feature;
538
540
  const shouldDisplayControls =
541
+ ( supportsPatternEditing &&
542
+ context[ mayDisplayPatternEditingControlsKey ] ) ||
539
543
  context[ mayDisplayControlsKey ] ||
540
544
  ( context[ mayDisplayParentControlsKey ] &&
541
545
  shareWithChildBlocks );
@@ -71,8 +71,11 @@ export default {
71
71
  onChange,
72
72
  layoutBlockSupport = {},
73
73
  } ) {
74
- const { allowOrientation = true, allowJustification = true } =
75
- layoutBlockSupport;
74
+ const {
75
+ allowOrientation = true,
76
+ allowJustification = true,
77
+ allowWrap = true,
78
+ } = layoutBlockSupport;
76
79
  return (
77
80
  <>
78
81
  <Flex>
@@ -93,7 +96,9 @@ export default {
93
96
  </FlexItem>
94
97
  ) }
95
98
  </Flex>
96
- <FlexWrapControl layout={ layout } onChange={ onChange } />
99
+ { allowWrap && (
100
+ <FlexWrapControl layout={ layout } onChange={ onChange } />
101
+ ) }
97
102
  </>
98
103
  );
99
104
  },
@@ -1,8 +1,15 @@
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import { render, screen } from '@testing-library/react';
5
+
1
6
  /**
2
7
  * Internal dependencies
3
8
  */
4
9
  import flex from '../flex';
5
10
 
11
+ const FlexLayoutInspectorControls = flex.inspectorControls;
12
+
6
13
  describe( 'getLayoutStyle', () => {
7
14
  it( 'should return an empty string if no non-default params are provided', () => {
8
15
  const expected = '';
@@ -19,3 +26,49 @@ describe( 'getLayoutStyle', () => {
19
26
  expect( result ).toBe( expected );
20
27
  } );
21
28
  } );
29
+
30
+ describe( 'FlexLayoutInspectorControls', () => {
31
+ it( 'should render the wrap toggle by default', () => {
32
+ render(
33
+ <FlexLayoutInspectorControls layout={ {} } onChange={ jest.fn() } />
34
+ );
35
+
36
+ expect(
37
+ screen.getByRole( 'checkbox', {
38
+ name: 'Allow to wrap to multiple lines',
39
+ } )
40
+ ).toBeInTheDocument();
41
+ } );
42
+
43
+ it( 'should render the wrap toggle when allowWrap is true', () => {
44
+ render(
45
+ <FlexLayoutInspectorControls
46
+ layout={ {} }
47
+ onChange={ jest.fn() }
48
+ layoutBlockSupport={ { allowWrap: true } }
49
+ />
50
+ );
51
+
52
+ expect(
53
+ screen.getByRole( 'checkbox', {
54
+ name: 'Allow to wrap to multiple lines',
55
+ } )
56
+ ).toBeInTheDocument();
57
+ } );
58
+
59
+ it( 'should not render the wrap toggle when allowWrap is false', () => {
60
+ render(
61
+ <FlexLayoutInspectorControls
62
+ layout={ {} }
63
+ onChange={ jest.fn() }
64
+ layoutBlockSupport={ { allowWrap: false } }
65
+ />
66
+ );
67
+
68
+ expect(
69
+ screen.queryByRole( 'checkbox', {
70
+ name: 'Allow to wrap to multiple lines',
71
+ } )
72
+ ).not.toBeInTheDocument();
73
+ } );
74
+ } );
@@ -38,6 +38,10 @@ import {
38
38
  isIsolatedEditorKey,
39
39
  deviceTypeKey,
40
40
  } from './private-keys';
41
+ import {
42
+ BLOCK_VISIBILITY_VIEWPORT_ENTRIES,
43
+ BLOCK_VISIBILITY_VIEWPORTS,
44
+ } from '../components/block-visibility/constants';
41
45
 
42
46
  const { isContentBlock } = unlock( blocksPrivateApis );
43
47
 
@@ -730,7 +734,8 @@ export const isBlockHidden = ( state, clientId ) => {
730
734
  // Only apply when a device is explicitly selected.
731
735
  if ( typeof blockVisibility === 'object' && blockVisibility !== null ) {
732
736
  const settings = getSettings( state );
733
- const viewportType = settings[ deviceTypeKey ] ?? 'Desktop';
737
+ const viewportType =
738
+ settings[ deviceTypeKey ] ?? BLOCK_VISIBILITY_VIEWPORTS.desktop.key;
734
739
  const viewportKey = viewportType.toLowerCase();
735
740
  return blockVisibility?.[ viewportKey ] === false;
736
741
  }
@@ -738,6 +743,64 @@ export const isBlockHidden = ( state, clientId ) => {
738
743
  return false;
739
744
  };
740
745
 
746
+ /**
747
+ * Returns true if any of the provided blocks are hidden.
748
+ *
749
+ * @param {Object} state Global application state.
750
+ * @param {Array} clientIds Array of block client IDs to check.
751
+ * @return {boolean} Whether any block is hidden.
752
+ */
753
+ export const areBlocksHidden = ( state, clientIds ) => {
754
+ if ( ! clientIds || clientIds.length === 0 ) {
755
+ return false;
756
+ }
757
+ return clientIds.some( ( clientId ) => isBlockHidden( state, clientId ) );
758
+ };
759
+
760
+ /**
761
+ * Checks if at least one block in an array is hidden according to viewport visibility metadata.
762
+ *
763
+ * This is typically used to determine if the block visibility button should be shown in the toolbar.
764
+ * TODO: This is temporary for now. Later the UI will
765
+ * want to know where exactly the block is hidden, e.g., to display icons or other things.
766
+ *
767
+ * A block is considered hidden if:
768
+ * - Its `blockVisibility` metadata is `false` (hidden everywhere), or
769
+ * - Any viewport is set to `false`
770
+ *
771
+ * @param {Object} state Global application state.
772
+ * @param {Array} clientIds Array of block client IDs to check.
773
+ * @return {boolean} `true` if at least one block meets the visibility criteria, `false` otherwise.
774
+ */
775
+ export const areBlocksHiddenAnywhere = ( state, clientIds ) => {
776
+ if ( ! clientIds?.length ) {
777
+ return false;
778
+ }
779
+ return clientIds.some( ( clientId ) => {
780
+ if ( ! clientId ) {
781
+ return false;
782
+ }
783
+
784
+ const attributes = state.blocks.attributes.get( clientId );
785
+ const blockVisibility = attributes?.metadata?.blockVisibility;
786
+
787
+ // If explicitly hidden everywhere (false), return true.
788
+ if ( typeof blockVisibility === 'boolean' ) {
789
+ return blockVisibility === false;
790
+ }
791
+
792
+ // If not an object, block is not hidden in any viewport.
793
+ if ( 'object' !== typeof blockVisibility ) {
794
+ return false;
795
+ }
796
+
797
+ // Check viewport-specific visibility.
798
+ return BLOCK_VISIBILITY_VIEWPORT_ENTRIES.some(
799
+ ( [ , { key } ] ) => blockVisibility?.[ key ] === false
800
+ );
801
+ } );
802
+ };
803
+
741
804
  /**
742
805
  * Returns true if there is a spotlighted block.
743
806
  *
@@ -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