-
Notifications
You must be signed in to change notification settings - Fork 4.3k
/
Copy pathutils.js
169 lines (149 loc) · 3.86 KB
/
utils.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
/**
* External dependencies
*/
import { keyBy, omit } from 'lodash';
/**
* WordPress dependencies
*/
import { serialize } from '@wordpress/blocks';
/**
* Internal dependencies
*/
import {
getNavigationPostForMenu,
getPendingActions,
isProcessingPost,
} from './controls';
/**
* Builds an ID for a new navigation post.
*
* @param {number} menuId Menu id.
* @return {string} An ID.
*/
export const buildNavigationPostId = ( menuId ) =>
`navigation-post-${ menuId }`;
/**
* Builds a query to resolve menu items.
*
* @param {number} menuId Menu id.
* @return {Object} Query.
*/
export function menuItemsQuery( menuId ) {
return { menus: menuId, per_page: -1 };
}
/**
* This wrapper guarantees serial execution of data processing actions.
*
* Examples:
* * saveNavigationPost() needs to wait for all the missing items to be created.
* * Concurrent createMissingMenuItems() could result in sending more requests than required.
*
* @param {Function} callback An action creator to wrap
* @return {Function} Original callback wrapped in a serial execution context
*/
export function serializeProcessing( callback ) {
return function* ( post ) {
const postId = post.id;
const isProcessing = yield isProcessingPost( postId );
if ( isProcessing ) {
yield {
type: 'ENQUEUE_AFTER_PROCESSING',
postId,
action: callback,
};
return { status: 'pending' };
}
yield {
type: 'POP_PENDING_ACTION',
postId,
action: callback,
};
yield {
type: 'START_PROCESSING_POST',
postId,
};
try {
yield* callback(
// re-select the post as it could be outdated by now
yield getNavigationPostForMenu( post.meta.menuId )
);
} finally {
yield {
type: 'FINISH_PROCESSING_POST',
postId,
action: callback,
};
const pendingActions = yield getPendingActions( postId );
if ( pendingActions.length ) {
const serializedCallback = serializeProcessing(
pendingActions[ 0 ]
);
yield* serializedCallback( post );
}
}
};
}
export function computeCustomizedAttribute(
blocks,
menuId,
menuItemsByClientId
) {
const blocksList = blocksTreeToFlatList( blocks );
const dataList = blocksList.map( ( { block, parentId, position } ) =>
blockToRequestItem( block, parentId, position )
);
// Create an object like { "nav_menu_item[12]": {...}} }
const computeKey = ( item ) => `nav_menu_item[${ item.id }]`;
const dataObject = keyBy( dataList, computeKey );
// Deleted menu items should be sent as false, e.g. { "nav_menu_item[13]": false }
for ( const clientId in menuItemsByClientId ) {
const key = computeKey( menuItemsByClientId[ clientId ] );
if ( ! ( key in dataObject ) ) {
dataObject[ key ] = false;
}
}
return JSON.stringify( dataObject );
function blocksTreeToFlatList( innerBlocks, parentId = 0 ) {
return innerBlocks.flatMap( ( block, index ) =>
[ { block, parentId, position: index + 1 } ].concat(
blocksTreeToFlatList(
block.innerBlocks,
getMenuItemForBlock( block )?.id
)
)
);
}
function blockToRequestItem( block, parentId, position ) {
const menuItem = omit( getMenuItemForBlock( block ), 'menus', 'meta' );
let attributes;
if ( block.name === 'core/navigation-link' ) {
attributes = {
type: 'custom',
title: block.attributes?.label,
original_title: '',
url: block.attributes.url,
description: block.attributes.description,
xfn: block.attributes.rel?.split( ' ' ),
classes: block.attributes.className?.split( ' ' ),
attr_title: block.attributes.title,
};
} else {
attributes = {
type: 'block',
content: serialize( block ),
};
}
return {
...menuItem,
...attributes,
position,
nav_menu_term_id: menuId,
menu_item_parent: parentId,
status: 'publish',
_invalid: false,
};
}
function getMenuItemForBlock( block ) {
return omit( menuItemsByClientId[ block.clientId ] || {}, '_links' );
}
}