From 69190959ce8e08481be7ebdfefd314edf0c5dce1 Mon Sep 17 00:00:00 2001 From: qsm-odoo Date: Thu, 28 May 2020 10:04:42 +0000 Subject: [PATCH] [IMP] website, *: introduce new classes to color snippets *: web, web_editor, website_blog, website_event New o_cc${xxx} classes where ${xxx} is a number [1-5] to style snippets: they come with a background color, a text color, headings colors, a link color, primary button colors and secondary button colors which will later be possible to customize by the user. The classes are made so that they are working on two levels. So if a column receives a color class and the snippet too, everything will work fine. More than 2 levels are not supported and the editor is made so those cases should not appear. See o_colored_level class. Part of https://github.com/odoo/odoo/pull/45856 task-2197038 --- .../scss/bootstrap_overridden_frontend.scss | 12 ++ .../static/src/scss/bs_mixins_overrides.scss | 28 +++- addons/web/static/src/scss/utils.scss | 3 - .../static/src/js/editor/snippets.options.js | 38 ++++++ .../src/js/wysiwyg/widgets/link_dialog.js | 14 +- .../static/src/scss/bootstrap_overridden.scss | 14 +- .../static/src/scss/secondary_variables.scss | 69 ++++++++-- .../static/src/scss/web_editor.common.scss | 125 ++++++++++++++++++ .../static/src/scss/web_editor.variables.scss | 57 +++++++- addons/web_editor/static/src/xml/wysiwyg.xml | 19 +-- addons/website/models/mixins.py | 2 +- .../static/src/js/editor/snippets.options.js | 4 +- .../static/src/scss/bootstrap_overridden.scss | 18 +-- .../website/views/snippets/s_blockquote.xml | 2 +- addons/website/views/snippets/s_picture.xml | 2 +- .../views/snippets/s_quotes_carousel.xml | 2 +- addons/website/views/snippets/snippets.xml | 17 ++- addons/website/views/website_templates.xml | 2 +- .../website_blog/data/website_blog_demo.xml | 34 ++--- .../static/src/scss/website_blog.scss | 1 - .../static/src/scss/website_event.scss | 2 +- .../website_event/views/event_templates.xml | 6 +- 22 files changed, 401 insertions(+), 70 deletions(-) diff --git a/addons/web/static/src/scss/bootstrap_overridden_frontend.scss b/addons/web/static/src/scss/bootstrap_overridden_frontend.scss index d7f30a8d869..0c656ae1db2 100644 --- a/addons/web/static/src/scss/bootstrap_overridden_frontend.scss +++ b/addons/web/static/src/scss/bootstrap_overridden_frontend.scss @@ -3,3 +3,15 @@ // Font, line-height, and color for body text, headings, and more. $font-size-base: (14 / 16) * 1rem !default; + +// Inherit color for blockquote-footer, use text-muted on top of it if you want +// a muted one. This is defined here to be consistent in the whole frontend, as +// when used with background classes, it should not be forced to a gray color. +$blockquote-small-color: inherit !default; + +// Figures + +// Inherit color for figure-caption, use text-muted on top of it if you want +// a muted one. This is defined here to be consistent in the whole frontend, as +// when used with background classes, it should not be forced to a gray color. +$figure-caption-color: inherit !default; diff --git a/addons/web/static/src/scss/bs_mixins_overrides.scss b/addons/web/static/src/scss/bs_mixins_overrides.scss index b2b54d554d8..c705e5741ba 100644 --- a/addons/web/static/src/scss/bs_mixins_overrides.scss +++ b/addons/web/static/src/scss/bs_mixins_overrides.scss @@ -14,19 +14,33 @@ $enable-deprecation-messages: false !default; @return o-get-most-contrast($color, $light, $dark, $background, $threshold, $cross-mix); } -@mixin o-bg-color($color, $text-color: null, $with-muted: true) { +// This placeholder regroups the rules that will apply on all elements with a +// bg-* class (see o-bg-color, bg-variant). The optimized-css way would be to +// have a common class for them all. +%o-bg-color-component-color-reset { + h1, h2, h3, h4, h5, h6 { + color: inherit; + } +} + +$o-color-extras-nesting-selector: '&' !default; + +@mixin o-bg-color($color, $text-color: null, $with-extras: true, $important: true, $nesting-selector: $o-color-extras-nesting-selector) { @if ($color) { $-yiq-color: if($text-color, $text-color, color-yiq($color)); - background-color: $color !important; + background-color: $color#{if($important, ' !important', '')}; color: $-yiq-color; // not important so that text utilities still work - @if ($with-muted) { - .text-muted { - color: scale-color($-yiq-color, $alpha: -30%) !important; + @if $with-extras { + #{$nesting-selector} { + @extend %o-bg-color-component-color-reset; + + .text-muted { + // Always important since the basic BS rule is important + color: scale-color($-yiq-color, $alpha: -30%) !important; + } } } - - @include o-bg-color-extension($color, $text-color, $with-muted); } } diff --git a/addons/web/static/src/scss/utils.scss b/addons/web/static/src/scss/utils.scss index 1d410fc7475..89c38290da3 100644 --- a/addons/web/static/src/scss/utils.scss +++ b/addons/web/static/src/scss/utils.scss @@ -197,9 +197,6 @@ @return $-with-support-font; } -// Mixin which allows to extend the BS4 bg-variant mixin -@mixin o-bg-color-extension($color, $text-color, $with-muted) {} - // Function to remove all null values of a map @function o-map-omit($map) { $-map: (); diff --git a/addons/web_editor/static/src/js/editor/snippets.options.js b/addons/web_editor/static/src/js/editor/snippets.options.js index 8decd41bb16..0b30bf856ce 100644 --- a/addons/web_editor/static/src/js/editor/snippets.options.js +++ b/addons/web_editor/static/src/js/editor/snippets.options.js @@ -2273,6 +2273,44 @@ const registry = {}; //:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: +/** + * Marks color levels of any element that may get or has a color classes. This + * is done for the specific main colorpicker option so that those are marked on + * snippet drop (so that base snippet definition do not need to care about that) + * and on first focus (for compatibility). + */ +registry.MainColorpicker = SnippetOptionWidget.extend({ + /** + * @override + */ + start: function () { + this._markColorLevel(); + return this._super(...arguments); + }, + /** + * @override + */ + onBuilt: function () { + this._markColorLevel(); + }, + + //-------------------------------------------------------------------------- + // Private + //-------------------------------------------------------------------------- + + /** + * Adds a specific class indicating the element is colored so that nested + * color classes work (we support one-level). Removing it is not useful, + * technically the class can be added on anything that *may* receive a color + * class: this does not come with any CSS rule. + * + * @private + */ + _markColorLevel: function () { + this.$target.addClass('o_colored_level'); + }, +}); + registry.sizing = SnippetOptionWidget.extend({ /** * @override diff --git a/addons/web_editor/static/src/js/wysiwyg/widgets/link_dialog.js b/addons/web_editor/static/src/js/wysiwyg/widgets/link_dialog.js index 699995f53d9..ed17127c55a 100644 --- a/addons/web_editor/static/src/js/wysiwyg/widgets/link_dialog.js +++ b/addons/web_editor/static/src/js/wysiwyg/widgets/link_dialog.js @@ -60,7 +60,19 @@ var LinkDialog = Dialog.extend({ this.needLabel = !r || (r.sc === r.ec && r.so === r.eo); if (this.data.range) { - this.data.iniClassName = $(this.data.range.sc).filter("a").attr("class") || ""; + const $link = $(this.data.range.sc).filter("a"); + this.data.iniClassName = $link.attr("class") || ""; + this.colorCombinationClass = false; + let $node = $link; + while ($node.length && !$node.is('body')) { + const className = $node.attr('class') || ''; + const m = className.match(/\b(o_cc\d+)\b/g); + if (m) { + this.colorCombinationClass = m[0]; + break; + } + $node = $node.parent(); + } this.data.className = this.data.iniClassName.replace(/(^|\s+)btn(-[a-z0-9_-]*)?/gi, ' '); var is_link = this.data.range.isOnAnchor(); diff --git a/addons/web_editor/static/src/scss/bootstrap_overridden.scss b/addons/web_editor/static/src/scss/bootstrap_overridden.scss index d1644a1174e..c1144ec23a7 100644 --- a/addons/web_editor/static/src/scss/bootstrap_overridden.scss +++ b/addons/web_editor/static/src/scss/bootstrap_overridden.scss @@ -3,16 +3,22 @@ $yiq-contrasted-threshold: false !default; // Automatically update bootstrap colors map (unused by BS itself) $colors: () !default; -$colors: map-merge($o-color-palette, $colors); +@each $name, $color in $o-color-palette { + $colors: map-merge(('#{$name}': o-color($color)), $colors); +} // Automatically extend bootstrap to create theme background/text/button classes $theme-colors: () !default; -$theme-colors: map-merge($o-theme-color-palette, $theme-colors); +@each $name, $color in $o-theme-color-palette { + $theme-colors: map-merge(('#{$name}': o-color($color)), $theme-colors); +} // Automatically extend bootstrap gray palette (the theme palette is supposed to // at least declare white and black) $grays: () !default; -$grays: map-merge($o-gray-color-palette, $grays); +@each $name, $color in $o-gray-color-palette { + $grays: map-merge(('#{$name}': o-color($color)), $grays); +} // Bootstrap use standard variables to define individual colors which are then // placed into a map which is then used to get the value of each individual @@ -55,3 +61,5 @@ $gray-700: gray('700') !default; $gray-800: gray('800') !default; $gray-900: gray('900') !default; $black: gray('black') !default; + +$o-color-system-initialized: true; diff --git a/addons/web_editor/static/src/scss/secondary_variables.scss b/addons/web_editor/static/src/scss/secondary_variables.scss index fa8f1a39120..52030130514 100644 --- a/addons/web_editor/static/src/scss/secondary_variables.scss +++ b/addons/web_editor/static/src/scss/secondary_variables.scss @@ -6,9 +6,6 @@ // Colors $o-color-palette: nth($o-color-palettes, $o-color-palette-number) !default; $o-color-palette: map-merge($o-base-color-palette, o-map-omit($o-color-palette)); -@function palette-color($key) { - @return map-get($o-color-palette, $key); -} // Theme colors $o-theme-color-palette: nth($o-theme-color-palettes, $o-theme-color-palette-number) !default; @@ -27,9 +24,6 @@ $o-theme-color-palette: map-merge(( 'dark': darken(desaturate($-main-color, 80%), min(70%, max(0%, $-main-color-lightness - 10%))), // Does not lower under 10% lightness ), o-map-omit($o-theme-color-palette)); $o-theme-color-palette: map-merge($o-base-theme-color-palette, o-map-omit($o-theme-color-palette)); -@function palette-theme-color($key) { - @return map-get($o-theme-color-palette, $key); -} // Gray colors // Extend grays with transparent ones (for some reason, BS4 create black-50 and @@ -37,6 +31,65 @@ $o-theme-color-palette: map-merge($o-base-theme-color-palette, o-map-omit($o-the $o-gray-color-palette: nth($o-gray-color-palettes, $o-gray-color-palette-number) !default; $o-gray-color-palette: map-merge($o-transparent-grays, o-map-omit($o-gray-color-palette)); $o-gray-color-palette: map-merge($o-base-gray-color-palette, o-map-omit($o-gray-color-palette)); -@function palette-gray($key) { - @return map-get($o-gray-color-palette, $key); + +// Color combinations +$o-color-combinations: nth($o-color-combinations-presets, $o-color-combinations-preset-number) !default; + +$o-color-system-initialized: false; + +// Returns: +// - true if the given name is a css color or null +// - false if a potential valid color name +// - throws an error if the given arg cannot reference a color +@function check-color-identifier-type($name) { + $-type: type-of($name); + @if $-type == 'color' or $-type == 'null' { + @return true; + } @else if $-type != 'string' { + @error "Color name '#{$name}' is of unsupported type '#{$-type}'"; + } + @return false; +} +// Looks up for the color related to the given name in the related odoo palettes +// following redirection a maximum number of time (by default none). +@function o-related-color($name, $max-recursions: 0, $original-name: $name) { + @if $max-recursions < 0 or check-color-identifier-type($name) { + @return $name; + } + + $-value: null; + @if map-has-key($o-color-palette, $name) { + $-value: map-get($o-color-palette, $name); + } @else if map-has-key($o-theme-color-palette, $name) { + $-value: map-get($o-theme-color-palette, $name); + } @else if map-has-key($o-gray-color-palette, $name) { + $-value: map-get($o-gray-color-palette, $name); + } + @return o-related-color($-value, $max-recursions - 1, $original-name); +} +// Function which allows to retrieve a color value from a name, the color being +// either in $theme-colors, $grays or $colors maps. If those maps are not +// initialized yet, it will look up the color in the related odoo palettes. +@function o-color($name) { + @if check-color-identifier-type($name) { + @return $name; + } + + // When the system is initialized, it means that the bootstrap maps have + // been configured and contain a direct mapping between color name -> css + // value. We can thus search in those. + @if $o-color-system-initialized { + @if map-has-key($colors, $name) { + @return color($name); + } + @if map-has-key($theme-colors, $name) { + @return theme-color($name); + } + @if map-has-key($grays, $name) { + @return gray($name); + } + } + + // If not initialized, search the css color value in selected color palettes + @return o-related-color($name, $max-recursions: 10); } diff --git a/addons/web_editor/static/src/scss/web_editor.common.scss b/addons/web_editor/static/src/scss/web_editor.common.scss index 1d8ee69551f..fbaa1557fa0 100644 --- a/addons/web_editor/static/src/scss/web_editor.common.scss +++ b/addons/web_editor/static/src/scss/web_editor.common.scss @@ -517,3 +517,128 @@ blockquote { border-left: 5px solid $gray-300; font-style: italic } + +// Bg/text color classes generation +.o_cc { + #{$o-color-extras-nesting-selector} { + // Re-force dropdown-item colors inside presets otherwise the presets + // 'link' colors take over. + .dropdown-menu .dropdown-item { // Need to add +1 priority thanks to + // .dropdown-menu to counter a:not(.btn) + &, h6 { // Quick fix: sometimes we use h6 in dropdowns + color: $dropdown-link-color; + + @include hover-focus { + color: $dropdown-link-hover-color; + } + } + &.disabled, + &:disabled { + &, h6 { // Quick fix: sometimes we use h6 in dropdowns + color: $dropdown-link-disabled-color; + } + } + } + } +} +@for $index from 1 through length($o-color-combinations) { + $config: nth($o-color-combinations, $index); + $-bg: map-get($config, 'bg'); + $-text: map-get($config, 'text'); + $-headings: map-get($config, 'headings'); + $-h2: map-get($config, 'h2'); + $-h3: map-get($config, 'h3'); + $-h4: map-get($config, 'h4'); + $-h5: map-get($config, 'h5'); + $-h6: map-get($config, 'h6'); + $-link: map-get($config, 'link'); + $-btn-primary: map-get($config, 'btn-primary'); + $-btn-primary-border: map-get($config, 'btn-primary-border'); + $-btn-secondary: map-get($config, 'btn-secondary'); + $-btn-secondary-border: map-get($config, 'btn-secondary-border'); + + // Those color classes color multiple elements when applied on a snippet. + // Those rules are not important so that they can be overridden through + // bg and text utility classes. ** + .o_cc#{$index} { + // Background & Text + @include o-bg-color(o-color($-bg), o-color($-text), $important: false); + + #{$o-color-extras-nesting-selector} { + // Headings + h1, h2, h3, h4, h5, h6 { + // 'inherit' comes from the o-bg-color mixin + color: o-color($-headings); + } + h2, h3, h4, h5, h6 { + color: o-color($-h2); + } + h3, h4, h5, h6 { + color: o-color($-h3); + } + h4, h5, h6 { + color: o-color($-h4); + } + h5, h6 { + color: o-color($-h5); + } + h6 { + color: o-color($-h6); + } + + // Links + $-link-color: if($-link, o-color($-link), theme-color('primary')); + $-link-hover-color: darken($-link-color, 15%); + a:not(.btn), .btn-link { + color: $-link-color; + + @include hover { + color: $-link-hover-color; + } + } + + // Buttons + + // Primary + $-btn-primary-color: if($-btn-primary, o-color($-btn-primary), theme-color('primary')); + $-btn-primary-border-color: if($-btn-primary-border, o-color($-btn-primary-border), $-btn-primary-color); + .btn-fill-primary { + @include button-variant($-btn-primary-color, $-btn-primary-border-color); + } + .btn-outline-primary { + @include button-outline-variant($-btn-primary-border-color); + } + + // Secondary + $-btn-secondary-color: if($-btn-secondary, o-color($-btn-secondary), theme-color('secondary')); + $-btn-secondary-border-color: if($-btn-secondary-border, o-color($-btn-secondary-border), $-btn-secondary-color); + .btn-fill-secondary { + @include button-variant($-btn-secondary-color, $-btn-secondary-border-color); + } + .btn-outline-secondary { + @include button-outline-variant($-btn-secondary-border-color); + } + + // 'Active' states. Note: this only emulates very common components + // used in snippets. This might need to be more complex the day we + // can apply color combinations anywhere (page-item, ...). + .nav-pills { + .nav-link.active, + .show > .nav-link { + background-color: $-btn-primary-color; + color: color-yiq($-btn-primary-color); + } + } + .dropdown-menu .dropdown-item { // Need to add +1 priority thanks to + // .dropdown-menu (see .o_cc). + &.active, + &:active { + &, h6 { // Quick fix: sometimes we use h6 in dropdowns + @include gradient-bg($-btn-primary-color); + color: color-yiq($-btn-primary-color); + } + } + } + } + } +} diff --git a/addons/web_editor/static/src/scss/web_editor.variables.scss b/addons/web_editor/static/src/scss/web_editor.variables.scss index d0fa36444c4..ecfda70dd87 100644 --- a/addons/web_editor/static/src/scss/web_editor.variables.scss +++ b/addons/web_editor/static/src/scss/web_editor.variables.scss @@ -338,7 +338,7 @@ $o-theme-color-palette-number: 1 !default; // Greyscale transparent colours // Note: BS values are forced by default in every palette as the values can -// be used in bootstrap_overridden.scss files through the palette-gray function. +// be used in bootstrap_overridden.scss files through the o-color function. // Also, all of the gray colors generates bg- classes in Odoo so black and white // are added for the same reason. @@ -370,6 +370,61 @@ $o-gray-color-palettes: ( ) !default; $o-gray-color-palette-number: 1 !default; +// Color combinations +$o-color-combinations-presets: ( + ( + ( + 'bg': #FFFFFF, + 'text': null, + 'headings': null, + 'link': null, + 'btn-primary': null, + 'btn-secondary': null, + ), + ( + 'bg': 'gamma', + 'text': null, + 'headings': 'alpha', + 'link': null, + 'btn-primary': null, + 'btn-secondary': null, + ), + ( + 'bg': 'beta', + 'text': null, + 'headings': null, + 'link': null, + 'btn-primary': null, + 'btn-secondary': 'gamma', + ), + ( + 'bg': 'alpha', + 'text': null, + 'headings': null, + 'link': 'epsilon', + 'btn-primary': 'epsilon', + 'btn-secondary': 'gamma', + ), + ( + 'bg': 'epsilon', + 'text': null, + 'headings': 'delta', + 'link': null, + 'btn-primary': null, + 'btn-secondary': 'gamma', + ), + ), +) !default; +$o-color-combinations-preset-number: 1; + // Translations $o-we-content-to-translate-color: rgb(255, 255, 90) !default; $o-we-translated-content-color: rgb(120, 215, 110) !default; + +// We allow snippets to be colored and elements like card and columns to be +// colored as well. We need components targeted by those colored classes to +// use the deepest coloring element config. We only allow here for this to +// work for one level of nesting. Note: snippets which can contain other +// snippets will have problem because of this; this is a limitation of the +// system until a better solution is found. +$o-color-extras-nesting-selector: '&, .o_colored_level &'; diff --git a/addons/web_editor/static/src/xml/wysiwyg.xml b/addons/web_editor/static/src/xml/wysiwyg.xml index 402f6ef3858..f23165e8959 100644 --- a/addons/web_editor/static/src/xml/wysiwyg.xml +++ b/addons/web_editor/static/src/xml/wysiwyg.xml @@ -461,13 +461,15 @@
- - - +
+ + + +