Skip to content

Commit

Permalink
Merge pull request #737 from Badsender-com/fix-block-template
Browse files Browse the repository at this point in the history
Fix style block template
  • Loading branch information
FlorianGille authored Oct 20, 2023
2 parents 05fbbec + 8da3f72 commit 88577d8
Show file tree
Hide file tree
Showing 7 changed files with 187 additions and 65 deletions.
4 changes: 4 additions & 0 deletions packages/editor/src/css/badsender-editor.less
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,10 @@ body {
position: absolute;
top: 3px;
left: 0;
max-width: 100%;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}

.mo {
Expand Down
33 changes: 33 additions & 0 deletions packages/editor/src/css/style_mosaico_tools.less
Original file line number Diff line number Diff line change
Expand Up @@ -643,6 +643,29 @@
overflow: hidden;
}


.block {
position: relative;
}

.block-custom {
min-height: 5rem;
margin-bottom: 5px;
background-color: #F3F3F3;
padding-top: 35px;
}

.block-content-custom {
margin-left: 10px;

.block-content-custom-text {
margin-top: 25px;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
}

#tooltabs.ui-tabs {
overflow: hidden;
.ui-tabs-panel {
Expand Down Expand Up @@ -731,6 +754,16 @@
bottom: 10px;
right: 5px;
display: none;

&.custom {
right: 10px;
}

.buttons-container {
.ui-button {
margin: 0px;
}
}
}

.image,
Expand Down
6 changes: 2 additions & 4 deletions packages/editor/src/js/converter/editor.js
Original file line number Diff line number Diff line change
Expand Up @@ -416,8 +416,6 @@ var _propEditor = function (
model._blockDescription +
'</div>';
}


if (typeof prop != 'undefined') {
html += '<!-- ko with: ' + prop + ' -->';

Expand Down Expand Up @@ -611,7 +609,7 @@ var _propEditor = function (
var bindings = [];

if (typeof globalStyleProp != 'undefined')
bindings.push('css: { notnull: ' + prop + '() !== null }');
bindings.push('css: { notnull: ' + prop + '() !== null && !$root.isCurrentCustomBlock() }');
title =
model !== null && typeof model._help !== 'undefined'
? ' title="' +
Expand Down Expand Up @@ -663,7 +661,7 @@ var _propEditor = function (

if (checkboxes) {
html +=
'<div class="propCheck"><label data-bind="tooltips: {}"><input type="checkbox" data-bind="focusable: true, click: function(evt, obj) { $root.localGlobalSwitch(' +
'<div class="propCheck"><label data-bind="tooltips: {},visible: !$root.isCurrentCustomBlock()"><input type="checkbox" data-bind="focusable: true, click: function(evt, obj) { $root.localGlobalSwitch(' +
prop +
', ' +
globalStyleProp +
Expand Down
20 changes: 19 additions & 1 deletion packages/editor/src/js/template-loader.js
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,8 @@ var templateCompiler = function (

delete jsorjson.tracking;

const mainPersonalizedBlocks = jsorjson.mainBlocks?.blocks?.filter(block => block.blockInformation);

// we strip content before <html> tag and after </html> because jquery doesn't parse it.
// we'll keep it "raw" and use it in the preview/output methods.
var res = templatecode.match(
Expand Down Expand Up @@ -424,7 +426,21 @@ var templateCompiler = function (
undefined,
content._unwrap(),
blockDefs,
unwrapped
{
...unwrapped,
mainBlocks: {
...unwrapped.mainBlocks,
blocks: unwrapped.mainBlocks.blocks.map(item => {
// We ignore blockInformation prop from blocks and don't display modal
// if there is only this change between definition and current blocks
// Note: if you need to ignore an other prop inside blocks, you can add the prop in the line below
// For example:
// const { blockInformation, myOtherPropThatIWantIgnore, anOtherPropIgnored, ...restBlock } = item;
const { blockInformation, ...restBlock } = item;
return restBlock;
})
}
}
)
);
// if checkModelRes is 1 then the model is not fully compatible but we fixed it
Expand Down Expand Up @@ -506,6 +522,8 @@ var templateCompiler = function (
utmCampaignValue: utmCampaignValueObs,
});

viewModel.mainPersonalizedBlocks(mainPersonalizedBlocks);

if (tracking && tracking.hasGoogleAnalyticsUtm) {
viewModel.content().tracking().hasGoogleAnalyticsUtm(tracking.hasGoogleAnalyticsUtm);
}
Expand Down
91 changes: 78 additions & 13 deletions packages/editor/src/js/viewmodel.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ function initializeEditor(content, blockDefs, thumbPathConverter, galleryUrl) {
galleryRecent: ko.observableArray([]),
galleryRemote: ko.observableArray([]),
selectedBlock: ko.observable(null),
mainPersonalizedBlocks: ko.observable([]),
isCurrentCustomBlock: ko.observable(null),
selectedItem: ko.observable(null),
selectedTool: ko.observable(0),
selectedImageTab: ko.observable(0),
Expand Down Expand Up @@ -217,18 +219,73 @@ function initializeEditor(content, blockDefs, thumbPathConverter, galleryUrl) {
viewModel.toggleSaveBlockModal(true, actualData, 'EDIT');
};

// Helper function to merge blockData and templateData
/**
* Checks if the provided value is an object (excluding arrays).
*
* @param {any} value - The value to be checked.
* @returns {boolean} - Returns true if the value is an object and not an array.
*/
function isObject(value) {
return value && typeof value === 'object' && !Array.isArray(value);
}

/**
* Merges block styles with template styles. If an attribute in block styles
* is undefined or null, it takes the value from the template styles. If an attribute
* doesn't exist in the template styles, the original value from block styles is retained.
*
* @param {object} blockStyles - The styles from the block.
* @param {object} templateStyles - The styles from the template.
* @returns {object} - Returns the merged styles.
*/
function mergeBlockStylesWithTemplate(blockStyles, templateStyles) {
return Object.keys(blockStyles).reduce((mergedStyles, styleKey) => {
if (templateStyles.hasOwnProperty(styleKey) && styleKey !== 'type') {
mergedStyles[styleKey] = templateStyles[styleKey];
return Object.keys(blockStyles).reduce((mergedStyles, key) => {
const blockValue = blockStyles[key];
// Use the template style only if it exists, otherwise set to undefined.
const templateValue = templateStyles ? templateStyles[key] : undefined;

// If both blockValue and templateValue are objects, merge them recursively.
if (isObject(blockValue) && isObject(templateValue)) {
mergedStyles[key] = mergeBlockStylesWithTemplate(blockValue, templateValue);
} else {
mergedStyles[styleKey] = blockStyles[styleKey];
// If blockValue is undefined or null, use the templateValue, else keep the blockValue.
mergedStyles[key] = blockValue === undefined || blockValue === null ? templateValue : blockValue;
}

return mergedStyles;
}, {});
}

/**
* Merges block data styles with template styles deeply. For each attribute in block data,
* if it's an object, it looks through the template data to find a matching template style and merges them.
* If the attribute is not an object or doesn't have a corresponding template style, it retains its original value.
*
* @param {object} blockData - The main block data containing styles.
* @param {object} templateData - The main template data containing styles.
* @returns {object} - Returns the deeply merged block styles.
*/
function deepMergeStylesWithTemplates(blockData, templateData) {
return Object.keys(blockData).reduce((result, key) => {
// If the block data attribute is an object, look for a matching template style.
if (isObject(blockData[key])) {
let mergedSubObject = blockData[key];
for (let templateKey in templateData) {
if (isObject(templateData[templateKey]) && templateData[templateKey][key]) {
mergedSubObject = mergeBlockStylesWithTemplate(blockData[key], templateData[templateKey][key]);
break;
}
}
result[key] = mergedSubObject;
} else {
// If the block data attribute is not an object, retain its original value.
result[key] = blockData[key];
}

return result;
}, {});
}

function getTemplateData() {
// gather meta
// remove keys that aren't necessary to update
Expand All @@ -243,13 +300,12 @@ function initializeEditor(content, blockDefs, thumbPathConverter, galleryUrl) {
// block-wysiwyg.tmpl.html
viewModel.saveBlock = function (blockData) {
const allTemplateData = getTemplateData();
const templateContentTheme = recursivelyUnwrapObservable(allTemplateData)
?.data?.theme?.contentTheme ?? {};
const templateContentTheme =
recursivelyUnwrapObservable(allTemplateData)?.data?.theme ?? {};

const unwrappedBlockData = recursivelyUnwrapObservable(blockData);

const finalizedBlockData = unwrappedBlockData?.customStyle
? unwrappedBlockData
: mergeBlockStylesWithTemplate(unwrappedBlockData, templateContentTheme);
const finalizedBlockData = deepMergeStylesWithTemplates(unwrappedBlockData, templateContentTheme);

viewModel.toggleSaveBlockModal(true, finalizedBlockData, 'CREATE');
};
Expand Down Expand Up @@ -312,7 +368,6 @@ function initializeEditor(content, blockDefs, thumbPathConverter, galleryUrl) {
// is not the same as the default block in the template.
// To fix this issue, we need to remove blockInformation when we are adding a block
// in a mail.
const { blockInformation, ...newBlock } = obj;
// if there is a selected block we try to add the block just after the selected one.
var selected = viewModel.selectedBlock();
// search the selected block position.
Expand All @@ -333,14 +388,14 @@ function initializeEditor(content, blockDefs, thumbPathConverter, galleryUrl) {
var pos;
if (typeof found !== 'undefined') {
pos = found + 1;
viewModel.content().mainBlocks().blocks.splice(pos, 0, newBlock);
viewModel.content().mainBlocks().blocks.splice(pos, 0, obj);
viewModel.notifier.info(
viewModel.t('New block added after the selected one (__pos__)', {
pos: pos,
})
);
} else {
viewModel.content().mainBlocks().blocks.push(newBlock);
viewModel.content().mainBlocks().blocks.push(obj);
pos = viewModel.content().mainBlocks().blocks().length - 1;
viewModel.notifier.info(
viewModel.t('New block added at the model bottom (__pos__)', {
Expand All @@ -351,6 +406,12 @@ function initializeEditor(content, blockDefs, thumbPathConverter, galleryUrl) {
// find the newly added block and select it!
var added = viewModel.content().mainBlocks().blocks()[pos]();
viewModel.selectBlock(added, true);

if (added.blockInformation()) {
const blockToAdd = recursivelyUnwrapObservable(added);
viewModel.mainPersonalizedBlocks([...viewModel.mainPersonalizedBlocks(), blockToAdd]);
}

// prevent click propagation (losing url hash - see #43)
return false;
};
Expand Down Expand Up @@ -463,6 +524,10 @@ function initializeEditor(content, blockDefs, thumbPathConverter, galleryUrl) {
}
}.bind(viewModel, viewModel.selectedBlock);

viewModel.isCurrentCustomBlock = function() {
return viewModel.mainPersonalizedBlocks().find(block => block.id == viewModel.selectedBlock()?.id()) != undefined;
};

// DEBUG
viewModel.countSubscriptions = function (model, debug) {
var res = 0;
Expand Down
48 changes: 25 additions & 23 deletions packages/editor/src/tmpl-badsender/toolbox.tmpl.html
Original file line number Diff line number Diff line change
Expand Up @@ -63,35 +63,37 @@
<div id="personalizedBlocksPlugin">
<personalized-blocks-plugin />
</div>
<div data-bind="visible: personalizedBlocks().length > 0, foreach: personalizedBlocks()"
style="text-align: center">
<div data-bind="visible: personalizedBlocks().length > 0, foreach: personalizedBlocks()">
<div class="draggable-item" data-bind="withProperties: { templateMode: 'show' }">
<div class="block"
data-bind="extdraggable: { connectClass: 'sortable-blocks-edit', data: $data, dropContainer: '#main-wysiwyg-area', dragging: $root.dragging, 'options': { handle: '.handle', distance: 10, 'appendTo': '#page' } }, click: $root.addBlock"
style="position: relative;">
<div class="block block-custom"
data-bind="extdraggable: { connectClass: 'sortable-blocks-edit', data: $data, dropContainer: '#main-wysiwyg-area', dragging: $root.dragging, 'options': { handle: '.handle', distance: 10, 'appendTo': '#page' } }, click: $root.addBlock">
<div title="Click or drag to add this block to the template" class="handle"
data-bind="attr: { title: $root.t('Click or drag to add this block to the template') }, tooltips: {}">
</div>
<img
class="block-image"
data-bind="attr: { alt: $root.t('__name__', { name: ko.utils.unwrapObservable(blockInformation.name) }), src: $root.templatePath('edres/'+ko.utils.unwrapObservable(type)+'.png') }"
alt="__name__" />
<span class="block-name"
data-bind="html: $root.t('__name__', { name: ko.utils.unwrapObservable(blockInformation.name) })">__name__</span>
</div>
<div class="addblockbutton">
<div class="buttons-container">
<div data-bind="if: $root.currentUser()?.isAdminOfCurrentGroup">
<a href="javascript:void(0)" class="ui-button" data-bind="click: $root.deleteBlock">
<i class="fa fa-trash"></i> <!-- Delete Icon -->
</a>
</div>
<div data-bind="if: $root.currentUser()?.isAdminOfCurrentGroup">
<a href="javascript:void(0)" class="ui-button" data-bind="click: $root.editBlock">
<i class="fa fa-pencil"></i> <!-- Edit Icon -->
</a>
<div class="block-content-custom">
<span class="block-content-custom-text"
data-bind="html: $root.t('__name__', { name: ko.utils.unwrapObservable(blockInformation.category) })">__name__</span>
</div>
<div class="block-content-custom">
<span class="block-content-custom-text"
data-bind="html: $root.t('__name__', { name: ko.utils.unwrapObservable(blockInformation.userDetails.name) })">__name__</span>
</div>
<div class="addblockbutton custom">
<div class="buttons-container">
<div data-bind="if: $root.currentUser()?.isAdminOfCurrentGroup">
<a href="javascript:void(0)" class="ui-button" data-bind="click: $root.deleteBlock">
<i class="fa fa-trash"></i> <!-- Delete Icon -->
</a>
</div>
<div data-bind="if: $root.currentUser()?.isAdminOfCurrentGroup">
<a href="javascript:void(0)" class="ui-button" data-bind="click: $root.editBlock">
<i class="fa fa-pencil"></i> <!-- Edit Icon -->
</a>
</div>
<a href="javascript:void(0)" data-bind="click: $root.addBlock, button: { label: $root.t('Add') }">Add</a>
</div>
<a href="javascript:void(0)" data-bind="click: $root.addBlock, button: { label: $root.t('Add') }">Add</a>
</div>
</div>
</div>
Expand Down Expand Up @@ -219,4 +221,4 @@
<!-- ko block: $root.content().theme --><!-- /ko -->
<!-- /ko -->
<!-- /ko -->
</div>
</div>
Loading

0 comments on commit 88577d8

Please sign in to comment.