-
Notifications
You must be signed in to change notification settings - Fork 4k
/
use-nested-settings-update.js
155 lines (140 loc) · 5.54 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
144
145
146
147
148
149
150
151
152
153
154
155
/**
* WordPress dependencies
*/
import { useLayoutEffect, useMemo } from '@wordpress/element';
import { useSelect, useDispatch, useRegistry } from '@wordpress/data';
/**
* 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 {string[]} prioritizedInserterBlocks Block names and/or block variations to be prioritized in the inserter, in the format {blockName}/{variationName}.
* @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,
prioritizedInserterBlocks,
__experimentalDefaultBlock,
__experimentalDirectInsert,
templateLock,
captureToolbars,
orientation,
layout
) {
const { updateBlockListSettings } = useDispatch( blockEditorStore );
const registry = useRegistry();
const { parentLock } = useSelect(
( select ) => {
const rootClientId =
select( blockEditorStore ).getBlockRootClientId( clientId );
return {
parentLock:
select( blockEditorStore ).getTemplateLock( rootClientId ),
};
},
[ clientId ]
);
// Memoize allowedBlocks and prioritisedInnerBlocks based on the contents
// of the arrays. Implementors often pass a new array on every render,
// and the contents of the arrays are just strings, so the entire array
// can be passed as dependencies.
const _allowedBlocks = useMemo(
() => allowedBlocks,
// eslint-disable-next-line react-hooks/exhaustive-deps
allowedBlocks
);
const _prioritizedInserterBlocks = useMemo(
() => prioritizedInserterBlocks,
// eslint-disable-next-line react-hooks/exhaustive-deps
prioritizedInserterBlocks
);
const _templateLock =
templateLock === undefined || parentLock === 'contentOnly'
? parentLock
: templateLock;
useLayoutEffect( () => {
const newSettings = {
allowedBlocks: _allowedBlocks,
prioritizedInserterBlocks: _prioritizedInserterBlocks,
templateLock: _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;
}
// 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,
_allowedBlocks,
_prioritizedInserterBlocks,
_templateLock,
__experimentalDefaultBlock,
__experimentalDirectInsert,
captureToolbars,
orientation,
updateBlockListSettings,
layout,
registry,
] );
}