@@ -7,10 +7,13 @@ import { I18nInternalNS } from '../../i18n/namespace-internal';
7
7
import Flipper from '../../flipper' ;
8
8
import { TunesMenuConfigItem } from '../../../../types/tools' ;
9
9
import { resolveAliases } from '../../utils/resolve-aliases' ;
10
- import { type Popover , PopoverDesktop , PopoverMobile } from '../../utils/popover' ;
10
+ import { type Popover , PopoverDesktop , PopoverMobile , PopoverItemParams , PopoverItemDefaultParams } from '../../utils/popover' ;
11
11
import { PopoverEvent } from '../../utils/popover/popover.types' ;
12
12
import { isMobileScreen } from '../../utils' ;
13
13
import { EditorMobileLayoutToggled } from '../../events' ;
14
+ import * as _ from '../../utils' ;
15
+ import { IconReplace } from '@codexteam/icons' ;
16
+ import { isSameBlockData } from '../../utils/blocks' ;
14
17
15
18
/**
16
19
* HTML Elements that used for BlockSettings
@@ -105,7 +108,7 @@ export default class BlockSettings extends Module<BlockSettingsNodes> {
105
108
*
106
109
* @param targetBlock - near which Block we should open BlockSettings
107
110
*/
108
- public open ( targetBlock : Block = this . Editor . BlockManager . currentBlock ) : void {
111
+ public async open ( targetBlock : Block = this . Editor . BlockManager . currentBlock ) : Promise < void > {
109
112
this . opened = true ;
110
113
111
114
/**
@@ -120,10 +123,8 @@ export default class BlockSettings extends Module<BlockSettingsNodes> {
120
123
this . Editor . BlockSelection . selectBlock ( targetBlock ) ;
121
124
this . Editor . BlockSelection . clearCache ( ) ;
122
125
123
- /**
124
- * Fill Tool's settings
125
- */
126
- const [ tunesItems , customHtmlTunesContainer ] = targetBlock . getTunes ( ) ;
126
+ /** Get tool's settings data */
127
+ const { toolTunes, commonTunes, customHtmlTunes } = targetBlock . getTunes ( ) ;
127
128
128
129
/** Tell to subscribers that block settings is opened */
129
130
this . eventsDispatcher . emit ( this . events . opened ) ;
@@ -132,9 +133,9 @@ export default class BlockSettings extends Module<BlockSettingsNodes> {
132
133
133
134
this . popover = new PopoverClass ( {
134
135
searchable : true ,
135
- items : tunesItems . map ( tune => this . resolveTuneAliases ( tune ) ) ,
136
- customContent : customHtmlTunesContainer ,
137
- customContentFlippableItems : this . getControls ( customHtmlTunesContainer ) ,
136
+ items : await this . getTunesItems ( targetBlock , commonTunes , toolTunes ) ,
137
+ customContent : customHtmlTunes ,
138
+ customContentFlippableItems : this . getControls ( customHtmlTunes ) ,
138
139
scopeElement : this . Editor . API . methods . ui . nodes . redactor ,
139
140
messages : {
140
141
nothingFound : I18n . ui ( I18nInternalNS . ui . popover , 'Nothing found' ) ,
@@ -197,6 +198,117 @@ export default class BlockSettings extends Module<BlockSettingsNodes> {
197
198
}
198
199
} ;
199
200
201
+ /**
202
+ * Returns list of items to be displayed in block tunes menu.
203
+ * Merges tool specific tunes, conversion menu and common tunes in one list in predefined order
204
+ *
205
+ * @param currentBlock – block we are about to open block tunes for
206
+ * @param commonTunes – common tunes
207
+ * @param toolTunes - tool specific tunes
208
+ */
209
+ private async getTunesItems ( currentBlock : Block , commonTunes : TunesMenuConfigItem [ ] , toolTunes ?: TunesMenuConfigItem [ ] ) : Promise < PopoverItemParams [ ] > {
210
+ const items = [ ] as TunesMenuConfigItem [ ] ;
211
+
212
+ if ( toolTunes !== undefined && toolTunes . length > 0 ) {
213
+ items . push ( ...toolTunes ) ;
214
+ items . push ( {
215
+ type : 'separator' ,
216
+ } ) ;
217
+ }
218
+
219
+ const convertToItems = await this . getConvertToItems ( currentBlock ) ;
220
+
221
+ if ( convertToItems . length > 0 ) {
222
+ items . push ( {
223
+ icon : IconReplace ,
224
+ title : I18n . ui ( I18nInternalNS . ui . popover , 'Convert to' ) ,
225
+ children : {
226
+ items : convertToItems ,
227
+ } ,
228
+ } ) ;
229
+ items . push ( {
230
+ type : 'separator' ,
231
+ } ) ;
232
+ }
233
+
234
+ items . push ( ...commonTunes ) ;
235
+
236
+ return items . map ( tune => this . resolveTuneAliases ( tune ) ) ;
237
+ }
238
+
239
+ /**
240
+ * Returns list of all available conversion menu items
241
+ *
242
+ * @param currentBlock - block we are about to open block tunes for
243
+ */
244
+ private async getConvertToItems ( currentBlock : Block ) : Promise < PopoverItemDefaultParams [ ] > {
245
+ const conversionEntries = Array . from ( this . Editor . Tools . blockTools . entries ( ) ) ;
246
+
247
+ const resultItems : PopoverItemDefaultParams [ ] = [ ] ;
248
+
249
+ const blockData = await currentBlock . data ;
250
+
251
+ conversionEntries . forEach ( ( [ toolName , tool ] ) => {
252
+ const conversionConfig = tool . conversionConfig ;
253
+
254
+ /**
255
+ * Skip tools without «import» rule specified
256
+ */
257
+ if ( ! conversionConfig || ! conversionConfig . import ) {
258
+ return ;
259
+ }
260
+
261
+ tool . toolbox ?. forEach ( ( toolboxItem ) => {
262
+ /**
263
+ * Skip tools that don't pass 'toolbox' property
264
+ */
265
+ if ( _ . isEmpty ( toolboxItem ) || ! toolboxItem . icon ) {
266
+ return ;
267
+ }
268
+
269
+ let shouldSkip = false ;
270
+
271
+ if ( toolboxItem . data !== undefined ) {
272
+ /**
273
+ * When a tool has several toolbox entries, we need to make sure we do not add
274
+ * toolbox item with the same data to the resulting array. This helps exclude duplicates
275
+ */
276
+ const hasSameData = isSameBlockData ( toolboxItem . data , blockData ) ;
277
+
278
+ shouldSkip = hasSameData ;
279
+ } else {
280
+ shouldSkip = toolName === currentBlock . name ;
281
+ }
282
+
283
+
284
+ if ( shouldSkip ) {
285
+ return ;
286
+ }
287
+
288
+ resultItems . push ( {
289
+ icon : toolboxItem . icon ,
290
+ title : toolboxItem . title ,
291
+ name : toolName ,
292
+ onActivate : ( ) => {
293
+ const { BlockManager, BlockSelection, Caret } = this . Editor ;
294
+
295
+ BlockManager . convert ( this . Editor . BlockManager . currentBlock , toolName , toolboxItem . data ) ;
296
+
297
+ BlockSelection . clearSelection ( ) ;
298
+
299
+ this . close ( ) ;
300
+
301
+ window . requestAnimationFrame ( ( ) => {
302
+ Caret . setToBlock ( this . Editor . BlockManager . currentBlock , Caret . positions . END ) ;
303
+ } ) ;
304
+ } ,
305
+ } ) ;
306
+ } ) ;
307
+ } ) ;
308
+
309
+ return resultItems ;
310
+ }
311
+
200
312
/**
201
313
* Handles popover close event
202
314
*/
@@ -224,7 +336,10 @@ export default class BlockSettings extends Module<BlockSettingsNodes> {
224
336
*
225
337
* @param item - item with resolved aliases
226
338
*/
227
- private resolveTuneAliases ( item : TunesMenuConfigItem ) : TunesMenuConfigItem {
339
+ private resolveTuneAliases ( item : TunesMenuConfigItem ) : PopoverItemParams {
340
+ if ( item . type === 'separator' ) {
341
+ return item ;
342
+ }
228
343
const result = resolveAliases ( item , { label : 'title' } ) ;
229
344
230
345
if ( item . confirmation ) {
0 commit comments