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 @@