@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
@@ -1,19 +1,23 @@
1
1
  /**
2
2
  * WordPress dependencies
3
3
  */
4
- import { addFilter } from '@wordpress/hooks';
5
- import { privateApis as blocksPrivateApis } from '@wordpress/blocks';
4
+ import {
5
+ privateApis as blocksPrivateApis,
6
+ getBlockType,
7
+ } from '@wordpress/blocks';
6
8
  import {
7
9
  __experimentalHStack as HStack,
8
10
  __experimentalTruncate as Truncate,
9
11
  } from '@wordpress/components';
10
- import { createHigherOrderComponent } from '@wordpress/compose';
12
+ import { useSelect } from '@wordpress/data';
11
13
  import { DataForm } from '@wordpress/dataviews';
12
14
  import { useContext, useState, useMemo } from '@wordpress/element';
15
+ import { __ } from '@wordpress/i18n';
13
16
 
14
17
  /**
15
18
  * Internal dependencies
16
19
  */
20
+ import { store as blockEditorStore } from '../../store';
17
21
  import { unlock } from '../../lock-unlock';
18
22
  import BlockIcon from '../../components/block-icon';
19
23
  import useBlockDisplayTitle from '../../components/block-title/use-block-display-title';
@@ -21,155 +25,45 @@ import useBlockDisplayInformation from '../../components/use-block-display-infor
21
25
  const { fieldsKey, formKey } = unlock( blocksPrivateApis );
22
26
  import FieldsDropdownMenu from './fields-dropdown-menu';
23
27
  import { PrivateBlockContext } from '../../components/block-list/private-block-context';
24
- import { PrivateInspectorControlsFill } from '../../components/inspector-controls/fill';
28
+ import InspectorControls from '../../components/inspector-controls/fill';
25
29
 
26
30
  // controls
27
31
  import RichText from './rich-text';
28
32
  import Media from './media';
29
33
  import Link from './link';
30
34
 
31
- const CONTROLS = {
32
- richtext: RichText,
33
- media: Media,
34
- link: Link,
35
- };
36
-
37
35
  /**
38
36
  * Creates a configured control component that wraps a custom control
39
37
  * and passes configuration as props.
40
38
  *
41
- * @param {Object} config - The control configuration
42
- * @param {string} config.control - The control type (key in CONTROLS map)
39
+ * @param {Component} ControlComponent The React component for the control.
40
+ * @param {Object} config The control configuration passed as a prop.
41
+ *
43
42
  * @return {Function} A wrapped control component
44
43
  */
45
- function createConfiguredControl( config ) {
46
- const { control, ...controlConfig } = config;
47
- const ControlComponent = CONTROLS[ control ];
48
-
49
- if ( ! ControlComponent ) {
50
- throw new Error( `Control type "${ control }" not found` );
51
- }
52
-
44
+ function createConfiguredControl( ControlComponent, config = {} ) {
53
45
  return function ConfiguredControl( props ) {
54
- return <ControlComponent { ...props } config={ controlConfig } />;
55
- };
56
- }
57
-
58
- /**
59
- * Normalize a media value to a canonical structure.
60
- * Only includes properties that are present in the field's mapping (if provided).
61
- *
62
- * @param {Object} value - The mapped value from the block attributes (with canonical keys)
63
- * @param {Object} fieldDef - Optional field definition containing the mapping
64
- * @return {Object} Normalized media value with canonical properties
65
- */
66
- function normalizeMediaValue( value, fieldDef ) {
67
- const defaults = {
68
- id: null,
69
- url: '',
70
- caption: '',
71
- alt: '',
72
- type: 'image',
73
- poster: '',
74
- featuredImage: false,
75
- link: '',
76
- };
77
-
78
- const result = {};
79
-
80
- // If there's a mapping, only include properties that are in it
81
- if ( fieldDef?.mapping ) {
82
- Object.keys( fieldDef.mapping ).forEach( ( key ) => {
83
- result[ key ] = value?.[ key ] ?? defaults[ key ] ?? '';
84
- } );
85
- return result;
86
- }
87
-
88
- // Without mapping, include all default properties
89
- Object.keys( defaults ).forEach( ( key ) => {
90
- result[ key ] = value?.[ key ] ?? defaults[ key ];
91
- } );
92
- return result;
93
- }
94
-
95
- /**
96
- * Denormalize a media value from canonical structure back to mapped keys.
97
- * Only includes properties that are present in the field's mapping.
98
- *
99
- * @param {Object} value - The normalized media value
100
- * @param {Object} fieldDef - The field definition containing the mapping
101
- * @return {Object} Value with only mapped properties
102
- */
103
- function denormalizeMediaValue( value, fieldDef ) {
104
- if ( ! fieldDef.mapping ) {
105
- return value;
106
- }
107
-
108
- const result = {};
109
- Object.entries( fieldDef.mapping ).forEach( ( [ key ] ) => {
110
- if ( key in value ) {
111
- result[ key ] = value[ key ];
112
- }
113
- } );
114
- return result;
115
- }
116
-
117
- /**
118
- * Normalize a link value to a canonical structure.
119
- * Only includes properties that are present in the field's mapping (if provided).
120
- *
121
- * @param {Object} value - The mapped value from the block attributes (with canonical keys)
122
- * @param {Object} fieldDef - Optional field definition containing the mapping
123
- * @return {Object} Normalized link value with canonical properties
124
- */
125
- function normalizeLinkValue( value, fieldDef ) {
126
- const defaults = {
127
- url: '',
128
- rel: '',
129
- linkTarget: '',
130
- destination: '',
46
+ return <ControlComponent { ...props } config={ config } />;
131
47
  };
132
-
133
- const result = {};
134
-
135
- // If there's a mapping, only include properties that are in it
136
- if ( fieldDef?.mapping ) {
137
- Object.keys( fieldDef.mapping ).forEach( ( key ) => {
138
- result[ key ] = value?.[ key ] ?? defaults[ key ] ?? '';
139
- } );
140
- return result;
141
- }
142
-
143
- // Without mapping, include all default properties
144
- Object.keys( defaults ).forEach( ( key ) => {
145
- result[ key ] = value?.[ key ] ?? defaults[ key ];
146
- } );
147
- return result;
148
48
  }
149
49
 
150
50
  /**
151
- * Denormalize a link value from canonical structure back to mapped keys.
152
- * Only includes properties that are present in the field's mapping.
153
- *
154
- * @param {Object} value - The normalized link value
155
- * @param {Object} fieldDef - The field definition containing the mapping
156
- * @return {Object} Value with only mapped properties
51
+ * Component that renders a DataForm for a single block's attributes
52
+ * @param {Object} props
53
+ * @param {string} props.clientId The clientId of the block.
54
+ * @param {Object} props.blockType The blockType definition.
55
+ * @param {Function} props.setAttributes Action to set the block's attributes.
56
+ * @param {boolean} props.isCollapsed Whether the DataForm is rendered as 'collapsed' with only the first field
57
+ * displayed by default. When collapsed a dropdown is displayed to allow
58
+ * displaying additional fields. The block's title is displayed as the title.
59
+ * The collapsed mode is often used when multiple BlockForms are shown together.
157
60
  */
158
- function denormalizeLinkValue( value, fieldDef ) {
159
- if ( ! fieldDef.mapping ) {
160
- return value;
161
- }
162
-
163
- const result = {};
164
- Object.entries( fieldDef.mapping ).forEach( ( [ key ] ) => {
165
- if ( key in value ) {
166
- result[ key ] = value[ key ];
167
- }
168
- } );
169
- return result;
170
- }
171
-
172
- function BlockFields( { clientId, blockType, attributes, setAttributes } ) {
61
+ function BlockFields( {
62
+ clientId,
63
+ blockType,
64
+ setAttributes,
65
+ isCollapsed = false,
66
+ } ) {
173
67
  const blockTitle = useBlockDisplayTitle( {
174
68
  clientId,
175
69
  context: 'list-view',
@@ -178,9 +72,24 @@ function BlockFields( { clientId, blockType, attributes, setAttributes } ) {
178
72
 
179
73
  const blockTypeFields = blockType?.[ fieldsKey ];
180
74
 
181
- const [ form, setForm ] = useState( () => {
182
- return blockType?.[ formKey ];
183
- } );
75
+ const attributes = useSelect(
76
+ ( select ) => select( blockEditorStore ).getBlockAttributes( clientId ),
77
+ [ clientId ]
78
+ );
79
+
80
+ const computedForm = useMemo( () => {
81
+ if ( ! isCollapsed ) {
82
+ return blockType?.[ formKey ];
83
+ }
84
+
85
+ // For a collapsed form only show the first field by default.
86
+ return {
87
+ ...blockType?.[ formKey ],
88
+ fields: [ blockType?.[ formKey ]?.fields?.[ 0 ] ],
89
+ };
90
+ }, [ blockType, isCollapsed ] );
91
+
92
+ const [ form, setForm ] = useState( computedForm );
184
93
 
185
94
  // Build DataForm fields with proper structure
186
95
  const dataFormFields = useMemo( () => {
@@ -189,100 +98,45 @@ function BlockFields( { clientId, blockType, attributes, setAttributes } ) {
189
98
  }
190
99
 
191
100
  return blockTypeFields.map( ( fieldDef ) => {
192
- const ControlComponent = CONTROLS[ fieldDef.type ];
193
-
194
- const defaultValues = {};
195
- if ( fieldDef.mapping && blockType?.attributes ) {
196
- Object.entries( fieldDef.mapping ).forEach(
197
- ( [ key, attrKey ] ) => {
198
- defaultValues[ key ] =
199
- blockType.attributes[ attrKey ]?.defaultValue ??
200
- undefined;
201
- }
202
- );
203
- }
204
-
205
101
  const field = {
206
- id: fieldDef.id,
207
- label: fieldDef.label,
208
- type: fieldDef.type, // Use the field's type; DataForm will use built-in or custom Edit
209
- config: { ...fieldDef.args, defaultValues },
210
- hideLabelFromVision: fieldDef.id === 'content',
211
- // getValue and setValue handle the mapping to block attributes
212
- getValue: ( { item } ) => {
213
- if ( fieldDef.mapping ) {
214
- // Extract mapped properties from the block attributes
215
- const mappedValue = {};
216
- Object.entries( fieldDef.mapping ).forEach(
217
- ( [ key, attrKey ] ) => {
218
- mappedValue[ key ] = item[ attrKey ];
219
- }
220
- );
221
-
222
- // Normalize to canonical structure based on field type
223
- if ( fieldDef.type === 'media' ) {
224
- return normalizeMediaValue( mappedValue, fieldDef );
225
- }
226
- if ( fieldDef.type === 'link' ) {
227
- return normalizeLinkValue( mappedValue, fieldDef );
228
- }
229
-
230
- // For other types, return as-is
231
- return mappedValue;
232
- }
233
- // For simple id-based fields, use the id as the attribute key
234
- return item[ fieldDef.id ];
235
- },
236
- setValue: ( { item, value } ) => {
237
- if ( fieldDef.mapping ) {
238
- // Denormalize from canonical structure back to mapped keys
239
- let denormalizedValue = value;
240
- if ( fieldDef.type === 'media' ) {
241
- denormalizedValue = denormalizeMediaValue(
242
- value,
243
- fieldDef
244
- );
245
- } else if ( fieldDef.type === 'link' ) {
246
- denormalizedValue = denormalizeLinkValue(
247
- value,
248
- fieldDef
249
- );
250
- }
251
-
252
- // Build an object with all mapped attributes
253
- const updates = {};
254
- Object.entries( fieldDef.mapping ).forEach(
255
- ( [ key, attrKey ] ) => {
256
- // If key is explicitly in value, use it (even if undefined to allow clearing)
257
- // Otherwise, preserve the old value
258
- if ( key in denormalizedValue ) {
259
- updates[ attrKey ] =
260
- denormalizedValue[ key ];
261
- } else {
262
- updates[ attrKey ] = item[ attrKey ];
263
- }
264
- }
265
- );
266
- return updates;
267
- }
268
- // For simple id-based fields, use the id as the attribute key
269
- return { [ fieldDef.id ]: value };
270
- },
102
+ ...fieldDef,
271
103
  };
272
104
 
273
- // Only add custom Edit component if one exists for this type
274
- if ( ControlComponent ) {
275
- // Use EditConfig pattern: Edit is an object with control type and config props
276
- field.Edit = createConfiguredControl( {
277
- control: fieldDef.type,
105
+ // These should be custom Edit components, not replaced here.
106
+ //
107
+ // - rich-text control: it needs clientId
108
+ // - link control: does not need anything extra
109
+ // - media control: needs the Edit config
110
+ if (
111
+ 'string' === typeof fieldDef.Edit &&
112
+ fieldDef.Edit === 'rich-text'
113
+ ) {
114
+ field.Edit = createConfiguredControl( RichText, {
278
115
  clientId,
279
- fieldDef,
116
+ } );
117
+ } else if (
118
+ 'string' === typeof fieldDef.Edit &&
119
+ fieldDef.Edit === 'link'
120
+ ) {
121
+ field.Edit = createConfiguredControl( Link );
122
+ } else if (
123
+ 'object' === typeof fieldDef.Edit &&
124
+ fieldDef.Edit.control === 'media'
125
+ ) {
126
+ field.Edit = createConfiguredControl( Media, {
127
+ ...fieldDef.Edit,
280
128
  } );
281
129
  }
282
130
 
283
131
  return field;
284
132
  } );
285
- }, [ blockTypeFields, blockType?.attributes, clientId ] );
133
+ }, [ blockTypeFields, clientId ] );
134
+
135
+ if ( ! blockTypeFields?.length ) {
136
+ // TODO - we might still want to show a placeholder for blocks with no fields.
137
+ // for example, a way to select the block.
138
+ return null;
139
+ }
286
140
 
287
141
  const handleToggleField = ( fieldId ) => {
288
142
  setForm( ( prev ) => {
@@ -300,31 +154,33 @@ function BlockFields( { clientId, blockType, attributes, setAttributes } ) {
300
154
  } );
301
155
  };
302
156
 
303
- if ( ! blockTypeFields?.length ) {
304
- // TODO - we might still want to show a placeholder for blocks with no fields.
305
- // for example, a way to select the block.
306
- return null;
307
- }
308
-
309
157
  return (
310
158
  <div className="block-editor-block-fields__container">
311
159
  <div className="block-editor-block-fields__header">
312
160
  <HStack spacing={ 1 }>
313
- <BlockIcon
314
- className="block-editor-block-fields__header-icon"
315
- icon={ blockInformation?.icon }
316
- />
317
- <Truncate
318
- className="block-editor-block-fields__header-title"
319
- numberOfLines={ 1 }
320
- >
321
- { blockTitle }
322
- </Truncate>
323
- <FieldsDropdownMenu
324
- fields={ dataFormFields }
325
- visibleFields={ form.fields }
326
- onToggleField={ handleToggleField }
327
- />
161
+ { isCollapsed && (
162
+ <>
163
+ <BlockIcon
164
+ className="block-editor-block-fields__header-icon"
165
+ icon={ blockInformation?.icon }
166
+ />
167
+ <h2 className="block-editor-block-fields__header-title">
168
+ <Truncate numberOfLines={ 1 }>
169
+ { blockTitle }
170
+ </Truncate>
171
+ </h2>
172
+ <FieldsDropdownMenu
173
+ fields={ dataFormFields }
174
+ visibleFields={ form.fields }
175
+ onToggleField={ handleToggleField }
176
+ />
177
+ </>
178
+ ) }
179
+ { ! isCollapsed && (
180
+ <h2 className="block-editor-block-fields__header-title">
181
+ { __( 'Content' ) }
182
+ </h2>
183
+ ) }
328
184
  </HStack>
329
185
  </div>
330
186
  <DataForm
@@ -337,55 +193,34 @@ function BlockFields( { clientId, blockType, attributes, setAttributes } ) {
337
193
  );
338
194
  }
339
195
 
340
- const withBlockFields = createHigherOrderComponent(
341
- ( BlockEdit ) => ( props ) => {
342
- const {
343
- blockType,
344
- isSelectionWithinCurrentSection,
345
- isSectionBlock,
346
- blockEditingMode,
347
- isSelected,
348
- } = useContext( PrivateBlockContext );
349
-
350
- const shouldShowBlockFields =
351
- window?.__experimentalContentOnlyInspectorFields;
352
- const blockTypeFields = blockType?.[ fieldsKey ];
196
+ function hasBlockFieldsSupport( blockName ) {
197
+ return !! (
198
+ window?.__experimentalContentOnlyInspectorFields &&
199
+ getBlockType( blockName )?.[ fieldsKey ]
200
+ );
201
+ }
353
202
 
354
- if ( ! shouldShowBlockFields || ! blockTypeFields?.length ) {
355
- return <BlockEdit key="edit" { ...props } />;
356
- }
203
+ export function BlockFieldsPanel( props ) {
204
+ const { blockType, isSelectionWithinCurrentSection } =
205
+ useContext( PrivateBlockContext );
357
206
 
358
- return (
359
- <>
360
- <BlockEdit key="edit" { ...props } />
361
- {
362
- // Display the controls of all inner blocks for section/pattern editing.
363
- isSelectionWithinCurrentSection &&
364
- ( isSectionBlock ||
365
- blockEditingMode === 'contentOnly' ) && (
366
- <PrivateInspectorControlsFill
367
- group="content"
368
- forceDisplayControls
369
- >
370
- <BlockFields
371
- { ...props }
372
- blockType={ blockType }
373
- />
374
- </PrivateInspectorControlsFill>
375
- )
376
- }
377
- { ! isSelectionWithinCurrentSection && isSelected && (
378
- <PrivateInspectorControlsFill group="content">
379
- <BlockFields { ...props } blockType={ blockType } />
380
- </PrivateInspectorControlsFill>
381
- ) }
382
- </>
383
- );
384
- }
385
- );
207
+ return (
208
+ <InspectorControls group="content">
209
+ <BlockFields
210
+ { ...props }
211
+ blockType={ blockType }
212
+ isCollapsed={ isSelectionWithinCurrentSection }
213
+ />
214
+ </InspectorControls>
215
+ );
216
+ }
386
217
 
387
- addFilter(
388
- 'editor.BlockEdit',
389
- 'core/content-only-controls/block-fields',
390
- withBlockFields
391
- );
218
+ /**
219
+ * Export block support definition.
220
+ */
221
+ export default {
222
+ edit: BlockFieldsPanel,
223
+ hasSupport: hasBlockFieldsSupport,
224
+ attributeKeys: [],
225
+ supportsPatternEditing: true,
226
+ };
@@ -67,17 +67,11 @@ export function getUpdatedLinkAttributes( {
67
67
  };
68
68
  }
69
69
 
70
- export default function Link( { data, field, onChange, config = {} } ) {
70
+ export default function Link( { data, field, onChange } ) {
71
71
  const [ isLinkControlOpen, setIsLinkControlOpen ] = useState( false );
72
72
  const { popoverProps } = useInspectorPopoverPlacement( {
73
73
  isControl: true,
74
74
  } );
75
- const { fieldDef } = config;
76
- const updateAttributes = ( newValue ) => {
77
- const mappedChanges = field.setValue( { item: data, value: newValue } );
78
- onChange( mappedChanges );
79
- };
80
-
81
75
  const value = field.getValue( { item: data } );
82
76
  const url = value?.url;
83
77
  const rel = value?.rel || '';
@@ -145,52 +139,20 @@ export default function Link( { data, field, onChange, config = {} } ) {
145
139
  ...newValues,
146
140
  } );
147
141
 
148
- // Build update object dynamically based on what's in the mapping
149
- const updateValue = { ...value };
150
-
151
- if ( fieldDef?.mapping ) {
152
- Object.keys( fieldDef.mapping ).forEach(
153
- ( key ) => {
154
- if ( key === 'href' || key === 'url' ) {
155
- updateValue[ key ] =
156
- updatedAttrs.url;
157
- } else if ( key === 'rel' ) {
158
- updateValue[ key ] =
159
- updatedAttrs.rel;
160
- } else if (
161
- key === 'target' ||
162
- key === 'linkTarget'
163
- ) {
164
- updateValue[ key ] =
165
- updatedAttrs.linkTarget;
166
- }
167
- }
168
- );
169
- }
170
-
171
- updateAttributes( updateValue );
142
+ onChange(
143
+ field.setValue( {
144
+ item: data,
145
+ value: updatedAttrs,
146
+ } )
147
+ );
172
148
  } }
173
149
  onRemove={ () => {
174
- // Remove all link-related properties based on what's in the mapping
175
- const removeValue = {};
176
-
177
- if ( fieldDef?.mapping ) {
178
- Object.keys( fieldDef.mapping ).forEach(
179
- ( key ) => {
180
- if (
181
- key === 'href' ||
182
- key === 'url' ||
183
- key === 'rel' ||
184
- key === 'target' ||
185
- key === 'linkTarget'
186
- ) {
187
- removeValue[ key ] = undefined;
188
- }
189
- }
190
- );
191
- }
192
-
193
- updateAttributes( removeValue );
150
+ onChange(
151
+ field.setValue( {
152
+ item: data,
153
+ value: {},
154
+ } )
155
+ );
194
156
  } }
195
157
  />
196
158
  </Popover>