/// /// This file regroups the odoo mixins. They are available in every asset bundle. /// // ------------------------------------------------------------------ // Caret // ------------------------------------------------------------------ @mixin utils-caret-boilerplate { content: ""; display: inline-block; width: 0; height: 0; vertical-align: middle; } // ------------------------------------------------------------------ // Position absolute // ------------------------------------------------------------------ @mixin o-position-absolute($top: auto, $right: auto, $bottom: auto, $left: auto) { position: absolute; top: $top; left: $left; bottom: $bottom; right: $right; } // ------------------------------------------------------------------ // Position sticky // ------------------------------------------------------------------ @mixin o-position-sticky($top: auto, $right: auto, $bottom: auto, $left: auto) { position: -webkit-sticky; position: sticky; top: $top; left: $left; bottom: $bottom; right: $right; } // ------------------------------------------------------------------ // Text overflow // ------------------------------------------------------------------ @mixin o-text-overflow($display: inline-block, $max-width: 100%) { display: $display; max-width: $max-width; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; vertical-align: top; // To update display context changed by overflow:hidden } // ------------------------------------------------------------------ // Hovering effects // ------------------------------------------------------------------ @mixin o-hover-opacity($default-opacity: 0.5, $hover-opacity: 1) { opacity: $default-opacity; &:hover, &:focus, &.focus { opacity: $hover-opacity; } } //------------------------------------------------------------------------------ // Colors //------------------------------------------------------------------------------ @function luma($color) { @return ((red($color) * .299) + (green($color) * .587) + (blue($color) * .114)) / 255 * 100%; } // // Given two colors, returns the one which has the most constrast with another // given color. Careful: if you want to find the text color which will suit the // most on a given background color, you should use the 'color-yiq' function. // @function o-get-most-contrast($color, $c1, $c2, $background: #FFFFFF, $threshold: false, $cross-mix: true) { $background: if($background == null, #FFFFFF, $background); $real-color: mix(rgba($color, 1.0), $background, percentage(alpha($color))); $luma: luma($real-color); $cross-color: if($cross-mix, $real-color, $background); $real-c1: mix(rgba($c1, 1.0), $cross-color, percentage(alpha($c1))); $luma-c1: luma($real-c1); $real-c2: mix(rgba($c2, 1.0), $cross-color, percentage(alpha($c2))); $luma-c2: luma($real-c2); $-dark: if($luma-c1 <= $luma-c2, $c1, $c2); $-light: if($luma-c1 > $luma-c2, $c1, $c2); @if $threshold == false { // Automatic threshold: give a really small preference to light results // as bootstrap does by default (mainly by compatibility at the moment // this code is written) $threshold: ($luma-c1 + $luma-c2) * 0.515; // 150 / 145.63 * 0.5 would be the BS value } @return if($luma > $threshold, $-dark, $-light); } // Extend placeholder which adds a chess-like background below the color and // image of an element to preview the transparency of that color and image. // This is done thanks to both ::before and ::after elements so they must both // be available. %o-preview-alpha-background { position: relative; z-index: 0; &::before { content: ""; @include o-position-absolute(0, 0, 0, 0); z-index: -1; background-image: url('/web/static/src/img/transparent.png'); background-size: 10px auto; border-radius: inherit; } &::after { content: ""; @include o-position-absolute(0, 0, 0, 0); z-index: -1; background: inherit; // Inherit all background properties border-radius: inherit; } } // ------------------------------------------------------------------ // Padding // ------------------------------------------------------------------ @mixin o-webclient-padding($top: 0px, $right: $o-horizontal-padding, $bottom: 0px, $left: $o-horizontal-padding) { padding-top: $top; padding-right: $right; padding-bottom: $bottom; padding-left: $left; } // ------------------------------------------------------------------ // Caret // ------------------------------------------------------------------ @mixin o-caret-down($caret-width: $caret-width) { @include utils-caret-boilerplate; border-bottom: 0; border-left: $caret-width solid transparent; border-right: $caret-width solid transparent; border-top: $caret-width solid; -moz-transform: scale(0.9999); // Smooth the caret on firefox } @mixin o-caret-up($caret-width: $caret-width) { @include utils-caret-boilerplate; border-bottom: $caret-width solid; border-left: $caret-width solid transparent; border-right: $caret-width solid transparent; border-top: 0; -moz-transform: scale(0.9999); // Smooth the caret on firefox } @mixin o-caret-left($caret-width: $caret-width) { @include utils-caret-boilerplate; border-bottom: $caret-width solid transparent; border-left: 0; border-right: $caret-width solid; border-top: $caret-width solid transparent; -moz-transform: scale(0.9999); // Smooth the caret on firefox } @mixin o-caret-right($caret-width: $caret-width) { @include utils-caret-boilerplate; border-bottom: $caret-width solid transparent; border-left: $caret-width solid; border-right: 0; border-top: $caret-width solid transparent; -moz-transform: scale(0.9999); // Smooth the caret on firefox } // ------------------------------------------------------------------ // Hovering effects // ------------------------------------------------------------------ @mixin o-hover-text-color($default-color: $body-color, $hover-color: $link-color) { color: $default-color; &:hover, &:focus, &.focus { color: $hover-color; } } // ------------------------------------------------------------------ // Mixin to define variations for btn-links and muted btn-links // ------------------------------------------------------------------ @mixin o-btn-link-variant($color, $color-active) { text-transform: none; @include o-hover-text-color($default-color: $color, $hover-color: $color-active); &, &:hover, &:focus, &:active, &.active { border-color: transparent; background-color: transparent; } &.text-muted, .text-muted { @include o-hover-opacity; @include o-hover-text-color($default-color: $text-muted, $hover-color: $color-active); } } // Odoo defines a limited Noto font-family for a small variety of unicode // characters that are not necessary defined in the user system or even defined // but not properly readable. This function allows to add this font family in a // given font list. // // @param {list} $font - a list of font names ending with the generic one. // @param {integer} [$index] - the position where to add the support font, if // not given, it will be placed before the generic one. @function o-add-unicode-support-font($font, $index: false) { @if $index == false { $index: length($font); } $-with-support-font: (); @for $i from 1 through length($font) { @if $i == $index { $-with-support-font: append($-with-support-font, 'Odoo Unicode Support Noto', $separator: comma); } $-with-support-font: append($-with-support-font, nth($font, $i), $separator: comma); } @return $-with-support-font; } // Function to remove all null values of a map @function o-map-omit($map) { $-map: (); @each $key, $value in $map { @if $value != null { $-map: map-merge($-map, ( $key: $value, )); } } @return $-map; } // Function to swap two values in a list @function o-swap($list, $i, $j) { $tmp: nth($list, $i); $list: set-nth($list, $i, nth($list, $j)); @return set-nth($list, $j, $tmp); } // Function to get an element of a list with a default value in case the index // is out-of-bounds; also return that value if the retrieved value is null. @function o-safe-nth($list, $index, $default: null) { $value: if($index > 0 and $index <= length($list), nth($list, $index), null); @return if($value != null, $value, $default); } // Function to get an element of a map with a default value in case the key // does not exist; also return that value if the retrieved value is null. @function o-safe-get($map, $key, $default: null) { $value: map-get($map, $key); @return if($value != null, $value, $default); } // ------- Kanban grouped mixins ------- @mixin o-kanban-icon($base-opacity: 0.5) { display: block; text-align: center; color: $o-main-text-color; font-size: $font-size-sm; cursor: pointer; @include o-hover-opacity($base-opacity); } @mixin o-kanban-tag-color { @for $size from 1 through length($o-colors) { // Note: the first color is supposed to be invisible if there is a color // field but it is used as a default color when there is no color field &.o_tag_color_#{$size - 1} span { background-color: nth($o-colors, $size); } } } @mixin o-kanban-record-color { @for $size from 2 through length($o-colors) { // Note: the first color is not defined as it is the 'no color' for kanban .oe_kanban_color_#{$size - 1}::after { background-color: nth($o-colors, $size); } } } @mixin o-kanban-slim-col { position: relative; flex: 0 0 auto; margin: 0; padding: 0 floor($o-kanban-group-padding * 0.7); cursor: pointer; } @mixin o-kanban-header-title { display: flex; align-items: center; height: $o-kanban-header-title-height; line-height: 2.2; color: $headings-color; } @mixin o-kanban-v-title { @include o-position-absolute($o-kanban-inside-vgutter * 2, $left: floor(-$o-kanban-inside-vgutter * 1.2)); transform-origin: left bottom 0; transform: rotate(90deg); overflow: visible; white-space: nowrap; font-size: 15px; } // ------- Kanban records mixins ------- @mixin o-kanban-record-title($font-size) { color: $headings-color; font-size: $font-size; font-weight: 500; margin-bottom: 0; margin-top: 0; } @mixin o-kanban-dropdown($padding-base: $o-kanban-inside-vgutter) { padding: $padding-base/2 $padding-base; border: none; border-left: 1px solid transparent; vertical-align: top; color: $body-color; &:hover { color: $headings-color; } &:focus, &:active, &:focus:active { outline: none; } } @mixin o-kanban-dropdown-open { position: relative; background: white; border-color: gray('400'); z-index: $zindex-dropdown + 1; } @mixin o-kanban-dropdown-menu { @include o-position-absolute($right: -1px); margin-top: -1px; border-color: gray('400'); } @mixin o-kanban-colorpicker { max-width: 150px; padding: 3px ($o-dropdown-hpadding - $o-kanban-inner-hmargin) 3px $o-dropdown-hpadding; > li { display: inline-block; margin: $o-kanban-inner-hmargin $o-kanban-inner-hmargin 0 0; border: 1px solid white; box-shadow: 0 0 0 1px gray('300'); > a { display: block; &::after { content: ""; display: block; width: 20px; height: 15px; } } // No Color &:first-child > a { position: relative; &::before { content: ""; @include o-position-absolute(-2px, $left: 10px); display: block; width: 1px; height: 20px; transform: rotate(45deg); background-color: red; } &::after { background-color: white; } } } } // Emulate dropdown links @mixin o-kanban-dashboard-dropdown-link($link-padding-gap: $o-dropdown-hpadding) { padding: 0; > a { margin: auto auto auto (-$link-padding-gap); padding: 3px $link-padding-gap; color: $body-color; display: block; &:hover { background-color: gray('300'); color: $headings-color; } } &:last-child { margin-bottom: 5px; } } // No content helper @mixin o-nocontent-empty { pointer-events: auto; max-width: 650px; margin: auto; padding: 15px; z-index: 1000; text-align: center; color: $o-tooltip-text-color; font-size: 115%; > p:first-of-type { margin-top: 0; color: $o-tooltip-title-text-color; font-weight: bold; font-size: 125%; } a { cursor: pointer; } } %o-nocontent-init-image { content: ""; display: block; margin: auto; background-size: cover; } %o-nocontent-empty-document { @extend %o-nocontent-init-image; @include size(120px, 80px); margin-top: 30px; margin-bottom: 30px; background: transparent url(/web/static/src/img/empty_folder.svg) no-repeat center; }