-
Notifications
You must be signed in to change notification settings - Fork 4k
/
block-types-tab.js
179 lines (159 loc) · 4.73 KB
/
block-types-tab.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
173
174
175
176
177
178
179
/**
* External dependencies
*/
import { map, flow, groupBy, orderBy } from 'lodash';
/**
* WordPress dependencies
*/
import { __, _x } from '@wordpress/i18n';
import { useMemo, useEffect } from '@wordpress/element';
import { useAsyncList } from '@wordpress/compose';
/**
* Internal dependencies
*/
import BlockTypesList from '../block-types-list';
import InserterPanel from './panel';
import useBlockTypesState from './hooks/use-block-types-state';
import InserterListbox from '../inserter-listbox';
const getBlockNamespace = ( item ) => item.name.split( '/' )[ 0 ];
const MAX_SUGGESTED_ITEMS = 6;
/**
* Shared reference to an empty array for cases where it is important to avoid
* returning a new array reference on every invocation and rerendering the component.
*
* @type {Array}
*/
const EMPTY_ARRAY = [];
export function BlockTypesTab( {
rootClientId,
onInsert,
onHover,
showMostUsedBlocks,
} ) {
const [ items, categories, collections, onSelectItem ] = useBlockTypesState(
rootClientId,
onInsert
);
const suggestedItems = useMemo( () => {
return orderBy( items, [ 'frecency' ], [ 'desc' ] ).slice(
0,
MAX_SUGGESTED_ITEMS
);
}, [ items ] );
const uncategorizedItems = useMemo( () => {
return items.filter( ( item ) => ! item.category );
}, [ items ] );
const itemsPerCategory = useMemo( () => {
return flow(
( itemList ) =>
itemList.filter(
( item ) => item.category && item.category !== 'reusable'
),
( itemList ) => groupBy( itemList, 'category' )
)( items );
}, [ items ] );
const itemsPerCollection = useMemo( () => {
// Create a new Object to avoid mutating collection.
const result = { ...collections };
Object.keys( collections ).forEach( ( namespace ) => {
result[ namespace ] = items.filter(
( item ) => getBlockNamespace( item ) === namespace
);
if ( result[ namespace ].length === 0 ) {
delete result[ namespace ];
}
} );
return result;
}, [ items, collections ] );
// Hide block preview on unmount.
useEffect( () => () => onHover( null ), [] );
/**
* The inserter contains a big number of blocks and opening it is a costful operation.
* The rendering is the most costful part of it, in order to improve the responsiveness
* of the "opening" action, these lazy lists allow us to render the inserter category per category,
* once all the categories are rendered, we start rendering the collections and the uncategorized block types.
*/
const currentlyRenderedCategories = useAsyncList( categories );
const didRenderAllCategories =
categories.length === currentlyRenderedCategories.length;
// Async List requires an array
const collectionEntries = useMemo( () => {
return Object.entries( collections );
}, [ collections ] );
const currentlyRenderedCollections = useAsyncList(
didRenderAllCategories ? collectionEntries : EMPTY_ARRAY
);
return (
<InserterListbox>
<div>
{ showMostUsedBlocks && !! suggestedItems.length && (
<InserterPanel title={ _x( 'Most used', 'blocks' ) }>
<BlockTypesList
items={ suggestedItems }
onSelect={ onSelectItem }
onHover={ onHover }
label={ _x( 'Most used', 'blocks' ) }
/>
</InserterPanel>
) }
{ map( currentlyRenderedCategories, ( category ) => {
const categoryItems = itemsPerCategory[ category.slug ];
if ( ! categoryItems || ! categoryItems.length ) {
return null;
}
return (
<InserterPanel
key={ category.slug }
title={ category.title }
icon={ category.icon }
>
<BlockTypesList
items={ categoryItems }
onSelect={ onSelectItem }
onHover={ onHover }
label={ category.title }
/>
</InserterPanel>
);
} ) }
{ didRenderAllCategories && uncategorizedItems.length > 0 && (
<InserterPanel
className="block-editor-inserter__uncategorized-blocks-panel"
title={ __( 'Uncategorized' ) }
>
<BlockTypesList
items={ uncategorizedItems }
onSelect={ onSelectItem }
onHover={ onHover }
label={ __( 'Uncategorized' ) }
/>
</InserterPanel>
) }
{ map(
currentlyRenderedCollections,
( [ namespace, collection ] ) => {
const collectionItems = itemsPerCollection[ namespace ];
if ( ! collectionItems || ! collectionItems.length ) {
return null;
}
return (
<InserterPanel
key={ namespace }
title={ collection.title }
icon={ collection.icon }
>
<BlockTypesList
items={ collectionItems }
onSelect={ onSelectItem }
onHover={ onHover }
label={ collection.title }
/>
</InserterPanel>
);
}
) }
</div>
</InserterListbox>
);
}
export default BlockTypesTab;