[IMP] website, web_editor, mass_mailing: move margin and size options

website -> web_editor so that they are available for mass_mailing.

To allow setting margins on td elements:

As the above mentionned option allows to control the mt and mb classes
on the mass mailing items, the problem is that we would ideally want it
to be functionnal on td element as they are the main part of a mail.
As they don't support margins, there were many options to fix the
problem:
- Add more JS to place the mt and mb classes on children of td element
- Place the option on children of td element and not td element itself
but in that case, the width control of td would imply a lot of parent
option hierarchy, or the width control had also to be adapted to control
children
- Control padding instead of margin. This forces to add lots of
css rules like pt4 pt8, ... (or td.mt4, td.mt8, ...).

The last solution has been chosen. Padding is more supported than
margins in mail client anyway and the css rules will be isolated in
the mail editor anyway. Using td.mt4 instead of pt4 had the advantage
to not require any JS modification.
This commit is contained in:
qsm-odoo
2016-09-20 13:52:29 +02:00
parent 4f93d5c687
commit 9749b93250
4 changed files with 263 additions and 225 deletions
@@ -282,3 +282,32 @@
width: 100%!important;
}
}
// ===== Margins on td (replaced by paddings) =====
.o-paddings-all(@factor: 1) {
.o-paddings(128, @factor);
.o-paddings(96, @factor);
.o-paddings(92, @factor);
.o-paddings(64, @factor);
.o-paddings(48, @factor);
.o-paddings(32, @factor);
.o-paddings(24, @factor);
.o-paddings(16, @factor);
.o-paddings(8, @factor);
.o-paddings(4, @factor);
.o-paddings(0, @factor);
}
.o-paddings(@name, @factor: 1) {
.o-paddings-define(@name, @factor * @name);
}
.o-paddings-define(@name, @size: @name) {
td.mt@{name} {
margin-top: auto!important;
padding-top: ~"@{size}px"!important;
}
td.mb@{name} {
margin-bottom: auto!important;
padding-bottom: ~"@{size}px"!important;
}
}
.o-paddings-all();
@@ -2,6 +2,7 @@
<odoo>
<template id="snippet_options">
<t t-call="web_editor.snippet_options"/>
<t t-raw="0"/>
<div data-js="width-x"
@@ -29,6 +30,14 @@
data-drop-in="[data-oe-field='body_html']:not(:has(.o_layout)), .oe_structure">
</div>
<div data-js="resize"
data-selector="[data-oe-field='body_html'] > div:not(.o_layout), [data-oe-field='body_html'] .oe_structure > div">
</div>
<div data-js="margin-y"
data-selector="[data-oe-field='body_html'] > div:not(.o_layout), [data-oe-field='body_html'] .oe_structure > div, td, th">
</div>
<div data-js="colorpicker"
data-selector="
[data-oe-field='body_html'] > div:not(.o_layout):not(.o_mail_block_event):not(.o_mail_block_steps),
@@ -214,6 +214,231 @@ odoo.define('web_editor.snippets.options', function (require) {
/* ----- default options ---- */
registry.marginAndResize = SnippetOption.extend({
preventChildPropagation: true,
start: function () {
var self = this;
this._super();
var resize_values = this.getSize();
if (resize_values.n) this.$overlay.find(".oe_handle.n").removeClass("readonly");
if (resize_values.s) this.$overlay.find(".oe_handle.s").removeClass("readonly");
if (resize_values.e) this.$overlay.find(".oe_handle.e").removeClass("readonly");
if (resize_values.w) this.$overlay.find(".oe_handle.w").removeClass("readonly");
if (resize_values.size) this.$overlay.find(".oe_handle.size").removeClass("readonly");
var $auto_size = this.$overlay.find(".oe_handle.size .auto_size");
var $fixed_size = this.$overlay.find(".oe_handle.size .size");
var auto_sized = isNaN(parseInt(self.$target.prop("style").height));
$fixed_size.toggleClass("active", !auto_sized);
$auto_size.toggleClass("active", auto_sized);
$fixed_size.add(this.$overlay.find(".oe_handle:not(.size)")).on('mousedown', function (event) {
event.preventDefault();
var $handle = $(this);
var resize_values = self.getSize();
var compass = false;
var XY = false;
if ($handle.hasClass('n')) {
compass = 'n';
XY = 'Y';
}
else if ($handle.hasClass('s')) {
compass = 's';
XY = 'Y';
}
else if ($handle.hasClass('e')) {
compass = 'e';
XY = 'X';
}
else if ($handle.hasClass('w')) {
compass = 'w';
XY = 'X';
}
else if ($handle.hasClass('size')) {
compass = 'size';
XY = 'Y';
}
var resize = resize_values[compass];
if (!resize) return;
if (compass === 'size') {
var offset = self.$target.offset().top;
if (self.$target.css("background").match(/rgba\(0, 0, 0, 0\)/)) {
self.$target.addClass("resize_editor_busy");
}
} else {
var xy = event['page'+XY];
var current = resize[2] || 0;
var margin_dir = {s:'bottom', n: 'top', w: 'left', e: 'right'}[compass];
var real_margin = parseInt(self.$target.css('margin-'+margin_dir));
_.each(resize[0], function (val, key) {
if (self.$target.hasClass(val)) {
current = key;
} else if (resize[1][key] === real_margin) {
current = key;
}
});
var begin = current;
var beginClass = self.$target.attr("class");
var regClass = new RegExp("\\s*" + resize[0][begin].replace(/[-]*[0-9]+/, '[-]*[0-9]+'), 'g');
}
self.buildingBlock.editor_busy = true;
var cursor = $handle.css("cursor")+'-important';
var $body = $(document.body);
$body.addClass(cursor);
var body_mousemove = function (event) {
event.preventDefault();
if (compass === 'size') {
var dy = event.pageY-offset;
dy = dy - dy%resize;
if (dy <= 0) dy = resize;
self.$target.css("height", dy+"px");
self.$target.css("overflow", "hidden");
self.on_resize(compass, null, dy);
self.buildingBlock.cover_target(self.$overlay, self.$target);
return;
}
var dd = event['page'+XY] - xy + resize[1][begin];
var next = current+1 === resize[1].length ? current : (current+1);
var prev = current ? (current-1) : 0;
var change = false;
if (dd > (2*resize[1][next] + resize[1][current])/3) {
self.$target.attr("class", (self.$target.attr("class")||'').replace(regClass, ''));
self.$target.addClass(resize[0][next]);
current = next;
change = true;
}
if (prev !== current && dd < (2*resize[1][prev] + resize[1][current])/3) {
self.$target.attr("class", (self.$target.attr("class")||'').replace(regClass, ''));
self.$target.addClass(resize[0][prev]);
current = prev;
change = true;
}
if (change) {
self.on_resize(compass, beginClass, current);
self.buildingBlock.cover_target(self.$overlay, self.$target);
}
};
var body_mouseup = function () {
$body.unbind('mousemove', body_mousemove);
$body.unbind('mouseup', body_mouseup);
$body.removeClass(cursor);
setTimeout(function () {
self.buildingBlock.editor_busy = false;
if (begin !== current) {
self.buildingBlock.getParent().rte.historyRecordUndo(self.$target, 'resize_' + XY);
}
},0);
self.$target.removeClass("resize_editor_busy");
if (compass === "size") {
$fixed_size.addClass("active");
$auto_size.removeClass("active");
}
};
$body.mousemove(body_mousemove);
$body.mouseup(body_mouseup);
});
$auto_size.on('click', function () {
self.$target.css("height", "");
self.$target.css("overflow", "");
self.buildingBlock.getParent().rte.historyRecordUndo(self.$target, 'resize_Y');
self.buildingBlock.cover_target(self.$overlay, self.$target);
$fixed_size.removeClass("active");
$auto_size.addClass("active");
return false;
});
},
getSize: function () {
this.grid = {};
return this.grid;
},
on_focus : function () {
this._super();
this.change_cursor();
},
change_cursor : function () {
var _class = this.$target.attr("class") || "";
var col = _class.match(/col-md-([0-9-]+)/i);
col = col ? +col[1] : 0;
var offset = _class.match(/col-md-offset-([0-9-]+)/i);
offset = offset ? +offset[1] : 0;
var overlay_class = this.$overlay.attr("class").replace(/(^|\s+)block-[^\s]*/gi, '');
if (col+offset >= 12) overlay_class+= " block-e-right";
if (col === 1) overlay_class+= " block-w-right block-e-left";
if (offset === 0) overlay_class+= " block-w-left";
var mb = _class.match(/mb([0-9-]+)/i);
mb = mb ? +mb[1] : parseInt(this.$target.css('margin-bottom'));
if (mb >= 128) overlay_class+= " block-s-bottom";
else if (!mb) overlay_class+= " block-s-top";
var mt = _class.match(/mt([0-9-]+)/i);
mt = mt ? +mt[1] : parseInt(this.$target.css('margin-top'));
if (mt >= 128) overlay_class+= " block-n-top";
else if (!mt) overlay_class+= " block-n-bottom";
this.$overlay.attr("class", overlay_class);
},
/* on_resize
* called when the box is resizing and the class change, before the cover_target
* @compass: resize direction : 'n', 's', 'e', 'w'
* @beginClass: attributes class at the begin
* @current: curent increment in this.grid
*/
on_resize: function () {
this.change_cursor();
}
});
registry["margin-y"] = registry.marginAndResize.extend({
preventChildPropagation: true,
getSize: function () {
this.grid = this._super();
var grid = [0,4,8,16,32,48,64,92,128];
this.grid = {
// list of class (Array), grid (Array), default value (INT)
n: [_.map(grid, function (v) {return 'mt'+v;}), grid],
s: [_.map(grid, function (v) {return 'mb'+v;}), grid],
// INT if the user can resize the snippet (resizing per INT px)
size: null
};
return this.grid;
},
});
registry.resize = registry.marginAndResize.extend({
preventChildPropagation: true,
getSize: function () {
this.grid = this._super();
this.grid.size = 8;
return this.grid;
},
});
/**
* The colorpicker option is designed to change the background color class of a snippet. This class change the
* default background color and text color of the snippet content.
@@ -248,221 +248,6 @@ options.registry.carousel = options.registry.slider.extend({
},
});
options.registry.marginAndResize = options.Class.extend({
preventChildPropagation: true,
start: function () {
var self = this;
this._super();
var resize_values = this.getSize();
if (resize_values.n) this.$overlay.find(".oe_handle.n").removeClass("readonly");
if (resize_values.s) this.$overlay.find(".oe_handle.s").removeClass("readonly");
if (resize_values.e) this.$overlay.find(".oe_handle.e").removeClass("readonly");
if (resize_values.w) this.$overlay.find(".oe_handle.w").removeClass("readonly");
if (resize_values.size) this.$overlay.find(".oe_handle.size").removeClass("readonly");
var $auto_size = this.$overlay.find(".oe_handle.size .auto_size");
var $fixed_size = this.$overlay.find(".oe_handle.size .size");
var auto_sized = isNaN(parseInt(self.$target.prop("style").height));
$fixed_size.toggleClass("active", !auto_sized);
$auto_size.toggleClass("active", auto_sized);
$fixed_size.add(this.$overlay.find(".oe_handle:not(.size)")).on('mousedown', function (event) {
event.preventDefault();
var $handle = $(this);
var resize_values = self.getSize();
var compass = false;
var XY = false;
if ($handle.hasClass('n')) {
compass = 'n';
XY = 'Y';
}
else if ($handle.hasClass('s')) {
compass = 's';
XY = 'Y';
}
else if ($handle.hasClass('e')) {
compass = 'e';
XY = 'X';
}
else if ($handle.hasClass('w')) {
compass = 'w';
XY = 'X';
}
else if ($handle.hasClass('size')) {
compass = 'size';
XY = 'Y';
}
var resize = resize_values[compass];
if (!resize) return;
if (compass === 'size') {
var offset = self.$target.offset().top;
if (self.$target.css("background").match(/rgba\(0, 0, 0, 0\)/)) {
self.$target.addClass("resize_editor_busy");
}
} else {
var xy = event['page'+XY];
var current = resize[2] || 0;
var margin_dir = {s:'bottom', n: 'top', w: 'left', e: 'right'}[compass];
var real_margin = parseInt(self.$target.css('margin-'+margin_dir));
_.each(resize[0], function (val, key) {
if (self.$target.hasClass(val)) {
current = key;
} else if (resize[1][key] === real_margin) {
current = key;
}
});
var begin = current;
var beginClass = self.$target.attr("class");
var regClass = new RegExp("\\s*" + resize[0][begin].replace(/[-]*[0-9]+/, '[-]*[0-9]+'), 'g');
}
self.buildingBlock.editor_busy = true;
var cursor = $handle.css("cursor")+'-important';
var $body = $(document.body);
$body.addClass(cursor);
var body_mousemove = function (event) {
event.preventDefault();
if (compass === 'size') {
var dy = event.pageY-offset;
dy = dy - dy%resize;
if (dy <= 0) dy = resize;
self.$target.css("height", dy+"px");
self.$target.css("overflow", "hidden");
self.on_resize(compass, null, dy);
self.buildingBlock.cover_target(self.$overlay, self.$target);
return;
}
var dd = event['page'+XY] - xy + resize[1][begin];
var next = current+1 === resize[1].length ? current : (current+1);
var prev = current ? (current-1) : 0;
var change = false;
if (dd > (2*resize[1][next] + resize[1][current])/3) {
self.$target.attr("class", (self.$target.attr("class")||'').replace(regClass, ''));
self.$target.addClass(resize[0][next]);
current = next;
change = true;
}
if (prev !== current && dd < (2*resize[1][prev] + resize[1][current])/3) {
self.$target.attr("class", (self.$target.attr("class")||'').replace(regClass, ''));
self.$target.addClass(resize[0][prev]);
current = prev;
change = true;
}
if (change) {
self.on_resize(compass, beginClass, current);
self.buildingBlock.cover_target(self.$overlay, self.$target);
}
};
var body_mouseup = function () {
$body.unbind('mousemove', body_mousemove);
$body.unbind('mouseup', body_mouseup);
$body.removeClass(cursor);
setTimeout(function () {
self.buildingBlock.editor_busy = false;
if (begin !== current) {
self.buildingBlock.getParent().rte.historyRecordUndo(self.$target, 'resize_' + XY);
}
},0);
self.$target.removeClass("resize_editor_busy");
if (compass === "size") {
$fixed_size.addClass("active");
$auto_size.removeClass("active");
}
};
$body.mousemove(body_mousemove);
$body.mouseup(body_mouseup);
});
$auto_size.on('click', function () {
self.$target.css("height", "");
self.$target.css("overflow", "");
self.buildingBlock.getParent().rte.historyRecordUndo(self.$target, 'resize_Y');
self.buildingBlock.cover_target(self.$overlay, self.$target);
$fixed_size.removeClass("active");
$auto_size.addClass("active");
return false;
});
},
getSize: function () {
this.grid = {};
return this.grid;
},
on_focus : function () {
this._super();
this.change_cursor();
},
change_cursor : function () {
var _class = this.$target.attr("class") || "";
var col = _class.match(/col-md-([0-9-]+)/i);
col = col ? +col[1] : 0;
var offset = _class.match(/col-md-offset-([0-9-]+)/i);
offset = offset ? +offset[1] : 0;
var overlay_class = this.$overlay.attr("class").replace(/(^|\s+)block-[^\s]*/gi, '');
if (col+offset >= 12) overlay_class+= " block-e-right";
if (col === 1) overlay_class+= " block-w-right block-e-left";
if (offset === 0) overlay_class+= " block-w-left";
var mb = _class.match(/mb([0-9-]+)/i);
mb = mb ? +mb[1] : parseInt(this.$target.css('margin-bottom'));
if (mb >= 128) overlay_class+= " block-s-bottom";
else if (!mb) overlay_class+= " block-s-top";
var mt = _class.match(/mt([0-9-]+)/i);
mt = mt ? +mt[1] : parseInt(this.$target.css('margin-top'));
if (mt >= 128) overlay_class+= " block-n-top";
else if (!mt) overlay_class+= " block-n-bottom";
this.$overlay.attr("class", overlay_class);
},
/* on_resize
* called when the box is resizing and the class change, before the cover_target
* @compass: resize direction : 'n', 's', 'e', 'w'
* @beginClass: attributes class at the begin
* @current: curent increment in this.grid
*/
on_resize: function () {
this.change_cursor();
}
});
options.registry["margin-y"] = options.registry.marginAndResize.extend({
preventChildPropagation: true,
getSize: function () {
this.grid = this._super();
var grid = [0,4,8,16,32,48,64,92,128];
this.grid = {
// list of class (Array), grid (Array), default value (INT)
n: [_.map(grid, function (v) {return 'mt'+v;}), grid],
s: [_.map(grid, function (v) {return 'mb'+v;}), grid],
// INT if the user can resize the snippet (resizing per INT px)
size: null
};
return this.grid;
},
});
options.registry["margin-x"] = options.registry.marginAndResize.extend({
preventChildPropagation: true,
@@ -508,16 +293,6 @@ options.registry["margin-x"] = options.registry.marginAndResize.extend({
},
});
options.registry.resize = options.registry.marginAndResize.extend({
preventChildPropagation: true,
getSize: function () {
this.grid = this._super();
this.grid.size = 8;
return this.grid;
},
});
options.registry.parallax = options.Class.extend({
getSize: function () {
this.grid = this._super.apply(this, arguments);