-
Notifications
You must be signed in to change notification settings - Fork 4.3k
/
Copy pathblock-list-context.native.js
172 lines (158 loc) · 4.63 KB
/
block-list-context.native.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
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
/**
* WordPress dependencies
*/
import { createContext, useContext } from '@wordpress/element';
/**
* Internal dependencies
*/
import { orderBy } from '../../utils/sorting';
export const DEFAULT_BLOCK_LIST_CONTEXT = {
scrollRef: null,
blocksLayouts: { current: {} },
findBlockLayoutByClientId,
getBlockLayoutsOrderedByYCoord,
findBlockLayoutByPosition,
updateBlocksLayouts,
};
const Context = createContext( DEFAULT_BLOCK_LIST_CONTEXT );
const { Provider, Consumer } = Context;
/**
* Finds a block's layout data by position.
*
* @param {Object} data Blocks layouts object.
* @param {Object} position Position to use for finding the block.
* @param {number} position.x X coordinate.
* @param {number} position.y Y coordinate.
*
* @return {Object|undefined} Found block layout data that matches the provided position. If none is found, `undefined` will be returned.
*/
function findBlockLayoutByPosition( data, position ) {
// Only enabled for root level blocks
return Object.values( data ).find( ( block ) => {
return (
position.x >= block.x &&
position.x <= block.x + block.width &&
position.y >= block.y &&
position.y <= block.y + block.height
);
} );
}
/**
* Finds a block's layout data by its client Id.
*
* @param {Object} data Blocks layouts object.
* @param {string} clientId Block's clientId.
*
* @return {Object} Found block layout data.
*/
function findBlockLayoutByClientId( data, clientId ) {
return Object.entries( data ).reduce( ( acc, entry ) => {
const item = entry[ 1 ];
if ( acc ) {
return acc;
}
if ( item?.clientId === clientId ) {
return item;
}
if ( item?.innerBlocks && Object.keys( item.innerBlocks ).length > 0 ) {
return findBlockLayoutByClientId( item.innerBlocks, clientId );
}
return null;
}, null );
}
/**
* Deletes the layout data of a block by its client Id.
*
* @param {Object} data Blocks layouts object.
* @param {string} clientId Block's clientsId.
*
* @return {Object} Updated data object.
*/
export function deleteBlockLayoutByClientId( data, clientId ) {
return Object.keys( data ).reduce( ( acc, key ) => {
if ( key !== clientId ) {
acc[ key ] = data[ key ];
}
if (
data[ key ]?.innerBlocks &&
Object.keys( data[ key ].innerBlocks ).length > 0
) {
if ( acc[ key ] ) {
acc[ key ].innerBlocks = deleteBlockLayoutByClientId(
data[ key ].innerBlocks,
clientId
);
}
}
return acc;
}, {} );
}
/**
* Orders the block's layout data by its Y coordinate.
*
* @param {Object} data Blocks layouts object.
*
* @return {Object} Blocks layouts object ordered by its Y coordinate.
*/
function getBlockLayoutsOrderedByYCoord( data ) {
// Only enabled for root level blocks.
return orderBy( Object.values( data ), 'y' );
}
/**
* Updates or deletes a block's layout data in the blocksLayouts object,
* in case of deletion, the layout data is not required.
*
* @param {Object} blocksLayouts Blocks layouts object.
* @param {Object} blockData Block's layout data to add or remove to/from the blockLayouts object.
* @param {string} blockData.clientId Block's clientId.
* @param {?string} blockData.rootClientId Optional. Block's rootClientId.
* @param {?boolean} blockData.shouldRemove Optional. Flag to remove it from the blocksLayout list.
* @param {number} blockData.width Block's width.
* @param {number} blockData.height Block's height.
* @param {number} blockData.x Block's x coordinate (relative to the parent).
* @param {number} blockData.y Block's y coordinate (relative to the parent).
*/
function updateBlocksLayouts( blocksLayouts, blockData ) {
const { clientId, rootClientId, shouldRemove, ...layoutProps } = blockData;
if ( clientId && shouldRemove ) {
blocksLayouts.current = deleteBlockLayoutByClientId(
blocksLayouts.current,
clientId
);
return;
}
if ( clientId && ! rootClientId ) {
blocksLayouts.current[ clientId ] = {
clientId,
rootClientId,
...layoutProps,
innerBlocks: {
...blocksLayouts.current[ clientId ]?.innerBlocks,
},
};
} else if ( clientId && rootClientId ) {
const block = findBlockLayoutByClientId(
blocksLayouts.current,
rootClientId
);
if ( block ) {
block.innerBlocks[ clientId ] = {
clientId,
rootClientId,
...layoutProps,
innerBlocks: {
...block.innerBlocks[ clientId ]?.innerBlocks,
},
};
}
}
}
export { Provider as BlockListProvider, Consumer as BlockListConsumer };
/**
* Hook that returns the block list context.
*
* @return {Object} Block list context
*/
export const useBlockListContext = () => {
return useContext( Context );
};