@@ -47,6 +47,8 @@ function initializeEditor(content, blockDefs, thumbPathConverter, galleryUrl) {
47
47
galleryRecent : ko . observableArray ( [ ] ) ,
48
48
galleryRemote : ko . observableArray ( [ ] ) ,
49
49
selectedBlock : ko . observable ( null ) ,
50
+ mainPersonalizedBlocks : ko . observable ( [ ] ) ,
51
+ isCurrentCustomBlock : ko . observable ( null ) ,
50
52
selectedItem : ko . observable ( null ) ,
51
53
selectedTool : ko . observable ( 0 ) ,
52
54
selectedImageTab : ko . observable ( 0 ) ,
@@ -217,18 +219,73 @@ function initializeEditor(content, blockDefs, thumbPathConverter, galleryUrl) {
217
219
viewModel . toggleSaveBlockModal ( true , actualData , 'EDIT' ) ;
218
220
} ;
219
221
220
- // Helper function to merge blockData and templateData
222
+ /**
223
+ * Checks if the provided value is an object (excluding arrays).
224
+ *
225
+ * @param {any } value - The value to be checked.
226
+ * @returns {boolean } - Returns true if the value is an object and not an array.
227
+ */
228
+ function isObject ( value ) {
229
+ return value && typeof value === 'object' && ! Array . isArray ( value ) ;
230
+ }
231
+
232
+ /**
233
+ * Merges block styles with template styles. If an attribute in block styles
234
+ * is undefined or null, it takes the value from the template styles. If an attribute
235
+ * doesn't exist in the template styles, the original value from block styles is retained.
236
+ *
237
+ * @param {object } blockStyles - The styles from the block.
238
+ * @param {object } templateStyles - The styles from the template.
239
+ * @returns {object } - Returns the merged styles.
240
+ */
221
241
function mergeBlockStylesWithTemplate ( blockStyles , templateStyles ) {
222
- return Object . keys ( blockStyles ) . reduce ( ( mergedStyles , styleKey ) => {
223
- if ( templateStyles . hasOwnProperty ( styleKey ) && styleKey !== 'type' ) {
224
- mergedStyles [ styleKey ] = templateStyles [ styleKey ] ;
242
+ return Object . keys ( blockStyles ) . reduce ( ( mergedStyles , key ) => {
243
+ const blockValue = blockStyles [ key ] ;
244
+ // Use the template style only if it exists, otherwise set to undefined.
245
+ const templateValue = templateStyles ? templateStyles [ key ] : undefined ;
246
+
247
+ // If both blockValue and templateValue are objects, merge them recursively.
248
+ if ( isObject ( blockValue ) && isObject ( templateValue ) ) {
249
+ mergedStyles [ key ] = mergeBlockStylesWithTemplate ( blockValue , templateValue ) ;
225
250
} else {
226
- mergedStyles [ styleKey ] = blockStyles [ styleKey ] ;
251
+ // If blockValue is undefined or null, use the templateValue, else keep the blockValue.
252
+ mergedStyles [ key ] = blockValue === undefined || blockValue === null ? templateValue : blockValue ;
227
253
}
254
+
228
255
return mergedStyles ;
229
256
} , { } ) ;
230
257
}
231
258
259
+ /**
260
+ * Merges block data styles with template styles deeply. For each attribute in block data,
261
+ * if it's an object, it looks through the template data to find a matching template style and merges them.
262
+ * If the attribute is not an object or doesn't have a corresponding template style, it retains its original value.
263
+ *
264
+ * @param {object } blockData - The main block data containing styles.
265
+ * @param {object } templateData - The main template data containing styles.
266
+ * @returns {object } - Returns the deeply merged block styles.
267
+ */
268
+ function deepMergeStylesWithTemplates ( blockData , templateData ) {
269
+ return Object . keys ( blockData ) . reduce ( ( result , key ) => {
270
+ // If the block data attribute is an object, look for a matching template style.
271
+ if ( isObject ( blockData [ key ] ) ) {
272
+ let mergedSubObject = blockData [ key ] ;
273
+ for ( let templateKey in templateData ) {
274
+ if ( isObject ( templateData [ templateKey ] ) && templateData [ templateKey ] [ key ] ) {
275
+ mergedSubObject = mergeBlockStylesWithTemplate ( blockData [ key ] , templateData [ templateKey ] [ key ] ) ;
276
+ break ;
277
+ }
278
+ }
279
+ result [ key ] = mergedSubObject ;
280
+ } else {
281
+ // If the block data attribute is not an object, retain its original value.
282
+ result [ key ] = blockData [ key ] ;
283
+ }
284
+
285
+ return result ;
286
+ } , { } ) ;
287
+ }
288
+
232
289
function getTemplateData ( ) {
233
290
// gather meta
234
291
// remove keys that aren't necessary to update
@@ -243,13 +300,12 @@ function initializeEditor(content, blockDefs, thumbPathConverter, galleryUrl) {
243
300
// block-wysiwyg.tmpl.html
244
301
viewModel . saveBlock = function ( blockData ) {
245
302
const allTemplateData = getTemplateData ( ) ;
246
- const templateContentTheme = recursivelyUnwrapObservable ( allTemplateData )
247
- ?. data ?. theme ?. contentTheme ?? { } ;
303
+ const templateContentTheme =
304
+ recursivelyUnwrapObservable ( allTemplateData ) ?. data ?. theme ?? { } ;
305
+
248
306
const unwrappedBlockData = recursivelyUnwrapObservable ( blockData ) ;
249
307
250
- const finalizedBlockData = unwrappedBlockData ?. customStyle
251
- ? unwrappedBlockData
252
- : mergeBlockStylesWithTemplate ( unwrappedBlockData , templateContentTheme ) ;
308
+ const finalizedBlockData = deepMergeStylesWithTemplates ( unwrappedBlockData , templateContentTheme ) ;
253
309
254
310
viewModel . toggleSaveBlockModal ( true , finalizedBlockData , 'CREATE' ) ;
255
311
} ;
@@ -312,7 +368,6 @@ function initializeEditor(content, blockDefs, thumbPathConverter, galleryUrl) {
312
368
// is not the same as the default block in the template.
313
369
// To fix this issue, we need to remove blockInformation when we are adding a block
314
370
// in a mail.
315
- const { blockInformation, ...newBlock } = obj ;
316
371
// if there is a selected block we try to add the block just after the selected one.
317
372
var selected = viewModel . selectedBlock ( ) ;
318
373
// search the selected block position.
@@ -333,14 +388,14 @@ function initializeEditor(content, blockDefs, thumbPathConverter, galleryUrl) {
333
388
var pos ;
334
389
if ( typeof found !== 'undefined' ) {
335
390
pos = found + 1 ;
336
- viewModel . content ( ) . mainBlocks ( ) . blocks . splice ( pos , 0 , newBlock ) ;
391
+ viewModel . content ( ) . mainBlocks ( ) . blocks . splice ( pos , 0 , obj ) ;
337
392
viewModel . notifier . info (
338
393
viewModel . t ( 'New block added after the selected one (__pos__)' , {
339
394
pos : pos ,
340
395
} )
341
396
) ;
342
397
} else {
343
- viewModel . content ( ) . mainBlocks ( ) . blocks . push ( newBlock ) ;
398
+ viewModel . content ( ) . mainBlocks ( ) . blocks . push ( obj ) ;
344
399
pos = viewModel . content ( ) . mainBlocks ( ) . blocks ( ) . length - 1 ;
345
400
viewModel . notifier . info (
346
401
viewModel . t ( 'New block added at the model bottom (__pos__)' , {
@@ -351,6 +406,12 @@ function initializeEditor(content, blockDefs, thumbPathConverter, galleryUrl) {
351
406
// find the newly added block and select it!
352
407
var added = viewModel . content ( ) . mainBlocks ( ) . blocks ( ) [ pos ] ( ) ;
353
408
viewModel . selectBlock ( added , true ) ;
409
+
410
+ if ( added . blockInformation ( ) ) {
411
+ const blockToAdd = recursivelyUnwrapObservable ( added ) ;
412
+ viewModel . mainPersonalizedBlocks ( [ ...viewModel . mainPersonalizedBlocks ( ) , blockToAdd ] ) ;
413
+ }
414
+
354
415
// prevent click propagation (losing url hash - see #43)
355
416
return false ;
356
417
} ;
@@ -463,6 +524,10 @@ function initializeEditor(content, blockDefs, thumbPathConverter, galleryUrl) {
463
524
}
464
525
} . bind ( viewModel , viewModel . selectedBlock ) ;
465
526
527
+ viewModel . isCurrentCustomBlock = function ( ) {
528
+ return viewModel . mainPersonalizedBlocks ( ) . find ( block => block . id == viewModel . selectedBlock ( ) ?. id ( ) ) != undefined ;
529
+ } ;
530
+
466
531
// DEBUG
467
532
viewModel . countSubscriptions = function ( model , debug ) {
468
533
var res = 0 ;
0 commit comments