-
Notifications
You must be signed in to change notification settings - Fork 4.3k
/
Copy pathuse-nested-settings-update.js
143 lines (131 loc) · 5.11 KB
/
use-nested-settings-update.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
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
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
/**
* WordPress dependencies
*/
import { useLayoutEffect, useMemo } from '@wordpress/element';
import { useSelect, useDispatch, useRegistry } from '@wordpress/data';
import isShallowEqual from '@wordpress/is-shallow-equal';
/**
* Internal dependencies
*/
import { store as blockEditorStore } from '../../store';
import { getLayoutType } from '../../layouts';
/** @typedef {import('../../selectors').WPDirectInsertBlock } WPDirectInsertBlock */
const pendingSettingsUpdates = new WeakMap();
/**
* This hook is a side effect which updates the block-editor store when changes
* happen to inner block settings. The given props are transformed into a
* settings object, and if that is different from the current settings object in
* the block-editor store, then the store is updated with the new settings which
* came from props.
*
* @param {string} clientId The client ID of the block to update.
* @param {string[]} allowedBlocks An array of block names which are permitted
* in inner blocks.
* @param {?WPDirectInsertBlock} __experimentalDefaultBlock The default block to insert: [ blockName, { blockAttributes } ].
* @param {?Function|boolean} __experimentalDirectInsert If a default block should be inserted directly by the
* appender.
* @param {string} [templateLock] The template lock specified for the inner
* blocks component. (e.g. "all")
* @param {boolean} captureToolbars Whether or children toolbars should be shown
* in the inner blocks component rather than on
* the child block.
* @param {string} orientation The direction in which the block
* should face.
* @param {Object} layout The layout object for the block container.
*/
export default function useNestedSettingsUpdate(
clientId,
allowedBlocks,
__experimentalDefaultBlock,
__experimentalDirectInsert,
templateLock,
captureToolbars,
orientation,
layout
) {
const { updateBlockListSettings } = useDispatch( blockEditorStore );
const registry = useRegistry();
const { blockListSettings, parentLock } = useSelect(
( select ) => {
const rootClientId =
select( blockEditorStore ).getBlockRootClientId( clientId );
return {
blockListSettings:
select( blockEditorStore ).getBlockListSettings( clientId ),
parentLock:
select( blockEditorStore ).getTemplateLock( rootClientId ),
};
},
[ clientId ]
);
// Memoize as inner blocks implementors often pass a new array on every
// render.
const _allowedBlocks = useMemo( () => allowedBlocks, allowedBlocks );
useLayoutEffect( () => {
const newSettings = {
allowedBlocks: _allowedBlocks,
templateLock:
templateLock === undefined || parentLock === 'contentOnly'
? parentLock
: templateLock,
};
// These values are not defined for RN, so only include them if they
// are defined.
if ( captureToolbars !== undefined ) {
newSettings.__experimentalCaptureToolbars = captureToolbars;
}
// Orientation depends on layout,
// ideally the separate orientation prop should be deprecated.
if ( orientation !== undefined ) {
newSettings.orientation = orientation;
} else {
const layoutType = getLayoutType( layout?.type );
newSettings.orientation = layoutType.getOrientation( layout );
}
if ( __experimentalDefaultBlock !== undefined ) {
newSettings.__experimentalDefaultBlock = __experimentalDefaultBlock;
}
if ( __experimentalDirectInsert !== undefined ) {
newSettings.__experimentalDirectInsert = __experimentalDirectInsert;
}
if ( ! isShallowEqual( blockListSettings, newSettings ) ) {
// Batch updates to block list settings to avoid triggering cascading renders
// for each container block included in a tree and optimize initial render.
// To avoid triggering updateBlockListSettings for each container block
// causing X re-renderings for X container blocks,
// we batch all the updatedBlockListSettings in a single "data" batch
// which results in a single re-render.
if ( ! pendingSettingsUpdates.get( registry ) ) {
pendingSettingsUpdates.set( registry, [] );
}
pendingSettingsUpdates
.get( registry )
.push( [ clientId, newSettings ] );
window.queueMicrotask( () => {
if ( pendingSettingsUpdates.get( registry )?.length ) {
registry.batch( () => {
pendingSettingsUpdates
.get( registry )
.forEach( ( args ) => {
updateBlockListSettings( ...args );
} );
pendingSettingsUpdates.set( registry, [] );
} );
}
} );
}
}, [
clientId,
blockListSettings,
_allowedBlocks,
__experimentalDefaultBlock,
__experimentalDirectInsert,
templateLock,
parentLock,
captureToolbars,
orientation,
updateBlockListSettings,
layout,
registry,
] );
}