@wordpress/block-editor 12.19.4 → 12.19.6
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.
- package/build/components/block-actions/index.js +45 -32
- package/build/components/block-actions/index.js.map +1 -1
- package/build/components/block-bindings-toolbar-indicator/index.js +25 -0
- package/build/components/block-bindings-toolbar-indicator/index.js.map +1 -0
- package/build/components/block-list/use-block-props/index.js +8 -1
- package/build/components/block-list/use-block-props/index.js.map +1 -1
- package/build/components/block-list/use-block-props/use-selected-block-event-handlers.js +3 -3
- package/build/components/block-list/use-block-props/use-selected-block-event-handlers.js.map +1 -1
- package/build/components/block-settings-menu/block-settings-dropdown.js +12 -10
- package/build/components/block-settings-menu/block-settings-dropdown.js.map +1 -1
- package/build/components/block-toolbar/index.js +14 -5
- package/build/components/block-toolbar/index.js.map +1 -1
- package/build/components/global-styles/use-global-styles-output.js +13 -13
- package/build/components/global-styles/use-global-styles-output.js.map +1 -1
- package/build/components/list-view/block-select-button.js +10 -2
- package/build/components/list-view/block-select-button.js.map +1 -1
- package/build/components/list-view/use-clipboard-handler.js +2 -1
- package/build/components/list-view/use-clipboard-handler.js.map +1 -1
- package/build/components/rich-text/index.js +1 -1
- package/build/components/rich-text/index.js.map +1 -1
- package/build/components/url-popover/index.js +3 -3
- package/build/components/url-popover/index.js.map +1 -1
- package/build/components/use-moving-animation/index.js +4 -0
- package/build/components/use-moving-animation/index.js.map +1 -1
- package/build/components/writing-flow/use-clipboard-handler.js +2 -1
- package/build/components/writing-flow/use-clipboard-handler.js.map +1 -1
- package/build/components/writing-flow/utils.js +23 -6
- package/build/components/writing-flow/utils.js.map +1 -1
- package/build/hooks/block-hooks.js +26 -2
- package/build/hooks/block-hooks.js.map +1 -1
- package/build/hooks/use-bindings-attributes.js +171 -46
- package/build/hooks/use-bindings-attributes.js.map +1 -1
- package/build/private-apis.js +2 -0
- package/build/private-apis.js.map +1 -1
- package/build/store/actions.js +40 -6
- package/build/store/actions.js.map +1 -1
- package/build-module/components/block-actions/index.js +45 -32
- package/build-module/components/block-actions/index.js.map +1 -1
- package/build-module/components/block-bindings-toolbar-indicator/index.js +18 -0
- package/build-module/components/block-bindings-toolbar-indicator/index.js.map +1 -0
- package/build-module/components/block-list/use-block-props/index.js +9 -2
- package/build-module/components/block-list/use-block-props/index.js.map +1 -1
- package/build-module/components/block-list/use-block-props/use-selected-block-event-handlers.js +3 -3
- package/build-module/components/block-list/use-block-props/use-selected-block-event-handlers.js.map +1 -1
- package/build-module/components/block-settings-menu/block-settings-dropdown.js +12 -10
- package/build-module/components/block-settings-menu/block-settings-dropdown.js.map +1 -1
- package/build-module/components/block-toolbar/index.js +14 -5
- package/build-module/components/block-toolbar/index.js.map +1 -1
- package/build-module/components/global-styles/use-global-styles-output.js +13 -13
- package/build-module/components/global-styles/use-global-styles-output.js.map +1 -1
- package/build-module/components/list-view/block-select-button.js +11 -3
- package/build-module/components/list-view/block-select-button.js.map +1 -1
- package/build-module/components/list-view/use-clipboard-handler.js +3 -2
- package/build-module/components/list-view/use-clipboard-handler.js.map +1 -1
- package/build-module/components/rich-text/index.js +2 -2
- package/build-module/components/rich-text/index.js.map +1 -1
- package/build-module/components/url-popover/index.js +3 -3
- package/build-module/components/url-popover/index.js.map +1 -1
- package/build-module/components/use-moving-animation/index.js +4 -0
- package/build-module/components/use-moving-animation/index.js.map +1 -1
- package/build-module/components/writing-flow/use-clipboard-handler.js +3 -2
- package/build-module/components/writing-flow/use-clipboard-handler.js.map +1 -1
- package/build-module/components/writing-flow/utils.js +22 -7
- package/build-module/components/writing-flow/utils.js.map +1 -1
- package/build-module/hooks/block-hooks.js +26 -2
- package/build-module/hooks/block-hooks.js.map +1 -1
- package/build-module/hooks/use-bindings-attributes.js +171 -46
- package/build-module/hooks/use-bindings-attributes.js.map +1 -1
- package/build-module/private-apis.js +2 -0
- package/build-module/private-apis.js.map +1 -1
- package/build-module/store/actions.js +40 -6
- package/build-module/store/actions.js.map +1 -1
- package/build-style/content-rtl.css +1 -0
- package/build-style/content.css +1 -0
- package/build-style/default-editor-styles-rtl.css +1 -0
- package/build-style/default-editor-styles.css +1 -0
- package/build-style/style-rtl.css +23 -0
- package/build-style/style.css +23 -0
- package/package.json +8 -8
- package/src/components/block-actions/index.js +57 -47
- package/src/components/block-bindings-toolbar-indicator/index.js +20 -0
- package/src/components/block-bindings-toolbar-indicator/style.scss +14 -0
- package/src/components/block-list/use-block-props/index.js +12 -2
- package/src/components/block-list/use-block-props/use-selected-block-event-handlers.js +3 -7
- package/src/components/block-settings-menu/block-settings-dropdown.js +12 -9
- package/src/components/block-toolbar/index.js +14 -4
- package/src/components/global-styles/use-global-styles-output.js +11 -11
- package/src/components/list-view/block-select-button.js +16 -2
- package/src/components/list-view/style.scss +8 -0
- package/src/components/list-view/use-clipboard-handler.js +3 -2
- package/src/components/rich-text/index.js +2 -2
- package/src/components/url-popover/index.js +5 -5
- package/src/components/use-moving-animation/index.js +1 -0
- package/src/components/writing-flow/use-clipboard-handler.js +3 -2
- package/src/components/writing-flow/utils.js +31 -16
- package/src/hooks/block-hooks.js +36 -3
- package/src/hooks/use-bindings-attributes.js +214 -62
- package/src/private-apis.js +2 -0
- package/src/store/actions.js +54 -14
- 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
|
|
23
|
-
* @param {WPBlock[]} blocks
|
|
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
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
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 );
|
package/src/hooks/block-hooks.js
CHANGED
|
@@ -19,18 +19,28 @@ import { store as blockEditorStore } from '../store';
|
|
|
19
19
|
|
|
20
20
|
const EMPTY_OBJECT = {};
|
|
21
21
|
|
|
22
|
-
function BlockHooksControlPure( {
|
|
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 } ) =>
|
|
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
|
-
|
|
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
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
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
|
-
|
|
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={
|
|
233
|
+
attributes={ { ...props.attributes, ...boundAttributes } }
|
|
85
234
|
/>
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
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 ( ! (
|
|
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
|
|
254
|
+
return {
|
|
255
|
+
...settings,
|
|
256
|
+
edit: withBlockBindingSupport( settings.edit ),
|
|
257
|
+
};
|
|
106
258
|
}
|
|
107
259
|
|
|
108
260
|
addFilter(
|
package/src/private-apis.js
CHANGED
|
@@ -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
|
} );
|
package/src/store/actions.js
CHANGED
|
@@ -1575,7 +1575,7 @@ export const duplicateBlocks =
|
|
|
1575
1575
|
};
|
|
1576
1576
|
|
|
1577
1577
|
/**
|
|
1578
|
-
* Action that inserts
|
|
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
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
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
|
|
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
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
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";
|