@wordpress/block-editor 12.19.4 → 12.19.5

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 (90) hide show
  1. package/build/components/block-actions/index.js +45 -32
  2. package/build/components/block-actions/index.js.map +1 -1
  3. package/build/components/block-bindings-toolbar-indicator/index.js +25 -0
  4. package/build/components/block-bindings-toolbar-indicator/index.js.map +1 -0
  5. package/build/components/block-list/use-block-props/index.js +8 -1
  6. package/build/components/block-list/use-block-props/index.js.map +1 -1
  7. package/build/components/block-list/use-block-props/use-selected-block-event-handlers.js +3 -3
  8. package/build/components/block-list/use-block-props/use-selected-block-event-handlers.js.map +1 -1
  9. package/build/components/block-settings-menu/block-settings-dropdown.js +12 -10
  10. package/build/components/block-settings-menu/block-settings-dropdown.js.map +1 -1
  11. package/build/components/block-toolbar/index.js +14 -5
  12. package/build/components/block-toolbar/index.js.map +1 -1
  13. package/build/components/list-view/block-select-button.js +10 -2
  14. package/build/components/list-view/block-select-button.js.map +1 -1
  15. package/build/components/list-view/use-clipboard-handler.js +2 -1
  16. package/build/components/list-view/use-clipboard-handler.js.map +1 -1
  17. package/build/components/rich-text/index.js +1 -1
  18. package/build/components/rich-text/index.js.map +1 -1
  19. package/build/components/url-popover/index.js +3 -3
  20. package/build/components/url-popover/index.js.map +1 -1
  21. package/build/components/writing-flow/use-clipboard-handler.js +2 -1
  22. package/build/components/writing-flow/use-clipboard-handler.js.map +1 -1
  23. package/build/components/writing-flow/utils.js +23 -6
  24. package/build/components/writing-flow/utils.js.map +1 -1
  25. package/build/hooks/block-hooks.js +26 -2
  26. package/build/hooks/block-hooks.js.map +1 -1
  27. package/build/hooks/use-bindings-attributes.js +171 -46
  28. package/build/hooks/use-bindings-attributes.js.map +1 -1
  29. package/build/private-apis.js +2 -0
  30. package/build/private-apis.js.map +1 -1
  31. package/build/store/actions.js +40 -6
  32. package/build/store/actions.js.map +1 -1
  33. package/build-module/components/block-actions/index.js +45 -32
  34. package/build-module/components/block-actions/index.js.map +1 -1
  35. package/build-module/components/block-bindings-toolbar-indicator/index.js +18 -0
  36. package/build-module/components/block-bindings-toolbar-indicator/index.js.map +1 -0
  37. package/build-module/components/block-list/use-block-props/index.js +9 -2
  38. package/build-module/components/block-list/use-block-props/index.js.map +1 -1
  39. package/build-module/components/block-list/use-block-props/use-selected-block-event-handlers.js +3 -3
  40. package/build-module/components/block-list/use-block-props/use-selected-block-event-handlers.js.map +1 -1
  41. package/build-module/components/block-settings-menu/block-settings-dropdown.js +12 -10
  42. package/build-module/components/block-settings-menu/block-settings-dropdown.js.map +1 -1
  43. package/build-module/components/block-toolbar/index.js +14 -5
  44. package/build-module/components/block-toolbar/index.js.map +1 -1
  45. package/build-module/components/list-view/block-select-button.js +11 -3
  46. package/build-module/components/list-view/block-select-button.js.map +1 -1
  47. package/build-module/components/list-view/use-clipboard-handler.js +3 -2
  48. package/build-module/components/list-view/use-clipboard-handler.js.map +1 -1
  49. package/build-module/components/rich-text/index.js +2 -2
  50. package/build-module/components/rich-text/index.js.map +1 -1
  51. package/build-module/components/url-popover/index.js +3 -3
  52. package/build-module/components/url-popover/index.js.map +1 -1
  53. package/build-module/components/writing-flow/use-clipboard-handler.js +3 -2
  54. package/build-module/components/writing-flow/use-clipboard-handler.js.map +1 -1
  55. package/build-module/components/writing-flow/utils.js +22 -7
  56. package/build-module/components/writing-flow/utils.js.map +1 -1
  57. package/build-module/hooks/block-hooks.js +26 -2
  58. package/build-module/hooks/block-hooks.js.map +1 -1
  59. package/build-module/hooks/use-bindings-attributes.js +171 -46
  60. package/build-module/hooks/use-bindings-attributes.js.map +1 -1
  61. package/build-module/private-apis.js +2 -0
  62. package/build-module/private-apis.js.map +1 -1
  63. package/build-module/store/actions.js +40 -6
  64. package/build-module/store/actions.js.map +1 -1
  65. package/build-style/content-rtl.css +1 -0
  66. package/build-style/content.css +1 -0
  67. package/build-style/default-editor-styles-rtl.css +1 -0
  68. package/build-style/default-editor-styles.css +1 -0
  69. package/build-style/style-rtl.css +23 -0
  70. package/build-style/style.css +23 -0
  71. package/package.json +8 -8
  72. package/src/components/block-actions/index.js +57 -47
  73. package/src/components/block-bindings-toolbar-indicator/index.js +20 -0
  74. package/src/components/block-bindings-toolbar-indicator/style.scss +14 -0
  75. package/src/components/block-list/use-block-props/index.js +12 -2
  76. package/src/components/block-list/use-block-props/use-selected-block-event-handlers.js +3 -7
  77. package/src/components/block-settings-menu/block-settings-dropdown.js +12 -9
  78. package/src/components/block-toolbar/index.js +14 -4
  79. package/src/components/list-view/block-select-button.js +16 -2
  80. package/src/components/list-view/style.scss +8 -0
  81. package/src/components/list-view/use-clipboard-handler.js +3 -2
  82. package/src/components/rich-text/index.js +2 -2
  83. package/src/components/url-popover/index.js +5 -5
  84. package/src/components/writing-flow/use-clipboard-handler.js +3 -2
  85. package/src/components/writing-flow/utils.js +31 -16
  86. package/src/hooks/block-hooks.js +36 -3
  87. package/src/hooks/use-bindings-attributes.js +214 -62
  88. package/src/private-apis.js +2 -0
  89. package/src/store/actions.js +54 -14
  90. package/src/style.scss +1 -0
@@ -5,7 +5,7 @@ import {
5
5
  documentHasSelection,
6
6
  documentHasUncollapsedSelection,
7
7
  } from '@wordpress/dom';
8
- import { useDispatch, useSelect } from '@wordpress/data';
8
+ import { useDispatch, useRegistry, useSelect } from '@wordpress/data';
9
9
  import { useRefEffect } from '@wordpress/compose';
10
10
 
11
11
  /**
@@ -16,6 +16,7 @@ import { useNotifyCopy } from '../../utils/use-notify-copy';
16
16
  import { getPasteBlocks, setClipboardBlocks } from './utils';
17
17
 
18
18
  export default function useClipboardHandler() {
19
+ const registry = useRegistry();
19
20
  const {
20
21
  getBlocksByClientId,
21
22
  getSelectedBlockClientIds,
@@ -104,7 +105,7 @@ export default function useClipboardHandler() {
104
105
  blocks = [ head, ...inBetweenBlocks, tail ];
105
106
  }
106
107
 
107
- setClipboardBlocks( event, blocks );
108
+ setClipboardBlocks( event, blocks, registry );
108
109
  }
109
110
  }
110
111
 
@@ -8,36 +8,51 @@ import {
8
8
  pasteHandler,
9
9
  findTransform,
10
10
  getBlockTransforms,
11
+ store as blocksStore,
11
12
  } from '@wordpress/blocks';
12
13
 
13
14
  /**
14
15
  * Internal dependencies
15
16
  */
16
17
  import { getPasteEventData } from '../../utils/pasting';
18
+ import { store as blockEditorStore } from '../../store';
19
+
20
+ export const requiresWrapperOnCopy = Symbol( 'requiresWrapperOnCopy' );
17
21
 
18
22
  /**
19
23
  * Sets the clipboard data for the provided blocks, with both HTML and plain
20
24
  * text representations.
21
25
  *
22
- * @param {ClipboardEvent} event Clipboard event.
23
- * @param {WPBlock[]} blocks Blocks to set as clipboard data.
26
+ * @param {ClipboardEvent} event Clipboard event.
27
+ * @param {WPBlock[]} blocks Blocks to set as clipboard data.
28
+ * @param {Object} registry The registry to select from.
24
29
  */
25
- export function setClipboardBlocks( event, blocks ) {
30
+ export function setClipboardBlocks( event, blocks, registry ) {
26
31
  let _blocks = blocks;
27
- const wrapperBlockName = event.clipboardData.getData(
28
- '__unstableWrapperBlockName'
29
- );
30
32
 
31
- if ( wrapperBlockName ) {
32
- _blocks = createBlock(
33
- wrapperBlockName,
34
- JSON.parse(
35
- event.clipboardData.getData(
36
- '__unstableWrapperBlockAttributes'
37
- )
38
- ),
39
- _blocks
40
- );
33
+ const [ firstBlock ] = blocks;
34
+
35
+ if ( firstBlock ) {
36
+ const firstBlockType = registry
37
+ .select( blocksStore )
38
+ .getBlockType( firstBlock.name );
39
+
40
+ if ( firstBlockType[ requiresWrapperOnCopy ] ) {
41
+ const { getBlockRootClientId, getBlockName, getBlockAttributes } =
42
+ registry.select( blockEditorStore );
43
+ const wrapperBlockClientId = getBlockRootClientId(
44
+ firstBlock.clientId
45
+ );
46
+ const wrapperBlockName = getBlockName( wrapperBlockClientId );
47
+
48
+ if ( wrapperBlockName ) {
49
+ _blocks = createBlock(
50
+ wrapperBlockName,
51
+ getBlockAttributes( wrapperBlockClientId ),
52
+ _blocks
53
+ );
54
+ }
55
+ }
41
56
  }
42
57
 
43
58
  const serialized = serialize( _blocks );
@@ -19,18 +19,28 @@ import { store as blockEditorStore } from '../store';
19
19
 
20
20
  const EMPTY_OBJECT = {};
21
21
 
22
- function BlockHooksControlPure( { name, clientId } ) {
22
+ function BlockHooksControlPure( {
23
+ name,
24
+ clientId,
25
+ metadata: { ignoredHookedBlocks = [] } = {},
26
+ } ) {
23
27
  const blockTypes = useSelect(
24
28
  ( select ) => select( blocksStore ).getBlockTypes(),
25
29
  []
26
30
  );
27
31
 
32
+ // A hooked block added via a filter will not be exposed through a block
33
+ // type's `blockHooks` property; however, if the containing layout has been
34
+ // modified, it will be present in the anchor block's `ignoredHookedBlocks`
35
+ // metadata.
28
36
  const hookedBlocksForCurrentBlock = useMemo(
29
37
  () =>
30
38
  blockTypes?.filter(
31
- ( { blockHooks } ) => blockHooks && name in blockHooks
39
+ ( { name: blockName, blockHooks } ) =>
40
+ ( blockHooks && name in blockHooks ) ||
41
+ ignoredHookedBlocks.includes( blockName )
32
42
  ),
33
- [ blockTypes, name ]
43
+ [ blockTypes, name, ignoredHookedBlocks ]
34
44
  );
35
45
 
36
46
  const { blockIndex, rootClientId, innerBlocksLength } = useSelect(
@@ -79,6 +89,16 @@ function BlockHooksControlPure( { name, clientId } ) {
79
89
  // inserted and then moved around a bit by the user.
80
90
  candidates = getBlocks( clientId );
81
91
  break;
92
+
93
+ case undefined:
94
+ // If we haven't found a blockHooks field with a relative position for the hooked
95
+ // block, it means that it was added by a filter. In this case, we look for the block
96
+ // both among the current block's siblings and its children.
97
+ candidates = [
98
+ ...getBlocks( rootClientId ),
99
+ ...getBlocks( clientId ),
100
+ ];
101
+ break;
82
102
  }
83
103
 
84
104
  const hookedBlock = candidates?.find(
@@ -151,6 +171,18 @@ function BlockHooksControlPure( { name, clientId } ) {
151
171
  false
152
172
  );
153
173
  break;
174
+
175
+ case undefined:
176
+ // If we do not know the relative position, it is because the block was
177
+ // added via a filter. In this case, we default to inserting it after the
178
+ // current block.
179
+ insertBlock(
180
+ block,
181
+ blockIndex + 1,
182
+ rootClientId, // Insert as a child of the current block's parent
183
+ false
184
+ );
185
+ break;
154
186
  }
155
187
  };
156
188
 
@@ -219,6 +251,7 @@ function BlockHooksControlPure( { name, clientId } ) {
219
251
 
220
252
  export default {
221
253
  edit: BlockHooksControlPure,
254
+ attributeKeys: [ 'metadata' ],
222
255
  hasSupport() {
223
256
  return true;
224
257
  },
@@ -4,12 +4,13 @@
4
4
  import { getBlockType, store as blocksStore } from '@wordpress/blocks';
5
5
  import { createHigherOrderComponent } from '@wordpress/compose';
6
6
  import { useSelect } from '@wordpress/data';
7
+ import { useLayoutEffect, useCallback, useState } from '@wordpress/element';
7
8
  import { addFilter } from '@wordpress/hooks';
9
+ import { RichTextData } from '@wordpress/rich-text';
10
+
8
11
  /**
9
12
  * Internal dependencies
10
13
  */
11
- import { store as blockEditorStore } from '../store';
12
- import { useBlockEditContext } from '../components/block-edit/context';
13
14
  import { unlock } from '../lock-unlock';
14
15
 
15
16
  /** @typedef {import('@wordpress/compose').WPHigherOrderComponent} WPHigherOrderComponent */
@@ -22,87 +23,238 @@ import { unlock } from '../lock-unlock';
22
23
  * @return {WPHigherOrderComponent} Higher-order component.
23
24
  */
24
25
 
25
- export const BLOCK_BINDINGS_ALLOWED_BLOCKS = {
26
+ const BLOCK_BINDINGS_ALLOWED_BLOCKS = {
26
27
  'core/paragraph': [ 'content' ],
27
28
  'core/heading': [ 'content' ],
28
29
  'core/image': [ 'url', 'title', 'alt' ],
29
30
  'core/button': [ 'url', 'text', 'linkTarget' ],
30
31
  };
31
32
 
32
- const createEditFunctionWithBindingsAttribute = () =>
33
- createHigherOrderComponent(
34
- ( BlockEdit ) => ( props ) => {
35
- const { clientId, name: blockName } = useBlockEditContext();
36
- const blockBindingsSources = unlock(
37
- useSelect( blocksStore )
38
- ).getAllBlockBindingsSources();
39
- const { getBlockAttributes } = useSelect( blockEditorStore );
40
-
41
- const updatedAttributes = getBlockAttributes( clientId );
42
- if ( updatedAttributes?.metadata?.bindings ) {
43
- Object.entries( updatedAttributes.metadata.bindings ).forEach(
44
- ( [ attributeName, settings ] ) => {
45
- const source = blockBindingsSources[ settings.source ];
46
-
47
- if ( source && source.useSource ) {
48
- // Second argument (`updateMetaValue`) will be used to update the value in the future.
49
- const {
50
- placeholder,
51
- useValue: [ metaValue = null ] = [],
52
- } = source.useSource( props, settings.args );
53
-
54
- if ( placeholder && ! metaValue ) {
55
- // If the attribute is `src` or `href`, a placeholder can't be used because it is not a valid url.
56
- // Adding this workaround until attributes and metadata fields types are improved and include `url`.
57
- const htmlAttribute =
58
- getBlockType( blockName ).attributes[
59
- attributeName
60
- ].attribute;
61
- if (
62
- htmlAttribute === 'src' ||
63
- htmlAttribute === 'href'
64
- ) {
65
- updatedAttributes[ attributeName ] = null;
66
- } else {
67
- updatedAttributes[ attributeName ] =
68
- placeholder;
69
- }
70
- }
71
-
72
- if ( metaValue ) {
73
- updatedAttributes[ attributeName ] = metaValue;
74
- }
75
- }
76
- }
77
- );
33
+ /**
34
+ * Based on the given block name,
35
+ * check if it is possible to bind the block.
36
+ *
37
+ * @param {string} blockName - The block name.
38
+ * @return {boolean} Whether it is possible to bind the block to sources.
39
+ */
40
+ export function canBindBlock( blockName ) {
41
+ return blockName in BLOCK_BINDINGS_ALLOWED_BLOCKS;
42
+ }
43
+
44
+ /**
45
+ * Based on the given block name and attribute name,
46
+ * check if it is possible to bind the block attribute.
47
+ *
48
+ * @param {string} blockName - The block name.
49
+ * @param {string} attributeName - The attribute name.
50
+ * @return {boolean} Whether it is possible to bind the block attribute.
51
+ */
52
+ export function canBindAttribute( blockName, attributeName ) {
53
+ return (
54
+ canBindBlock( blockName ) &&
55
+ BLOCK_BINDINGS_ALLOWED_BLOCKS[ blockName ].includes( attributeName )
56
+ );
57
+ }
58
+
59
+ /**
60
+ * This component is responsible for detecting and
61
+ * propagating data changes from the source to the block.
62
+ *
63
+ * @param {Object} props - The component props.
64
+ * @param {string} props.attrName - The attribute name.
65
+ * @param {Object} props.blockProps - The block props with bound attribute.
66
+ * @param {Object} props.source - Source handler.
67
+ * @param {Object} props.args - The arguments to pass to the source.
68
+ * @param {Function} props.onPropValueChange - The function to call when the attribute value changes.
69
+ * @return {null} Data-handling component. Render nothing.
70
+ */
71
+ const BindingConnector = ( {
72
+ args,
73
+ attrName,
74
+ blockProps,
75
+ source,
76
+ onPropValueChange,
77
+ } ) => {
78
+ const { placeholder, value: propValue } = source.useSource(
79
+ blockProps,
80
+ args
81
+ );
82
+
83
+ const { name: blockName } = blockProps;
84
+ const attrValue = blockProps.attributes[ attrName ];
85
+
86
+ const updateBoundAttibute = useCallback(
87
+ ( newAttrValue, prevAttrValue ) => {
88
+ /*
89
+ * If the attribute is a RichTextData instance,
90
+ * (core/paragraph, core/heading, core/button, etc.)
91
+ * compare its HTML representation with the new value.
92
+ *
93
+ * To do: it looks like a workaround.
94
+ * Consider improving the attribute and metadata fields types.
95
+ */
96
+ if ( prevAttrValue instanceof RichTextData ) {
97
+ // Bail early if the Rich Text value is the same.
98
+ if ( prevAttrValue.toHTMLString() === newAttrValue ) {
99
+ return;
100
+ }
101
+
102
+ /*
103
+ * To preserve the value type,
104
+ * convert the new value to a RichTextData instance.
105
+ */
106
+ newAttrValue = RichTextData.fromHTMLString( newAttrValue );
107
+ }
108
+
109
+ if ( prevAttrValue === newAttrValue ) {
110
+ return;
78
111
  }
79
112
 
80
- return (
113
+ onPropValueChange( { [ attrName ]: newAttrValue } );
114
+ },
115
+ [ attrName, onPropValueChange ]
116
+ );
117
+
118
+ useLayoutEffect( () => {
119
+ if ( typeof propValue !== 'undefined' ) {
120
+ updateBoundAttibute( propValue, attrValue );
121
+ } else if ( placeholder ) {
122
+ /*
123
+ * Placeholder fallback.
124
+ * If the attribute is `src` or `href`,
125
+ * a placeholder can't be used because it is not a valid url.
126
+ * Adding this workaround until
127
+ * attributes and metadata fields types are improved and include `url`.
128
+ */
129
+ const htmlAttribute =
130
+ getBlockType( blockName ).attributes[ attrName ].attribute;
131
+
132
+ if ( htmlAttribute === 'src' || htmlAttribute === 'href' ) {
133
+ updateBoundAttibute( null );
134
+ return;
135
+ }
136
+
137
+ updateBoundAttibute( placeholder );
138
+ }
139
+ }, [
140
+ updateBoundAttibute,
141
+ propValue,
142
+ attrValue,
143
+ placeholder,
144
+ blockName,
145
+ attrName,
146
+ ] );
147
+
148
+ return null;
149
+ };
150
+
151
+ /**
152
+ * BlockBindingBridge acts like a component wrapper
153
+ * that connects the bound attributes of a block
154
+ * to the source handlers.
155
+ * For this, it creates a BindingConnector for each bound attribute.
156
+ *
157
+ * @param {Object} props - The component props.
158
+ * @param {Object} props.blockProps - The BlockEdit props object.
159
+ * @param {Object} props.bindings - The block bindings settings.
160
+ * @param {Function} props.onPropValueChange - The function to call when the attribute value changes.
161
+ * @return {null} Data-handling component. Render nothing.
162
+ */
163
+ function BlockBindingBridge( { blockProps, bindings, onPropValueChange } ) {
164
+ const blockBindingsSources = unlock(
165
+ useSelect( blocksStore )
166
+ ).getAllBlockBindingsSources();
167
+
168
+ return (
169
+ <>
170
+ { Object.entries( bindings ).map(
171
+ ( [ attrName, boundAttribute ] ) => {
172
+ // Bail early if the block doesn't have a valid source handler.
173
+ const source =
174
+ blockBindingsSources[ boundAttribute.source ];
175
+ if ( ! source?.useSource ) {
176
+ return null;
177
+ }
178
+
179
+ return (
180
+ <BindingConnector
181
+ key={ attrName }
182
+ attrName={ attrName }
183
+ source={ source }
184
+ blockProps={ blockProps }
185
+ args={ boundAttribute.args }
186
+ onPropValueChange={ onPropValueChange }
187
+ />
188
+ );
189
+ }
190
+ ) }
191
+ </>
192
+ );
193
+ }
194
+
195
+ const withBlockBindingSupport = createHigherOrderComponent(
196
+ ( BlockEdit ) => ( props ) => {
197
+ /*
198
+ * Collect and update the bound attributes
199
+ * in a separate state.
200
+ */
201
+ const [ boundAttributes, setBoundAttributes ] = useState( {} );
202
+ const updateBoundAttributes = useCallback(
203
+ ( newAttributes ) =>
204
+ setBoundAttributes( ( prev ) => ( {
205
+ ...prev,
206
+ ...newAttributes,
207
+ } ) ),
208
+ []
209
+ );
210
+
211
+ /*
212
+ * Create binding object filtering
213
+ * only the attributes that can be bound.
214
+ */
215
+ const bindings = Object.fromEntries(
216
+ Object.entries( props.attributes.metadata?.bindings || {} ).filter(
217
+ ( [ attrName ] ) => canBindAttribute( props.name, attrName )
218
+ )
219
+ );
220
+
221
+ return (
222
+ <>
223
+ { Object.keys( bindings ).length > 0 && (
224
+ <BlockBindingBridge
225
+ blockProps={ props }
226
+ bindings={ bindings }
227
+ onPropValueChange={ updateBoundAttributes }
228
+ />
229
+ ) }
230
+
81
231
  <BlockEdit
82
- key="edit"
83
232
  { ...props }
84
- attributes={ updatedAttributes }
233
+ attributes={ { ...props.attributes, ...boundAttributes } }
85
234
  />
86
- );
87
- },
88
- 'useBoundAttributes'
89
- );
235
+ </>
236
+ );
237
+ },
238
+ 'withBlockBindingSupport'
239
+ );
90
240
 
91
241
  /**
92
242
  * Filters a registered block's settings to enhance a block's `edit` component
93
243
  * to upgrade bound attributes.
94
244
  *
95
- * @param {WPBlockSettings} settings Registered block settings.
96
- *
245
+ * @param {WPBlockSettings} settings - Registered block settings.
246
+ * @param {string} name - Block name.
97
247
  * @return {WPBlockSettings} Filtered block settings.
98
248
  */
99
- function shimAttributeSource( settings ) {
100
- if ( ! ( settings.name in BLOCK_BINDINGS_ALLOWED_BLOCKS ) ) {
249
+ function shimAttributeSource( settings, name ) {
250
+ if ( ! canBindBlock( name ) ) {
101
251
  return settings;
102
252
  }
103
- settings.edit = createEditFunctionWithBindingsAttribute()( settings.edit );
104
253
 
105
- return settings;
254
+ return {
255
+ ...settings,
256
+ edit: withBlockBindingSupport( settings.edit ),
257
+ };
106
258
  }
107
259
 
108
260
  addFilter(
@@ -27,6 +27,7 @@ import { ExperimentalBlockCanvas } from './components/block-canvas';
27
27
  import { getDuotoneFilter } from './components/duotone/utils';
28
28
  import { useFlashEditableBlocks } from './components/use-flash-editable-blocks';
29
29
  import { selectBlockPatternsKey } from './store/private-keys';
30
+ import { requiresWrapperOnCopy } from './components/writing-flow/utils';
30
31
  import { PrivateRichText } from './components/rich-text/';
31
32
 
32
33
  /**
@@ -59,5 +60,6 @@ lock( privateApis, {
59
60
  usesContextKey,
60
61
  useFlashEditableBlocks,
61
62
  selectBlockPatternsKey,
63
+ requiresWrapperOnCopy,
62
64
  PrivateRichText,
63
65
  } );
@@ -1575,7 +1575,7 @@ export const duplicateBlocks =
1575
1575
  };
1576
1576
 
1577
1577
  /**
1578
- * Action that inserts an empty block before a given block.
1578
+ * Action that inserts a default block before a given block.
1579
1579
  *
1580
1580
  * @param {string} clientId
1581
1581
  */
@@ -1591,16 +1591,34 @@ export const insertBeforeBlock =
1591
1591
  return;
1592
1592
  }
1593
1593
 
1594
- const firstSelectedIndex = select.getBlockIndex( clientId );
1595
- return dispatch.insertDefaultBlock(
1596
- {},
1597
- rootClientId,
1598
- firstSelectedIndex
1599
- );
1594
+ const blockIndex = select.getBlockIndex( clientId );
1595
+ const directInsertBlock = rootClientId
1596
+ ? select.getDirectInsertBlock( rootClientId )
1597
+ : null;
1598
+
1599
+ if ( ! directInsertBlock ) {
1600
+ return dispatch.insertDefaultBlock( {}, rootClientId, blockIndex );
1601
+ }
1602
+
1603
+ const copiedAttributes = {};
1604
+ if ( directInsertBlock.attributesToCopy ) {
1605
+ const attributes = select.getBlockAttributes( clientId );
1606
+ directInsertBlock.attributesToCopy.forEach( ( key ) => {
1607
+ if ( attributes[ key ] ) {
1608
+ copiedAttributes[ key ] = attributes[ key ];
1609
+ }
1610
+ } );
1611
+ }
1612
+
1613
+ const block = createBlock( directInsertBlock.name, {
1614
+ ...directInsertBlock.attributes,
1615
+ ...copiedAttributes,
1616
+ } );
1617
+ return dispatch.insertBlock( block, blockIndex, rootClientId );
1600
1618
  };
1601
1619
 
1602
1620
  /**
1603
- * Action that inserts an empty block after a given block.
1621
+ * Action that inserts a default block after a given block.
1604
1622
  *
1605
1623
  * @param {string} clientId
1606
1624
  */
@@ -1616,12 +1634,34 @@ export const insertAfterBlock =
1616
1634
  return;
1617
1635
  }
1618
1636
 
1619
- const firstSelectedIndex = select.getBlockIndex( clientId );
1620
- return dispatch.insertDefaultBlock(
1621
- {},
1622
- rootClientId,
1623
- firstSelectedIndex + 1
1624
- );
1637
+ const blockIndex = select.getBlockIndex( clientId );
1638
+ const directInsertBlock = rootClientId
1639
+ ? select.getDirectInsertBlock( rootClientId )
1640
+ : null;
1641
+
1642
+ if ( ! directInsertBlock ) {
1643
+ return dispatch.insertDefaultBlock(
1644
+ {},
1645
+ rootClientId,
1646
+ blockIndex + 1
1647
+ );
1648
+ }
1649
+
1650
+ const copiedAttributes = {};
1651
+ if ( directInsertBlock.attributesToCopy ) {
1652
+ const attributes = select.getBlockAttributes( clientId );
1653
+ directInsertBlock.attributesToCopy.forEach( ( key ) => {
1654
+ if ( attributes[ key ] ) {
1655
+ copiedAttributes[ key ] = attributes[ key ];
1656
+ }
1657
+ } );
1658
+ }
1659
+
1660
+ const block = createBlock( directInsertBlock.name, {
1661
+ ...directInsertBlock.attributes,
1662
+ ...copiedAttributes,
1663
+ } );
1664
+ return dispatch.insertBlock( block, blockIndex + 1, rootClientId );
1625
1665
  };
1626
1666
 
1627
1667
  /**
package/src/style.scss CHANGED
@@ -1,5 +1,6 @@
1
1
  @import "./autocompleters/style.scss";
2
2
  @import "./components/block-alignment-control/style.scss";
3
+ @import "./components/block-bindings-toolbar-indicator/style.scss";
3
4
  @import "./components/block-canvas/style.scss";
4
5
  @import "./components/block-icon/style.scss";
5
6
  @import "./components/block-inspector/style.scss";