[REF] website: move s_countdown snippet to dedicated files
This commit is contained in:
@@ -45,6 +45,7 @@
|
||||
'views/snippets/s_hr.xml',
|
||||
'views/snippets/s_facebook_page.xml',
|
||||
'views/snippets/s_image_gallery.xml',
|
||||
'views/snippets/s_countdown.xml',
|
||||
'views/website_views.xml',
|
||||
'views/website_visitor_views.xml',
|
||||
'views/res_config_settings_views.xml',
|
||||
|
||||
@@ -919,412 +919,6 @@ registry.anchorSlide = publicWidget.Widget.extend({
|
||||
},
|
||||
});
|
||||
|
||||
registry.countdown = publicWidget.Widget.extend({
|
||||
selector: '.s_countdown',
|
||||
xmlDependencies: ['/website/static/src/xml/website.s_countdown.xml'],
|
||||
disabledInEditableMode: false,
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
start: function () {
|
||||
this.$wrapper = this.$('.s_countdown_canvas_wrapper');
|
||||
this.hereBeforeTimerEnds = false;
|
||||
this.endAction = this.el.dataset.endAction;
|
||||
this.endTime = parseInt(this.el.dataset.endTime);
|
||||
this.size = parseInt(this.el.dataset.size);
|
||||
this.display = this.el.dataset.display;
|
||||
|
||||
this.layout = this.el.dataset.layout;
|
||||
this.layoutBackground = this.el.dataset.layoutBackground;
|
||||
this.progressBarStyle = this.el.dataset.progressBarStyle;
|
||||
this.progressBarWeight = this.el.dataset.progressBarWeight;
|
||||
|
||||
this.textColor = this._ensureCssColor(this.el.dataset.textColor);
|
||||
this.layoutBackgroundColor = this._ensureCssColor(this.el.dataset.layoutBackgroundColor);
|
||||
this.progressBarColor = this._ensureCssColor(this.el.dataset.progressBarColor);
|
||||
|
||||
this.onlyOneUnit = this.display === 'd';
|
||||
this.width = parseInt(this.size);
|
||||
if (this.layout === 'boxes') {
|
||||
this.width /= 1.75;
|
||||
}
|
||||
this._initTimeDiff();
|
||||
|
||||
this._render();
|
||||
|
||||
this.setInterval = setInterval(this._render.bind(this), 1000);
|
||||
return this._super(...arguments);
|
||||
},
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
destroy: function () {
|
||||
this.$('.s_countdown_end_redirect_message').remove();
|
||||
this.$('canvas').remove();
|
||||
this.$('.s_countdown_end_message').addClass('d-none');
|
||||
this.$('.s_countdown_text_wrapper').remove();
|
||||
this.$('.s_countdown_canvas_wrapper').removeClass('d-none');
|
||||
|
||||
clearInterval(this.setInterval);
|
||||
this._super(...arguments);
|
||||
},
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Private
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Ensures the color is an actual css color. In case of a color variable,
|
||||
* the color will be mapped to hexa.
|
||||
*
|
||||
* @private
|
||||
* @param {string} color
|
||||
* @returns {string}
|
||||
*/
|
||||
_ensureCssColor: function (color) {
|
||||
if (ColorpickerDialog.isCSSColor(color)) {
|
||||
return color;
|
||||
}
|
||||
const style = window.getComputedStyle(document.documentElement);
|
||||
return style.getPropertyValue('--' + color).trim();
|
||||
},
|
||||
/**
|
||||
* Gets the time difference in seconds between now and countdown due date.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
_getDelta: function () {
|
||||
const currentTimestamp = Date.now() / 1000;
|
||||
return this.endTime - currentTimestamp;
|
||||
},
|
||||
/**
|
||||
* Handles the action that should be executed once the countdown ends.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
_handleEndCountdownAction: function () {
|
||||
if (this.endAction === 'redirect') {
|
||||
const redirectUrl = this.el.dataset.redirectUrl;
|
||||
if (this.hereBeforeTimerEnds) {
|
||||
// Wait a bit, if the landing page has the same publish date
|
||||
setTimeout(() => window.location = redirectUrl, 500);
|
||||
} else {
|
||||
// Show (non editable) msg when user lands on already finished countdown
|
||||
if (!this.$('.s_countdown_end_redirect_message').length) {
|
||||
this.$target.find('.container').append(
|
||||
$(qweb.render('website.s_countdown.end_redirect_message', {
|
||||
redirectUrl: redirectUrl,
|
||||
}))
|
||||
);
|
||||
}
|
||||
}
|
||||
} else if (this.endAction === 'message') {
|
||||
this.$('.s_countdown_end_message').removeClass('d-none');
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Initializes the `diff` object. It will contains every visible time unit
|
||||
* which will each contain its related canvas, total step, label..
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
_initTimeDiff: function () {
|
||||
const delta = this._getDelta();
|
||||
this.diff = [];
|
||||
if (this._isUnitVisible('d') && !(this.onlyOneUnit && delta < 86400)) {
|
||||
this.diff.push({
|
||||
canvas: $('<canvas/>').appendTo(this.$wrapper)[0],
|
||||
// There is no logical number of unit (total) on which day units
|
||||
// can be compared against, so we use an arbitrary number.
|
||||
total: 15,
|
||||
label: _t("Days"),
|
||||
nbSeconds: 86400,
|
||||
});
|
||||
}
|
||||
if (this._isUnitVisible('h') || (this.onlyOneUnit && delta < 86400 && delta > 3600)) {
|
||||
this.diff.push({
|
||||
canvas: $('<canvas/>').appendTo(this.$wrapper)[0],
|
||||
total: 24,
|
||||
label: _t("Hours"),
|
||||
nbSeconds: 3600,
|
||||
});
|
||||
}
|
||||
if (this._isUnitVisible('m') || (this.onlyOneUnit && delta < 3600 && delta > 60)) {
|
||||
this.diff.push({
|
||||
canvas: $('<canvas/>').appendTo(this.$wrapper)[0],
|
||||
total: 60,
|
||||
label: _t("Minutes"),
|
||||
nbSeconds: 60,
|
||||
});
|
||||
}
|
||||
if (this._isUnitVisible('s') || (this.onlyOneUnit && delta < 60)) {
|
||||
this.diff.push({
|
||||
canvas: $('<canvas/>').appendTo(this.$wrapper)[0],
|
||||
total: 60,
|
||||
label: _t("Seconds"),
|
||||
nbSeconds: 1,
|
||||
});
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Returns weither or not the countdown should be displayed for the given
|
||||
* unit (days, sec..).
|
||||
*
|
||||
* @private
|
||||
* @param {string} unit - either 'd', 'm', 'h', or 's'
|
||||
* @returns {boolean}
|
||||
*/
|
||||
_isUnitVisible: function (unit) {
|
||||
return this.display.includes(unit);
|
||||
},
|
||||
/**
|
||||
* Draws the whole countdown, including one countdown for each time unit.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
_render: function () {
|
||||
// If only one unit mode, restart widget on unit change to populate diff
|
||||
if (this.onlyOneUnit && this._getDelta() < this.diff[0].nbSeconds) {
|
||||
this.$('canvas').remove();
|
||||
this._initTimeDiff();
|
||||
}
|
||||
this._updateTimeDiff();
|
||||
|
||||
const hideCountdown = this.isFinished && !this.editableMode && this.$el.hasClass('hide-countdown');
|
||||
if (this.layout === 'text') {
|
||||
this.$('canvas').addClass('d-none');
|
||||
if (!this.$textWrapper) {
|
||||
this.$textWrapper = $('<span/>').attr({
|
||||
class: 's_countdown_text_wrapper d-none',
|
||||
});
|
||||
this.$textWrapper.text(_t("Countdown ends in"));
|
||||
this.$textWrapper.append($('<span/>').attr({
|
||||
class: 's_countdown_text ml-1',
|
||||
}));
|
||||
this.$textWrapper.appendTo(this.$wrapper);
|
||||
}
|
||||
|
||||
this.$textWrapper.toggleClass('d-none', hideCountdown);
|
||||
|
||||
const countdownText = this.diff.map(e => e.nb + ' ' + e.label).join(', ');
|
||||
this.$('.s_countdown_text').text(countdownText.toLowerCase());
|
||||
} else {
|
||||
for (const val of this.diff) {
|
||||
const canvas = val.canvas;
|
||||
const ctx = canvas.getContext("2d");
|
||||
ctx.canvas.width = this.width;
|
||||
ctx.canvas.height = this.size;
|
||||
this._clearCanvas(ctx);
|
||||
|
||||
$(canvas).toggleClass('d-none', hideCountdown);
|
||||
if (hideCountdown) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Draw canvas elements
|
||||
if (this.layoutBackground !== 'none') {
|
||||
this._drawBgShape(ctx, this.layoutBackground === 'plain');
|
||||
}
|
||||
this._drawText(canvas, val.nb, val.label, this.layoutBackground === 'plain');
|
||||
if (this.progressBarStyle === 'surrounded') {
|
||||
this._drawProgressBarBg(ctx, this.progressBarWeight === 'thin');
|
||||
}
|
||||
if (this.progressBarStyle !== 'none') {
|
||||
this._drawProgressBar(ctx, val.nb, val.total, this.progressBarWeight === 'thin');
|
||||
}
|
||||
$(canvas).toggleClass('mx-2', this.layout === 'boxes');
|
||||
}
|
||||
}
|
||||
|
||||
if (this.isFinished) {
|
||||
clearInterval(this.setInterval);
|
||||
if (!this.editableMode) {
|
||||
this._handleEndCountdownAction();
|
||||
}
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Updates the remaining units into the `diff` object.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
_updateTimeDiff: function () {
|
||||
let delta = this._getDelta();
|
||||
this.isFinished = delta < 0;
|
||||
if (this.isFinished) {
|
||||
for (const unitData of this.diff) {
|
||||
unitData.nb = 0;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
this.hereBeforeTimerEnds = true;
|
||||
for (const unitData of this.diff) {
|
||||
unitData.nb = Math.floor(delta / unitData.nbSeconds);
|
||||
delta -= unitData.nb * unitData.nbSeconds;
|
||||
}
|
||||
},
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Canvas drawing methods
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Erases the canvas.
|
||||
*
|
||||
* @private
|
||||
* @param {RenderingContext} ctx - Context of the canvas
|
||||
*/
|
||||
_clearCanvas: function (ctx) {
|
||||
ctx.clearRect(0, 0, this.size, this.size);
|
||||
},
|
||||
/**
|
||||
* Draws a text into the canvas.
|
||||
*
|
||||
* @private
|
||||
* @param {HTMLCanvasElement} canvas
|
||||
* @param {string} textNb - text to display in the center of the canvas, in big
|
||||
* @param {string} textUnit - text to display bellow `textNb` in small
|
||||
* @param {boolean} full - if true, the shape will be drawn up to the progressbar
|
||||
*/
|
||||
_drawText: function (canvas, textNb, textUnit, full = false) {
|
||||
const ctx = canvas.getContext("2d");
|
||||
const nbSize = this.size / 4;
|
||||
ctx.font = `${nbSize}px Arial`;
|
||||
ctx.fillStyle = this.textColor;
|
||||
ctx.textAlign = 'center';
|
||||
ctx.textBaseline = 'middle';
|
||||
ctx.fillText(textNb, canvas.width / 2, canvas.height / 2);
|
||||
|
||||
const unitSize = this.size / 12;
|
||||
ctx.font = `${unitSize}px Arial`;
|
||||
ctx.fillText(textUnit, canvas.width / 2, canvas.height / 2 + nbSize / 1.5, this.width);
|
||||
|
||||
if (this.layout === 'boxes' && this.layoutBackground !== 'none' && this.progressBarStyle === 'none') {
|
||||
let barWidth = this.size / (this.progressBarWeight === 'thin' ? 31 : 10);
|
||||
if (full) {
|
||||
barWidth = 0;
|
||||
}
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(barWidth, this.size / 2);
|
||||
ctx.lineTo(this.width - barWidth, this.size / 2);
|
||||
ctx.stroke();
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Draws a plain shape into the canvas.
|
||||
*
|
||||
* @private
|
||||
* @param {RenderingContext} ctx - Context of the canvas
|
||||
* @param {boolean} full - if true, the shape will be drawn up to the progressbar
|
||||
*/
|
||||
_drawBgShape: function (ctx, full = false) {
|
||||
ctx.fillStyle = this.layoutBackgroundColor;
|
||||
ctx.beginPath();
|
||||
if (this.layout === 'circle') {
|
||||
let rayon = this.size / 2;
|
||||
if (this.progressBarWeight === 'thin') {
|
||||
rayon -= full ? this.size / 29 : this.size / 15;
|
||||
} else {
|
||||
rayon -= full ? 0 : this.size / 10;
|
||||
}
|
||||
ctx.arc(this.size / 2, this.size / 2, rayon, 0, Math.PI * 2);
|
||||
ctx.fill();
|
||||
} else if (this.layout === 'boxes') {
|
||||
let barWidth = this.size / (this.progressBarWeight === 'thin' ? 31 : 10);
|
||||
if (full) {
|
||||
barWidth = 0;
|
||||
}
|
||||
|
||||
ctx.fillStyle = this.layoutBackgroundColor;
|
||||
ctx.rect(barWidth, barWidth, this.width - barWidth * 2, this.size - barWidth * 2);
|
||||
ctx.fill();
|
||||
|
||||
const gradient = ctx.createLinearGradient(0, this.width, 0, 0);
|
||||
gradient.addColorStop(0, '#ffffff24');
|
||||
gradient.addColorStop(1, this.layoutBackgroundColor);
|
||||
ctx.fillStyle = gradient;
|
||||
ctx.rect(barWidth, barWidth, this.width - barWidth * 2, this.size - barWidth * 2);
|
||||
ctx.fill();
|
||||
$(ctx.canvas).css({'border-radius': '8px'});
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Draws a progress bar around the countdown shape.
|
||||
*
|
||||
* @private
|
||||
* @param {RenderingContext} ctx - Context of the canvas
|
||||
* @param {string} nbUnit - how many unit should fill progress bar
|
||||
* @param {string} totalUnit - number of unit to do a complete progress bar
|
||||
* @param {boolean} thinLine - if true, the progress bar will be thiner
|
||||
*/
|
||||
_drawProgressBar: function (ctx, nbUnit, totalUnit, thinLine) {
|
||||
ctx.strokeStyle = this.progressBarColor;
|
||||
ctx.lineWidth = thinLine ? this.size / 35 : this.size / 10;
|
||||
if (this.layout === 'circle') {
|
||||
ctx.beginPath();
|
||||
ctx.arc(this.size / 2, this.size / 2, this.size / 2 - this.size / 20, Math.PI / -2, (Math.PI * 2) * (nbUnit / totalUnit) + (Math.PI / -2));
|
||||
ctx.stroke();
|
||||
} else if (this.layout === 'boxes') {
|
||||
ctx.lineWidth *= 2;
|
||||
let pc = nbUnit / totalUnit * 100;
|
||||
|
||||
// Lines: Top(x1,y1,x2,y2) Right(x1,y1,x2,y2) Bottom(x1,y1,x2,y2) Left(x1,y1,x2,y2)
|
||||
const linesCoordFuncs = [
|
||||
(linePc) => [0 + ctx.lineWidth / 2, 0, (this.width - ctx.lineWidth / 2) * linePc / 25 + ctx.lineWidth / 2, 0],
|
||||
(linePc) => [this.width, 0 + ctx.lineWidth / 2, this.width, (this.size - ctx.lineWidth / 2) * linePc / 25 + ctx.lineWidth / 2],
|
||||
(linePc) => [this.width - ((this.width - ctx.lineWidth / 2) * linePc / 25) - ctx.lineWidth / 2, this.size, this.width - ctx.lineWidth / 2, this.size],
|
||||
(linePc) => [0, this.size - ((this.size - ctx.lineWidth / 2) * linePc / 25) - ctx.lineWidth / 2, 0, this.size - ctx.lineWidth / 2],
|
||||
];
|
||||
while (pc > 0 && linesCoordFuncs.length) {
|
||||
const linePc = Math.min(pc, 25);
|
||||
const lineCoord = (linesCoordFuncs.shift())(linePc);
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(lineCoord[0], lineCoord[1]);
|
||||
ctx.lineTo(lineCoord[2], lineCoord[3]);
|
||||
ctx.stroke();
|
||||
pc -= linePc;
|
||||
}
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Draws a full lighter background progressbar around the shape.
|
||||
*
|
||||
* @private
|
||||
* @param {RenderingContext} ctx - Context of the canvas
|
||||
* @param {boolean} thinLine - if true, the progress bar will be thiner
|
||||
*/
|
||||
_drawProgressBarBg: function (ctx, thinLine) {
|
||||
ctx.strokeStyle = this.progressBarColor;
|
||||
ctx.globalAlpha = 0.2;
|
||||
ctx.lineWidth = thinLine ? this.size / 35 : this.size / 10;
|
||||
if (this.layout === 'circle') {
|
||||
ctx.beginPath();
|
||||
ctx.arc(this.size / 2, this.size / 2, this.size / 2 - this.size / 20, 0, Math.PI * 2);
|
||||
ctx.stroke();
|
||||
} else if (this.layout === 'boxes') {
|
||||
ctx.lineWidth *= 2;
|
||||
|
||||
// Lines: Top(x1,y1,x2,y2) Right(x1,y1,x2,y2) Bottom(x1,y1,x2,y2) Left(x1,y1,x2,y2)
|
||||
const points = [
|
||||
[0 + ctx.lineWidth / 2, 0, this.width, 0],
|
||||
[this.width, 0 + ctx.lineWidth / 2, this.width, this.size],
|
||||
[0, this.size, this.width - ctx.lineWidth / 2, this.size],
|
||||
[0, 0, 0, this.size - ctx.lineWidth / 2],
|
||||
];
|
||||
while (points.length) {
|
||||
const point = points.shift();
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(point[0], point[1]);
|
||||
ctx.lineTo(point[2], point[3]);
|
||||
ctx.stroke();
|
||||
}
|
||||
}
|
||||
ctx.globalAlpha = 1;
|
||||
},
|
||||
});
|
||||
|
||||
registry.chart = publicWidget.Widget.extend({
|
||||
selector: '.s_chart',
|
||||
disabledInEditableMode: false,
|
||||
@@ -844,122 +844,6 @@ options.registry.collapse = options.Class.extend({
|
||||
},
|
||||
});
|
||||
|
||||
options.registry.countdown = options.Class.extend({
|
||||
events: _.extend({}, options.Class.prototype.events || {}, {
|
||||
'click .toggle-edit-message': '_onToggleEndMessageClick',
|
||||
}),
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Options
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Changes the countdown action at zero.
|
||||
*
|
||||
* @see this.selectClass for parameters
|
||||
*/
|
||||
endAction: function (previewMode, widgetValue, params) {
|
||||
this.$target[0].dataset.endAction = widgetValue;
|
||||
if (widgetValue === 'message') {
|
||||
if (!this.$target.find('.s_countdown_end_message').length) {
|
||||
const message = this.endMessage || qweb.render('website.s_countdown.end_message');
|
||||
this.$target.find('.container').append(message);
|
||||
}
|
||||
} else {
|
||||
const $message = this.$target.find('.s_countdown_end_message').detach();
|
||||
if ($message.length) {
|
||||
this.endMessage = $message[0].outerHTML;
|
||||
}
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Changes the countdown style.
|
||||
*
|
||||
* @see this.selectClass for parameters
|
||||
*/
|
||||
layout: function (previewMode, widgetValue, params) {
|
||||
switch (widgetValue) {
|
||||
case 'circle':
|
||||
this.$target[0].dataset.progressBarStyle = 'disappear';
|
||||
this.$target[0].dataset.progressBarWeight = 'thin';
|
||||
this.$target[0].dataset.layoutBackground = 'none';
|
||||
break;
|
||||
case 'boxes':
|
||||
this.$target[0].dataset.progressBarStyle = 'none';
|
||||
this.$target[0].dataset.layoutBackground = 'plain';
|
||||
break;
|
||||
case 'clean':
|
||||
this.$target[0].dataset.progressBarStyle = 'none';
|
||||
this.$target[0].dataset.layoutBackground = 'none';
|
||||
break;
|
||||
case 'text':
|
||||
this.$target[0].dataset.progressBarStyle = 'none';
|
||||
this.$target[0].dataset.layoutBackground = 'none';
|
||||
break;
|
||||
}
|
||||
this.$target[0].dataset.layout = widgetValue;
|
||||
},
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Public
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
updateUIVisibility: async function () {
|
||||
await this._super(...arguments);
|
||||
const dataset = this.$target[0].dataset;
|
||||
|
||||
// End Action UI
|
||||
this.$el.find('.toggle-edit-message')
|
||||
.toggleClass('d-none', dataset.endAction !== 'message');
|
||||
|
||||
// End Message UI
|
||||
this.updateUIEndMessage();
|
||||
},
|
||||
/**
|
||||
* @see this.updateUI
|
||||
*/
|
||||
updateUIEndMessage: function () {
|
||||
this.$target.find('.s_countdown_canvas_wrapper')
|
||||
.toggleClass("d-none", this.showEndMessage === true && this.$target.hasClass("hide-countdown"));
|
||||
this.$target.find('.s_countdown_end_message')
|
||||
.toggleClass("d-none", !this.showEndMessage);
|
||||
},
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Private
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
_computeWidgetState: function (methodName, params) {
|
||||
switch (methodName) {
|
||||
case 'endAction':
|
||||
case 'layout':
|
||||
return this.$target[0].dataset[methodName];
|
||||
}
|
||||
return this._super(...arguments);
|
||||
},
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Handlers
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
_onToggleEndMessageClick: function () {
|
||||
this.showEndMessage = !this.showEndMessage;
|
||||
this.$el.find(".toggle-edit-message")
|
||||
.toggleClass('text-primary', this.showEndMessage);
|
||||
this.updateUIEndMessage();
|
||||
this.trigger_up('cover_update');
|
||||
},
|
||||
});
|
||||
|
||||
options.registry.topMenuTransparency = options.Class.extend({
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
@@ -0,0 +1,420 @@
|
||||
odoo.define('website.s_countdown', function (require) {
|
||||
'use strict';
|
||||
|
||||
const ColorpickerDialog = require('web.ColorpickerDialog');
|
||||
const core = require('web.core');
|
||||
const publicWidget = require('web.public.widget');
|
||||
|
||||
const qweb = core.qweb;
|
||||
const _t = core._t;
|
||||
|
||||
const CountdownWidget = publicWidget.Widget.extend({
|
||||
selector: '.s_countdown',
|
||||
xmlDependencies: ['/website/static/src/xml/website.s_countdown.xml'],
|
||||
disabledInEditableMode: false,
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
start: function () {
|
||||
this.$wrapper = this.$('.s_countdown_canvas_wrapper');
|
||||
this.hereBeforeTimerEnds = false;
|
||||
this.endAction = this.el.dataset.endAction;
|
||||
this.endTime = parseInt(this.el.dataset.endTime);
|
||||
this.size = parseInt(this.el.dataset.size);
|
||||
this.display = this.el.dataset.display;
|
||||
|
||||
this.layout = this.el.dataset.layout;
|
||||
this.layoutBackground = this.el.dataset.layoutBackground;
|
||||
this.progressBarStyle = this.el.dataset.progressBarStyle;
|
||||
this.progressBarWeight = this.el.dataset.progressBarWeight;
|
||||
|
||||
this.textColor = this._ensureCssColor(this.el.dataset.textColor);
|
||||
this.layoutBackgroundColor = this._ensureCssColor(this.el.dataset.layoutBackgroundColor);
|
||||
this.progressBarColor = this._ensureCssColor(this.el.dataset.progressBarColor);
|
||||
|
||||
this.onlyOneUnit = this.display === 'd';
|
||||
this.width = parseInt(this.size);
|
||||
if (this.layout === 'boxes') {
|
||||
this.width /= 1.75;
|
||||
}
|
||||
this._initTimeDiff();
|
||||
|
||||
this._render();
|
||||
|
||||
this.setInterval = setInterval(this._render.bind(this), 1000);
|
||||
return this._super(...arguments);
|
||||
},
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
destroy: function () {
|
||||
this.$('.s_countdown_end_redirect_message').remove();
|
||||
this.$('canvas').remove();
|
||||
this.$('.s_countdown_end_message').addClass('d-none');
|
||||
this.$('.s_countdown_text_wrapper').remove();
|
||||
this.$('.s_countdown_canvas_wrapper').removeClass('d-none');
|
||||
|
||||
clearInterval(this.setInterval);
|
||||
this._super(...arguments);
|
||||
},
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Private
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Ensures the color is an actual css color. In case of a color variable,
|
||||
* the color will be mapped to hexa.
|
||||
*
|
||||
* @private
|
||||
* @param {string} color
|
||||
* @returns {string}
|
||||
*/
|
||||
_ensureCssColor: function (color) {
|
||||
if (ColorpickerDialog.isCSSColor(color)) {
|
||||
return color;
|
||||
}
|
||||
const style = window.getComputedStyle(document.documentElement);
|
||||
return style.getPropertyValue('--' + color).trim();
|
||||
},
|
||||
/**
|
||||
* Gets the time difference in seconds between now and countdown due date.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
_getDelta: function () {
|
||||
const currentTimestamp = Date.now() / 1000;
|
||||
return this.endTime - currentTimestamp;
|
||||
},
|
||||
/**
|
||||
* Handles the action that should be executed once the countdown ends.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
_handleEndCountdownAction: function () {
|
||||
if (this.endAction === 'redirect') {
|
||||
const redirectUrl = this.el.dataset.redirectUrl;
|
||||
if (this.hereBeforeTimerEnds) {
|
||||
// Wait a bit, if the landing page has the same publish date
|
||||
setTimeout(() => window.location = redirectUrl, 500);
|
||||
} else {
|
||||
// Show (non editable) msg when user lands on already finished countdown
|
||||
if (!this.$('.s_countdown_end_redirect_message').length) {
|
||||
this.$target.find('.container').append(
|
||||
$(qweb.render('website.s_countdown.end_redirect_message', {
|
||||
redirectUrl: redirectUrl,
|
||||
}))
|
||||
);
|
||||
}
|
||||
}
|
||||
} else if (this.endAction === 'message') {
|
||||
this.$('.s_countdown_end_message').removeClass('d-none');
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Initializes the `diff` object. It will contains every visible time unit
|
||||
* which will each contain its related canvas, total step, label..
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
_initTimeDiff: function () {
|
||||
const delta = this._getDelta();
|
||||
this.diff = [];
|
||||
if (this._isUnitVisible('d') && !(this.onlyOneUnit && delta < 86400)) {
|
||||
this.diff.push({
|
||||
canvas: $('<canvas/>').appendTo(this.$wrapper)[0],
|
||||
// There is no logical number of unit (total) on which day units
|
||||
// can be compared against, so we use an arbitrary number.
|
||||
total: 15,
|
||||
label: _t("Days"),
|
||||
nbSeconds: 86400,
|
||||
});
|
||||
}
|
||||
if (this._isUnitVisible('h') || (this.onlyOneUnit && delta < 86400 && delta > 3600)) {
|
||||
this.diff.push({
|
||||
canvas: $('<canvas/>').appendTo(this.$wrapper)[0],
|
||||
total: 24,
|
||||
label: _t("Hours"),
|
||||
nbSeconds: 3600,
|
||||
});
|
||||
}
|
||||
if (this._isUnitVisible('m') || (this.onlyOneUnit && delta < 3600 && delta > 60)) {
|
||||
this.diff.push({
|
||||
canvas: $('<canvas/>').appendTo(this.$wrapper)[0],
|
||||
total: 60,
|
||||
label: _t("Minutes"),
|
||||
nbSeconds: 60,
|
||||
});
|
||||
}
|
||||
if (this._isUnitVisible('s') || (this.onlyOneUnit && delta < 60)) {
|
||||
this.diff.push({
|
||||
canvas: $('<canvas/>').appendTo(this.$wrapper)[0],
|
||||
total: 60,
|
||||
label: _t("Seconds"),
|
||||
nbSeconds: 1,
|
||||
});
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Returns weither or not the countdown should be displayed for the given
|
||||
* unit (days, sec..).
|
||||
*
|
||||
* @private
|
||||
* @param {string} unit - either 'd', 'm', 'h', or 's'
|
||||
* @returns {boolean}
|
||||
*/
|
||||
_isUnitVisible: function (unit) {
|
||||
return this.display.includes(unit);
|
||||
},
|
||||
/**
|
||||
* Draws the whole countdown, including one countdown for each time unit.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
_render: function () {
|
||||
// If only one unit mode, restart widget on unit change to populate diff
|
||||
if (this.onlyOneUnit && this._getDelta() < this.diff[0].nbSeconds) {
|
||||
this.$('canvas').remove();
|
||||
this._initTimeDiff();
|
||||
}
|
||||
this._updateTimeDiff();
|
||||
|
||||
const hideCountdown = this.isFinished && !this.editableMode && this.$el.hasClass('hide-countdown');
|
||||
if (this.layout === 'text') {
|
||||
this.$('canvas').addClass('d-none');
|
||||
if (!this.$textWrapper) {
|
||||
this.$textWrapper = $('<span/>').attr({
|
||||
class: 's_countdown_text_wrapper d-none',
|
||||
});
|
||||
this.$textWrapper.text(_t("Countdown ends in"));
|
||||
this.$textWrapper.append($('<span/>').attr({
|
||||
class: 's_countdown_text ml-1',
|
||||
}));
|
||||
this.$textWrapper.appendTo(this.$wrapper);
|
||||
}
|
||||
|
||||
this.$textWrapper.toggleClass('d-none', hideCountdown);
|
||||
|
||||
const countdownText = this.diff.map(e => e.nb + ' ' + e.label).join(', ');
|
||||
this.$('.s_countdown_text').text(countdownText.toLowerCase());
|
||||
} else {
|
||||
for (const val of this.diff) {
|
||||
const canvas = val.canvas;
|
||||
const ctx = canvas.getContext("2d");
|
||||
ctx.canvas.width = this.width;
|
||||
ctx.canvas.height = this.size;
|
||||
this._clearCanvas(ctx);
|
||||
|
||||
$(canvas).toggleClass('d-none', hideCountdown);
|
||||
if (hideCountdown) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Draw canvas elements
|
||||
if (this.layoutBackground !== 'none') {
|
||||
this._drawBgShape(ctx, this.layoutBackground === 'plain');
|
||||
}
|
||||
this._drawText(canvas, val.nb, val.label, this.layoutBackground === 'plain');
|
||||
if (this.progressBarStyle === 'surrounded') {
|
||||
this._drawProgressBarBg(ctx, this.progressBarWeight === 'thin');
|
||||
}
|
||||
if (this.progressBarStyle !== 'none') {
|
||||
this._drawProgressBar(ctx, val.nb, val.total, this.progressBarWeight === 'thin');
|
||||
}
|
||||
$(canvas).toggleClass('mx-2', this.layout === 'boxes');
|
||||
}
|
||||
}
|
||||
|
||||
if (this.isFinished) {
|
||||
clearInterval(this.setInterval);
|
||||
if (!this.editableMode) {
|
||||
this._handleEndCountdownAction();
|
||||
}
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Updates the remaining units into the `diff` object.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
_updateTimeDiff: function () {
|
||||
let delta = this._getDelta();
|
||||
this.isFinished = delta < 0;
|
||||
if (this.isFinished) {
|
||||
for (const unitData of this.diff) {
|
||||
unitData.nb = 0;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
this.hereBeforeTimerEnds = true;
|
||||
for (const unitData of this.diff) {
|
||||
unitData.nb = Math.floor(delta / unitData.nbSeconds);
|
||||
delta -= unitData.nb * unitData.nbSeconds;
|
||||
}
|
||||
},
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Canvas drawing methods
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Erases the canvas.
|
||||
*
|
||||
* @private
|
||||
* @param {RenderingContext} ctx - Context of the canvas
|
||||
*/
|
||||
_clearCanvas: function (ctx) {
|
||||
ctx.clearRect(0, 0, this.size, this.size);
|
||||
},
|
||||
/**
|
||||
* Draws a text into the canvas.
|
||||
*
|
||||
* @private
|
||||
* @param {HTMLCanvasElement} canvas
|
||||
* @param {string} textNb - text to display in the center of the canvas, in big
|
||||
* @param {string} textUnit - text to display bellow `textNb` in small
|
||||
* @param {boolean} full - if true, the shape will be drawn up to the progressbar
|
||||
*/
|
||||
_drawText: function (canvas, textNb, textUnit, full = false) {
|
||||
const ctx = canvas.getContext("2d");
|
||||
const nbSize = this.size / 4;
|
||||
ctx.font = `${nbSize}px Arial`;
|
||||
ctx.fillStyle = this.textColor;
|
||||
ctx.textAlign = 'center';
|
||||
ctx.textBaseline = 'middle';
|
||||
ctx.fillText(textNb, canvas.width / 2, canvas.height / 2);
|
||||
|
||||
const unitSize = this.size / 12;
|
||||
ctx.font = `${unitSize}px Arial`;
|
||||
ctx.fillText(textUnit, canvas.width / 2, canvas.height / 2 + nbSize / 1.5, this.width);
|
||||
|
||||
if (this.layout === 'boxes' && this.layoutBackground !== 'none' && this.progressBarStyle === 'none') {
|
||||
let barWidth = this.size / (this.progressBarWeight === 'thin' ? 31 : 10);
|
||||
if (full) {
|
||||
barWidth = 0;
|
||||
}
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(barWidth, this.size / 2);
|
||||
ctx.lineTo(this.width - barWidth, this.size / 2);
|
||||
ctx.stroke();
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Draws a plain shape into the canvas.
|
||||
*
|
||||
* @private
|
||||
* @param {RenderingContext} ctx - Context of the canvas
|
||||
* @param {boolean} full - if true, the shape will be drawn up to the progressbar
|
||||
*/
|
||||
_drawBgShape: function (ctx, full = false) {
|
||||
ctx.fillStyle = this.layoutBackgroundColor;
|
||||
ctx.beginPath();
|
||||
if (this.layout === 'circle') {
|
||||
let rayon = this.size / 2;
|
||||
if (this.progressBarWeight === 'thin') {
|
||||
rayon -= full ? this.size / 29 : this.size / 15;
|
||||
} else {
|
||||
rayon -= full ? 0 : this.size / 10;
|
||||
}
|
||||
ctx.arc(this.size / 2, this.size / 2, rayon, 0, Math.PI * 2);
|
||||
ctx.fill();
|
||||
} else if (this.layout === 'boxes') {
|
||||
let barWidth = this.size / (this.progressBarWeight === 'thin' ? 31 : 10);
|
||||
if (full) {
|
||||
barWidth = 0;
|
||||
}
|
||||
|
||||
ctx.fillStyle = this.layoutBackgroundColor;
|
||||
ctx.rect(barWidth, barWidth, this.width - barWidth * 2, this.size - barWidth * 2);
|
||||
ctx.fill();
|
||||
|
||||
const gradient = ctx.createLinearGradient(0, this.width, 0, 0);
|
||||
gradient.addColorStop(0, '#ffffff24');
|
||||
gradient.addColorStop(1, this.layoutBackgroundColor);
|
||||
ctx.fillStyle = gradient;
|
||||
ctx.rect(barWidth, barWidth, this.width - barWidth * 2, this.size - barWidth * 2);
|
||||
ctx.fill();
|
||||
$(ctx.canvas).css({'border-radius': '8px'});
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Draws a progress bar around the countdown shape.
|
||||
*
|
||||
* @private
|
||||
* @param {RenderingContext} ctx - Context of the canvas
|
||||
* @param {string} nbUnit - how many unit should fill progress bar
|
||||
* @param {string} totalUnit - number of unit to do a complete progress bar
|
||||
* @param {boolean} thinLine - if true, the progress bar will be thiner
|
||||
*/
|
||||
_drawProgressBar: function (ctx, nbUnit, totalUnit, thinLine) {
|
||||
ctx.strokeStyle = this.progressBarColor;
|
||||
ctx.lineWidth = thinLine ? this.size / 35 : this.size / 10;
|
||||
if (this.layout === 'circle') {
|
||||
ctx.beginPath();
|
||||
ctx.arc(this.size / 2, this.size / 2, this.size / 2 - this.size / 20, Math.PI / -2, (Math.PI * 2) * (nbUnit / totalUnit) + (Math.PI / -2));
|
||||
ctx.stroke();
|
||||
} else if (this.layout === 'boxes') {
|
||||
ctx.lineWidth *= 2;
|
||||
let pc = nbUnit / totalUnit * 100;
|
||||
|
||||
// Lines: Top(x1,y1,x2,y2) Right(x1,y1,x2,y2) Bottom(x1,y1,x2,y2) Left(x1,y1,x2,y2)
|
||||
const linesCoordFuncs = [
|
||||
(linePc) => [0 + ctx.lineWidth / 2, 0, (this.width - ctx.lineWidth / 2) * linePc / 25 + ctx.lineWidth / 2, 0],
|
||||
(linePc) => [this.width, 0 + ctx.lineWidth / 2, this.width, (this.size - ctx.lineWidth / 2) * linePc / 25 + ctx.lineWidth / 2],
|
||||
(linePc) => [this.width - ((this.width - ctx.lineWidth / 2) * linePc / 25) - ctx.lineWidth / 2, this.size, this.width - ctx.lineWidth / 2, this.size],
|
||||
(linePc) => [0, this.size - ((this.size - ctx.lineWidth / 2) * linePc / 25) - ctx.lineWidth / 2, 0, this.size - ctx.lineWidth / 2],
|
||||
];
|
||||
while (pc > 0 && linesCoordFuncs.length) {
|
||||
const linePc = Math.min(pc, 25);
|
||||
const lineCoord = (linesCoordFuncs.shift())(linePc);
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(lineCoord[0], lineCoord[1]);
|
||||
ctx.lineTo(lineCoord[2], lineCoord[3]);
|
||||
ctx.stroke();
|
||||
pc -= linePc;
|
||||
}
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Draws a full lighter background progressbar around the shape.
|
||||
*
|
||||
* @private
|
||||
* @param {RenderingContext} ctx - Context of the canvas
|
||||
* @param {boolean} thinLine - if true, the progress bar will be thiner
|
||||
*/
|
||||
_drawProgressBarBg: function (ctx, thinLine) {
|
||||
ctx.strokeStyle = this.progressBarColor;
|
||||
ctx.globalAlpha = 0.2;
|
||||
ctx.lineWidth = thinLine ? this.size / 35 : this.size / 10;
|
||||
if (this.layout === 'circle') {
|
||||
ctx.beginPath();
|
||||
ctx.arc(this.size / 2, this.size / 2, this.size / 2 - this.size / 20, 0, Math.PI * 2);
|
||||
ctx.stroke();
|
||||
} else if (this.layout === 'boxes') {
|
||||
ctx.lineWidth *= 2;
|
||||
|
||||
// Lines: Top(x1,y1,x2,y2) Right(x1,y1,x2,y2) Bottom(x1,y1,x2,y2) Left(x1,y1,x2,y2)
|
||||
const points = [
|
||||
[0 + ctx.lineWidth / 2, 0, this.width, 0],
|
||||
[this.width, 0 + ctx.lineWidth / 2, this.width, this.size],
|
||||
[0, this.size, this.width - ctx.lineWidth / 2, this.size],
|
||||
[0, 0, 0, this.size - ctx.lineWidth / 2],
|
||||
];
|
||||
while (points.length) {
|
||||
const point = points.shift();
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(point[0], point[1]);
|
||||
ctx.lineTo(point[2], point[3]);
|
||||
ctx.stroke();
|
||||
}
|
||||
}
|
||||
ctx.globalAlpha = 1;
|
||||
},
|
||||
});
|
||||
|
||||
publicWidget.registry.countdown = CountdownWidget;
|
||||
|
||||
return CountdownWidget;
|
||||
});
|
||||
@@ -0,0 +1,124 @@
|
||||
odoo.define('website.s_countdown_options', function (require) {
|
||||
'use strict';
|
||||
|
||||
const core = require('web.core');
|
||||
const options = require('web_editor.snippets.options');
|
||||
|
||||
const qweb = core.qweb;
|
||||
|
||||
options.registry.countdown = options.Class.extend({
|
||||
events: _.extend({}, options.Class.prototype.events || {}, {
|
||||
'click .toggle-edit-message': '_onToggleEndMessageClick',
|
||||
}),
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Options
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Changes the countdown action at zero.
|
||||
*
|
||||
* @see this.selectClass for parameters
|
||||
*/
|
||||
endAction: function (previewMode, widgetValue, params) {
|
||||
this.$target[0].dataset.endAction = widgetValue;
|
||||
if (widgetValue === 'message') {
|
||||
if (!this.$target.find('.s_countdown_end_message').length) {
|
||||
const message = this.endMessage || qweb.render('website.s_countdown.end_message');
|
||||
this.$target.find('.container').append(message);
|
||||
}
|
||||
} else {
|
||||
const $message = this.$target.find('.s_countdown_end_message').detach();
|
||||
if ($message.length) {
|
||||
this.endMessage = $message[0].outerHTML;
|
||||
}
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Changes the countdown style.
|
||||
*
|
||||
* @see this.selectClass for parameters
|
||||
*/
|
||||
layout: function (previewMode, widgetValue, params) {
|
||||
switch (widgetValue) {
|
||||
case 'circle':
|
||||
this.$target[0].dataset.progressBarStyle = 'disappear';
|
||||
this.$target[0].dataset.progressBarWeight = 'thin';
|
||||
this.$target[0].dataset.layoutBackground = 'none';
|
||||
break;
|
||||
case 'boxes':
|
||||
this.$target[0].dataset.progressBarStyle = 'none';
|
||||
this.$target[0].dataset.layoutBackground = 'plain';
|
||||
break;
|
||||
case 'clean':
|
||||
this.$target[0].dataset.progressBarStyle = 'none';
|
||||
this.$target[0].dataset.layoutBackground = 'none';
|
||||
break;
|
||||
case 'text':
|
||||
this.$target[0].dataset.progressBarStyle = 'none';
|
||||
this.$target[0].dataset.layoutBackground = 'none';
|
||||
break;
|
||||
}
|
||||
this.$target[0].dataset.layout = widgetValue;
|
||||
},
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Public
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
updateUIVisibility: async function () {
|
||||
await this._super(...arguments);
|
||||
const dataset = this.$target[0].dataset;
|
||||
|
||||
// End Action UI
|
||||
this.$el.find('.toggle-edit-message')
|
||||
.toggleClass('d-none', dataset.endAction !== 'message');
|
||||
|
||||
// End Message UI
|
||||
this.updateUIEndMessage();
|
||||
},
|
||||
/**
|
||||
* @see this.updateUI
|
||||
*/
|
||||
updateUIEndMessage: function () {
|
||||
this.$target.find('.s_countdown_canvas_wrapper')
|
||||
.toggleClass("d-none", this.showEndMessage === true && this.$target.hasClass("hide-countdown"));
|
||||
this.$target.find('.s_countdown_end_message')
|
||||
.toggleClass("d-none", !this.showEndMessage);
|
||||
},
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Private
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
_computeWidgetState: function (methodName, params) {
|
||||
switch (methodName) {
|
||||
case 'endAction':
|
||||
case 'layout':
|
||||
return this.$target[0].dataset[methodName];
|
||||
}
|
||||
return this._super(...arguments);
|
||||
},
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Handlers
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
_onToggleEndMessageClick: function () {
|
||||
this.showEndMessage = !this.showEndMessage;
|
||||
this.$el.find(".toggle-edit-message")
|
||||
.toggleClass('text-primary', this.showEndMessage);
|
||||
this.updateUIEndMessage();
|
||||
this.trigger_up('cover_update');
|
||||
},
|
||||
});
|
||||
});
|
||||
@@ -110,6 +110,7 @@
|
||||
<script type="text/javascript" src="/website/static/src/js/editor/snippets.options.js"/>
|
||||
<script type="text/javascript" src="/website/static/src/snippets/s_facebook_page/options.js"/>
|
||||
<script type="text/javascript" src="/website/static/src/snippets/s_image_gallery/options.js"/>
|
||||
<script type="text/javascript" src="/website/static/src/snippets/s_countdown/options.js"/>
|
||||
|
||||
<script type="text/javascript" src="/website/static/src/js/editor/wysiwyg_multizone.js"/>
|
||||
<script type="text/javascript" src="/website/static/src/js/editor/wysiwyg_multizone_translate.js"/>
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
|
||||
<template id="s_countdown" name="Countdown">
|
||||
<section class="s_countdown"
|
||||
data-display="dhms" data-end-action="nothing" data-size="175"
|
||||
t-att-data-end-time="datetime.datetime.now().timestamp() + 228307"
|
||||
data-layout="circle" data-layout-background="none"
|
||||
data-progress-bar-style="surrounded" data-progress-bar-weight="thin"
|
||||
data-text-color="alpha" data-layout-background-color="400" data-progress-bar-color="alpha">
|
||||
<div class="container">
|
||||
<div class="s_countdown_canvas_wrapper text-center pt8 pb8"/>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<template id="s_countdown_options" inherit_id="website.snippet_options">
|
||||
<xpath expr="." position="inside">
|
||||
<div data-js="countdown" data-selector=".s_countdown">
|
||||
<we-datetimepicker string="Due Date" data-select-data-attribute="" data-attribute-name="endTime"/>
|
||||
<we-row>
|
||||
<we-select string="What should happen at the end?" data-no-preview="true">
|
||||
<we-button data-end-action="nothing" data-name="no_end_action_opt">Nothing</we-button>
|
||||
<we-button data-end-action="redirect" data-name="redirect_end_action_opt">Redirect</we-button>
|
||||
<we-button data-end-action="message">Show Message</we-button>
|
||||
</we-select>
|
||||
<we-button class="far fa-edit fa-fw align-self-end toggle-edit-message" title="Edit Message"/>
|
||||
</we-row>
|
||||
<we-checkbox string="Hide countdown at the end" data-dependencies="!no_end_action_opt"
|
||||
data-select-class="hide-countdown"/>
|
||||
<we-urlpicker string="URL" data-dependencies="redirect_end_action_opt"
|
||||
data-select-data-attribute="/my-awesome-page" data-attribute-name="redirectUrl"/>
|
||||
<we-select string="Size" data-attribute-name="size">
|
||||
<we-button data-select-data-attribute="80">Small</we-button>
|
||||
<we-button data-select-data-attribute="120">Medium</we-button>
|
||||
<we-button data-select-data-attribute="175">Large</we-button>
|
||||
</we-select>
|
||||
<we-select string="Display" data-attribute-name="display">
|
||||
<we-button data-select-data-attribute="d">D</we-button>
|
||||
<we-button data-select-data-attribute="dhm">D - H - M</we-button>
|
||||
<we-button data-select-data-attribute="dhms">D - H - M - S</we-button>
|
||||
</we-select>
|
||||
<we-colorpicker string="Text Color" data-attribute-name="textColor" data-select-data-attribute=""/>
|
||||
<we-select string="Layout">
|
||||
<we-button data-layout="circle" data-name="circle_layout_opt">Circle</we-button>
|
||||
<we-button data-layout="boxes" data-name="boxes_layout_opt">Boxes</we-button>
|
||||
<we-button data-layout="clean">Clean</we-button>
|
||||
<we-button data-layout="text">Text Inline</we-button>
|
||||
</we-select>
|
||||
<we-select string="Layout Background" data-attribute-name="layoutBackground" data-dependencies="circle_layout_opt, boxes_layout_opt">
|
||||
<we-button data-select-data-attribute="inner">Inner</we-button>
|
||||
<we-button data-select-data-attribute="plain">Plain</we-button>
|
||||
<we-button data-select-data-attribute="none" data-name="no_background_layout_opt">None</we-button>
|
||||
</we-select>
|
||||
<we-colorpicker string="Layout Background Color" data-dependencies="!no_background_layout_opt"
|
||||
data-attribute-name="layoutBackgroundColor" data-select-data-attribute=""/>
|
||||
<we-select string="Progress Bar Style" data-attribute-name="progressBarStyle" data-dependencies="circle_layout_opt, boxes_layout_opt">
|
||||
<we-button data-select-data-attribute="surrounded">Surrounded</we-button>
|
||||
<we-button data-select-data-attribute="disappear">Disappearing</we-button>
|
||||
<we-button data-select-data-attribute="none" data-name="no_progressbar_style_opt">None</we-button>
|
||||
</we-select>
|
||||
<we-select string="Progress Bar Weight" data-attribute-name="progressBarWeight" data-dependencies="!no_progressbar_style_opt">
|
||||
<we-button data-select-data-attribute="thin">Thin</we-button>
|
||||
<we-button data-select-data-attribute="thick">Thick</we-button>
|
||||
</we-select>
|
||||
<we-colorpicker string="Progress Bar Color" data-dependencies="!no_progressbar_style_opt"
|
||||
data-attribute-name="progressBarColor" data-select-data-attribute=""/>
|
||||
</div>
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
<template id="assets_snippet_s_countdown_js_000" inherit_id="website.assets_frontend">
|
||||
<xpath expr="//script[last()]" position="after">
|
||||
<script type="text/javascript" src="/website/static/src/snippets/s_countdown/000.js"/>
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
</odoo>
|
||||
@@ -1,19 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
|
||||
<template id="s_countdown" name="Countdown">
|
||||
<section class="s_countdown"
|
||||
data-display="dhms" data-end-action="nothing" data-size="175"
|
||||
t-att-data-end-time="datetime.datetime.now().timestamp() + 228307"
|
||||
data-layout="circle" data-layout-background="none"
|
||||
data-progress-bar-style="surrounded" data-progress-bar-weight="thin"
|
||||
data-text-color="alpha" data-layout-background-color="400" data-progress-bar-color="alpha">
|
||||
<div class="container">
|
||||
<div class="s_countdown_canvas_wrapper text-center pt8 pb8"/>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<template id="s_comparisons" name="Comparisons">
|
||||
<section class="s_comparisons pt48 pb24">
|
||||
<div class="container">
|
||||
@@ -969,58 +956,6 @@
|
||||
<we-checkbox string="Stretch" data-select-class="container|container-fluid" data-no-preview="true"/>
|
||||
</div>
|
||||
|
||||
<!-- DISPLAY | .s_countdown -->
|
||||
<div data-js="countdown" data-selector=".s_countdown">
|
||||
<we-datetimepicker string="Due Date" data-select-data-attribute="" data-attribute-name="endTime"/>
|
||||
<we-row>
|
||||
<we-select string="What should happen at the end?" data-no-preview="true">
|
||||
<we-button data-end-action="nothing" data-name="no_end_action_opt">Nothing</we-button>
|
||||
<we-button data-end-action="redirect" data-name="redirect_end_action_opt">Redirect</we-button>
|
||||
<we-button data-end-action="message">Show Message</we-button>
|
||||
</we-select>
|
||||
<we-button class="far fa-edit fa-fw align-self-end toggle-edit-message" title="Edit Message"/>
|
||||
</we-row>
|
||||
<we-checkbox string="Hide countdown at the end" data-dependencies="!no_end_action_opt"
|
||||
data-select-class="hide-countdown"/>
|
||||
<we-urlpicker string="URL" data-dependencies="redirect_end_action_opt"
|
||||
data-select-data-attribute="/my-awesome-page" data-attribute-name="redirectUrl"/>
|
||||
<we-select string="Size" data-attribute-name="size">
|
||||
<we-button data-select-data-attribute="80">Small</we-button>
|
||||
<we-button data-select-data-attribute="120">Medium</we-button>
|
||||
<we-button data-select-data-attribute="175">Large</we-button>
|
||||
</we-select>
|
||||
<we-select string="Display" data-attribute-name="display">
|
||||
<we-button data-select-data-attribute="d">D</we-button>
|
||||
<we-button data-select-data-attribute="dhm">D - H - M</we-button>
|
||||
<we-button data-select-data-attribute="dhms">D - H - M - S</we-button>
|
||||
</we-select>
|
||||
<we-colorpicker string="Text Color" data-attribute-name="textColor" data-select-data-attribute=""/>
|
||||
<we-select string="Layout">
|
||||
<we-button data-layout="circle" data-name="circle_layout_opt">Circle</we-button>
|
||||
<we-button data-layout="boxes" data-name="boxes_layout_opt">Boxes</we-button>
|
||||
<we-button data-layout="clean">Clean</we-button>
|
||||
<we-button data-layout="text">Text Inline</we-button>
|
||||
</we-select>
|
||||
<we-select string="Layout Background" data-attribute-name="layoutBackground" data-dependencies="circle_layout_opt, boxes_layout_opt">
|
||||
<we-button data-select-data-attribute="inner">Inner</we-button>
|
||||
<we-button data-select-data-attribute="plain">Plain</we-button>
|
||||
<we-button data-select-data-attribute="none" data-name="no_background_layout_opt">None</we-button>
|
||||
</we-select>
|
||||
<we-colorpicker string="Layout Background Color" data-dependencies="!no_background_layout_opt"
|
||||
data-attribute-name="layoutBackgroundColor" data-select-data-attribute=""/>
|
||||
<we-select string="Progress Bar Style" data-attribute-name="progressBarStyle" data-dependencies="circle_layout_opt, boxes_layout_opt">
|
||||
<we-button data-select-data-attribute="surrounded">Surrounded</we-button>
|
||||
<we-button data-select-data-attribute="disappear">Disappearing</we-button>
|
||||
<we-button data-select-data-attribute="none" data-name="no_progressbar_style_opt">None</we-button>
|
||||
</we-select>
|
||||
<we-select string="Progress Bar Weight" data-attribute-name="progressBarWeight" data-dependencies="!no_progressbar_style_opt">
|
||||
<we-button data-select-data-attribute="thin">Thin</we-button>
|
||||
<we-button data-select-data-attribute="thick">Thick</we-button>
|
||||
</we-select>
|
||||
<we-colorpicker string="Progress Bar Color" data-dependencies="!no_progressbar_style_opt"
|
||||
data-attribute-name="progressBarColor" data-select-data-attribute=""/>
|
||||
</div>
|
||||
|
||||
<!-- Chart Inner -->
|
||||
<div data-js="InnerChart" string="Chart"
|
||||
data-selector=".s_chart">
|
||||
Reference in New Issue
Block a user