[IMP] website, *: add new logo sizing option

* portal, web

This commit introduces the first of many-to-come non-color website user
values: the logo height. Now, the user can choose it in the customize
dialog.

PR https://github.com/odoo/odoo/pull/29624
task-1904244
This commit is contained in:
qsm-odoo
2019-02-08 14:38:31 +00:00
parent 9f21427cdf
commit 905d18e044
11 changed files with 325 additions and 29 deletions
+11 -2
View File
@@ -5,6 +5,7 @@
// ====== Variables =========
$-navbar-height: $nav-link-height !default;
$o-theme-navbar-logo-height: $-navbar-height !default;
// Portal toolbar (filters, search bar)
$o-portal-mobile-toolbar: true; // Enable/Disable custom design
@@ -42,15 +43,23 @@ $o-portal-use-default-colors: $body-bg == $o-portal-default-body-bg;
header {
.navbar-brand {
flex: 0 0 auto;
max-width: 75%;
&.logo {
padding-top: 0;
padding-bottom: 0;
img {
// object-fit does not work on IE but is only used as a fallback
object-fit: contain;
display: block;
width: auto;
height: $-navbar-height;
max-width: none;
height: $o-theme-navbar-logo-height;
@include media-breakpoint-down(sm) {
height: auto;
max-height: min($o-theme-navbar-logo-height, 5rem);
}
}
}
}
@@ -1,3 +1,5 @@
$o-portal-default-body-bg: #FCFCFC;
$o-theme-navbar-logo-height: null;
$o-theme-btn-icon-hover-decoration: none;
+2 -2
View File
@@ -177,11 +177,11 @@
// Mixin which allows to extend the BS4 bg-variant mixin
@mixin o-bg-color-extension($color, $text-color, $with-muted) {}
// Function to remove all falsy values of a map
// Function to remove all null values of a map
@function o-map-omit($map) {
$-map: ();
@each $key, $value in $map {
@if $value {
@if $value != null {
$-map: map-merge($-map, (
$key: $value,
));
+174 -2
View File
@@ -4,6 +4,7 @@ odoo.define('website.theme', function (require) {
var config = require('web.config');
var core = require('web.core');
var Dialog = require('web.Dialog');
var Widget = require('web.Widget');
var weWidgets = require('wysiwyg.widgets');
var ColorpickerDialog = require('wysiwyg.widgets.ColorpickerDialog');
var websiteNavbarData = require('website.navbar');
@@ -12,6 +13,96 @@ var _t = core._t;
var templateDef = null;
var QuickEdit = Widget.extend({
xmlDependencies: ['/website/static/src/xml/website.editor.xml'],
template: 'website.theme_customize_active_input',
events: {
'keydown input': '_onInputKeydown',
'click .btn-primary': '_onSaveClick',
'click .btn-secondary': '_onResetClick',
},
/**
* @constructor
*/
init: function (parent, value, unit) {
this._super.apply(this, arguments);
this.value = value;
this.unit = unit;
},
/**
* @override
*/
start: function () {
this.$input = this.$('input');
this.$input.select();
return this._super.apply(this, arguments);
},
//--------------------------------------------------------------------------
// Private
//--------------------------------------------------------------------------
/**
* @private
* @param {string} [value]
*/
_save: function (value) {
if (value === undefined) {
value = parseFloat(this.$input.val());
value = isNaN(value) ? 'null' : (value + this.unit);
}
this.trigger_up('QuickEdit:save', {
value: value,
});
this.destroy();
},
//--------------------------------------------------------------------------
// Handlers
//--------------------------------------------------------------------------
/**
* @private
* @param {Event} ev
*/
_onInputKeydown: function (ev) {
var inputValue = this.$input.val();
var value = 0;
if (inputValue !== '') {
value = parseFloat(this.$input.val());
if (isNaN(value)) {
return;
}
}
switch (ev.which) {
case $.ui.keyCode.UP:
this.$input.val(value + 1);
break;
case $.ui.keyCode.DOWN:
this.$input.val(value - 1);
break;
case $.ui.keyCode.ENTER:
// Do not listen to change events, we want the user to be able
// to confirm in all cases.
this._save();
break;
}
},
/**
* @private
*/
_onSaveClick: function () {
this._save();
},
/**
* @private
*/
_onResetClick: function () {
this._save('null');
},
});
var ThemeCustomizeDialog = Dialog.extend({
xmlDependencies: (Dialog.prototype.xmlDependencies || [])
.concat(['/website/static/src/xml/website.editor.xml']),
@@ -60,8 +151,12 @@ var ThemeCustomizeDialog = Dialog.extend({
start: function () {
var self = this;
this.PX_BY_REM = parseFloat($(document.documentElement).css('font-size'));
this.$modal.addClass('o_theme_customize_modal');
this.style = window.getComputedStyle(document.documentElement);
var $tabs;
var loadDef = this._loadViews().then(function (data) {
self._generateDialogHTML(data);
@@ -209,6 +304,7 @@ var ThemeCustomizeDialog = Dialog.extend({
}
});
this._setActive();
this._updateValues();
function _processItems($items, $container) {
var optionsName = _.uniqueId('option-');
@@ -412,6 +508,8 @@ var ThemeCustomizeDialog = Dialog.extend({
var self = this;
var defs = [];
var $options = $inputs.closest('.o_theme_customize_option');
// Handle body image changes
var $bodyImageInputs = $inputs.filter('[data-xmlid*="website.' + this.CUSTOM_BODY_IMAGE_XML_ID + '"]:checked');
defs = defs.concat(_.map($bodyImageInputs, function () {
@@ -419,13 +517,46 @@ var ThemeCustomizeDialog = Dialog.extend({
}));
// Handle color changes
var $colors = $inputs.closest('.o_theme_customize_option').find('.o_theme_customize_color');
var $colors = $options.find('.o_theme_customize_color');
defs = defs.concat(_.map($colors, function (colorElement) {
return self._pickColor($(colorElement));
}));
// Handle input changes
var $inputsData = $options.find('.o_theme_customize_input');
defs = defs.concat(_.map($inputsData, function (inputData, i) {
return self._quickEdit($(inputData));
}));
return $.when.apply($, defs);
},
/**
* @private
*/
_quickEdit: function ($inputData) {
var text = $inputData.text().trim();
var value = parseFloat(text) || '';
var unit = text.match(/([^\s\d]+)$/)[1];
var def = $.Deferred();
var qEdit = new QuickEdit(this, value, unit);
qEdit.on('QuickEdit:save', this, function (ev) {
ev.stopPropagation();
var value = ev.data.value;
// Convert back to rem if needed
if ($inputData.data('unit') === 'rem' && unit === 'px' && value !== 'null') {
value = parseFloat(value) / this.PX_BY_REM + 'rem';
}
var values = {};
values[$inputData.data('value')] = value;
this._makeSCSSCusto('/website/static/src/scss/options/user_values.scss', values)
.always(def.resolve.bind(def));
});
qEdit.appendTo($inputData.closest('.o_theme_customize_option'));
return def;
},
/**
* @private
*/
@@ -466,6 +597,8 @@ var ThemeCustomizeDialog = Dialog.extend({
* @private
*/
_updateStyle: function (enable, disable, reload) {
var self = this;
var $loading = $('<i/>', {class: 'fa fa-refresh fa-spin'});
this.$modal.find('.modal-title').append($loading);
@@ -509,11 +642,49 @@ var ThemeCustomizeDialog = Dialog.extend({
$links.last().after($newLinks);
return linksLoaded;
});
return $.when.apply($, defs).always(function () {
$loading.remove();
$allLinks.remove();
});
}).then(function () {
// Some animations may depend on the variables that were
// customized, so we have to restart them.
self.trigger_up('animation_start_demand');
});
},
/**
* @private
*/
_updateValues: function () {
var self = this;
// Put user values
_.each(this.$('.o_theme_customize_color'), function (el) {
var $el = $(el);
var value = self.style.getPropertyValue('--' + $el.data('color')).trim();
$el.css('background-color', value);
});
_.each(this.$('.o_theme_customize_input'), function (el) {
var $el = $(el);
var value = self.style.getPropertyValue('--' + $el.data('value')).trim();
// Convert rem values to px values
if (_.str.endsWith(value, 'rem')) {
value = parseFloat(value) * self.PX_BY_REM + 'px';
}
var $span = $el.find('span');
$span.removeClass().text('');
switch (value) {
case '':
case 'false':
case 'true':
// When null or a boolean value, shows an icon which tells
// the user that there is no numeric/text value
$span.addClass('fa fa-ban text-danger');
break;
default:
$span.text(value);
}
});
},
@@ -572,6 +743,7 @@ var ThemeCustomizeDialog = Dialog.extend({
$option.data('reload') && window.location.href.match(new RegExp($option.data('reload')))
);
}).then(function () {
self._updateValues();
self.$inputs.prop('disabled', false);
});
},
@@ -0,0 +1,7 @@
// This file is meant to be edited automatically by the user. The variables it
// contains should not be renamed otherwise it would break existing customers
// customizations.
$o-user-website-values: map-merge($o-user-website-values, o-map-omit((
// -- hook --
)));
@@ -40,6 +40,21 @@ $o-social-colors: (
$o-theme-figcaption-opacity: 0.6;
//------------------------------------------------------------------------------
// Website customizations
//------------------------------------------------------------------------------
$o-website-values-palettes: (
(
'logo-height': null, // Default to navbar height (see portal)
),
) !default;
$o-website-values-palette-number: 1 !default;
// By default, all user website values are null. Each null value is
// automatically replaced with corresponsing value of chosen values palette.
$o-user-website-values: () !default;
//------------------------------------------------------------------------------
// Fonts
//------------------------------------------------------------------------------
@@ -34,6 +34,20 @@ $o-theme-color-palettes: append($o-theme-color-palettes, map-merge($-palette-def
// Enable last color and theme color palettes, which are now the user customized
// color palettes
// color palettes.
$o-color-palette-number: length($o-color-palettes);
$o-theme-color-palette-number: length($o-theme-color-palettes);
//------------------------------------------------------------------------------
// Website customizations
//------------------------------------------------------------------------------
$-website-values-default: nth($o-website-values-palettes, $o-website-values-palette-number);
$o-website-values: map-merge($-website-values-default, o-map-omit($o-user-website-values)) !default;
$o-website-values-palettes: append($o-website-values-palettes, $o-website-values);
$o-website-values-palette-number: length($o-website-values-palettes);
@function o-website-value($key) {
@return map-get($o-website-values, $key);
}
$o-theme-navbar-logo-height: o-website-value('logo-height') !default;
@@ -138,28 +138,37 @@
&::after {
background-color: inherit;
}
}
$palette: nth($o-color-palettes, $o-color-palette-number);
$-text-color: if(map-get($palette, 'text'), map-get($palette, 'text'), #212529); // BS default as fallback (TODO find real value)
$-h1-color: if(map-get($palette, 'h1'), map-get($palette, 'h1'), $-text-color);
$-h2-color: if(map-get($palette, 'h2'), map-get($palette, 'h2'), $-h1-color);
$-h3-color: if(map-get($palette, 'h3'), map-get($palette, 'h3'), $-h2-color);
$-h4-color: if(map-get($palette, 'h4'), map-get($palette, 'h4'), $-h3-color);
$-h5-color: if(map-get($palette, 'h5'), map-get($palette, 'h5'), $-h4-color);
$-h6-color: if(map-get($palette, 'h6'), map-get($palette, 'h6'), $-h5-color);
$-defaults: (
'body': white, // BS default as fallback (TODO find real value)
'text': $-text-color,
'h1': $-h1-color,
'h2': $-h2-color,
'h3': $-h3-color,
'h4': $-h4-color,
'h5': $-h5-color,
'h6': $-h6-color,
);
@each $name, $value in map-merge($-defaults, o-map-omit($palette)) {
&.o_theme_customize_color_#{$name} {
background-color: $value;
.o_theme_customize_input {
flex: 1 1 auto;
text-align: right;
.fa-edit {
visibility: hidden;
color: $o-brand-primary;
}
}
.o_theme_customize_option:hover .o_theme_customize_input .fa-edit {
visibility: visible;
}
.o_theme_customize_active_input {
@include o-position-absolute(0, 0, 0, 0);
width: auto;
padding: 3px;
> * {
height: 100% !important;
&.form-control {
background: white !important;
}
&.form-control, .btn {
padding: 2px 8px !important;
text-align: right !important;
font-size: $font-size-sm !important;
}
}
}
@@ -19,6 +19,41 @@ $-font-numbers: (
}
}
:root {
// The theme customize modal JS will need to know the value of some scss
// variables used to render the user website, and those may have been
// customized by themes, the user or anything else (so there is no file to
// parse to get them). Those will be printed here as CSS variables.
// 1) The values in the $theme-colors map are already printed by Bootstrap.
// 2) The values in the $colors map are also printed by Bootstrap.
// 3) The Odoo values map, $o-website-values, must be printed.
@each $key, $value in $o-website-values {
--#{$key}: #{$value};
}
// Handle keys which can be null but default to other values
$-h1-color: if($headings-color == inherit, $body-color, $headings-color);
$-h2-color: if(color('h2'), color('h2'), $-h1-color);
$-h3-color: if(color('h3'), color('h3'), $-h2-color);
$-h4-color: if(color('h4'), color('h4'), $-h3-color);
$-h5-color: if(color('h5'), color('h5'), $-h4-color);
$-h6-color: if(color('h6'), color('h6'), $-h5-color);
--body: #{$body-bg};
--text: #{$body-color};
--h1: #{$-h1-color};
--h2: #{$-h2-color};
--h3: #{$-h3-color};
--h4: #{$-h4-color};
--h5: #{$-h5-color};
--h6: #{$-h6-color};
--logo-height: #{$o-theme-navbar-logo-height};
}
#wrapwrap {
background-size: cover;
background-repeat: no-repeat;
@@ -64,6 +99,17 @@ $-font-numbers: (
}
}
@if $o-theme-navbar-logo-height {
// With default values, this makes it slightly bigger than standard
// navbar-brand, which is what we want
header .navbar-brand {
font-size: $o-theme-navbar-logo-height / $line-height-base;
$-logo-padding-y: max(0, $nav-link-height - $o-theme-navbar-logo-height) / 2;
padding-top: $-logo-padding-y;
padding-bottom: $-logo-padding-y;
}
}
.o_footer {
@if color('footer') {
@include o-bg-color(color('footer'));
@@ -49,10 +49,28 @@
</div>
</t>
<t t-name="website.theme_customize_widget_color">
<div t-attf-class="bg-#{color} o_theme_customize_color o_theme_customize_color_#{color}"
<div t-attf-class="o_theme_customize_color o_theme_customize_color_#{color}"
t-att-data-color="color"
t-att-data-color-type="colorType"/>
</t>
<t t-name="website.theme_customize_widget_input">
<div t-attf-class="o_theme_customize_input o_theme_customize_input_#{value}"
t-att-data-value="value"
t-att-data-unit="unit">
<i class="fa fa-edit"/>
<span/>
</div>
</t>
<t t-name="website.theme_customize_active_input">
<div class="input-group input-group-sm align-items-center o_theme_customize_active_input">
<input type="text" class="form-control" t-att-value="widget.value"/>
<div class="input-group-append">
<div t-if="widget.unit" class="input-group-text"><t t-esc="widget.unit"/></div>
<button type="button" class="btn btn-primary fa fa-check" title="Save"/>
<button type="button" class="btn btn-secondary fa fa-undo" title="Reset"/>
</div>
</div>
</t>
<t t-extend="wysiwyg.widgets.link">
<t t-jquery="#o_link_dialog_url_input" t-operation="after">
@@ -4,6 +4,9 @@
<template id="_assets_primary_variables" inherit_id="portal._assets_primary_variables">
<xpath expr="//link[last()]" position="after">
<link rel="stylesheet" type="text/scss" href="/website/static/src/scss/primary_variables.scss"/>
<!-- Those files will be automatically edited by users -->
<link rel="stylesheet" type="text/scss" href="/website/static/src/scss/options/user_values.scss"/>
<link rel="stylesheet" type="text/scss" href="/website/static/src/scss/options/colors/user_color_palette.scss"/>
<link rel="stylesheet" type="text/scss" href="/website/static/src/scss/options/colors/user_theme_color_palette.scss"/>
</xpath>
@@ -524,6 +527,7 @@
</list>
<list string="Logo">
<checkbox><opt data-xmlid="website.layout_logo_show" data-reload="/"/></checkbox>
<opt data-widget="input" data-unit="rem" data-value="logo-height" string="Logo Height"/>
</list>
</content>