diff options
| author | stephanchrst <stephanchrst@gmail.com> | 2022-05-10 21:51:50 +0700 |
|---|---|---|
| committer | stephanchrst <stephanchrst@gmail.com> | 2022-05-10 21:51:50 +0700 |
| commit | 3751379f1e9a4c215fb6eb898b4ccc67659b9ace (patch) | |
| tree | a44932296ef4a9b71d5f010906253d8c53727726 /addons/web/static/src/scss | |
| parent | 0a15094050bfde69a06d6eff798e9a8ddf2b8c21 (diff) | |
initial commit 2
Diffstat (limited to 'addons/web/static/src/scss')
75 files changed, 9692 insertions, 0 deletions
diff --git a/addons/web/static/src/scss/animation.scss b/addons/web/static/src/scss/animation.scss new file mode 100644 index 00000000..d65a509b --- /dev/null +++ b/addons/web/static/src/scss/animation.scss @@ -0,0 +1,146 @@ +/************** DEFINITION of ANIMATIONS **************/ +// 'marked' effects +@keyframes markAnim { + 0% { + opacity: 0; + transform: scaleX(0.50) scaleY(0.50); + } + 30% { + opacity: 1; + transform: scaleX(1.00) scaleY(1.00); + } + 100% { + opacity: 0; + transform: scaleX(1.00) scaleY(1.00); + } +} + +@-moz-keyframes markAnim { + 0% { + opacity: 0; + -moz-transform: scaleX(0.50) scaleY(0.50); + } + 30% { + opacity: 1; + -moz-transform: scaleX(1.00) scaleY(1.00); + } + 100% { + opacity: 0; + -moz-transform: scaleX(1.00) scaleY(1.00); + } +} + +@-webkit-keyframes markAnim { + 0% { + opacity: 0; + -webkit-transform: scaleX(0.50) scaleY(0.50); + } + 30% { + opacity: 1; + -webkit-transform: scaleX(1.00) scaleY(1.00); + } + 100% { + opacity: 0; + -webkit-transform: scaleX(1.00) scaleY(1.00); + } +} + +@-o-keyframes markAnim { + 0% { + opacity: 0; + -o-transform: scaleX(0.50) scaleY(0.50); + } + 30% { + opacity: 1; + -o-transform: scaleX(1.00) scaleY(1.00); + } + 100% { + opacity: 0; + -o-transform: scaleX(1.00) scaleY(1.00); + } +} + +@-ms-keyframes markAnim { + 0% { + opacity: 0; + -ms-transform: scaleX(0.50) scaleY(0.50); + } + 30% { + opacity: 1; + -ms-transform: scaleX(1.00) scaleY(1.00); + } + 100% { + opacity: 0; + -ms-transform: scaleX(1.00) scaleY(1.00); + } +} + +// 'bounce' effect +@-webkit-keyframes bounceIn { + 0%, 20%, 40%, 60%, 80%, 100% { + -webkit-transition-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); + transition-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); + } + + 0% { + opacity: 0; + -webkit-transform: scale3d(.3, .3, .3); + transform: scale3d(.3, .3, .3); + } + 20% { + -webkit-transform: scale3d(1.1, 1.1, 1.1); + transform: scale3d(1.1, 1.1, 1.1); + } + 40% { + -webkit-transform: scale3d(.9, .9, .9); + transform: scale3d(.9, .9, .9); + } + 60% { + opacity: 1; + -webkit-transform: scale3d(1.03, 1.03, 1.03); + transform: scale3d(1.03, 1.03, 1.03); + } + 80% { + -webkit-transform: scale3d(.97, .97, .97); + transform: scale3d(.97, .97, .97); + } + 100% { + opacity: 1; + -webkit-transform: scale3d(1, 1, 1); + transform: scale3d(1, 1, 1); + } +} + +@keyframes bounceIn { + 0%, 20%, 40%, 60%, 80%, 100% { + -webkit-transition-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); + transition-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); + } + 0% { + opacity: 0; + -webkit-transform: scale3d(.3, .3, .3); + transform: scale3d(.3, .3, .3); + } + 20% { + -webkit-transform: scale3d(1.1, 1.1, 1.1); + transform: scale3d(1.1, 1.1, 1.1); + } + 40% { + -webkit-transform: scale3d(.9, .9, .9); + transform: scale3d(.9, .9, .9); + } + 60% { + opacity: 1; + -webkit-transform: scale3d(1.03, 1.03, 1.03); + transform: scale3d(1.03, 1.03, 1.03); + } + 80% { + -webkit-transform: scale3d(.97, .97, .97); + transform: scale3d(.97, .97, .97); + } + 100% { + opacity: 1; + -webkit-transform: scale3d(1, 1, 1); + transform: scale3d(1, 1, 1); + } +} diff --git a/addons/web/static/src/scss/attachment_preview.scss b/addons/web/static/src/scss/attachment_preview.scss new file mode 100644 index 00000000..eca2527f --- /dev/null +++ b/addons/web/static/src/scss/attachment_preview.scss @@ -0,0 +1,113 @@ +// Attachment Icons (common for many2many_binary widget and chat thread and chat composer) +// ------------------------------------------------------------------ +$o-attachment-image-size: 38px; +$o-attachment-margin: 5px; + +.o_attachment { + position: relative; + width: 100%; + padding: $o-attachment-margin; + + @include media-breakpoint-up(md) { + width: 50%; + } + @include media-breakpoint-up(lg) { + width: 25%; + } + // many2many_binary widget for send mail with attachment + &.o_attachment_many2many { + @include media-breakpoint-up(md) { + width: 50%; + } + @include media-breakpoint-up(lg) { + width: 62%; + } + } + + .o_attachment_wrap { + overflow: hidden; + position: relative; + border-bottom: 1px solid rgba(black, 0.1); + border-radius: 2px; + padding: 4px 6px 0 4px; + background-color: rgba(black, 0.05); + + .o_attachment_delete_cross { + float: right; + cursor: pointer; + } + } + + &.o_attachment_editable .o_attachment_wrap { + padding-right: 40px; + } + + .o_image { + width: $o-attachment-image-size; + height: $o-attachment-image-size; + image-orientation: from-image; // Only supported in Firefox + &.o_hover { + @include o-hover-opacity($default-opacity: 1, $hover-opacity: 0.7); + } + } + + .o_attachment_view { + cursor: zoom-in; + } + + .caption { + @include o-text-overflow(block); + + a { + @include o-hover-text-color($default-color: $o-main-text-color, $hover-color: $headings-color); + } + } + + .o_attachment_progress_bar { + display: none; + } + + .o_attachment_uploaded, .o_attachment_delete { + @include o-position-absolute(0, 0, 0, $left: auto); + display: flex; + align-items: center; + justify-content: center; + width: 45px; + } + + .o_attachment_delete { + background: desaturate(theme-color('primary'), 50%); + color: white; + cursor: pointer; + font-size: 20px; + transform: translateX(100%); + transition: all 0.3s ease 0s; + + &:hover { + background: theme-color('primary'); + } + } + + &.o_attachment_uploading { + .o_attachment_progress_bar { + display: inline-block; + margin: 0 0 0 8px; + border-radius: 2px; + vertical-align: bottom; + + > div { + font-size: 11px; + padding: 0 7px; + } + } + + .o_attachment_delete, .o_attachment_uploaded { + display: none; + } + } + + &:hover .o_attachment_delete { + transition: all 0.1s ease 0s; + transform: translateX(0); + } +} diff --git a/addons/web/static/src/scss/banner.scss b/addons/web/static/src/scss/banner.scss new file mode 100644 index 00000000..be011d70 --- /dev/null +++ b/addons/web/static/src/scss/banner.scss @@ -0,0 +1,9 @@ +.o_has_banner { + .o_view_nocontent { + top: 30%; + @include media-breakpoint-down(sm) { + position: relative; + margin: auto; + } + } +} diff --git a/addons/web/static/src/scss/base_document_layout.scss b/addons/web/static/src/scss/base_document_layout.scss new file mode 100644 index 00000000..4203fe2b --- /dev/null +++ b/addons/web/static/src/scss/base_document_layout.scss @@ -0,0 +1,43 @@ +.o_document_layout .o_group { + display: flex; + .o_document_layout_company { + flex: 5; + @include media-breakpoint-up(sm, $o-extra-grid-breakpoints) { + max-width: 50%; + } + img { + max-height: 100px; + } + .o_document_layout_colors { + margin-bottom: 32px; + vertical-align: middle; + display: flex; + flex-direction: row; + align-items: center; + .o_field_widget { + width: 30px; + margin: 0 5px 0 0; + } + .btn { + padding: 0; + margin-left: 10px; + .o_form_label { + height: 30px; + font-size: 18px; + margin: 0; + cursor: pointer; + } + } + } + select.o_input { + height: 25px; + } + + } +} + +@media (max-width: 620px) { + .o_document_layout .o_group { + flex-direction: column; + } +} diff --git a/addons/web/static/src/scss/base_frontend.scss b/addons/web/static/src/scss/base_frontend.scss new file mode 100644 index 00000000..c300b3db --- /dev/null +++ b/addons/web/static/src/scss/base_frontend.scss @@ -0,0 +1,29 @@ +// Frontend general +html, body, #wrapwrap { + width: 100%; + height: 100%; + overflow: hidden; +} +#wrapwrap { + // The z-index is useful to prevent that children with a negative z-index + // go behind the wrapwrap (we create a new stacking context). + z-index: 0; + position: relative; + display: flex; + flex-flow: column nowrap; + // ... we delegate the scroll to that top element instead of the window/body + // This is at least needed for the edit mode to not have a double scrollbar + // due to the right editor panel (and since we want to minimize the style + // difference between edit mode and non-edit mode (wysiwyg)...). + overflow: auto; + + > * { + flex: 0 0 auto; + } + > main { + flex: 1 0 auto; + } +} +.modal-open #wrapwrap { + overflow: hidden; +} diff --git a/addons/web/static/src/scss/base_settings.scss b/addons/web/static/src/scss/base_settings.scss new file mode 100644 index 00000000..6b64f182 --- /dev/null +++ b/addons/web/static/src/scss/base_settings.scss @@ -0,0 +1,170 @@ +// MIXINS +@mixin o-base-settings-horizontal-padding($padding-base: $input-btn-padding-y-sm) { + padding: $padding-base $o-horizontal-padding; + + @include media-breakpoint-up(xl) { + padding-left: $o-horizontal-padding*2;; + } +} + +// Use a very specif selector to overwrite generic form-view rules +.o_form_view.o_form_nosheet.o_base_settings { + display: flex; + flex-flow: column nowrap; + padding: 0px; +} + +// BASE SETTINGS LAYOUT +.o_base_settings { + height: 100%; + + .o_control_panel { + flex: 0 0 auto; + + .o_panel { + display: flex; + flex-flow: row wrap; + width: 100%; + + .title, .o_setting_search { + flex: 1 0 auto; + width: map-get($o-extra-grid-breakpoints, vsm) / 2; + } + + .o_setting_search { + position: relative; + + .searchInput { + height: 28px; + padding: 0px; + border: 0px; + border-bottom: 1px solid gray('400'); + border-color: gray('700'); + box-shadow: none; + font-weight: 500; + } + + .searchIcon { + @include o-position-absolute(4px, 0); + color: gray('700'); + } + } + } + + .o_form_statusbar { + padding: 0; + margin: 5px 0px; + border: 0; + + .btn-primary, .btn-link { + padding: $btn-padding-y-sm $btn-padding-x-sm; + } + + .btn-link { + @include o-hover-text-color($link-color, $link-hover-color); + } + } + } + + .o_setting_container { + display: flex; + flex: 1 1 auto; + + overflow: auto; + + .settings_tab { + display: flex; + flex: 0 0 auto; + flex-flow: column nowrap; + background: gray('900'); + overflow: auto; + padding-top: $o-horizontal-padding*0.5; + + .selected { + background-color: gray('900'); + box-shadow: inset 3px 0 0 $o-brand-primary; + + .app_name { + color: white; + } + } + + .tab { + display: flex; + padding: 0 $o-horizontal-padding*2 0 $o-horizontal-padding; + height: 40px; + color: gray('400'); + font-size: 13px; + line-height: 40px; + cursor: pointer; + white-space: nowrap; + + .icon { + width: 23px; + min-width: 23px; + margin-right: 10px; + } + } + } + + .settings { + position: relative; + flex: 1 1 100%; + background-color: $o-view-background-color; + overflow: auto; + + > .app_settings_block { + h2 { + margin-bottom: 0; + @include o-base-settings-horizontal-padding; + background-color: gray('200'); + font-size: 15px; + font-weight: bold; + + &:first-of-type { + // Necessary to overwrite mt32 class + margin-top: 10px!important; + } + } + + .o_settings_container { + max-width: map-get($grid-breakpoints, lg); // Provide a maximum container size to ensure readability + @include o-base-settings-horizontal-padding(0); + } + } + + .settingSearchHeader { + display: flex; + margin: 30px 0 10px; + @include o-base-settings-horizontal-padding; + background-color: gray('200'); + + .icon { + width: 25px; + height: 25px; + margin-right: 10px; + } + + .appName { + color: $headings-color; + font-size: 17px; + } + } + + .notFound { + color : $text-muted; + text-align: center; + font-size: 25px; + padding-top: 50px; + } + + .highlighter { + background: yellow; + } + } + + .d-block { + display: block!important; + } + } +} diff --git a/addons/web/static/src/scss/bootstrap_overridden.scss b/addons/web/static/src/scss/bootstrap_overridden.scss new file mode 100644 index 00000000..c9014a03 --- /dev/null +++ b/addons/web/static/src/scss/bootstrap_overridden.scss @@ -0,0 +1,148 @@ +/// +/// This file is a copy of the bootstrap _variables.scss file where all the +/// left-untouched variables definition have been removed. +/// + +// +// Color system +// + +// All of those are BS default +$white: #fff !default; +$gray-100: $o-gray-100 !default; +$gray-200: #e9ecef !default; +$gray-300: #dee2e6 !default; +$gray-400: #ced4da !default; +$gray-500: #adb5bd !default; +$gray-600: #6c757d !default; +$gray-700: #495057 !default; +$gray-800: #343a40 !default; +$gray-900: #212529 !default; +$black: #000 !default; + +$primary: $o-brand-primary !default; +$secondary: $white !default; + +// Body +// +// Settings for the `<body>` element. + +$body-bg: $o-brand-secondary !default; +$body-color: $o-main-text-color !default; + +// Links +// +// Style anchor elements. + +$link-hover-decoration: none !default; + +// Muted +// +// Style .text-muted elements + +$text-muted: $gray-500 !default; + +// Grid columns +// +// Set the number of columns and specify the width of the gutters. + +$grid-gutter-width: $o-horizontal-padding * 2 !default; + +// Components +// +// Define common padding and border radius sizes and more. + +$border-radius: 3px !default; + +$component-active-color: $o-brand-primary !default; +$component-active-bg: $gray-200 !default; + +// Fonts +// +// Font, line-height, and color for body text, headings, and more. + +$font-family-sans-serif: o-add-unicode-support-font(("Lucida Grande", Helvetica, Verdana, Arial, sans-serif), 1) !default; + +$font-size-base: $o-font-size-base !default; +$line-height-base: $o-line-height-base !default; + +$h1-font-size: $font-size-base * 2.0 !default; +$h2-font-size: $font-size-base * 1.5 !default; +$h3-font-size: $font-size-base * 1.3 !default; +$h4-font-size: $font-size-base * 1.2 !default; +$h5-font-size: $font-size-base * 1.1 !default; + +$headings-font-weight: bold !default; + +// Tables +// +// Customizes the `.table` component with basic values, each used across all table variations. + +$table-accent-bg: rgba($black, .01) !default; +$table-hover-bg: rgba($black, .04) !default; + +// Dropdowns +// +// Dropdown menu container and contents. + +$dropdown-border-color: $gray-300 !default; + +$dropdown-link-color: $o-main-text-color !default; +$dropdown-link-hover-color: $gray-900 !default; +$dropdown-link-hover-bg: $gray-200 !default; + +$dropdown-link-active-color: $gray-900 !default; +$dropdown-link-active-bg: $gray-300 !default; + +// Z-index master list + +// Change the z-index of the modal-backdrop elements to be equal to the +// modal elements' ones. Bootstrap does not support multi-modals, and without +// this rule all the modal-backdrops are below all the opened modals. +// Indeed, bootstrap forces them to a lower z-index as the modal-backdrop +// element (unique in their supported cases) might be put after the modal +// element (if the modal is already in the DOM, hidden, then opened). This +// cannot happen in odoo though as modals are not hidden but removed from +// the DOM and are always put at the end of the body when opened. +// +// TODO the following code was disabled because it is saas-incompatible +// +// $zindex-modal-backdrop: $zindex-modal; + +// Navs + +$nav-tabs-link-active-bg: $white !default; + +$nav-pills-border-radius: 0 !default; +$nav-pills-link-active-color: $white !default; +$nav-pills-link-active-bg: $o-brand-primary !default; + +// Toasts + +$toast-max-width: 320px !default; +$toast-padding-x: 1.5rem !default; +$toast-padding-y: 0.5rem !default; +$toast-font-size: $font-size-base !default; +$toast-background-color: rgba($white, .7) !default; +$toast-header-background-color: $toast-background-color !default; + +// Badges + +$badge-font-weight: normal !default; + +// Modals + +// Padding applied to the modal body +$modal-inner-padding: $o-horizontal-padding !default; + +$modal-lg: $o-modal-lg !default; +$modal-md: $o-modal-md !default; + +// Breadcrumbs + +$breadcrumb-padding-y: 0 !default; +$breadcrumb-padding-x: 0 !default; +$breadcrumb-margin-bottom: 0 !default; + +$breadcrumb-bg: $o-control-panel-background-color !default; +$breadcrumb-active-color: #777777 !default; diff --git a/addons/web/static/src/scss/bootstrap_overridden_frontend.scss b/addons/web/static/src/scss/bootstrap_overridden_frontend.scss new file mode 100644 index 00000000..0c656ae1 --- /dev/null +++ b/addons/web/static/src/scss/bootstrap_overridden_frontend.scss @@ -0,0 +1,17 @@ +// Fonts +// +// 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/bootstrap_overridden_report.scss b/addons/web/static/src/scss/bootstrap_overridden_report.scss new file mode 100644 index 00000000..33af8de1 --- /dev/null +++ b/addons/web/static/src/scss/bootstrap_overridden_report.scss @@ -0,0 +1,2 @@ +// remove Emoji fonts +$font-family-sans-serif: o-add-unicode-support-font(("Lucida Grande", Helvetica, Verdana, Arial, sans-serif), 1); diff --git a/addons/web/static/src/scss/bootstrap_review.scss b/addons/web/static/src/scss/bootstrap_review.scss new file mode 100644 index 00000000..4e78247b --- /dev/null +++ b/addons/web/static/src/scss/bootstrap_review.scss @@ -0,0 +1,191 @@ +/// +/// This file regroups the CSS rules made to fix/extend bootstrap in all places +/// where it is used in Odoo (backend / frontend / reports / ...) +/// + +.alert { + // Alerts are block elements with relative positioning. + // They would go over floating elements, which is never what we want. + clear: both; +} + +// Extend bootstrap to create background and text utilities for gray colors too +// Note: the card-body rule below needs those grays utilities to be defined +// before so that the related o-bg-color text-muted rules work. +@each $color, $value in $grays { + @include bg-variant(".bg-#{$color}", $value); + @include text-emphasis-variant(".text-#{$color}", $value); +} + +.card-body { + // BS4 colored cards do not have a very popular design. This will reset them + // to a BS3-like one: only the header and footer are colored and the body + // will use the color of a default card background with a light opacity. + // Limitation: bg-* utilities cannot be used on card-body elements anymore. + @include o-bg-color(rgba($card-bg, $o-card-body-bg-opacity)); + + &:first-child { + @include border-top-radius($card-inner-border-radius); + } + &:last-child { + @include border-bottom-radius($card-inner-border-radius); + } + + &.row { + // The 'row' class should not be used on a 'card-body' element but if + // it is done, our custom bg color would overflow the card. As a fix + // for those cases (normally only one at the time this fix is made), + // remove the background color. + // TODO remove me in master. + background-color: transparent !important; + } +} +.accordion { + .collapsing, .collapse.show { + > .card-body:first-child { + // Above background color would overflow on the card-header border + // without this + margin-top: $card-border-width; + } + } +} + +.toast-header { + background-clip: border-box; +} +.toast-body { + // Same as card-body, see explanation above + @include o-bg-color(opacify($toast-background-color, 0.08)); +} + +// Modify modals so that their scrollable element is the modal-body (except in +// mobile). +@include media-breakpoint-up(sm) { + .modal-dialog { + height: 100%; + padding: $modal-dialog-margin-y-sm-up 0; + margin: 0 auto; + } + .modal-content { + max-height: 100%; + } + .modal-header, .modal-footer { + flex: 0 0 auto; + } + .modal-body { + overflow: auto; + // fix iOS issue https://github.com/scottjehl/Device-Bugs/issues/8 + -webkit-transform: translate3d(0, 0, 0); + min-height: 0; + } +} + +// Do not display the backdrop element added by bootstrap in the body and add a +// background on the modal to keep the same effect. The bootstrap backdrop was +// probably useful for compatibility with <IE9 but is no longer needed. This +// also avoids z-index issues because modals could be opened in an element +// (e.g. the website #wrapwrap) whose stacking context is different of the body +// one (where the backdrop is opened). This would make the backdrop appears on +// top of the modal. +.modal-backdrop { + display: none; +} +.modal:not([data-backdrop="false"]) { + background-color: rgba($modal-backdrop-bg, $modal-backdrop-opacity); +} + +// Restore pointer cursor which came with the 'btn' class until BS 4.3.1 +.btn:not(:disabled):not(.disabled) { + cursor: pointer; +} + +// Disable RTL for the popover position +.popover { + right: auto#{"/*rtl:ignore*/"}; +} + +// Review input group: BS allows to control button and input dimensions +// individually but fails to make input-group correctly in all cases (for some +// strange reasons they made it work well for sm and lg but not for the normal +// sizing). +.input-group-prepend, +.input-group-append { + > .btn { + @include button-size($input-padding-y, $input-padding-x, $input-font-size, $input-line-height, $input-border-radius); + border-width: $input-border-width; + } +} + +// Review $link-decoration behavior +@if $link-decoration and $link-decoration != none { + .btn:not(.btn-link), .nav-link, .dropdown-item, .page-link, .breadcrumb-item > a, .badge, .fa { + &, &:hover, &:focus { + text-decoration: none; + } + } +} + +// Generating bootstrap color buttons was disabled (see import_bootstrap.scss). +// We do it ourself here with a tweak: we introduce btn-fill-* (working as the +// normal btn-* classes (in opposition to btn-outline-* classes). We then map +// the btn-* classes to either btn-fill-* or btn-outline-* classes depending on +// the configuration. We also allow to define a border-color different than the +// background color. +$o-btn-bg-colors: () !default; +$o-btn-border-colors: () !default; +@each $color, $value in $theme-colors { + $-bg-color: map-get($o-btn-bg-colors, $color) or $value; + $-border-color: map-get($o-btn-border-colors, $color) or $-bg-color; + .btn-fill-#{$color} { + @include button-variant($-bg-color, $-border-color); + } +} +@each $color, $value in $theme-colors { + $-bg-color: map-get($o-btn-bg-colors, $color) or $value; + $-border-color: map-get($o-btn-border-colors, $color) or $-bg-color; + .btn-outline-#{$color} { + @include button-outline-variant($-border-color); + } +} +$o-btn-outline-defaults: () !default; +@each $color, $value in $theme-colors { + .btn-#{$color} { + @if index($o-btn-outline-defaults, $color) { + @extend .btn-outline-#{$color}; + } @else { + @extend .btn-fill-#{$color}; + } + } +} + +// Compensate navbar brand padding if no visible border +@if alpha($navbar-dark-toggler-border-color) < 0.001 { + .navbar-dark .navbar-toggler { + padding-left: 0; + padding-right: 0; + } +} +@if alpha($navbar-light-toggler-border-color) < 0.001 { + .navbar-light .navbar-toggler { + padding-left: 0; + padding-right: 0; + } +} + +// Review bootstrap navbar to work with different nav styles +$o-navbar-nav-pills-link-padding-x: $nav-link-padding-x !default; +$o-navbar-nav-pills-link-border-radius: $nav-pills-border-radius !default; +.navbar-nav.nav-pills .nav-link { + // The rules is needed so that the padding is not reset to 0 in mobile. + // Also use default nav-link paddings instead of navbar ones. + padding-right: $o-navbar-nav-pills-link-padding-x; + padding-left: $o-navbar-nav-pills-link-padding-x; + + @if $o-navbar-nav-pills-link-border-radius != $nav-pills-border-radius { + @include border-radius($o-navbar-nav-pills-link-border-radius); + } +} + +.carousel-control-next .sr-only { + left: 50%; // Avoid horizontal scrollbar in Chrome +} diff --git a/addons/web/static/src/scss/bs_mixins_overrides.scss b/addons/web/static/src/scss/bs_mixins_overrides.scss new file mode 100644 index 00000000..cc7ea1d4 --- /dev/null +++ b/addons/web/static/src/scss/bs_mixins_overrides.scss @@ -0,0 +1,74 @@ +//------------------------------------------------------------------------------ +// Bootstrap Mixins and Functions Extensions +// Those will affect the way bootstrap is generated wherever bootstrap is used +//------------------------------------------------------------------------------ + +// This variable must be defined here instead of bootstrap overridden files +// otherwise we will have deprecation messages for assets_common generation +$enable-deprecation-messages: false !default; + +// Override color-yiq function to handle the alpha component of colors and +// automatic threshold +@function color-yiq($color, $dark: $yiq-text-dark, $light: $yiq-text-light, $background: $body-bg, $cross-mix: true) { + $threshold: if($yiq-contrasted-threshold != false, $yiq-contrasted-threshold / 255 * 100%, false); + @return o-get-most-contrast($color, $light, $dark, $background, $threshold, $cross-mix); +} + +@function mute-color($color) { + @return scale-color($color, $alpha: -30%); +} + +// 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-yiq-min-opacity-threshold: 0.3 !default; +$o-color-extras-nesting-selector: '&' !default; + +@mixin o-bg-color($color, $text-color: null, $with-extras: true, $important: true, $yiq-min-opacity-threshold: $o-yiq-min-opacity-threshold, $background: $body-bg, $nesting-selector: $o-color-extras-nesting-selector) { + @if ($color) { + $-yiq-threshold-met: alpha($color) > $yiq-min-opacity-threshold; + + $-yiq-color: if($text-color, $text-color, if($-yiq-threshold-met, color-yiq($color, $background: $background), null)); + background-color: $color#{if($important, ' !important', '')}; + color: $-yiq-color; // not important so that text utilities still work + + @if $with-extras and $-yiq-threshold-met { + #{$nesting-selector} { + @extend %o-bg-color-component-color-reset; + + .text-muted { + // Always important since the basic BS rule is important + color: mute-color($-yiq-color) !important; + } + } + } + } +} + +// Override background utilities so that they come with a default contrasted +// color (especially useful in the frontend editor for example). Also modifies +// the way .text-muted elements are rendered in those environments. +@mixin bg-variant($parent, $color, $text-color: null) { + #{$parent} { + @include o-bg-color($color, $text-color); + } + a#{$parent}, + button#{$parent} { + @include hover-focus { + @include o-bg-color(darken($color, 10%), $text-color, false); + } + } +} +@mixin bg-gradient-variant($parent, $color, $text-color: null) { + #{$parent} { + @include o-bg-color($color, $text-color); + background-image: linear-gradient(180deg, mix($body-bg, $color, 15%), $color) !important; + background-repeat: repeat-x !important; + } +} diff --git a/addons/web/static/src/scss/color_picker.scss b/addons/web/static/src/scss/color_picker.scss new file mode 100644 index 00000000..18eb7aea --- /dev/null +++ b/addons/web/static/src/scss/color_picker.scss @@ -0,0 +1,62 @@ +.o_field_color_picker { + display: flex; + float: right; + margin-right: 7px; + ul { + display: flex; + justify-content: flex-end; + flex-wrap: wrap; + @include o-kanban-colorpicker; + @include o-kanban-record-color; + width: 100%; + max-width: unset; + margin: 0; + padding: 0; + > li { + border: 2px solid white; + box-shadow: 0 0 0 1px gray('300'); + > a:focus { + outline: none; + } + } + } +} + +.o_field_color_picker_preview { + @include o-kanban-record-color; + margin-right: 7px; + > 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 + a.oe_kanban_color_0 { + 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; + } + } + } +} diff --git a/addons/web/static/src/scss/colorpicker.scss b/addons/web/static/src/scss/colorpicker.scss new file mode 100644 index 00000000..ae618504 --- /dev/null +++ b/addons/web/static/src/scss/colorpicker.scss @@ -0,0 +1,48 @@ +// COLOR PICKER +.o_colorpicker_widget { + .o_color_pick_area { + height: 125px; + background-image: linear-gradient(to bottom, hsl(0, 0%, 100%) 0%, hsla(0, 0%, 100%, 0) 50%, hsla(0, 0%, 0%, 0) 50%, hsl(0, 0%, 0%) 100%), + linear-gradient(to right, hsl(0, 0%, 50%) 0%, hsla(0, 0%, 50%, 0) 100%); + cursor: crosshair; + } + .o_color_slider { + background: linear-gradient(#F00 0%, #FF0 16.66%, #0F0 33.33%, #0FF 50%, #00F 66.66%, #F0F 83.33%, #F00 100%); + } + .o_opacity_slider, .o_color_preview { + @extend %o-preview-alpha-background; + } + .o_color_slider, .o_opacity_slider { + width: 4%; + margin-right: 2%; + cursor: pointer; + } + .o_slider_pointer, .o_opacity_pointer { + @include o-position-absolute($left: -50%); + width: 200%; + height: 8px; + margin-top: -2px; + } + .o_slider_pointer, .o_opacity_pointer, .o_picker_pointer, .o_color_preview { + box-shadow: inset 0 0 0 1px rgba(white, 0.9); + } + .o_slider_pointer, .o_opacity_pointer, .o_picker_pointer, .o_color_preview, .o_hex_div, .o_rgba_div { + border: 1px solid black; + } + .o_color_picker_inputs { + font-size: 10px; + + input { + font-family: monospace !important; // FIXME: the monospace font used in the editor has not consistent ch units on Firefox + height: 18px; + font-size: 11px; + } + .o_hex_div input { + width: 7ch; + } + .o_rgba_div input { + margin-right: 3px; + width: 3ch; + } + } +} diff --git a/addons/web/static/src/scss/control_panel.scss b/addons/web/static/src/scss/control_panel.scss new file mode 100644 index 00000000..075fce5c --- /dev/null +++ b/addons/web/static/src/scss/control_panel.scss @@ -0,0 +1,137 @@ + +.o_control_panel { + border-bottom: 1px solid darken($o-control-panel-background-color, 20%); + @include o-webclient-padding($top: 5px, $bottom: 5px); + background-color: $o-control-panel-background-color; + + > div { + display: flex; + min-height: 30px; + margin: 5px 0px; + } + + @include media-breakpoint-up(md) { + .o_cp_top_left, .o_cp_top_right, + .o_cp_bottom_left, .o_cp_bottom_right { + width: 50%; + } + } + + .breadcrumb { + font-size: 18px; + + > li { + @include o-text-overflow($max-width: 90%); + } + } + + .o_cp_top_right { + min-height: $o-cp-breadcrumb-height; + } + + .o_cp_bottom_left { + display: flex; + justify-content: space-between; + + > .o_cp_action_menus { + padding-right: 10px; + + .o_hidden_input_file { + position: relative; + input.o_input_file { + position: absolute; + top: 1px; + opacity: 0; + width: 100%; + height: 26px; + } + .o_form_binary_form span { + padding: 3px 25px; + color: $o-brand-primary; + } + .o_form_binary_form:hover { + background-color: $table-hover-bg; + } + } + .o_sidebar_delete_attachment { + padding: 0px; + position: absolute; + top: 5px; + right: 10px; + } + .o_dropdown_toggler_btn { + margin-right: 15px; + } + } + } + + .o_cp_bottom_right { + display: flex; + + > .o_cp_pager { + margin: auto 0 auto auto; + padding-left: 5px; + text-align: center; + user-select: none; + + .o_pager { + display: flex; + align-items: center; + + .o_pager_counter { + margin-right: 5px; + min-width: $o-statbutton-height; + } + } + } + + > .o_cp_switch_buttons > .btn:first-child { + margin-left: $o-horizontal-padding; + @include media-breakpoint-down(sm) { + margin-left: 0; + } + } + } +} + +.o_x2m_control_panel { + display: flex; + flex-flow: row wrap; + + .o_cp_buttons { + display: flex; + margin-right: auto; + > div { + margin-top: 5px; + } + .o-kanban-button-new { + margin-left: $o-kanban-record-margin; + } + } + .o_cp_pager { + display: flex; + margin-left: auto; + } +} + +.o_pager_value { + display: inline-block; +} + +span.o_pager_value { + border-bottom: 1px solid transparent; +} + +input.o_pager_value { + text-align: right; + width: 60px; + &:focus { + outline: none; + } +} + +@media print { + .o_control_panel { + display: none; + } +} diff --git a/addons/web/static/src/scss/data_export.scss b/addons/web/static/src/scss/data_export.scss new file mode 100644 index 00000000..f52bcf10 --- /dev/null +++ b/addons/web/static/src/scss/data_export.scss @@ -0,0 +1,53 @@ +.o_web_client .o_export { + padding: 20px; + .o-export-panel { + height: 100%; + } + .o_left_panel { + padding-right: 10px; + flex-flow: column nowrap; + height: 100%; + .o_left_field_panel { + flex: 1; + overflow: auto; + } + .o_export_tree_item { + cursor: pointer; + position: relative; + padding-left: 20px; + &.o_selected > .o_tree_column { + background-color: $o-brand-odoo; + color: white; + } + .o_expand_parent { + @include o-position-absolute($top: 4px, $left: 5px); + font-size: 10px; + } + .o_required { + border-bottom: 2px solid $o-main-text-color; + } + } + } + + .o_right_panel { + padding-left: 10px; + flex-flow: column nowrap; + height: 100%; + .o_save_list { + display: none; + } + .o_right_field_panel { + flex: 1; + overflow: auto; + .o_short_field { + cursor: ns-resize; + } + .o-field-placeholder { + border: 1px dashed $o-brand-primary; + } + .o_remove_field { + cursor: pointer; + } + } + } +} diff --git a/addons/web/static/src/scss/datepicker.scss b/addons/web/static/src/scss/datepicker.scss new file mode 100644 index 00000000..570c0c01 --- /dev/null +++ b/addons/web/static/src/scss/datepicker.scss @@ -0,0 +1,99 @@ + +.sr-only { + position: absolute; + width: 1px; + height: 1px; + margin: -1px; + padding: 0; + overflow: hidden; + clip: rect(0,0,0,0); + border: 0; +} + +.o_datepicker { + position: relative; + + .o_datepicker_input { + width: 100%; + cursor: pointer; + } + + .o_datepicker_button { + @include o-position-absolute(2px, 4px); + pointer-events: none; // let click events go through the underlying input + &:after { + @include o-caret-down; + } + } + + .o_datepicker_warning { + top: 0; + right: 20px; + } +} + + +// The 'div.dropdown-menu' part is needed to override default bootstrap rule as +// this file is unfortunately placed in common assets. +div.dropdown-menu.bootstrap-datetimepicker-widget{ + // Need to put datetimepicker widget above everything else + z-index: $zindex-modal + 1; + + // Also fix the dropdown width + width: 19rem; +} + +.datepicker { + .table-sm { + > thead { + color: white; + background-color: $o-brand-odoo; + + > tr { + &:first-child { + th:hover { + color: white; + background-color: darken($o-brand-odoo, 10%); + } + } + + &:last-child { + color: $o-datepicker-week-color; + background-color: $o-datepicker-week-bg-color; + } + > th { + border-radius: 0; + } + } + } + + > tbody { + > tr { + > td { + &.active, .active { + background-color: $o-brand-primary; + border-radius: 100px; + } + + &.today:before { + border-bottom-color: $o-brand-primary; + } + } + } + } + } +} + +.picker-switch { + span.fa { + margin: 0; + @include transition($btn-transition); + &.primary { + background-color: $o-brand-primary; + color: white; + &:hover { + background-color: darken($o-brand-primary, 10%); + } + } + } +} diff --git a/addons/web/static/src/scss/daterangepicker.scss b/addons/web/static/src/scss/daterangepicker.scss new file mode 100644 index 00000000..aece2a99 --- /dev/null +++ b/addons/web/static/src/scss/daterangepicker.scss @@ -0,0 +1,69 @@ +// Restore date range picker style +.daterangepicker { + .drp-calendar { + .calendar-table { + thead { + tr:first-child { + color: #FFFFFF; + background-color: $o-brand-odoo; + th { + &.prev, &.next { + span { + color: #FFFFFF; + border-color: #FFFFFF; + } + &:hover { + background-color: darken($o-brand-odoo, 10%); + } + } + } + } + tr:last-child { + color: $o-datepicker-week-color; + background-color: $o-datepicker-week-bg-color; + } + th { + border-radius: 0; + } + } + tbody { + tr { + border-bottom: 1px solid $o-datepicker-week-bg-color; + td { + border-width: 0px; + &:not(.off) { + &.in-range { + background-color: gray('200'); + } + &.available:hover { + background-color: gray('200'); + } + &.active, &.active:hover { + background-color: $o-brand-primary; + } + } + &.off:hover { + background: #FFFFFF; + color: #999; + } + } + } + } + } + .calendar-time { + select { + &.hourselect, &.minuteselect, &.secondselect, &.ampmselect { + display: initial; + -webkit-appearance: menulist-button; + -moz-appearance: menulist-button; + appearance: menulist-button; + } + } + } + } + .drp-buttons { + .drp-selected { + display: none; + } + } +} diff --git a/addons/web/static/src/scss/debug_manager.scss b/addons/web/static/src/scss/debug_manager.scss new file mode 100644 index 00000000..74d20dda --- /dev/null +++ b/addons/web/static/src/scss/debug_manager.scss @@ -0,0 +1,8 @@ +.modal { + .o_debug_manager { + position: relative; + list-style: none; + margin: 2px 10px 2px 0; + float: left; + } +} diff --git a/addons/web/static/src/scss/domain_selector.scss b/addons/web/static/src/scss/domain_selector.scss new file mode 100644 index 00000000..b3593497 --- /dev/null +++ b/addons/web/static/src/scss/domain_selector.scss @@ -0,0 +1,204 @@ + +.o_domain_node { + $o-domain-selector-indent: 32px; + $o-domain-selector-panel-space: 60px; + $o-domain-selector-row-height: 35px; // should be greater than a "normal" row so that "header" parts are the same size of rows + $o-domain-animation-bar-height: 15px; + + position: relative; + + // Rows in a domain node (selector) have a minimum height and holds the control panel + .o_domain_selector_row { + display: flex; + align-items: center; + min-height: $o-domain-selector-row-height; + + > * { + flex: 0 0 auto; + } + + > .o_domain_node_control_panel { + @include o-position-absolute($top: 0, $bottom: 0, $right: -$o-domain-selector-panel-space); + + > button { + float: left; + width: $o-domain-selector-panel-space/2; + height: 100%; + padding: 0; + background-color: transparent; + font-size: 16px; + opacity: 0.2; + cursor: pointer; + + &.o_domain_delete_node_button { + font-size: 12px; + padding-left: 5px; + + &:hover { + color: theme-color('danger'); + } + } + } + } + } + + // Rules specific to domain parts which contain sub-domain parts + &.o_domain_tree { + > .o_domain_tree_header { + position: relative; + + .o_domain_tree_operator_caret::after { + @include o-caret-down; + } + } + + > .o_domain_node_children_container { + padding-left: $o-domain-selector-indent; + } + + // Rules specific to domain root + &.o_domain_selector { + > .o_domain_tree_header { + min-height: 22px; // prevents "records button" to overflow 1-row domain + } + + > .o_domain_node_children_container { + padding-left: 0; + } + + &.o_edit_mode > .o_domain_node_children_container { + padding-right: $o-domain-selector-panel-space; // delete button is over the row + } + + > .o_domain_debug_container { + display: block; + margin-top: 16px; + padding: 8px 10px 12px; + background: gray('900'); + color: rgba(white, 0.5); + font-family: monospace; + font-weight: normal; + + > input { + border: none; + padding-top: 8px; + background: transparent; + color: white; + } + } + } + } + + // Rules specific to domain parts which do not contain sub-domain parts + &.o_domain_leaf { + &.o_read_mode { + display: inline-flex; + margin-right: 4px; + } + + > .o_domain_leaf_info { + background: $o-brand-lightsecondary; + border: 1px solid darken($o-brand-lightsecondary, 10%); + padding: 2px 4px; + + > .o_field_selector { + float: left; + margin-right: 4px; + + > .o_field_selector_value > .o_field_selector_chain_part { + border: none; + font-weight: 700; + } + } + .o_domain_leaf_operator { + font-style: italic; + } + .o_domain_leaf_value { + font-weight: 700; + } + } + + > .o_domain_leaf_edition { + display: flex; + width: 100%; + padding: 4px ($o-domain-selector-panel-space/2) 4px 0; + + > * { + flex: 1 1 15%; // operator select + width: auto; + background-color: white; + + &:first-child { // field selector + flex: 0 1 55%; + } + + &:last-child { // field value + flex: 1 1 25%; + } + + + * { + margin-left: 4px; + } + + input, select, .o_datepicker, .o_datepicker_input { + height: 100%; + } + } + + .o_domain_leaf_value_tags { + display: flex; + + > * { + flex: 0 0 auto; + } + > input { + flex: 1 1 auto; + width: 0; + min-width: 50px; + } + .o_domain_leaf_value_remove_tag_button { + cursor: pointer; + } + } + } + } + + // Animation effects + transition: margin-bottom .05s ease .05s; + + &::after { + content: ""; + @include o-position-absolute($left: 0, $right: 0, $top: 100%); + display: block; + height: $o-domain-animation-bar-height; + max-height: 0; + background-image: linear-gradient(45deg, rgba($o-brand-primary, 0.4), rgba($o-brand-primary, 0.1)); + transition: max-height .05s ease .05s; + } + + &.o_hover_btns, &.o_hover_add_node { + .o_domain_node_control_panel > button { + opacity: 0.5; + &:hover { + opacity: 1.0; + } + } + } + + &.o_hover_btns { + background: darken(white, 3%); + } + + &.o_hover_add_node { + margin-bottom: $o-domain-animation-bar-height; + transition: margin .15s ease .5s; + + &::after { + max-height: $o-domain-animation-bar-height; + transition: max-height .15s ease .5s; + } + &.o_hover_add_inset_node::after { + left: $o-domain-selector-indent; + } + } +} diff --git a/addons/web/static/src/scss/dropdown.scss b/addons/web/static/src/scss/dropdown.scss new file mode 100644 index 00000000..30db84f2 --- /dev/null +++ b/addons/web/static/src/scss/dropdown.scss @@ -0,0 +1,91 @@ +%dropdown-li-a-padding { + padding: 3px $o-dropdown-hpadding; +} + +.ui-autocomplete { // Copy style of bootstrap dropdown + background-image: none; + background-color: $dropdown-bg; + border: $dropdown-border-width solid $dropdown-border-color; + font-size: $font-size-base; + padding: 5px 0px; + box-shadow: 0px 6px 12px rgba(0, 0, 0, 0.176); + + .ui-menu-item { + padding: 0; + > a { + display: block; + color: $dropdown-link-color; + + @extend %dropdown-li-a-padding; + + &.ui-state-active { + border: none; + font-weight: normal; + margin: 0; + } + } + &.o_m2o_dropdown_option, &.o_m2o_start_typing { + text-indent: 20px + } + &.o_m2o_start_typing { + font-style: italic; + cursor: default; + a.ui-menu-item-wrapper, a.ui-state-active, a.ui-state-active:hover { + background: none; + } + } + } +} + +.dropdown-menu { + box-shadow: 0 6px 12px -4px rgba(black, 0.25); + + .o_inline_dropdown { + position: relative; + + & > .dropdown-menu { + top: 0; + left: 100%; + } + &:hover .dropdown-menu { + display: block; + } + } +} + +@mixin dropdown-menu-mixin { + .show .dropdown-menu { + > * { + position: relative; // to allow absolute positionning inside the items + } + + .dropdown-item { + &:focus, &:active, &:focus:active { + outline: none; + } + + @extend %dropdown-li-a-padding; + } + + .dropdown-header { + @extend %dropdown-li-a-padding; + color: $o-main-text-color; + + @include media-breakpoint-down(sm) { + color: white; + text-decoration: underline; + } + } + } +} + +@include dropdown-menu-mixin; +.navbar-nav { + @include dropdown-menu-mixin; +} + +.o_rtl { + .dropdown-menu { + left: auto; + } +} diff --git a/addons/web/static/src/scss/dropdown_extra.scss b/addons/web/static/src/scss/dropdown_extra.scss new file mode 100644 index 00000000..e4f68ec0 --- /dev/null +++ b/addons/web/static/src/scss/dropdown_extra.scss @@ -0,0 +1,60 @@ +.ui-autocomplete .ui-menu-item { + > a.ui-state-active { + background-color: $o-brand-odoo; + color: white; + } + &.o_m2o_dropdown_option > a { + color: $o-brand-odoo; + &.ui-state-active { + color: white; + } + } + &.o_m2o_start_typing > a.ui-state-active { + color: $dropdown-link-color; + } +} + +.oe_dropdown, .oe_dropdown_toggle { + position: relative; + cursor: pointer; +} +.oe_dropdown_toggle { + color: #2B2B2B; + font-weight: normal; +} +.oe_dropdown_menu { + display: none; + position: absolute; + z-index: 3; + margin: 0; + border: 1px solid #afafb6; + background: white; + padding: 4px 0; + min-width: 140px; + text-align: left; + box-shadow: 0 1px 4px rgba(0,0,0,0.3); + &.oe_opened { + display: block; + } + > li { + list-style-type: none; + float: none; + display: block; + position: relative; + margin: 0; + padding: 2px 8px; + &:hover { + background-image: linear-gradient(to bottom, #f0f0fa, #eeeef6); + box-shadow: none; + } + > a { + white-space: nowrap; + display: block; + color: #4c4c4c; + text-decoration: none; + &:hover { + text-decoration: none; + } + } + } +} diff --git a/addons/web/static/src/scss/dropdown_menu.scss b/addons/web/static/src/scss/dropdown_menu.scss new file mode 100644 index 00000000..d2da44a3 --- /dev/null +++ b/addons/web/static/src/scss/dropdown_menu.scss @@ -0,0 +1,46 @@ +.o_dropdown_menu { + > .dropdown-divider:first-child { + display: none; + } + + min-width: 150px; + max-height: calc(100vh - 140px); // FIXME + overflow: auto; + + .o_menu_item { + position: relative; + + > .dropdown-item { + color: $o-main-text-color; + + &:hover { + color: darken($o-main-text-color, 10%); + } + } + } +} + +.dropdown-item { + position: relative; + color: $o-main-text-color; + + &:hover { + color: darken($o-main-text-color, 10%); + } + + &.selected { // TODO why not using standard BS4 "active" class ? + color: darken($o-main-text-color, 10%); + font-weight: bold; + + &:before { + font-family: FontAwesome; + position: absolute; + left: 4px; + content: "\f00c"; + } + } + .o_trash_button { + padding-left: 8px; + @include o-position-absolute($right: $dropdown-item-padding-x * 0.5); + } +}
\ No newline at end of file diff --git a/addons/web/static/src/scss/fields.scss b/addons/web/static/src/scss/fields.scss new file mode 100644 index 00000000..6f9e23ab --- /dev/null +++ b/addons/web/static/src/scss/fields.scss @@ -0,0 +1,521 @@ +// +// This file regroups all the rules which apply to field widgets wherever they +// are in the DOM, in both community and enterprise versions. +// + +// Invalid +.o_field_invalid { + &.o_input, .o_input, &.o_form_label { + color: theme-color('danger')!important; + border-color: theme-color('danger')!important; // enterprise o_required_modifier rule overrides this without !important + } +} + +// Empty +.o_field_empty { + display: none!important; +} + +// Numbers +.o_field_number { + white-space: nowrap; +} + +//------------------------------------------------------------------------------ +// Fields +//------------------------------------------------------------------------------ + +.o_field_widget { + // Default display and alignment of widget and internal <input/> + &, input.o_input { + display: inline-block; + text-align: inherit; + } + + // Block fields + &.note-editor, &.oe_form_field_html, &.oe_form_field_text, &.o_field_domain, &.o_graph_linechart, &.o_graph_barchart { + display: block; + } + + // Flex fields (inline) + &.o_field_many2one, &.o_field_radio, &.o_field_many2manytags, &.o_field_percent_pie, &.o_field_monetary, &.o_field_binary_file, &.o_field_float_percentage { + display: inline-flex; + > span, > button { + flex: 0 0 auto; + } + } + + // Dropdowns input (many2one, ...) + .o_input_dropdown { + flex: 1 1 auto; + position: relative; + width: 100%; + + > input { + padding-right: 15px; + cursor: pointer; + + &::-ms-clear { + display: none; + } + } + + .o_dropdown_button { + @include o-position-absolute(0, 0); + pointer-events: none; + &:after { + @include o-caret-down; + } + } + } + + // Text + &.o_field_text, &.oe_form_field_text .oe_form_text_content { + width: 100%; + white-space: pre-wrap; + } + + // Monetary + &.o_field_monetary, &.o_field_float_percentage { + &.o_input { + align-items: baseline; + + > input { + width: 100px; + flex: 1 0 auto; + } + } + } + + // Many2OneAvatar + &.o_field_many2one_avatar { + > img.o_m2o_avatar { + border-radius: 50%; + width: 19px; + height: 19px; + object-fit: cover; + margin-right: 4px; + } + } + + // Many2many tags + &.o_field_many2manytags { + flex-flow: row wrap; + align-items: baseline; + max-width: 100%; + + > .o_field_widget { + flex: 1 0 100px; + position: initial; + max-width: 100%; + } + + .badge { + flex: 0 0 auto; + border: 0; + font-size: 12px; + user-select: none; + display: flex; + max-width: 100%; + + a { + color: inherit; + } + + .o_badge_text { + @include o-text-overflow(inline-block); + max-width: 200px; + color: inherit; + } + + .o_delete { + color: inherit; + cursor: pointer; + padding-left: 4px; + } + } + + // o-kanban-colorpicker without customizing + // '&:first-child > a::before' (transparent red-crossed colorpick) + @mixin o-tag-colorpicker { + $o-tag-colorpicker-padding-right: $o-dropdown-hpadding - 2 * 5px; // FIXME was $o-kanban-inner-hmargin before scss convertion + + max-width: 100%; + padding: 3px $o-tag-colorpicker-padding-right 3px $o-dropdown-hpadding; + + > li { + display: inline-block; + margin: 5px 5px 0 0; // FIXME was $o-kanban-inner-hmargin before scss convertion + border: 1px solid white; + box-shadow: 0 0 0 1px gray('300'); + + > a { + display: block; + + &::after { + content: ""; + display: block; + width: 20px; + height: 15px; + } + } + + &:last-of-type { + box-shadow: 0 0 0 0px white; // remove grey border on hide in kanban + color: $o-main-text-color; + } + } + } + + .tagcolor_dropdown_menu { + min-width: 150px; // down from 160px of .dropdown-menu + margin-right: 0px; // cancel right margin of .dropdown-menu + } + + .o_colorpicker > ul { + @include o-tag-colorpicker; + white-space: normal; + li > .o_hide_in_kanban label { + line-height: $o-line-height-base; + } + } + + @for $size from 1 through length($o-colors) { + .o_tag_color_#{$size - 1} { + @if $size == 1 { + & { + background-color: white; + color: nth($o-colors, $size); + box-shadow: inset 0 0 0 2px nth($o-colors, $size); + } + &::after { + background-color: nth($o-colors, $size); + } + } @else { + &, &::after { + background-color: nth($o-colors, $size); + color: $white; + } + } + } + } + .o_tag_error { + box-shadow: inset 0 0 0 2px #FF0000; + + .o_delete { + color: #FF0000; + } + } + // Many2many tags avatar + &.avatar { + .badge { + padding-left: 4px; + img { + height: 18px; + width: 18px; + object-fit: cover; + } + &.o_tag_color_0 { + background-color: #F1F1F1; + box-shadow: none; + border: 1px solid gray('300'); + } + .o_delete { + margin-top: 3px; + } + } + } + } + + // Stars + &.o_priority { + display: inline-block; + padding: 0; + margin: 0; + vertical-align: baseline; + > .o_priority_star { + display: inline-block; + font-size: 1.35em; + &:hover, &:focus { + text-decoration: none; + outline: none; + } + + + .o_priority_star { + padding-left: 5px; + } + + &.fa-star-o { + color: $o-main-color-muted; + } + &.fa-star { + color: gold; + } + } + } + + // Favorite + &.o_favorite { + i.fa { + font-size: 16px; + } + i.fa-star-o { + color: $o-main-color-muted; + &:hover { + color: gold; + } + } + i.fa-star { + color: gold; + } + } + + // Specific success color for toggle_button widget + .fa.o_toggle_button_success { + color: theme-color('success'); + } + + // Handle widget + &.o_row_handle { + cursor: ns-resize; + } + + &.o_field_selection_badge { + .o_selection_badge { + display: inline-block; + margin: 0px 0px 4px 4px; + padding: 1px 6px; + color: $o-main-color-muted; + border: 1px solid gray('300'); + cursor: pointer; + &.active { + color: $o-brand-primary; + border-color: $o-brand-primary; + } + &:hover:not(.active) { + color: darken(gray('300'), 30%); + border-color: darken(gray('300'), 30%); + } + } + } + // Radio buttons + &.o_field_radio { + @include media-breakpoint-down(sm) { + display: inline-block; + } + .o_radio_input { + outline: none; + } + + .o_radio_item { + flex: 0 0 auto; + } + + &.o_horizontal { + .o_radio_item { + margin-right: $o-form-spacing-unit * 2; + } + } + + &.o_vertical { + flex-flow: column nowrap; + .o_radio_item { + margin-bottom: $o-form-spacing-unit; + } + } + } + + // Percent pie + &.o_field_percent_pie { + align-items: center; + + > .o_pie { + position: relative; + display: inline-block; + vertical-align: middle; + margin-right: 10px; + border-radius: 100%; + overflow: hidden; + background-color: $o-brand-primary; + + &:after { // Outside pie border to go over border-radius irregularities + content: " "; + @include o-position-absolute(0, 0); + width: 100%; + height: 100%; + border-radius: 100%; + } + + .o_mask { + @include o-position-absolute(0, 0); + transform-origin: 0; + width: 50%; + height: 100%; + background-color: $o-view-background-color; + &.o_full { + background-color: inherit; + } + } + + .o_pie_value { + display: flex; + justify-content: center; + align-items: center; + + border-radius: 100%; + + font-weight: 700; + color: $o-brand-odoo; + } + } + } + + // Ace editor + &.o_ace_view_editor { + width: 100%; + .ace-view-editor { + height: 300px; + padding: 0 1em; + } + } + + // Image + &.o_field_image { + position: relative; + + .o_form_image_controls { + @include o-position-absolute(0, 0); + width: 100%; + + color: white; + background-color: $o-brand-primary; + opacity: 0; + transition: opacity ease 400ms; + min-width: 35px; + + > button.fa { + border: none; + background-color: transparent; + } + + > .fa { + padding: 4px; + margin: 5px; + cursor: pointer; + } + } + @include media-breakpoint-down(xs, $o-extra-grid-breakpoints) { + .o_form_image_controls{ + position: initial; + opacity: 1; + > .fa{ + width: 50%; + padding: 6px; + margin: 0px; + text-align: center; + &.o_select_file_button{ + background: $o-brand-primary; + } + &.o_clear_file_button{ + background: theme-color('danger'); + } + } + } + } + + &:hover .o_form_image_controls { + opacity: 0.8; + } + + &.o_field_invalid > img { + border: 1px solid theme-color('danger'); + } + } + + // Input loading/file + .o_form_binary_progress, .o_hidden_input_file .o_input_file { + display: none; + } + + // Domain + &.o_field_domain { + > .o_field_domain_panel { + margin-top: 8px; + } + &.o_inline_mode.o_edit_mode { + position: relative; + + > .o_field_domain_panel { + @include o-position-absolute(0, 0); + margin-top: 0; + } + } + } + + // PDF Viewer + &.o_field_pdfviewer, .o_pdfview_iframe { + width: 100%; + height: 450px; + border: 0; + margin-top: 10px; + } + + // Copy to clipboard + &.o_field_copy { + position: relative; + width: 100% !important; + border-radius: 5px; + border: 1px solid $primary; + font-size: $font-size-sm; + color: $o-brand-primary; + font-weight: $badge-font-weight; + text-align: center; + padding-right: 100px; + word-break: break-word; + .o_clipboard_button { + @include o-position-absolute($top: 0, $right: 0); + &.o_btn_text_copy { + position: absolute; + top: 0; + right: 0; + } + &.o_btn_char_copy { + padding-top: 2px; + height: 100%; + } + } + } + + & > .o_field_color { + border: 2px solid rgba(0, 0, 0, 0.25); + border-radius: 100px; + width: 30px; + height: 30px; + margin: 0 5px 0 0; + display: inline-block; + } + + &.o_field_badge { + border: 0; + font-size: 12px; + user-select: none; + background-color: rgba(lightgray, 0.5); + font-weight: 500; + @include o-text-overflow; + transition: none; // remove transition to prevent badges from flickering at reload + } +} + +span.o_field_copy:empty { + border: none; +} + +button.o_field_float_toggle { + width: 100%; + text-align: center; +} + +// Selection fields +select.o_field_widget.o_input { + padding: 1px; // Other inputs get a 1px padding automatically added by the browsers but selects do not +} diff --git a/addons/web/static/src/scss/fields_extra.scss b/addons/web/static/src/scss/fields_extra.scss new file mode 100644 index 00000000..47fca992 --- /dev/null +++ b/addons/web/static/src/scss/fields_extra.scss @@ -0,0 +1,59 @@ +// +// This file regroups all the rules which apply to field widgets wherever they +// are in the DOM, in the community version. +// + +// Required +.o_required_modifier { + &.o_input, .o_input { + background-color: #D2D2FF!important; + } +} + +//------------------------------------------------------------------------------ +// Fields +//------------------------------------------------------------------------------ + +.o_field_widget { + // Dropdowns input carret + .o_input_dropdown .o_dropdown_button { + @include o-position-absolute(2px, 4px); + } + + // Many2one + &.o_field_many2one .o_external_button { + padding: 0; + margin-left: 2px; + font-size: 19px; + color: #7C7BAD; + border: none; + &:hover { + background-color: transparent; + } + } + + // Percent pie field + &.o_field_percent_pie { + $pie-dimension: 34px; + $pie-ring-width: 4px; + + .o_pie { + width: $pie-dimension; + height: $pie-dimension; + margin-left: 5px; + + &:after { // Outside pie border to go over border-radius irregularities + border: 1px solid $o-brand-odoo; + } + + .o_pie_value { + @include o-position-absolute($pie-ring-width, $pie-ring-width); + width: $pie-dimension - 2 * $pie-ring-width; + height: $pie-dimension - 2 * $pie-ring-width; + border: 1px solid white; + background-color: white; + font-size: 10px; + } + } + } +} diff --git a/addons/web/static/src/scss/file_upload.scss b/addons/web/static/src/scss/file_upload.scss new file mode 100644 index 00000000..155b39ad --- /dev/null +++ b/addons/web/static/src/scss/file_upload.scss @@ -0,0 +1,58 @@ +.o_content { + + .o_kanban_progress_card { + + position: relative; + padding: 0; + min-height: 80px; + cursor: pointer; + + .o_kanban_record_bottom { + color: $gray-900; + } + .o_kanban_image_wrapper { + opacity: 0.7; + } + } + + .o_list_progress_card { + height: 25px; + border: 1px solid #dfdfdf; + position: relative; + vertical-align: middle; + padding: 0; + + .o_file_upload_upload_title { + color: $headings-color; + font-size: 13px; + font-weight: 500; + } + } + + .o_file_upload_progress_bar { + @include o-position-absolute($top: 0px, $left: 0px); + height: 100%; + width: 100%; + .o_file_upload_progress_bar_value { + transition: width 0.1s; + border-right: 1px solid darken($o-brand-primary, 7.5%); + height: 100%; + width: 100%; + background-color: $o-brand-primary; + opacity: 0.5; + } + .o_upload_cross { + @include o-position-absolute(0, 0); + padding: 4px; + color: #963535; + cursor: pointer; + font-size: 16px; + &:active { + opacity: 0.7; + } + } + &:not(:hover) .o_upload_cross { + display: none; + } + } +} diff --git a/addons/web/static/src/scss/fontawesome_overridden.scss b/addons/web/static/src/scss/fontawesome_overridden.scss new file mode 100644 index 00000000..a234dbc9 --- /dev/null +++ b/addons/web/static/src/scss/fontawesome_overridden.scss @@ -0,0 +1,38 @@ +// This is rtl language specific fix +// It will override the font awesome symbols and flip them 180 degree +.o_rtl { + .fa { + &.fa-align-right, + &.fa-align-left, + &.fa-chevron-right, + &.fa-chevron-left, + &.fa-arrow-right, + &.fa-arrow-left, + &.fa-hand-o-right, + &.fa-hand-o-left, + &.fa-arrow-circle-right, + &.fa-arrow-circle-left, + &.fa-caret-right, + &.fa-caret-left, + &.fa-rotate-right, + &.fa-rotate-left, + &.fa-angle-double-right, + &.fa-angle-double-left, + &.fa-angle-right, + &.fa-angle-left, + &.fa-quote-right, + &.fa-quote-left, + &.fa-chevron-circle-right, + &.fa-chevron-circle-left, + &.fa-long-arrow-right, + &.fa-long-arrow-left, + &.fa-toggle-right, + &.fa-toggle-left, + &.fa-caret-square-o-right, + &.fa-arrow-circle-o-left, + &.fa-arrow-circle-o-right, + &.fa-caret-square-o-left { + transform: rotate(180deg); + } + } +}
\ No newline at end of file diff --git a/addons/web/static/src/scss/fonts.scss b/addons/web/static/src/scss/fonts.scss new file mode 100644 index 00000000..26996d89 --- /dev/null +++ b/addons/web/static/src/scss/fonts.scss @@ -0,0 +1,91 @@ +// ------------------------------------------------------------------ +// Lato +// ------------------------------------------------------------------ +$lato-font-path: '../fonts/lato'; + +@mixin lato-font($type, $weight, $style) { + // Cyrillic: U+0400-04FF, U+0500-052F + @font-face { + font-family: 'Odoo Unicode Support Noto'; + src: url('https://fonts.odoocdn.com/fonts/noto/NotoSans-#{$type}.woff2') format('woff2'), + url('https://fonts.odoocdn.com/fonts/noto/NotoSans-#{$type}.woff') format('woff'), + url('https://fonts.odoocdn.com/fonts/noto/NotoSans-#{$type}.ttf') format('truetype'); + font-weight: $weight; + font-style: $style; + unicode-range: U+0400-04FF, U+0500-052F; + } + // Hebrew: U+0590-05FF, U+FB1D-FB4F + @font-face { + font-family: 'Odoo Unicode Support Noto'; + src: url('https://fonts.odoocdn.com/fonts/noto/NotoSansHebrew-#{$type}.woff2') format('woff2'), + url('https://fonts.odoocdn.com/fonts/noto/NotoSansHebrew-#{$type}.woff') format('woff'), + url('https://fonts.odoocdn.com/fonts/noto/NotoSansHebrew-#{$type}.ttf') format('truetype'); + font-weight: $weight; + font-style: $style; + unicode-range: U+0590-05FF, U+FB1D-FB4F; + } + // Arabic: U+0600-06FF, U+0750-077F + @font-face { + font-family: 'Odoo Unicode Support Noto'; + src: url('https://fonts.odoocdn.com/fonts/noto/NotoSansArabic-#{$type}.woff2') format('woff2'), + url('https://fonts.odoocdn.com/fonts/noto/NotoSansArabic-#{$type}.woff') format('woff'), + url('https://fonts.odoocdn.com/fonts/noto/NotoSansArabic-#{$type}.ttf') format('truetype'); + font-weight: $weight; + font-style: $style; + unicode-range: U+0600-06FF, U+0750-077F, U+08A0-08FF; + } + + @font-face { + font-family: 'Lato'; + src: url('#{$lato-font-path}/Lato-#{$type}-webfont.eot'); + src: url('#{$lato-font-path}/Lato-#{$type}-webfont.eot?#iefix') format('embedded-opentype'), + url('#{$lato-font-path}/Lato-#{$type}-webfont.woff') format('woff'), + url('#{$lato-font-path}/Lato-#{$type}-webfont.ttf') format('truetype'), + url('#{$lato-font-path}/Lato-#{$type}-webfont.svg#Lato') format('svg'); + font-weight: $weight; + font-style: $style; + } + @font-face { + font-family: 'Lato-#{$type}'; + src: url('#{$lato-font-path}/Lato-#{$type}-webfont.eot'); + src: url('#{$lato-font-path}/Lato-#{$type}-webfont.eot?#iefix') format('embedded-opentype'), + url('#{$lato-font-path}/Lato-#{$type}-webfont.woff') format('woff'), + url('#{$lato-font-path}/Lato-#{$type}-webfont.ttf') format('truetype'), + url('#{$lato-font-path}/Lato-#{$type}-webfont.svg#Roboto') format('svg'); + } +} + +@mixin lato-font-pair($type, $weight) { + @include lato-font('#{$type}', $weight, normal); + @include lato-font('#{$type}Ita', $weight, italic); +} + +@include lato-font-pair('Hai', 100); +@include lato-font-pair('Lig', 300); +@include lato-font-pair('Reg', 400); +@include lato-font-pair('Bol', 700); +@include lato-font-pair('Bla', 900); + +// ------------------------------------------------------------------ +// Google fonts +// ------------------------------------------------------------------ +$google-font-path: '../fonts/google'; + +@mixin google-font($family, $type, $weight, $style) { + @font-face { + font-family: $family; + src: url('#{$google-font-path}/#{$family}/#{$family}-#{$type}.ttf') format('truetype'); + font-weight: $weight; + font-style: $style; + } +} + +@mixin google-font-pair($family) { + @include google-font('#{$family}', 'Regular', 400, normal); +} + +@include google-font-pair('Montserrat'); +@include google-font-pair('Open_Sans'); +@include google-font-pair('Oswald'); +@include google-font-pair('Raleway'); +@include google-font-pair('Roboto'); diff --git a/addons/web/static/src/scss/form_view.scss b/addons/web/static/src/scss/form_view.scss new file mode 100644 index 00000000..23db8ea8 --- /dev/null +++ b/addons/web/static/src/scss/form_view.scss @@ -0,0 +1,996 @@ +// Define left and right padding according to screen resolution +@mixin o-form-sheet-inner-left-padding { + padding-left: $o-horizontal-padding; + @include media-breakpoint-between(lg, xl, $o-extra-grid-breakpoints) { + padding-left: $o-horizontal-padding*2; + } +} +@mixin o-form-sheet-inner-right-padding { + padding-right: $o-horizontal-padding; + @include media-breakpoint-between(lg, xl, $o-extra-grid-breakpoints) { + padding-right: $o-horizontal-padding*2; + } +} +// Compensate margins +@mixin o-form-sheet-negative-margin { + margin-left: -$o-horizontal-padding; + margin-right: -$o-horizontal-padding; + @include media-breakpoint-between(lg, xl, $o-extra-grid-breakpoints) { + margin-left: -$o-horizontal-padding*2; + margin-right: -$o-horizontal-padding*2; + } +} + +.o_form_view { + background-color: $o-view-background-color; + + // Utility classes + .oe_form_box_info { + @include o-webclient-padding($top: 5px, $bottom: 5px); + > p { + margin: auto; + } + } + .oe_text_center { + text-align: center; + } + .oe_grey { + opacity: 0.5; + } + .oe_inline { + width: auto!important; + @include media-breakpoint-up(vsm, $o-extra-grid-breakpoints) { + &.o_inner_group { + width: 1px!important; + } + } + } + .oe_left { + @extend .oe_inline; + float: left!important; + } + .oe_right { + @extend .oe_inline; + float: right!important; + } + + @include media-breakpoint-up(vsm, $o-extra-grid-breakpoints) { + .o_row { + &, &.o_field_widget { // Some field may want to use o_row as root and these rules must prevalue + display: flex; + width: auto!important; + } + + align-items: baseline; + min-width: 50px; + margin: 0 (-$o-form-spacing-unit/2); + + > div, > span, > button, > label, > a, > input, > select { // > * did not add a level of priority to the rule + flex: 0 0 auto; + width: auto!important; + margin-right: $o-form-spacing-unit/2; + margin-left: $o-form-spacing-unit/2; + } + + > .o_row { + margin: 0; + } + > .btn { + padding-top: 0; + padding-bottom: 0; + } + > .o_field_boolean { + align-self: center; + } + } + } + + .o_row > div > .o_field_widget { + width: 100%; + } + + // Readonly specific rules + &.o_form_readonly { + .oe_edit_only { + display: none!important; + } + + .o_row:not(.o_row_readonly) { + &, & > div { + display: inline-block; + } + } + + .o_field_color_picker_preview > li > a { + cursor: default; + } + } + + .o_form_uri { + display: inline-block; + color: $link-color; + &:hover { + color: darken($link-color, 15%); + } + > span { + color: $o-main-text-color; + &:hover { + color: $o-main-text-color; + } + } + > span:first-child { + color: $link-color; + &:hover { + color: darken($link-color, 15%); + } + } + } + + // Editable specific rules + &.o_form_editable { + .oe_read_only { + display: none!important; + } + + .oe_title { + max-width: map-get($container-max-widths, md) - (2 * $o-horizontal-padding); + } + + @include media-breakpoint-up(vsm, $o-extra-grid-breakpoints) { + .o_row { + > .o_field_widget, > div { + flex: 1 1 auto; + width: 0!important; // 3rd flex argument does not work with input (must be auto and real width 0) + + &.o_field_boolean, &.o_priority { + flex: 0 0 auto; + width: auto!important; + } + } + } + } + } + + // No sheet + &.o_form_nosheet { + display: block; + @include o-webclient-padding($top: $o-sheet-vpadding, $bottom: $o-sheet-vpadding); + + .o_form_statusbar { + margin: (-$o-sheet-vpadding) (-$o-horizontal-padding) $o-sheet-vpadding (-$o-horizontal-padding); + } + } + + // Non-chatter rules + .o_form_sheet_bg { + position: relative; + } + + // Statusbar + .o_form_statusbar { + position: relative; // Needed for the "More" dropdown + display: flex; + justify-content: space-between; + padding-left: $o-horizontal-padding; + border-bottom: 1px solid gray('400'); + background-color: $o-view-background-color; + + > .o_statusbar_buttons, > .o_statusbar_status { + display: flex; + align-items: center; + align-content: space-around; + } + + > .o_field_widget { + align-self: center; + margin-bottom: 0px; + } + + > .o_statusbar_buttons { + flex-flow: row wrap; + + > .btn { + $o-statusbar-buttons-vmargin: 4px; + min-height: $o-statusbar-height - 2 * $o-statusbar-buttons-vmargin; + margin: $o-statusbar-buttons-vmargin 3px $o-statusbar-buttons-vmargin 0; + padding-top: 2px; + padding-bottom: 2px; + } + } + + > .o_statusbar_status { + margin-left: auto; + flex-flow: row-reverse wrap-reverse; + align-self: stretch; + align-items: stretch; + + > .o_arrow_button { + min-height: $o-statusbar-height; + font-size: 11px; + font-weight: bold; + position: relative; + padding-left: $o-statusbar-arrow-width*2; + color: gray('700'); + border-width: 0 0 0; + border-radius: 0; + transition: all 0.1s ease 0s; + + &:first-child { + padding-right: $o-horizontal-padding; // Compensate container padding + overflow-x: hidden; // to prevent horizontal scroll due to last arrow + } + &:last-child { + padding-left: $o-horizontal-padding - 1; + border-left: 1px solid gray('400'); + } + + &:active { + box-shadow: none; + } + + &.disabled { + opacity: 1.0; + color: $text-muted; + pointer-events: none; + border-left: 1px solid gray('300'); + } + + &:not(:first-child):before, &:not(:first-child):after { + content: " "; + display: block; + @include o-position-absolute(0, -$o-statusbar-arrow-width + 1); + + border-top: floor($o-statusbar-height/2) solid transparent; + border-bottom: ceil($o-statusbar-height/2) solid transparent; + border-right: none; + border-left: $o-statusbar-arrow-width solid $o-view-background-color; + transition: border 0.2s ease 0s; + -moz-transform: scale(0.9999); // Smooth the triangle on firefox + } + + &:not(.disabled) { + &:hover, &:active, &:focus { + color: $o-brand-primary; + background-color: gray('200'); + + &:after { + border-left-color: gray('200'); + } + } + } + + &:not(:first-child):before { + right: -$o-statusbar-arrow-width; + border-left-color: gray('300'); + } + + &.btn-primary.disabled { + color: $o-brand-odoo; + font-size: 11px; + background-color: gray('200'); + cursor: default; + + &:after { + border-left-color: gray('200'); + } + } + } + + } + + // Touch device mode + > .o_statusbar_buttons > .btn-group, > .o_statusbar_status { + > .dropdown-toggle { + &:after { + @include o-caret-down; + margin-left: $o-form-spacing-unit; + } + } + + > .dropdown-menu { + padding: 5px 0 2px 0; + min-width: 100px; + + .dropdown-item.btn { + min-width: 100%; + margin-bottom: 3px; + } + } + } + } + + // Button box + .oe_button_box { + position: relative; + display: block; + margin-bottom: $o-sheet-vpadding; + margin-top: -$o-sheet-vpadding; + @include o-form-sheet-negative-margin; + text-align: right; + // Use box-shadow instead of border-bottom because some button boxes are + // empty in some cases and we do not want to see a floating border in + // that cases. + box-shadow: inset 0 -1px 0 gray('400'); + + &.o_full .oe_stat_button:not(.o_invisible_modifier) ~ .oe_stat_button { + border-left: 1px solid gray('400'); + } + &.o_not_full .oe_stat_button { + border-left: 1px solid gray('400'); + } + + > .btn.oe_stat_button, > .o_dropdown_more { + flex: 0 0 auto; + width: percentage(1/3); // Adapt the number of visible buttons for each screen width + @include media-breakpoint-up(md) { + width: percentage(1/5); + } + @include media-breakpoint-up(lg) { + width: percentage(1/7); + } + @include media-breakpoint-up(xl) { + width: percentage(1/8); + } + } + + .btn.oe_stat_button { + color: $o-main-text-color; + height: $o-statbutton-height; + // Use !important to avoid touch_device style + padding: 0 $o-statbutton-spacing 0 0 !important; // padding-left will be achieved through margin-left of content + text-align: left; + white-space: nowrap; + background-color: transparent; + opacity: 0.8; + border-radius: 0px; + margin-bottom: 0; // If the button comes from a field + + &:hover, &:focus { + background-color: rgba(black, 0.03); + color: inherit; + opacity: 1; + } + + > .o_button_icon { + margin-left: $o-statbutton-spacing; // To create the button padding left (firefox bug) + display: inline-block; + vertical-align: middle; + line-height: $o-statbutton-height; + width: 30%; + + &:before { + font-size: 22px; + vertical-align: middle; + } + } + + > .o_field_percent_pie { + margin-left: $o-statbutton-spacing; // To create the button padding left (firefox bug) + } + + // Some buttons only display text without using StatInfo template + > span { + @include o-text-overflow(block); + white-space: normal; // text on several lines if needed + } + + > .o_stat_info, > span { // contains the value and text + display: inline-block; + vertical-align: middle; + font-weight: 500; + + max-width: 70%; + padding-right: $o-statbutton-spacing; + line-height: 1.3; + + > .o_stat_value, > .o_stat_text { + @include o-text-overflow(block); + } + + .o_stat_value { + font-weight: 700; + color: $o-brand-odoo; + line-height: 1.2; + } + .o_stat_text { + line-height: 1.2; + } + } + + &:not(:hover) .o_stat_info > .o_hover { + display: none !important; + } + &:hover .o_stat_info > .o_not_hover { + display: none !important + } + + &.o_button_more { + text-align: center; + &:after { + margin-left: 5px; + @include o-caret-down; + } + &[aria-expanded="true"]:after { + margin-left: 5px; + @include o-caret-up; + } + } + } + + > .o_dropdown_more { + @include o-position-absolute(100%, 0); + min-width: 0; + border: none; + border: 1px solid gray('300'); + margin: 0; + padding: 0; + @include media-breakpoint-down(sm) { + // avoid b4 drowdown inline style + position: relative !important; + transform: none !important; + will-change: inherit!important; + margin-bottom: 20px; + width: 100%; + border-width: 0px; + } + > .btn.oe_stat_button { + width: 100%; + border: none; + border-bottom: 1px solid gray('300'); + + @include media-breakpoint-down(sm) { + display: inline-block; + width: percentage(1/3); + } + } + } + + @mixin dropdownButtonsFix { + .btn.oe_stat_button.dropdown-item { + height: 44px !important; + padding: 5px 0 5px 0 !important; + border-left: none !important; + + > .o_button_icon { + line-height: normal; + } + } + } + + // IE 11 only + @media all and (-ms-high-contrast:none) { + @include dropdownButtonsFix + } + + // Edge only + @supports (display:-ms-grid) { + @include dropdownButtonsFix + } + } + + // Title + .oe_title { + > h1, > h2, > h3 { + width: 100%; // Needed because inline-block when is a hx.o_row + margin-top: 0; + margin-bottom: 0; + line-height: inherit; + + &.d-flex > .o_input { + height: max-content; + } + } + .o_priority > .o_priority_star { + font-size: inherit; + } + } + + // Avatar + .oe_avatar { + float: right; + margin-bottom: 10px; + + > img { + max-width: $o-avatar-size; + max-height: $o-avatar-size; + vertical-align: top; + border: 1px solid $o-main-color-muted; + } + } + + // Groups + .o_group { + display: inline-block; + width: 100%; + margin: 10px 0; + + // o_group contains nested groups + @for $i from 1 through $o-form-group-cols { + .o_group_col_#{$i} { + display: inline-block; + width: floor(100% / 12 * $i); + vertical-align: top; + } + } + + &.o_inner_group { + display: inline-table; + + > tbody > tr > td { + &.o_td_label { + width: 0%; + padding: 0 15px 0 0; + min-width: 150px; + } + vertical-align: top; + + span, .o_field_boolean, .oe_avatar, .o_form_uri { + &.o_field_widget { + width: auto; + } + } + } + } + + .o_form_label { + font-weight: normal; + } + + .o_field_widget { + width: 100%; + + > .btn { + flex: 0 0 auto; + padding: 0 10px; + } + } + + :not(.o_row):not(.o_data_cell) > .o_field_widget, + .o_row > .o_field_widget:last-child { // Note: this does not take care + // of an invisible last-child but + // it does not really matter + // Makes extra buttons (e.g. m2o external button) overflow on the + // right padding of the parent element + > .o_input_dropdown { + flex: 1 0 auto; + } + } + + &.o_label_nowrap .o_form_label { + white-space: nowrap; + } + + .o_td_label .o_form_label { + font-weight: bold; + margin-right: 0px; + } + } + + // Separators + .o_horizontal_separator { + font-size: $h2-font-size; + margin: $o-form-spacing-unit 0; + &:empty { + height: $o-form-spacing-unit * 2; + } + } + + // Notebooks + .o_notebook { + clear: both; // For the notebook not to have alongside floating elements + margin-top: $o-form-spacing-unit * 2; + + .tab-content > .tab-pane { + padding: $o-horizontal-padding 0; + } + } + + // Labels + .o_form_label { + margin: 0 $o-form-spacing-unit 0 0; + font-size: $font-size-base; // The label muse have the same size whatever their position + line-height: $line-height-base; + font-weight: bold; + } + + // Form fields + .o_field_widget { + margin-bottom: $o-form-spacing-unit; + } + .o_field_widget, .btn { + .o_field_widget { + margin-bottom: 0px; + } + } + + // Translate icon + span.o_field_translate { + padding: 0 $o-form-spacing-unit 0 0 !important; + vertical-align: top; + position: relative; + margin-left: -35px; + width: 35px !important; // important is usefull for textarea + display: inline-block; + text-align: right; + border: none; // usefull for textarea + } + input, textarea { + &.o_field_translate { + padding-right: 25px; + } + } + iframe.wysiwyg_iframe + .o_field_translate { + right: 30px !important; + top: 7px !important; + } + + // Text field with oe_inline class + .o_field_text.oe_inline { + width: 100%!important; + @include media-breakpoint-up(vsm, $o-extra-grid-breakpoints) { + width: 45%!important; + } + } + + // One2Many, Many2Many outside of group + .o_field_widget { + &.o_field_one2many, &.o_field_many2many { + width: 100%; + > div { + width: 100%; + } + } + } + + // Specific style classes + .o_group.o_inner_group.oe_subtotal_footer { + @extend .oe_right; + + > tbody > tr > td { + padding: 0; + + &.o_td_label { + text-align: right; + } + + .o_form_label { + padding-right: 20px; + + min-width: 0; + white-space: nowrap; + &:after { + content: ":"; + } + } + + .o_field_widget { + text-align: right; + justify-content: flex-end; + width: 100%; + } + } + + > tbody > tr:first-child > td { + padding-top: 4px; + } + + .oe_subtotal_footer_separator { + width: 100%; + text-align: right; + border-top: 1px solid gray('300'); + font-weight: bold; + font-size: 1.3em; + } + } + + .o_address_format { + width: 100%; + .o_address_street, .o_address_country { + display: flex; + } + + .o_address_city { + margin-right: 2%; + } + .o_address_state { + margin-right: 2%; + } + &.o_zip_city { + .o_address_zip { + margin-right: 2%; + } + .o_address_city { + margin-right: 0; + } + .o_address_state { + display: block; + margin-right: 0; + } + } + &.o_city_state { + .o_address_state { + margin-right: 0; + } + .o_address_zip { + display: block; + margin-right: 0; + } + } + > span.o_field_widget { + width: auto; + } + } + &.o_form_editable .o_address_format { + .o_address_city { + width: 38%; + } + div.o_address_state { + width: 33%; + } + input.o_address_zip { + width: 25%; + } + &.o_zip_city { + .o_address_zip { + width: 38%; + } + .o_address_city { + width: 60%; + } + .o_address_state { + width: 100%; + } + } + &.o_city_state { + .o_address_city { + width: 50%; + } + .o_address_state { + width: 48%; + } + .o_address_zip { + width: 100%; + } + } + } + + // Boolean + .o_field_boolean { + margin-right: $o-form-spacing-unit; + } + + // Timezone widget warning + .o_tz_warning { + color: theme-color('danger'); + cursor: help; + position: absolute; + margin-left: 10px; + margin-top: 5px; + } + + // One2Many Kanban views + .o_field_widget .o_kanban_view.o_kanban_ungrouped { + padding: 0; + .o_kanban_record { + box-shadow: none; + } + } + + // One2Many List views + .o_field_widget .o_list_view { + margin-bottom: 10px; + + > tfoot > tr > td { + padding: 3px; + color: $o-main-text-color; + } + } + &.o_form_readonly .o_field_widget .o_list_view .o_row_handle { + display: none; // Hide the handler in non-edit mode + } + .o_field_widget.o_readonly_modifier .o_list_view .o_row_handle { + display: none; // Hide the handler on readonly fields + } + + &.oe_form_configuration { + .o_group .o_form_label { + white-space: nowrap; + } + h2 { + margin-top: 32px !important; + } + } + &.o_company_document_layout { + .report_layout_container { + display: inline-block; + div { + display: inline-block; + img { + margin-left: 0 !important; + } + } + } + img[name="logo"] { + max-height: 100px; + max-width: 300px; + } + } +} + +// Overridden style when form view in modal +.modal .modal-dialog { + .o_form_view { + .o_statusbar_buttons > .btn { + /** + * Override to prevent the status bar from increasing height when + * inside dialog due to the original margin on those buttons. This + * prevents the other status buttons, those on the far right of the + * bar, from having a gap between their bottom and the bottom border + * of the status bar itself. + */ + margin-top: 2px; + margin-bottom: 2px; + } + + .o_form_sheet_bg { + padding: 0; + + > .o_form_statusbar, > .alert { + /** + * Override to prevent the status bar from overflowing on its + * far right. The original value is a negative margin that + * is supposed to compensate unwanted padding of the original + * view but the view inside dialog already has this padding + * removed. + */ + margin-left: 0; + margin-right: 0; + } + + > .o_form_sheet { + box-shadow: none; + width: 100%; + margin: 0 auto; + max-width: none; + /** + * Override to prevent double border, because the borders of the + * dialog itself or the borders of other surrounding elements + * (status bar, dialog footer) already act as a border for the + * sheet when inside dialog. + */ + border: none; + } + } + } + &:not(.modal-lg) .o_form_view { + .o_group { + width: 100%; + } + } + .o_onboarding_payment_acquirer_wizard { + a[type="action"] { + color: $link-color; + cursor: pointer; + } + } +} + +@media print { + .oe_button_box, .o_form_statusbar { + display: none !important; + } +} + +// Buttons in ControlPanel +.o_control_panel .o_form_buttons_view > button:first-child { + float: left; // Unfortunately needed for the bounce effect + margin-right: 4px; +} + + +// XXS form view specific rules +@mixin form-break-table { + display: block; + margin-bottom: $o-form-spacing-unit * 4; + + > tbody { + display: block; + + > tr { + display: flex; + flex-flow: row wrap; + + > td { + flex: 1 0 auto; + display: block; + max-width: 100%; + padding: 0; + + width: auto!important; // !important is required to override the width computed in JS + &.o_td_label { + width: 94%!important; // The label must be on its own line except if the form field is small enough (checkbox) + line-height: $o-label-font-size-factor; + } + + .o_field_widget { + margin-bottom: $o-form-spacing-unit * 2; + + > .o_field_widget { + margin-bottom: 0; + } + + &.o_field_boolean { + margin-right: 0; + } + } + + .o_input_dropdown { + width: auto; + max-width: 100%; + } + } + } + } +} +.o_form_view.o_xxs_form_view { + .oe_title { + word-break: break-all; + } + .o_group { + &.o_inner_group { + @include form-break-table; + } + } +} + +// Settings form views +.o_settings_container { + display: flex; + flex: 0 1 auto; + flex-flow: row wrap; + + .o_form_label.o_light_label, .o_light_label .o_form_label { + font-weight: normal; + } + .o_setting_box:visible:nth-child(odd) { + clear: left; + } + .text-muted { + color: #aaaaaa; + } + .o_setting_box { + margin-bottom: 8px; + margin-top: 8px; + .o_setting_left_pane { + width: 24px; + float: left; + .o_enterprise_label { + position: absolute; + top: 0px; + right: 40px; + } + } + .o_setting_right_pane { + margin-left: 30px; + border-left: 1px solid #bbbbbb; + padding-left: 10px; + .o_input_dropdown > .o_input { + width: 100%; + } + .o_field_widget { + width: 50%; + flex: 0 0 auto; + + &.o_field_many2manytags > .o_field_widget { + flex: 1 0 50px; + } + } + button.btn-link:first-child { + padding: 0; + } + a.oe-link { + font-size: 12px; + } + } + } +} diff --git a/addons/web/static/src/scss/form_view_extra.scss b/addons/web/static/src/scss/form_view_extra.scss new file mode 100644 index 00000000..f6b8b2fa --- /dev/null +++ b/addons/web/static/src/scss/form_view_extra.scss @@ -0,0 +1,109 @@ +.o_form_view { + $sheet-min-width: 650px; + $sheet-padding: 16px; + + &.o_form_nosheet.oe_form_nomargin { + margin: 0; + } + .o_form_sheet_bg { + border-bottom: 1px solid #ddd; + background: url(/web/static/src/img/form_sheetbg.png); + + > .o_form_sheet { + min-width: $sheet-min-width; + max-width: $o-form-view-sheet-max-width; + min-height: 330px; + border: 1px solid #c8c8d3; + box-shadow: 0 4px 20px rgba(0,0,0,0.15); + background: white; + + margin: $o-sheet-vpadding*0.2 auto; + @include media-breakpoint-up(md) { + margin: $o-sheet-vpadding*0.5 auto; + } + padding: $o-sheet-vpadding; + @include o-form-sheet-inner-right-padding; + @include o-form-sheet-inner-left-padding; + + .ui-tabs { + margin: 0 -16px; + } + .oe_notebook_page { + padding: 0 16px; + } + } + } + + // Button box + .oe_button_box { + &, & + .oe_avatar { + + .oe_title { + width: 400px; + } + } + + // TODO remove me in master (this rule is a copy of the one in the + // bootstrap_review.scss file) + + .alert { + clear: both; + } + + .oe_stat_button { + &:hover { + background-color: #e6e6e6; + } + .o_button_icon { + color: #7C7BAD; + } + } + } + + // Groups + .o_group { + .o_td_label { + border-right: 1px solid #ddd; + } + .o_td_label + td { + padding: 0px 36px 0px 8px; + } + .o_field_widget.o_text_overflow { + width: 1px!important; // hack to make the table layout believe it is a small element (so that the table does not grow too much) ... + min-width: 100%; // ... but in fact it takes the whole table space + } + } + + // Separators + .o_horizontal_separator { + color: $o-brand-primary; + font-weight: bold; + } + + // Specific style classes + .o_group.o_inner_group.oe_subtotal_footer { + .oe_subtotal_footer_separator { + border-top: 1px solid #cacaca; + } + .o_td_label { + border-right: none; + } + } +} + +// Overridden style when form view in modal +.modal .modal-dialog { + .o_form_view { + .o_form_sheet_bg, .o_form_sheet { + border: none; + } + .o_form_sheet_bg > .o_form_sheet { + min-height: 0; + } + } +} + +// XXS form view specific rules +.o_form_view.o_xxs_form_view { + .o_group .o_td_label { + border: none; + } +} diff --git a/addons/web/static/src/scss/graph_view.scss b/addons/web/static/src/scss/graph_view.scss new file mode 100644 index 00000000..45e733ec --- /dev/null +++ b/addons/web/static/src/scss/graph_view.scss @@ -0,0 +1,81 @@ +.o_graph_controller { + .o_graph_renderer { + height: 100%; + .o_graph_canvas_container { + padding-top: 5px; + position: relative; + height: 100%; + width: 100%; + top: 0px; + left: 0px; + canvas { + background-color: $o-view-background-color; + } + } + } + + div.o_tooltip_legend { + background-color: white; + color:black; + font-size: 12px; + border: black solid 0.5px; + border-radius: 3px; + opacity: 1; + position: absolute; + padding:10px; + z-index: 2; + pointer-events: none; + word-break: break-all; + } + + div.o_graph_custom_tooltip { + border-radius: 3px; + background-color: white; + border: black solid 0.5px; + position: absolute; + z-index: 1; + padding: 6px; + pointer-events: none; + opacity: 0.9; + font-family: Arial; + table { + font-size: 12px; + overflow: hidden; + display: block; + border-collapse: collapse; + } + th.o_measure { + color: black; + font-weight: bolder; + vertical-align: baseline; + padding-bottom: 1px; + } + td { + span.o_square { + height: 12px; + width: 12px; + display: inline-block; + vertical-align: middle; + } + span.o_label { + text-overflow: ellipsis; + overflow: hidden; + white-space: nowrap; + color: black; + display: inline-block; + vertical-align: middle; + } + &.o_value { + color: black; + padding-left: 5px; + text-align: right; + font-weight: bold; + } + } + tr.o_show_more { + text-align: center; + color: black; + font-weight: bolder; + } + } +} diff --git a/addons/web/static/src/scss/import_bootstrap.scss b/addons/web/static/src/scss/import_bootstrap.scss new file mode 100644 index 00000000..feebf61e --- /dev/null +++ b/addons/web/static/src/scss/import_bootstrap.scss @@ -0,0 +1,53 @@ + +// This file is importing bootstrap. While a simple "import "bootstrap";" +// should be enough, this does not allow overridding mixins/functions. +// Overridding those is necessary for some of our need and allow to generate +// more efficient CSS than adding more rules. This file instead copies the +// content of the "bootstrap.scss" files but do not import functions, variables +// and mixins which will be handled "by hand" in _assets_helpers. + +// @import "functions"; +// @import "variables"; +// @import "mixins"; +@import "root"; +@import "reboot"; +@import "type"; +@import "images"; +@import "code"; +@import "grid"; +@import "tables"; +@import "forms"; + +// Small hack in bootstrap (see bootstrap_review.scss): prevent it to generate +// primary, secondary, and other color/outline classes by itself to be able to +// do it ourself properly. +$-tmp: $theme-colors; +$theme-colors: (); +@import "buttons"; +$theme-colors: $-tmp; + +@import "transitions"; +@import "dropdown"; +@import "button-group"; +@import "input-group"; +@import "custom-forms"; +@import "nav"; +@import "navbar"; +@import "card"; +@import "breadcrumb"; +@import "pagination"; +@import "badge"; +@import "jumbotron"; +@import "alert"; +@import "progress"; +@import "media"; +@import "list-group"; +@import "close"; +@import "toasts"; +@import "modal"; +@import "tooltip"; +@import "popover"; +@import "carousel"; +@import "spinners"; +@import "utilities"; +@import "print"; diff --git a/addons/web/static/src/scss/kanban_column_progressbar.scss b/addons/web/static/src/scss/kanban_column_progressbar.scss new file mode 100644 index 00000000..bfea1693 --- /dev/null +++ b/addons/web/static/src/scss/kanban_column_progressbar.scss @@ -0,0 +1,147 @@ + +@mixin o-kanban-css-filter($class, $accent-color) { + // Provide CSS reordering and highlight + &.o_kanban_group_show_#{$class} { + $mix-soft: mix($accent-color, $o-webclient-background-color, 5%); + $mix-full: mix($accent-color, $o-webclient-background-color); + + &, .o_kanban_header { + background-color: $mix-soft; + border-color: $mix-full; + } + + .progress-bar.bg-#{$class}-full { + border: 1px solid white; + } + + .oe_kanban_card_#{$class} { + order: 1; + margin-bottom: $o-kanban-inside-vgutter*0.5; + + ~ .oe_kanban_card_#{$class} { + margin-top: -$o-kanban-inside-vgutter*0.5 - 1px; + } + + ~ .o_kanban_load_more { + margin-top: -$o-kanban-inside-vgutter*0.5; + } + } + + .o_kanban_load_more { + order: 2; + padding: $o-kanban-inside-vgutter*0.5 0 $o-kanban-inside-vgutter; + } + + .o_kanban_record:not(.oe_kanban_card_#{$class}) { + order: 3; + @include o-hover-opacity(0.5); + box-shadow: none; + } + } +} + +// If columns has the progressbar, adapt the layout +.o_kanban_view .o_kanban_group.o_kanban_has_progressbar { + > .o_kanban_header .o_kanban_header_title { + height: $o-kanban-header-title-height*0.6; + margin-top: 5px; + } + + &.o_kanban_no_records { + .o_kanban_counter { + opacity: 0.3; + } + } +} + +.o_kanban_counter { + position: relative; + display: flex; + align-items: center; + transition: opacity 0.3s ease 0s; + margin-bottom: $o-kanban-record-margin*2; + + > .o_kanban_counter_progress { + width: 76%; + height: $font-size-sm; + margin-bottom: 0; + background-color: gray('300'); + box-shadow: none; + + .progress-bar { + margin-bottom: 0; + box-shadow: none; + cursor: pointer; + } + + .o_kanban_counter_label { + font-size: 10px; + user-select: none; + } + } + + > .o_kanban_counter_side { + width: 21%; + margin-left: 3%; + color: $headings-color; + text-align: right; + white-space: nowrap; + transform-origin: right center; + + &.o_kanban_grow { + animation: grow 1s ease 0s 1 normal none running; + } + + &.o_kanban_grow_huge { + animation: grow_huge 1s ease 0s 1 normal none running; + } + + // Target currency icon + > span { + margin-left: 2px; + } + } +} +.o_column_folded .o_kanban_counter { + display: none; +} + +.o_kanban_view .o_kanban_group { + @include o-kanban-css-filter(success, theme-color('success')); + @include o-kanban-css-filter(warning, theme-color('warning')); + @include o-kanban-css-filter(danger, theme-color('danger')); + @include o-kanban-css-filter(muted, theme-color('dark')); + + &.o_kanban_group_show { + display: flex; + flex-flow: column nowrap; + + > * { + flex: 0 0 auto; + } + } +} + +@keyframes grow { + 0% { + transform: scale3d(1, 1, 1); + } + 30% { + transform: scale3d(1.1, 1.1, 1.1); + } + 100% { + transform: scale3d(1, 1, 1); + } +} + +@keyframes grow_huge { + 0% { + transform: scale3d(1, 1, 1); + } + 30% { + transform: scale3d(1.3, 1.3, 1.3); + } + 100% { + transform: scale3d(1, 1, 1); + } +} diff --git a/addons/web/static/src/scss/kanban_dashboard.scss b/addons/web/static/src/scss/kanban_dashboard.scss new file mode 100644 index 00000000..bba243de --- /dev/null +++ b/addons/web/static/src/scss/kanban_dashboard.scss @@ -0,0 +1,210 @@ + +.o_kanban_view.o_kanban_dashboard { + &:not(.o_kanban_grouped) { + // correctly display the no_content_helper in dashboards + flex-flow: row wrap; + } + + .o_kanban_record { + position: relative; + display: flex; + flex-flow: column nowrap; + justify-content: space-between; + padding: $o-kanban-dashboard-vpadding $o-kanban-dashboard-hpadding; + + @include media-breakpoint-down(sm) { + margin-bottom: 10px; + } + + // ------- Generic layout adaptations ------- + .container { + width: 100%; + } + + // ------- Dropdown toggle & menu ------- + $o-kanban-manage-toggle-height: 35px; + + .o_kanban_manage_toggle_button { + @include o-kanban-dropdown($o-kanban-dashboard-hpadding); + height: $o-kanban-manage-toggle-height; + } + + .o_kanban_card_manage_pane { + @include o-kanban-dropdown-menu; + + // Arbitrary value to place the dropdown-menu exactly below the + // dropdown-toggle (height is forced so that it works on Firefox) + top: $o-kanban-manage-toggle-height; + + > div:not(.o_no_padding_kanban_colorpicker) { + padding: 3px 0 3px 20px; + visibility: visible; + margin-bottom: 5px; + } + + > .o_kanban_card_manage_section { + border-bottom: 1px solid gray('300'); + margin-bottom: 10px; + + > div { + @include o-kanban-dashboard-dropdown-link; + } + } + + // Dropdown menu with complex layout + &.container { + width: 95%; + max-width: 400px; + + .row { + display: flex; + flex-flow: row nowrap; + justify-content: space-between; + margin-left: 0; + margin-right: 0; + padding-left: $o-kanban-dashboard-dropdown-complex-gap*2; + padding-right: $o-kanban-dashboard-dropdown-complex-gap*2; + } + + div[class*="col-"] { + flex: 1 1 percentage(1/3); + padding-left: $o-kanban-dashboard-dropdown-complex-gap; + padding-right: $o-kanban-dashboard-dropdown-complex-gap; + max-width: none; + + > .o_kanban_card_manage_title { + margin: (($font-size-base * $line-height-base) / 2) 0; + color: $headings-color; + font-size: $h5-font-size; + font-weight: 500; + line-height: $headings-line-height; + } + > div:not(.o_kanban_card_manage_title) { + @include o-kanban-dashboard-dropdown-link($link-padding-gap: $o-kanban-dashboard-dropdown-complex-gap); + } + } + + .row.o_kanban_card_manage_settings { + border-top: 1px solid gray('300'); + padding-top: $o-kanban-dashboard-dropdown-complex-gap*3; + + .oe_kanban_colorpicker { + max-width: none; + padding: 0; + } + + div[class*="col-"] + div[class*="col-"] { + border-left: 1px solid gray('300'); + } + + // Default options box + div.text-right { + text-align: left; // :/ + @include o-kanban-dashboard-dropdown-link(0); + > a { + margin-left: 40px; + padding-left: 20px; + } + } + } + } + + } + + &.o_dropdown_open { + .o_kanban_card_manage_pane { + display: block; + } + .o_kanban_manage_toggle_button { + @include o-kanban-dropdown-open; + position: absolute; + } + } + + // ------- Kanban Record Titles ------- + // Uniform design across different HTML layouts + + // Provide enough room for the dropdown-toggle + .o_primary { + padding-right: $o-kanban-dashboard-hpadding*2; + } + + // Uniform titles + .o_kanban_card_header_title .o_primary, + .o_kanban_primary_left .o_primary > span:first-child, + .oe_kanban_content > .o_title > h3 { + @include o-kanban-record-title($font-size: 16px); + display: block; + } + + // Identify subtitles without classes + .o_kanban_primary_left .o_primary > span:nth-child(2) > strong { + font-weight: 500; + font-size: $font-size-sm; + color: $text-muted; + } + + // Provide enough room to add an icon before the title + &.o_has_icon .o_primary { + padding-left: $o-kanban-record-margin*1.5; + } + + // ------- Kanban Content ------- + .o_kanban_card_content { + display: inline-block; + vertical-align: top; + min-height: 80px; + } + + .o_kanban_card_header + .container.o_kanban_card_content { + flex: 1 0 auto; + display: flex; + flex-flow: column nowrap; + justify-content: space-between; + margin-top: $o-kanban-dashboard-vpadding * 2; + padding-right: 0; + padding-left: 0; + + &::before, &::after { + content: normal; // so that space-between works + } + + a { + position: relative; + @include o-text-overflow(inline-block); + } + + .o_kanban_primary_bottom { + margin-top: $o-kanban-dashboard-vpadding; + margin-bottom: -$o-kanban-dashboard-vpadding; + + &.bottom_block { + border-top: 1px solid gray('300'); + background-color: gray('200'); + padding-top: $o-kanban-dashboard-vpadding; + padding-bottom: $o-kanban-dashboard-vpadding; + } + } + } + + .o_dashboard_graph { + overflow: hidden; + } + } + + .o_favorite, .o_kanban_manage_toggle_button { + @include o-position-absolute(0, 0); + padding: $o-kanban-record-margin; + } + + .o_favorite { + top: 3px; + left: 0; + right: auto; + } + + // Emphasize records' colors when necessary + &.o_emphasize_colors .o_kanban_record::after { + width: $o-kanban-color-border-width * 2; + } +} diff --git a/addons/web/static/src/scss/kanban_examples_dialog.scss b/addons/web/static/src/scss/kanban_examples_dialog.scss new file mode 100644 index 00000000..82cb9e3c --- /dev/null +++ b/addons/web/static/src/scss/kanban_examples_dialog.scss @@ -0,0 +1,97 @@ +// container ghost card in modal +.o_kanban_examples_dialog { + display: flex; + padding: 0px; + background: theme-color('light'); + + // sidebar + .o_kanban_examples_dialog_nav { + flex: 0 0 auto; + border-right: 1px solid gray('300'); + background: white; + + > ul > li { + margin: 0; + border-bottom: 1px solid gray('300'); + + > a { + padding: 12px 15px; + color: $o-main-text-color; + border-left: 3px solid transparent; + &:focus { + outline: none; + } + } + &.active > a { + color: $headings-color; + border-left-color: $o-brand-primary; + font-weight: bold; + } + } + } + + // content + .o_kanban_examples_dialog_content { + flex: 1 1 100%; + min-height: 300px; + + .o_kanban_examples_description { + padding: 16px 16px 0; + text-align: justify; + } + } +} + +// container ghost card in background +.o_kanban_example_background_container { + display: flex; + padding: 0px; + opacity: 0.4; + + // content + .o_kanban_example_background { + flex: 1 1 100%; + + .o_kanban_examples { + .o_kanban_examples_group { + .o_kanban_examples_ghost { + width: 224px; + margin-bottom: 10px; + } + } + } + } +} + + +// kanban ghost card +.o_kanban_examples { + display: flex; + padding: 0 6px; + + .o_kanban_examples_group { + flex: 1 1 100%; + margin: 10px; + + .o_kanban_examples_ghost { + padding: 5px; + margin-top: -1px; + border: thin solid gray('300'); + background-color: white; + .o_ghost_content { + height: 12px; + background: gray('300'); + &.o_ghost_tag { + display: inline-block; + width: 40%; + margin-top: 10px; + } + } + .o_ghost_avatar { + height: 20px; + width: 20px; + margin-top: 6px; + } + } + } +} diff --git a/addons/web/static/src/scss/kanban_view.scss b/addons/web/static/src/scss/kanban_view.scss new file mode 100644 index 00000000..ee7f3f2a --- /dev/null +++ b/addons/web/static/src/scss/kanban_view.scss @@ -0,0 +1,648 @@ +// ------- Kanban View Layout ------- +.o_kanban_view { + display: flex; + align-content: stretch; + overflow-x: visible; + + @include media-breakpoint-down(sm) { + padding: 0px!important; + } + + .o_kanban_record, .o_kanban_quick_create { + padding: $o-kanban-inside-vgutter $o-kanban-inside-hgutter; + border: 1px solid gray('300'); + background-color: white; + + @include media-breakpoint-down(sm) { + padding: $o-kanban-inside-vgutter $o-kanban-inside-hgutter-mobile; + } + &:focus, &:focus-within { + z-index: 1; // show the shadow on top of the previous & next cards in grouped mode + outline: none; + } + } + + .o_kanban_quick_create { + box-shadow: 0 0 20px -10px; + margin: 0 $o-kanban-record-margin -1px $o-kanban-record-margin; + margin-bottom: 8px; + + .o_form_view { + padding: 0; + .o_group.o_inner_group { + margin: 0; + } + } + + &.o_disabled { + pointer-events: none; + opacity: 0.7; + } + + &:focus, &:focus-within { + -webkit-box-shadow: 1px 1px 1px 0px gray('600'); + -moz-box-shadow: 1px 1px 1px 0px gray('600'); + box-shadow: 1px 1px 1px 0px gray('600'); + } + } + + .o_kanban_record { + position: relative; + display: inline-block; // Force the creation of a block formatting context to clear float items + border-color: gray('400'); + min-width: 150px; + margin: 0 0 -1px; + + // ------- Kanban Record, v11 Layout ------- + // Records colours + &::after { + content: ""; + @include o-position-absolute(0, auto, 0, -1px); + width: $o-kanban-color-border-width; + } + + .o_kanban_record_details { + display: flex; + flex-direction: column; + justify-content: space-between; + width: 100%, + } + + // Inner Sections + .o_kanban_record_top, .o_kanban_record_body { + margin-bottom: $o-kanban-inner-hmargin; + } + + .o_kanban_record_top, .o_kanban_record_bottom { + display: flex; + } + + .o_kanban_record_top { + align-items: flex-start; + + .o_dropdown_kanban { + // For v11 layout, reset positioning to default to properly use + // flex-box + position: relative; + top: auto; + right: auto; + } + + .o_kanban_record_headings { + line-height: 1.2; + flex: 1 1 auto; + // Ensure long word doesn't break out of container + word-wrap: break-word; + overflow: hidden; + } + } + + .o_kanban_record_title { + @include o-kanban-record-title($font-size: 13px); + } + + .o_kanban_record_subtitle { + display: block; + margin-top: $o-kanban-inner-hmargin*0.5; + } + + .o_kanban_record_bottom { + .oe_kanban_bottom_left, .oe_kanban_bottom_right { + display: flex; + align-items: center; + min-height: 20px; + } + .oe_kanban_bottom_left { + flex: 1 1 auto; + + > * { + margin-right: 6px; + line-height: 1; + } + + .o_priority_star { + margin-top: 1px; + font-size: 18px; + } + } + .oe_kanban_bottom_right{ + flex: 0 1 auto; + + .oe_kanban_avatar { + border-radius: 50%; + object-fit: cover; + } + .oe_kanban_avatar, .o_field_many2one_avatar > .o_m2o_avatar { + width: 20px; + height: 20px; + margin-left: 6px; + } + } + .o_link_muted { + color: $body-color; + &:hover { + color: theme-color('primary'); + text-decoration: underline; + } + } + } + + // ---------- Kanban Record, fill image design ---------- + // Records with images that compensate record's padding + // filling all the available space (eg. hr, partners.. ) + &.o_kanban_record_has_image_fill { + display: flex; + + .o_kanban_image_fill_left { + position: relative; + margin-right: $o-kanban-inside-hgutter*2; + @include media-breakpoint-up(sm) { + margin: { + top: $o-kanban-inside-vgutter*-1; + bottom: $o-kanban-inside-vgutter*-1; + left: $o-kanban-inside-hgutter*-1; + } + } + flex: 1 0 $o-kanban-image-fill-width; + min-height: 95px; + background: { + size: cover; + position: center; + repeat: no-repeat; + }; + + &.o_kanban_image_full { + background-size: contain; + } + } + + // Adapt default 'o_kanban_image' element if present. + // This adaptation allow to use both images type. + // Eg. In partners list we use to fill user picture only, keeping the + // default design for company logos. + .o_kanban_image { + margin-right: $o-kanban-inside-hgutter*2; + flex: 0 0 $o-kanban-image-width; + min-height: $o-kanban-image-width; + align-self: center; + background: { + size: cover; + repeat: no-repeat; + position: center; + } + + // On medium screen size, align 'o_kanban_image' to 'o_kanban_image_fill_left' elements. + @include media-breakpoint-up(md) { + $fill-notfill-gap: ($o-kanban-image-fill-width - $o-kanban-inside-hgutter*2) - $o-kanban-image-width; + margin: { left: $fill-notfill-gap*0.5; right: $fill-notfill-gap*2;} + } + + @include media-breakpoint-down(sm) { + flex-basis: $o-kanban-image-fill-width; + min-height: $o-kanban-image-fill-width; + } + + // Reset immedialy after div padding + + div { + padding-left: 0 + } + } + + // Images (backgrounds) could accomodate another image inside. + // (eg. Company logo badge inside a contact picture) + .o_kanban_image_fill_left, .o_kanban_image { + .o_kanban_image_inner_pic { + @include o-position-absolute($right: 0, $bottom:0); + max: { height: 25px; width: 80%;} + background: white; + box-shadow: -1px -1px 0 1px white; + } + } + } + } + + // ------- Compatibility of old (<= v10) Generic layouts ------- + + // Kanban Records - Uniform Design + // Provide a basic style for different kanban record layouts + // --------------------------------------------------------- + .oe_kanban_card, .o_kanban_record { + // ------- v11 Layout + generic layouts (~v10) ------- + // Kanban Record - Dropdown + .o_dropdown_kanban { + @include o-position-absolute($o-kanban-inside-vgutter, $o-kanban-inside-hgutter); + visibility: hidden; + margin: (-$o-kanban-inside-vgutter) (-$o-kanban-inside-hgutter) 0 0; + @include media-breakpoint-down(sm) { + visibility: visible; + } + + .dropdown-toggle { + @include o-kanban-dropdown; + } + &.show .dropdown-toggle { + @include o-kanban-dropdown-open; + } + .dropdown-menu { + @include o-kanban-dropdown-menu; + right: (-$o-kanban-inside-hgutter - 1); + min-width: 11rem; + } + } + + &:hover .o_dropdown_kanban, .o_dropdown_kanban.show { + visibility: visible; + } + + // Kanban Record - Dropdown colorpicker + .oe_kanban_colorpicker { + @include o-kanban-colorpicker; + } + + // Kanban Record - Inner elements + .o_field_many2manytags, .o_kanban_tags { + display: block; + margin-bottom: $o-kanban-inner-hmargin; + line-height: 1.2; + word-break: break-all; + + .o_tag { + display: inline-block; + margin-right: 4px; + font-size: 11px; + font-weight: 500; + background-color: transparent; + color: inherit; + box-shadow: none; + @include o-kanban-tag-color; + + span { + display: inline-block; + width: 6px; + height: 6px; + margin-right: 4px; + border-radius: 100%; + } + } + } + + .o_field_many2one_avatar { + img.o_m2o_avatar { + margin-right: 0; + } + } + + // Commonly used to place an image beside the text + // (e.g. Fleet, Employees, ...) + .o_kanban_image { + position: relative; + text-align: center; + + img { + max-width: 100%; + } + } + + .o_kanban_button { + margin-top: 15px; + + > button, > a { + @include o-position-absolute($right: $o-kanban-record-margin, $bottom: $o-kanban-record-margin); + @include media-breakpoint-down(sm) { + right: $o-kanban-inside-hgutter-mobile; + } + } + } + + // Kanban Record - Utility classes + &.oe_kanban_global_click, &.oe_kanban_global_click_edit { + cursor: pointer; + &:focus, &:focus-within { + -webkit-box-shadow: 1px 1px 1px 0px gray('600'); + -moz-box-shadow: 1px 1px 1px 0px gray('600'); + box-shadow: 1px 1px 1px 0px gray('600'); + } + } + + &.ui-sortable-helper { + transform: rotate(-3deg); + box-shadow: 0 5px 25px -10px black; + transition: transform 0.6s, box-shadow 0.3s; + } + + .o_attachment_image > img { + width: 100%; + height: auto; + } + + .o_progressbar { + display: flex; + height: $o-kanban-progressbar-height; + + .o_progressbar_title { + flex: 0 0 auto; + } + .o_progress { + flex: 1 1 auto; + margin-top: 3px; + } + .o_progressbar_value { + flex: 0 0 auto; + width: auto; + height: 100%; + text-align: right; + } + input.o_progressbar_value { + width: 15%; + margin-left: 8px; + } + } + + .o_kanban_image { + float: left; + width: $o-kanban-image-width; + + + div { + padding-left: $o-kanban-image-width + $o-kanban-inside-hgutter; + @include media-breakpoint-down(sm) { + padding-left: $o-kanban-image-width + $o-kanban-inside-hgutter-mobile; + } + } + } + + .oe_kanban_details { + width: 100%; + + // Useful for the class 'o_text_overflow' + min-width: 0; + + ul { + margin-bottom: $o-kanban-inner-hmargin*0.5;; + padding-left: 0; + list-style: none; + font-size: $font-size-sm; + + li { + margin-bottom: 2px; + } + } + } + + .o_kanban_footer { + display: flex; + justify-content: space-between; + align-items: center; + > * { + flex: 0 0 auto; + } + } + + .oe_kanban_text_red { + color: #A61300; + font-weight: bold; + } + + .o_text_bold { + font-weight: bold; + } + + .o_text_block { + display: block; + } + } + + // Kanban Grouped Layout + &.o_kanban_grouped { + min-height: 100%; + padding: 0; + background-color: desaturate($gray-100, 100%); + + .o_kanban_record, .o_kanban_quick_create { + width: 100%; + margin-left: 0; + margin-right: 0; + } + + &.o_kanban_small_column .o_kanban_group:not(.o_column_folded) { + width: $o-kanban-small-record-width + 2*$o-kanban-group-padding; + } + } + + // Kanban Grouped Layout - Column default + .o_kanban_group { + flex: 0 0 auto; + padding: 0 $o-kanban-group-padding $o-kanban-group-padding $o-kanban-group-padding; + + .o_kanban_header > .o_kanban_header_title { + @include o-kanban-header-title; + + .o_column_title { + flex: 1 1 auto; + @include o-text-overflow; + font-size: 16px; + font-weight: 500; + } + + .o_column_unfold { + display: none; + } + + .o_kanban_quick_add i { + @include o-kanban-icon; + margin-left: $o-kanban-inside-hgutter*2; + } + + &:hover .o_kanban_config, .o_kanban_config.show { + visibility: visible; + } + + .o_kanban_config { + visibility: hidden; + + > .dropdown-menu { + cursor: default; + } + i { + @include o-kanban-icon; + } + } + } + + .o_kanban_load_more { + padding: $o-kanban-record-margin 0; + box-shadow: inset 0 10px 13px -13px black; + text-align: center; + } + + &.ui-sortable-helper { + box-shadow: 0 0 30px -10px black; + } + + &:not(.o_column_folded) { + width: $o-kanban-default-record-width + 2*$o-kanban-group-padding; + } + + &.o_kanban_dragged { + background-color: $o-main-text-color; + + .o_kanban_record { + visibility: hidden; + } + } + } + + &.ui-sortable .o_kanban_header_title .o_column_title { + cursor: move; + } + + // Kanban Grouped Layout - Column Folded + .o_kanban_group.o_column_folded { + @include o-kanban-slim-col; + background-color: #f0f0f0; + background-color: desaturate($gray-200, 100%); + + & + .o_kanban_group.o_column_folded { + margin-left: 1px; + } + + .o_kanban_header_title { + position: relative; + opacity: 0.5; + + .o_column_title { + @include o-kanban-v-title; + } + .o_column_unfold { + @include o-kanban-icon(1); + } + } + + > .o_kanban_record, .o_kanban_quick_add, .o_kanban_config, .o_kanban_load_more { + display: none!important; + } + + &:hover, &.o_kanban_hover { + .o_kanban_header_title { + opacity: 1; + } + } + } + + // Kanban Grouped Layout - "Create new column" column + .o_column_quick_create { + .o_quick_create_folded { + cursor: pointer; + padding: 12px 16px; + white-space: nowrap; + color: $text-muted; + opacity: 0.6; + &:hover { + opacity: 1; + } + + .o_kanban_add_column { + display: inline-block; + padding: 10px 14px; + background-color: gray('300'); + } + .o_kanban_title { + margin-left: 10px; + } + } + + .o_quick_create_unfolded { + padding: $o-kanban-inside-vgutter $o-kanban-inside-hgutter; + width: $o-kanban-small-record-width; + height: 100%; + border: none; + box-shadow: 0 0 20px -10px; + background-color: theme-color('light'); + + .o_kanban_header { + height: 50px; + + .o_kanban_examples { + cursor: pointer; + } + + input, input:focus, input:hover { + font-size: 16px; + background: transparent; + } + } + + .o_kanban_muted_record { + background: gray('300'); + height: 70px; + margin: 10px 0px; + } + + } + } + + // Kanban UN-grouped Layout + &.o_kanban_ungrouped { + min-height: 100%; + align-content: flex-start; + flex-flow: row wrap; + justify-content: flex-start; + + padding: $o-kanban-record-margin ($o-horizontal-padding - $o-kanban-record-margin); + + .o_kanban_record { + flex: 1 1 auto; + width: $o-kanban-default-record-width; + margin: ($o-kanban-record-margin * 0.5) $o-kanban-record-margin; + + @include media-breakpoint-down(sm) { + margin: 0 0 -1px 0; + flex: 1 0 100%; + } + + &.o_kanban_ghost { + height: 0; + min-height: 0!important; // to prevent view writers to override this height + visibility: hidden; + margin-top: 0; + margin-bottom: 0; + padding: 0; + } + } + } + + &.o_kanban_mobile .o_kanban_record { + div.label { + @include o-text-overflow; + } + } + + // Records colours + @include o-kanban-record-color; + + .oe_kanban_color_help { + @include o-position-absolute(0, auto, 0, -1px); + width: $o-kanban-color-border-width; + z-index: 1; // show the title over kanban color + } +} + +// ----------------- Set Cover Dialog ----------------- +.modal .o_kanban_cover_container .o_kanban_cover_image { + display: inline-block; + position: relative; + height: 120px; + width: 120px; + margin: 10px; + cursor: pointer; + outline: 2px solid transparent; + box-shadow: 0px 0px 1px 1px #ccc; + &.o_selected { + outline-color: $o-brand-primary; + } + > img { + @include o-position-absolute(0, 0, 0, 0); + margin: auto; + max-height: 100%; + max-width: 100%; + } +} diff --git a/addons/web/static/src/scss/keyboard.scss b/addons/web/static/src/scss/keyboard.scss new file mode 100644 index 00000000..563179a6 --- /dev/null +++ b/addons/web/static/src/scss/keyboard.scss @@ -0,0 +1,16 @@ +.o_shortcut_table { + border-collapse: separate; + border-spacing: 0 1em; + + .o_key { + background-color: #F4F7F8; + border-radius: 3px; + border: 1px solid #B4B4B4; + $shadowvalue: 0px 1px 1px rgba(0,0,0,0.2), inset 0px -1px 1px 1px rgba(230,230,230,0.8), inset 0px 2px 0px 0px rgba(255,255,255,0.8); + box-shadow: $shadowvalue; + display: inline-block; + font-family: Consolas,"Liberation Mono",Courier,monospace; + font-size: 0.85em; + padding: 2px 4px; + } +} diff --git a/addons/web/static/src/scss/layout_background.scss b/addons/web/static/src/scss/layout_background.scss new file mode 100644 index 00000000..0fea6d0a --- /dev/null +++ b/addons/web/static/src/scss/layout_background.scss @@ -0,0 +1,99 @@ + +.o_background_footer, .o_background_header, .o_report_layout_background { + color: gray('700'); +} +.o_background_header { + min-width: 900px; + border-bottom: 1px solid gray('200'); + img { + max-height: 96px; + max-width: 200px; + margin-right: 16px; + } + h3 { + color: $o-default-report-primary-color; + font-weight: 700; + font-size: 1.25rem; + max-width: 300px; + } +} +.o_background_footer { + .list-inline-item { + white-space: nowrap; + } + ul { + border-top: 1px solid $o-default-report-secondary-color; + border-bottom: 1px solid $o-default-report-secondary-color; + padding: 4px 0; + margin: 0 0 4px 0; + li { + color: $o-default-report-secondary-color; + } + } +} +.o_report_layout_background { + background-image: url(/base/static/img/bg_background_template.jpg); + background-size: cover; + background-position: bottom center; + background-repeat: no-repeat; + min-height: 620px; + + strong { + color: $o-default-report-secondary-color; + } + .table { + border-top: 1px solid gray('300'); + } + + .table td, .table th { + border-top: none; + } + h2 { + color: $o-default-report-primary-color; + } + thead tr th { + color: $o-default-report-secondary-color + } + tbody { + color: gray('700'); + tr { + &:nth-child(odd) { + background-color: rgba(220, 205, 216, 0.2); + } + &.o_line_section { + color: $o-brand-odoo; + background-color: rgba(73, 80, 87, 0.2) !important; + } + } + } + /*Total table*/ + /* row div rule compat 12.0 */ + .row > div > table, + div#total table { + tr { + &:nth-child(odd) { + background-color: transparent !important; + } + /* first-child & last-child rule compat 12.0 */ + &:first-child, + &:last-child, + &.o_subtotal, + &.o_total { + border-top: none !important; + td { + border-top: 1px solid gray('400') !important; + } + strong { + color: $o-default-report-primary-color; + } + } + } + } +} + +/* special case for displaying report in iframe */ +.o_in_iframe { + .o_background_header { + min-width: 0; + } +} diff --git a/addons/web/static/src/scss/layout_boxed.scss b/addons/web/static/src/scss/layout_boxed.scss new file mode 100644 index 00000000..4055994d --- /dev/null +++ b/addons/web/static/src/scss/layout_boxed.scss @@ -0,0 +1,108 @@ +.o_boxed_footer, .o_boxed_header, .o_report_layout_boxed { + color: gray('700'); + font-size: 15px; +} +.o_boxed_header { + border-bottom: 1px solid gray('200'); + img { + max-height: 100px; + } + h4 { + color: #999999; + font-weight: 700; + text-transform: uppercase; + } +} +.o_boxed_footer { + margin-top: 200px; + white-space: nowrap; + border-top: 3px solid $o-default-report-secondary-color; + ul { + margin: 4px 0; + } +} +.o_report_layout_boxed { + #total strong { + color: $o-default-report-primary-color; + } + #informations strong { + color: $o-default-report-secondary-color; + } + > h2 { + text-transform: uppercase; + } + h2 span { + color: $o-default-report-primary-color; + } + table { + border: 1px solid gray('700'); + thead { + border-bottom: 2px solid gray('700'); + tr th { + text-transform: uppercase; + border: 1px solid gray('700'); + color: $o-default-report-secondary-color; + } + } + tbody { + color: gray('700'); + tr { + td { + // remove border-top from standard layout + border-top: none; + border-right: 1px solid gray('700'); + } + &.o_line_section td, + &.o_line_note td, + &.is-subtotal td { + border-top: 1px solid gray('700'); + border-bottom: 1px solid gray('700'); + } + &.o_line_section td { + background-color: rgba($o-default-report-primary-color, 0.7); + color: #fff; + } + &.is-subtotal, + td.o_price_total { + background-color: rgba($o-default-report-secondary-color, 0.1); + } + } + } + } + /* compat 12.0 */ + .page > table:not(.o_main_table) tr td:last-child { + background-color: gray('200'); + } + /* compat 12.0 */ + .row:not(#total) > div > table tbody tr:not(:last-child) td:last-child { + background-color: gray('200'); + } + /*Total table*/ + /* row div rule compat 12.0 */ + .row > div > table, + div#total table { + thead tr:first-child, + tr.o_subtotal { + border-bottom: 1px solid gray('700'); + } + tr { + &.o_subtotal{ + td:first-child { + border-right: none; + } + } + &:last-child td, + &.o_total td { + background-color: rgba($o-default-report-primary-color, 0.9); + color: #fff; + + &:first-child { + border-right: none; + } + } + &.o_total strong { + color: white; + } + } + } +} diff --git a/addons/web/static/src/scss/layout_clean.scss b/addons/web/static/src/scss/layout_clean.scss new file mode 100644 index 00000000..5d559726 --- /dev/null +++ b/addons/web/static/src/scss/layout_clean.scss @@ -0,0 +1,88 @@ +.o_clean_footer, .o_clean_header, .o_report_layout_clean { + color: #000; +} +.o_clean_header img { + max-height: 90px; + max-width: 300px; +} +.o_clean_footer { + margin: 0 3px; + margin-top: 200px; + border-top: 3px solid $o-default-report-secondary-color; + h4 { + color: $o-default-report-secondary-color; + font-weight: bolder; + } + .pagenumber { + border: 3px solid $o-default-report-primary-color; + background-color: $o-default-report-secondary-color; + color: white; + padding: 4px 8px; + text-align: center; + } +} +.o_report_layout_clean { + h1, h2, h3 { + color: $o-default-report-primary-color; + font-weight: bolder; + } + strong { + color: $o-default-report-secondary-color; + } + table { + &.o_main_table { + margin-bottom: 0; + } + + thead { + color: $o-default-report-secondary-color; + tr th { + border-top: 3px solid $o-default-report-secondary-color !important; + text-transform: uppercase; + } + tr th:first-child { + width: 48%; + } + } + tbody { + color: #000; + tr:first-child td { + border-top: none; + } + tr:last-child td { + border-bottom: 3px solid $o-default-report-secondary-color; + } + tr { + td { + padding: 15px 5px; + } + td:last-child { + } + } + } + } + #total { + strong { + color: $o-default-report-secondary-color; + } + } + /*Total table*/ + /* compat 12.0 */ + .row:not(#total) > div:has(table) { + top: -16px; + } + /* row col rule compat 12.0 */ + .row > div > table, + div#total table { + tr { + &:first-child td, + &.o_subtotal { + border-top: none !important; + } + &:last-child td, + &.o_total { + border-top: 1px solid gray('200') !important; + } + } + } +} diff --git a/addons/web/static/src/scss/layout_standard.scss b/addons/web/static/src/scss/layout_standard.scss new file mode 100644 index 00000000..acd52b32 --- /dev/null +++ b/addons/web/static/src/scss/layout_standard.scss @@ -0,0 +1,25 @@ +.o_standard_footer { + margin-top: 200px; + .list-inline-item { + white-space: nowrap; + } +} + +.o_report_layout_standard { + h2 { + color: $o-default-report-primary-color; + } + + #informations strong { + color: $o-default-report-secondary-color; + } + + #total strong{ + color: $o-default-report-primary-color; + } + table { + thead { + color: $o-default-report-secondary-color; + } + } +} diff --git a/addons/web/static/src/scss/lazyloader.scss b/addons/web/static/src/scss/lazyloader.scss new file mode 100644 index 00000000..fb3f39f4 --- /dev/null +++ b/addons/web/static/src/scss/lazyloader.scss @@ -0,0 +1,8 @@ + +// Disable buttons and links marked with the o_wait_lazy_js class or in a +// section which is marked with the o_wait_lazy_js class. +a[href], button, input[type="submit"], input[type="button"], .btn { + &.o_wait_lazy_js, .o_wait_lazy_js & { + pointer-events: none; + } +} diff --git a/addons/web/static/src/scss/list_view.scss b/addons/web/static/src/scss/list_view.scss new file mode 100644 index 00000000..8f57209e --- /dev/null +++ b/addons/web/static/src/scss/list_view.scss @@ -0,0 +1,392 @@ +.o_list_view { + position: relative; + height: 100%; + + .o_list_table { + position: relative; + cursor: pointer; + background-color: $o-view-background-color; + margin-bottom: 0px; + + // Don't understand why but without those, the list view appears broken + // in both form views and standalone list views. + border-collapse: initial; + border-spacing: 0; + + thead { + color: $o-main-text-color; + border-bottom: 1px solid #cacaca; + > tr > th:not(.o_list_record_selector) { + border-left: 1px solid #dfdfdf; + @include o-text-overflow(table-cell); + &:not(.o_column_sortable):hover { + cursor: default; + } + &.o_list_number_th { + text-align: right; + } + } + } + tbody > tr > td:not(.o_list_record_selector) { + &:not(.o_handle_cell):not(.o_list_button) { + @include o-text-overflow(table-cell); + &.o_list_text { + white-space: pre-wrap; + } + } + + &.o_list_number { + text-align: right; + } + &.o_list_text { + word-break: break-word; + } + &.o_list_char.o_color_cell .o_field_color { + width: 17px; + height: 17px; + } + &.o_list_button { + white-space: nowrap; + > button { + padding: 0 5px; + &:not(:last-child) { + margin-right: 3px; + } + } + } + } + &.o_list_computing_widths { + > tbody > tr > td { + &.o_list_text, // text field, className on the td + > .o_field_text { // text field with widget, className inside the td + white-space: pre !important; + } + } + } + + tfoot { + cursor: default; + color: $o-list-footer-color; + background-color: $o-list-footer-bg-color; + font-weight: $o-list-footer-font-weight; + border-top: 2px solid #cacaca; + border-bottom: 1px solid #cacaca; + + > tr > td { + @include o-text-overflow(table-cell); + } + .o_list_number { + text-align: right; + } + } + + .o_column_sortable:not(.o_handle_cell) { + position: relative; + user-select: none; // Prevent unwanted selection while sorting + + &::after { + font-family: FontAwesome; + content: "\f0d7"; + opacity: 0; + } + &:not(:empty)::after { + margin-left: 6px; + } + &.o-sort-up { + cursor: n-resize; + &::after { + content: "\f0d7"; + } + } + &.o-sort-down { + cursor: s-resize; + &::after { + content: "\f0d8"; + } + } + &:hover::after { + opacity: 0.3; + } + &.o-sort-up, &.o-sort-down { + color: $headings-color; + &::after { + opacity: 1; + } + } + } + + .o_list_record_selector { + width: 40px; + padding-left: $o-horizontal-padding; + .custom-control-label { + cursor: pointer; + } + } + .o_list_record_remove, .o_handle_cell { + width: 1px; // to prevent the column to expand + } + + .o_list_record_remove button { + padding: 0px; + background: none; + border-style: none; + display: table-cell; + cursor: pointer; + } + + // Contextual classes + @each $color, $value in $theme-colors { + $safe: saturate(theme-color-level($color, 2), 10%); + @include text-emphasis-variant(".text-#{$color}", $safe); + } + + // Grouped list views + tbody > tr.o_group_header { + > th, > td { + vertical-align: middle; + padding-top: 5px; + padding-bottom: 5px; + } + .o_group_name { + @include o-text-overflow(table-cell); + } + .o_group_buttons { + display: inline-block; + margin-left: 10px; + + > button { + @include o-hover-text-color($o-brand-primary, darken($o-brand-primary, 20%)); + background-color: transparent; + border: none; + padding-top: 0; + padding-bottom: 0; + } + } + .o_pager { + cursor: text; + user-select: none; + text-align: right; + padding-top: 0 !important; + padding-bottom: 0 !important; + margin-top: -2px; + margin-bottom: -2px; + + .o_pager_previous, .o_pager_next { + max-height: 30px; + padding: 0 5px; + background-color: $o-list-group-header-color; + &:hover { + background-color: darken($o-list-group-header-color, 10%); + } + } + } + } + tbody + tbody { + border-top: none; // Override bootstrap for grouped list views + } + + &, &.table-striped { + tr:focus-within { + background-color: lighten($o-form-lightsecondary, 10%); + } + } + thead th:focus-within, + tbody.o_keyboard_navigation td:focus-within { + background-color: $o-form-lightsecondary; + outline: none; + } + + .o_data_cell.o_text_overflow, + .o_data_cell.o_invisible_modifier { + // Override display: none to keep table cells aligned. Note: cannot use + // 'initial' as it will force them to 'inline', not 'table-cell'. + display: table-cell!important; + } + + &.o_resizing tr > :not(.o_column_resizing) { + opacity: 0.5; + } + + &.o_empty_list { + table-layout: fixed; + } + + th { + position: relative; + } + + span.o_resize { + bottom: 0; + cursor: col-resize; + position: absolute; + right: 0; + top: 0; + width: 10px; + z-index: 1; // must be over the sort caret (::after pseudo-element) + } + + .o_list_record_remove_header { + width: 32px; + } + + .o_data_row:not(.o_selected_row) .o_data_cell { + + // Boolean fields in non-selected rows completely disabled. + .custom-checkbox { + pointer-events: none; + } + + // These widgets enable the checkbox if there is no readonly modifier. + &.o_boolean_button_cell, + &.o_boolean_favorite_cell, + &.o_boolean_toggle_cell, + &.o_toggle_button_cell { + + &:not(.o_readonly_modifier) .custom-checkbox { + pointer-events: auto; + } + } + } + + .o_data_row:not(.o_selected_row) { + .o_list_many2one, + .o_list_char, + .o_list_number { + white-space: nowrap; + .o_field_widget:not(.o_row_handle):not(.o_field_badge) { + display: inline; + } + } + } + + .o_data_row.o_selected_row > .o_data_cell:not(.o_readonly_modifier):not(.o_invisible_modifier) { + position: relative; // for o_field_translate + &:not(.o_handle_cell) { + background-color: white; + } + .o_input { + border: none; + padding: 0; + } + > .o_field_text { + vertical-align: top; + } + > .o_field_widget { + width: 100%; + > .o_external_button { + padding: 0; + border: none; + background-color: inherit; + margin-left: 5px; + font-size: 12px; + } + } + > .o_field_monetary input { + width: 0; // override width: 100px and let flex makes this input grow + margin: 0 4px; + } + > .o_field_many2manytags > .o_field_many2one { + // reduce basis to prevent many2one input from going to the next line to often + flex-basis: 40px; + } + .o_input_dropdown, .o_datepicker { + > .o_input { + padding-right: 15px; // to prevent caret overlapping + } + > .o_dropdown_button, .o_datepicker_button { + margin-right: 5px; + @include o-position-absolute(0, 0); + } + } + > input.o_field_translate, textarea.o_field_translate { + padding-right: 25px; + + span.o_field_translate { + margin-left: -35px; + padding: 0px 1px; + text-align: right; + width: 35px; + vertical-align: top; + font-size: 12px; + } + } + > .o_row_handle { + visibility: hidden; // hide sequence when editing + } + > .o_field_badge { + width: auto; // override width: 100% to keep the optimal badge width + } + } + + .o_field_x2many_list_row_add, .o_group_field_row_add { + a:focus, a:active { + color: $link-hover-color; + outline: none; + } + } + } + + // Optional fields + &.o_list_optional_columns { + th:last-child { + padding-right: 15px; + } + } + + .o_optional_columns_dropdown_toggle,.o_optional_columns { + @include o-position-absolute($top: 0, $right: 0); + } + + .o_optional_columns_dropdown_toggle { + cursor: pointer; + padding: 0 5px; + text-align: center; + line-height: 30px; + z-index: 1; // must be over the resize handle + } + + .o_optional_columns { + .o_optional_columns_dropdown { + margin-top: 30px; + user-select: none; + .dropdown-item { + label { + padding-left: 10px; + } + } + } + } +} + +.o_view_controller > .o_content > .o_list_view > .table-responsive { + // in main list views, let .o_content scroll + overflow: visible; +} + +// Buttons in ControlPanel +.o_list_buttons { + .o_list_button_save, .o_list_button_discard { + display: none; + } + &.o-editing { // for list view editable + .o_list_button_add { + display: none + } + .o_list_button_save, .o_list_button_discard { + display: inline-block + } + } +} + +// Selection box (next to the buttons, in ControlPanel) +$selected-box-color: #e7f2f6; +.o_list_selection_box { + display: inline-block; + padding: $btn-padding-y $btn-padding-x; + vertical-align: middle; // align with the buttons + border: 1px solid $selected-box-color; + background-color: $selected-box-color; + .o_list_select_domain { + font-weight: bold; + margin-left: 4px; + } +} diff --git a/addons/web/static/src/scss/list_view_extra.scss b/addons/web/static/src/scss/list_view_extra.scss new file mode 100644 index 00000000..2806940c --- /dev/null +++ b/addons/web/static/src/scss/list_view_extra.scss @@ -0,0 +1,42 @@ + +.o_list_view { + thead { + background-color: #eee; + } + + tbody > tr.o_group_header { + background-image: linear-gradient(to bottom, #fcfcfc, #dedede); + + &:focus-within { + background-image: linear-gradient(to bottom, #fcfcfc, $o-form-lightsecondary); + } + } + + .o_list_table .o_data_row.o_selected_row > .o_data_cell:not(.o_readonly_modifier):not(.o_invisible_modifier):not(.o_remaining_days_cell) { + padding: .15rem; + .o_input { + border: none; + padding: .15rem; + .o_input { + box-shadow: none; + padding: 0; + } + } + select.o_input { + padding: .25rem 0; + } + .o_field_boolean { + padding-top: .15rem; + padding-bottom: .15rem; + } + &.o_invalid_cell .o_input { + box-shadow: 0 0 0 1px theme-color('danger'); + } + &.o_list_button { + padding: .25rem; + } + &.o_color_cell > .o_field_widget { + padding: .15rem; + } + } +} diff --git a/addons/web/static/src/scss/mimetypes.scss b/addons/web/static/src/scss/mimetypes.scss new file mode 100644 index 00000000..678116e1 --- /dev/null +++ b/addons/web/static/src/scss/mimetypes.scss @@ -0,0 +1,73 @@ +.o_image { + display: inline-block; + width: 38px; + height: 38px; + background-image: url('/web/static/src/img/mimetypes/unknown.svg'); + background-size: contain; + background-repeat: no-repeat; + background-position: center; + + &[data-mimetype^='image'] { + background-image: url('/web/static/src/img/mimetypes/image.svg'); + } + &[data-mimetype^='audio'] { + background-image: url('/web/static/src/img/mimetypes/audio.svg'); + } + &[data-mimetype^='text'], &[data-mimetype$='rtf'] { + background-image: url('/web/static/src/img/mimetypes/text.svg'); + } + &[data-mimetype*='octet-stream'], &[data-mimetype*='download'], &[data-mimetype*='python'] { + background-image: url('/web/static/src/img/mimetypes/binary.svg'); + } + &[data-mimetype^='video'], &[title$='.mp4'], &[title$='.avi'] { + background-image: url('/web/static/src/img/mimetypes/video.svg'); + } + &[data-mimetype$='archive'], &[data-mimetype$='compressed'], &[data-mimetype*='zip'], &[data-mimetype$='tar'], &[data-mimetype*='package'] { + background-image: url('/web/static/src/img/mimetypes/archive.svg'); + } + &[data-mimetype='application/pdf'] { + background-image: url('/web/static/src/img/mimetypes/pdf.svg'); + } + &[data-mimetype^='text-master'], &[data-mimetype*='document'], &[data-mimetype*='msword'], &[data-mimetype*='wordprocessing'] { + background-image: url('/web/static/src/img/mimetypes/document.svg'); + } + &[data-mimetype*='application/xml'], &[data-mimetype$='html'] { + background-image: url('/web/static/src/img/mimetypes/web_code.svg'); + } + &[data-mimetype$='css'], &[data-mimetype$='less'], &[data-ext$='less'] { + background-image: url('/web/static/src/img/mimetypes/web_style.svg'); + } + &[data-mimetype*='-image'], &[data-mimetype*='diskimage'], &[data-ext$='dmg'] { + background-image: url('/web/static/src/img/mimetypes/disk.svg'); + } + &[data-mimetype$='csv'], &[data-mimetype*='vc'], &[data-mimetype*='excel'], &[data-mimetype$='numbers'], &[data-mimetype$='calc'], &[data-mimetype*='mods'], &[data-mimetype*='spreadsheet'] { + background-image: url('/web/static/src/img/mimetypes/spreadsheet.svg'); + } + &[data-mimetype^='key'] { + background-image: url('/web/static/src/img/mimetypes/certificate.svg'); + } + &[data-mimetype*='presentation'], &[data-mimetype*='keynote'], &[data-mimetype*='teacher'], &[data-mimetype*='slideshow'], &[data-mimetype*='powerpoint'] { + background-image: url('/web/static/src/img/mimetypes/presentation.svg'); + } + &[data-mimetype*='cert'], &[data-mimetype*='rules'], &[data-mimetype*='pkcs'], &[data-mimetype$='stl'], &[data-mimetype$='crl'] { + background-image: url('/web/static/src/img/mimetypes/certificate.svg'); + } + &[data-mimetype*='-font'], &[data-mimetype*='font-'], &[data-ext$='ttf'] { + background-image: url('/web/static/src/img/mimetypes/font.svg'); + } + &[data-mimetype*='-dvi'] { + background-image: url('/web/static/src/img/mimetypes/print.svg'); + } + &[data-mimetype*='script'], &[data-mimetype*='x-sh'], &[data-ext*='bat'], &[data-mimetype$='bat'], &[data-mimetype$='cgi'], &[data-mimetype$='-c'], &[data-mimetype*='java'], &[data-mimetype*='ruby'] { + background-image: url('/web/static/src/img/mimetypes/script.svg'); + } + &[data-mimetype*='javascript'] { + background-image: url('/web/static/src/img/mimetypes/javascript.svg'); + } + &[data-mimetype*='calendar'], &[data-mimetype$='ldif'] { + background-image: url('/web/static/src/img/mimetypes/calendar.svg'); + } + &[data-mimetype$='postscript'], &[data-mimetype$='cdr'], &[data-mimetype$='xara'], &[data-mimetype$='cgm'], &[data-mimetype$='graphics'], &[data-mimetype$='draw'], &[data-mimetype*='svg'] { + background-image: url('/web/static/src/img/mimetypes/vector.svg'); + } +} diff --git a/addons/web/static/src/scss/modal.scss b/addons/web/static/src/scss/modal.scss new file mode 100644 index 00000000..d5faa771 --- /dev/null +++ b/addons/web/static/src/scss/modal.scss @@ -0,0 +1,131 @@ +.modal.o_technical_modal { + + .modal-content { + border-radius: 0; + + .modal-header .o_subtitle { + margin-left: 10px; + } + + .modal-header .modal-title { + word-break: break-word; + } + + .modal-body { + &.o_act_window { + padding: 0; + } + + .o_modal_header { + @include o-webclient-padding($top: 10px, $bottom: 10px); + @include clearfix; + + .o_search_options { + display: inline-block; + } + .o_pager { + float: right; + } + } + + > :not(.o_view_sample_data) .o_view_nocontent { + position: unset; + } + + .o_modal_changes td { + &:first-child { + padding-right: 10px; + vertical-align: top; + white-space: nowrap; + } + &:not(:first-child) { + width: 100%; + } + } + } + + .modal-footer { + flex-wrap: wrap; + text-align: left; + justify-content: flex-start; + + // TODO These rules should not be necessary if we used buttons + // as direct children of the modal-footer as normally required + > div, > footer { + flex: 1 1 auto; + } + + footer { + > :not(:first-child) { margin-left: .25rem; } + > :not(:last-child) { margin-right: .25rem; } + button { + margin-bottom: .5rem; + } + } + } + } + + @include media-breakpoint-up(sm) { + .modal-dialog .modal-content .modal-body.o_dialog_error { + overflow: visible; + display: flex; + flex-flow: column nowrap; + + > .alert, > button { + flex: 0 0 auto; + } + + > .o_error_detail { + flex: 1 1 auto; + min-height: 0; + overflow: auto; + } + } + } + + @include media-breakpoint-down(xs) { + &.o_modal_full { + .modal-dialog { + margin: 0px; + height: 100%; + + .modal-content { + height: 100%; + border: none; + + .modal-header { + background: $o-brand-odoo; + .modal-title, .o_subtitle, button.close { + color: white; + } + } + + .modal-body { + height: 100%; + overflow-y: auto; + } + } + } + } + } +} + +.modal.o_inactive_modal { + z-index: $zindex-modal-backdrop - 1; +} + +.o_dialog { + + > .modal { + display: block; + } +} + +// Temporary fix for modals which are not instantiated thanks to the Dialog +// JS classes (deprecated case) (see bootstrap_overridden.scss) + Frontend. +// +// TODO the following code was disabled because it is saas-incompatible +// +// :not(body) > .modal { +// z-index: $zindex-modal-background + 1; +// } diff --git a/addons/web/static/src/scss/model_field_selector.scss b/addons/web/static/src/scss/model_field_selector.scss new file mode 100644 index 00000000..46d7d0b2 --- /dev/null +++ b/addons/web/static/src/scss/model_field_selector.scss @@ -0,0 +1,134 @@ + +.o_field_selector { + position: relative; + + > .o_field_selector_value { + display: flex; + flex-flow: row wrap; + align-items: center; + height: 100%; + min-height: 20px; // needed when there is no value in it and used standalone + &:active, &:focus, &:active:focus { + outline: none; + } + + > .o_field_selector_chain_part { + padding: 0px 1px; + border: 1px solid darken($o-brand-lightsecondary, 10%); + background: $o-brand-lightsecondary; + margin-bottom: 1px; + } + > i { + align-self: center; + margin: 0 2px; + font-size: 10px; + } + } + > .o_field_selector_controls { + @include o-position-absolute(0, 0, 1px); + display: flex; + align-items: center; + cursor: pointer; + } + + &.o_edit_mode { + > .o_field_selector_controls::after { + @include o-caret-down; + } + + > .o_field_selector_popover { + $o-field-selector-arrow-height: 7px; + @include o-position-absolute($top: 100%, $left: 0); + z-index: 1051; + width: 265px; + margin-top: $o-field-selector-arrow-height; + background: white; + box-shadow: 0 3px 10px rgba(0,0,0,.4); + + &:focus { + outline: none; + } + + .o_field_selector_popover_header { + color: white; + background: theme-color('primary'); + font-weight: bold; + padding: 5px 0 5px 0.4em; + + .o_field_selector_title { + width: 100%; + @include o-text-overflow; + padding: 0px 35px; + text-align: center; + } + .o_field_selector_search { + padding-right: 0.4rem; + > .o_input { + padding: 5px 0.4rem; + } + } + .o_field_selector_popover_option { + @include o-position-absolute($top: 0); + padding: 8px; + + &.o_field_selector_prev_page { + left: 0; + } + &.o_field_selector_close { + right: 0; + } + &:hover { + background: darken(theme-color('primary'), 10%); + } + } + &:before { + @include o-position-absolute($top: -$o-field-selector-arrow-height, $left: $o-field-selector-arrow-height); + content: ""; + border-left: $o-field-selector-arrow-height solid rgba(0, 0, 0, 0); + border-right: $o-field-selector-arrow-height solid rgba(0, 0, 0, 0); + border-bottom: $o-field-selector-arrow-height solid theme-color('primary'); + } + } + .o_field_selector_popover_body { + .o_field_selector_page { + position: relative; + max-height: 320px; + overflow: auto; + margin: 0; + padding: 0; + + > .o_field_selector_item { + list-style: none; + position: relative; + padding: 5px 0 5px 0.4em; + cursor: pointer; + font-family: Arial; + font-size: 13px; + color: #444; + border-bottom: 1px solid #eee; + &.active { + background: #f5f5f5; + } + .o_field_selector_item_title { + font-size: 12px; + } + .o_field_selector_relation_icon { + @include o-position-absolute($top: 0, $right: 0, $bottom: 0); + display: flex; + align-items: center; + padding: 10px; + } + } + } + } + .o_field_selector_popover_footer { + background: theme-color('primary'); + padding: 5px 0.4em; + + > input { + width: 100%; + } + } + } + } +} diff --git a/addons/web/static/src/scss/name_and_signature.scss b/addons/web/static/src/scss/name_and_signature.scss new file mode 100644 index 00000000..48ee85c2 --- /dev/null +++ b/addons/web/static/src/scss/name_and_signature.scss @@ -0,0 +1,60 @@ +.o_web_sign_name_and_signature { + // for the absolute of the font selection to work + position: relative; +} + +/* Signature Dialog */ +// The .card selector is important to make sure the position absolute will take +// priority over the position relative of the card. +.card.o_web_sign_auto_font_selection { + @include o-position-absolute(0, 0); + + .o_web_sign_auto_font_list { + overflow: auto; + + > a { + height: 100px; + + > img { + height: 100%; + } + } + } +} + +.o_field_widget { + .o_signature { + outline: 1px solid rgba(theme-color('secondary'), 0.3); + position: relative; + &.o_signature_empty { + display: flex; + } + > p { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + } + } +} + +.o_field_invalid { + .o_signature { + outline: 3px solid theme-color('danger'); + cursor: pointer; + } +} +.o_form_editable { + .o_signature:hover { + outline: 3px solid $o-enterprise-primary-color; + cursor: pointer; + } +} + +.o_signature_stroke { + position: absolute; + border-top: #D1D0CE solid 2px; + bottom: 16%; + width: 72%; + left: 14%; +} diff --git a/addons/web/static/src/scss/navbar.scss b/addons/web/static/src/scss/navbar.scss new file mode 100644 index 00000000..b4c1872e --- /dev/null +++ b/addons/web/static/src/scss/navbar.scss @@ -0,0 +1,171 @@ +body.o_is_superuser .o_menu_systray { + background: repeating-linear-gradient( + 135deg, + #d9b904, #d9b904 10px, + #373435 10px, #373435 20px + ); + border-bottom-left-radius: 20px; + + > li { + > a, > label { + &:hover { + background-color: fade_out($o-navbar-inverse-link-hover-bg, 0.5); + } + } + } + + .show .dropdown-toggle { + background-color: fade_out($o-navbar-inverse-link-hover-bg, 0.5); + } +} +.o_main_navbar { + position: relative; + height: $o-navbar-height; + border-bottom: 1px solid $o-navbar-inverse-link-hover-bg; + background-color: $o-brand-odoo; + color: white; + + > a, > button { + float: left; + height: $o-navbar-height; + border: none; + padding: 0 $o-horizontal-padding - 4px 0 $o-horizontal-padding; + line-height: $o-navbar-height; + background-color: transparent; + text-align: center; + color: inherit; + + font-size: 18px; + user-select: none; + + &:hover, &:focus { + background-color: $o-navbar-inverse-link-hover-bg; + color: inherit; + } + &:focus, &:active, &:focus:active { + outline: none; + } + } + + .o_app { + cursor: pointer; + } + + > .o_menu_brand { + display: block; + float: left; + margin-right: 35px; + user-select: none; + color: white; + font-size: 22px; + font-weight: 500; + line-height: $o-navbar-height; + cursor: pointer; + } + + > .o_menu_toggle { + margin-right: 5px; + } + + > ul { + display: block; + float: left; + margin: 0; + padding: 0; + list-style: none; + + > li { + position: relative; + display: block; + float: left; + + > a { + display: block; + + &:focus, &:active, &:focus:active { + outline: none; + } + &, &:hover, &:focus { + text-decoration: none; + } + } + + > a, > label { + height: $o-navbar-height; + padding: 0 10px; + color: white; + line-height: $o-navbar-height; + + &:hover { + background-color: $o-navbar-inverse-link-hover-bg; + } + } + + &.o_extra_menu_items { + > a { + width: $o-navbar-height; + text-align: center; + } + &.show > ul { + padding: 0; + + > li { + > a { + background-color: $o-brand-odoo; + color: white; + border-bottom: 1px solid $o-brand-lightsecondary; + + &.dropdown-toggle { + background-color: lighten($o-brand-odoo, 15%); + pointer-events: none; // hack to prevent clicking on it because dropdown always opened + } + } + > .dropdown-menu { // remove dropdown-menu default style as it is nested in another one + position: static; + float: none; + display: block; + border: none; + box-shadow: none; + max-height: none; + } + } + } + } + } + + &.o_menu_systray { + float: right; + } + } + + .dropdown-menu.show { + max-height: 90vh; // the dropdown should not overstep the viewport + min-width: 100%; + overflow: auto; + margin-top: 0; + + @for $index from 3 through 5 { + .o_menu_header_lvl_#{$index}, .o_menu_entry_lvl_#{$index} { + padding-left: $o-dropdown-hpadding + ($index - 2)*12px; + } + } + } + + .show .dropdown-toggle { + background-color: $o-navbar-inverse-link-hover-bg; + } + .o_user_menu { + margin-left: 6px; + + > a { + padding-right: $o-horizontal-padding; + } + + .oe_topbar_avatar { + height: 26px; + width: 26px; + object-fit: cover; + transform: translateY(-2px); + } + } +} diff --git a/addons/web/static/src/scss/navbar_mobile.scss b/addons/web/static/src/scss/navbar_mobile.scss new file mode 100644 index 00000000..f4593436 --- /dev/null +++ b/addons/web/static/src/scss/navbar_mobile.scss @@ -0,0 +1,98 @@ + +.o_main_navbar { + .o_app { + @include media-breakpoint-down(sm) { + float: none; + margin: 0; + border-bottom: 1px solid $o-navbar-inverse-link-hover-bg; + color: transparent !important; + } + } + + > .o_menu_brand { + @include media-breakpoint-down(sm) { + float: none; + margin: 0; + border-bottom: 1px solid $o-navbar-inverse-link-hover-bg; + color: transparent !important; + } + } + + @include media-breakpoint-down(sm) { + transition: height 200ms linear 0s; + + position: relative; + height: $o-navbar-height; + + > ul { + > li { + float: none; + .dropdown-backdrop { + display: none; + } + + .dropdown-menu.show { + max-height: none; + } + } + + &.o_menu_sections { + width: 100%; + display: none; + + .dropdown-menu.show { + position: static; + float: none; + background-color: transparent; + box-shadow: none; + border: none; + overflow: visible; + + > .dropdown-item { + background-color: transparent; + color: inherit; + } + } + } + + &.o_menu_systray { + @include o-position-absolute(0px, $o-navbar-height, auto, $o-navbar-height); + height: $o-navbar-height; + text-align: right; + + > li { + display: inline-block; + + .dropdown-menu.show { + @include o-position-absolute($o-navbar-height, 0, 0, 0); + position: fixed; + width: auto; + } + } + + .o_user_menu .oe_topbar_name { + display: none; + } + } + } + } +} + +@include media-breakpoint-down(sm) { + body.o_mobile_menu_opened > .o_main_navbar { + height: 100%; + overflow: auto; + .o_menu_sections { + display: block; + } + } +} + +@include media-breakpoint-down(sm) { + .o_switch_company_menu > .dropdown-menu { + padding-top: 0px; + .bg-info { + padding: 10px; + } + } +} diff --git a/addons/web/static/src/scss/notification.scss b/addons/web/static/src/scss/notification.scss new file mode 100644 index 00000000..561f7ab0 --- /dev/null +++ b/addons/web/static/src/scss/notification.scss @@ -0,0 +1,25 @@ + +.o_notification_manager { + @include o-position-absolute(3.5rem, 0); + position: fixed; + z-index: ($zindex-modal + $zindex-popover) / 2; + width: $toast-max-width; + margin-right: 8px; + max-width: 100%; + + .toast:not(:last-child) { + margin-bottom: 5px; + } + .o_notification { + width: 100%; + + .toast-body { + padding: 8px 10px; + + .o_notification_close { + height: 20px; + margin-left: 1rem; + } + } + } +} diff --git a/addons/web/static/src/scss/pivot_view.scss b/addons/web/static/src/scss/pivot_view.scss new file mode 100644 index 00000000..3dbfc809 --- /dev/null +++ b/addons/web/static/src/scss/pivot_view.scss @@ -0,0 +1,104 @@ +.o_pivot { + + .o_pivot_cell_value { + font-size: 1em; + .o_comparison { + font-size: 0.61em; + } + .o_variation { + &.o_positive { + color: green; + } + &.o_negative { + color: red; + } + } + } + + table { + background-color: $o-view-background-color; + width: auto; // bootstrap override + + // Inform the user that he may click on a cell to be redirected to a list view of the + // items corresponding to the clicked cell + &.o_enable_linking { + .o_pivot_cell_value:not(.o_empty):hover { + color: $o-brand-primary; + cursor: pointer; + + } + } + + thead th:not(.o_pivot_header_cell_closed):not(.o_pivot_header_cell_opened):not(.o_pivot_header_cell) { + text-align: center; + } + + th { + font-weight: normal; // bootstrap override + background-color: lighten($o-brand-secondary, 40%); + } + + @mixin o-pivot-header-cell { + background-color: lighten($o-brand-secondary, 40%); + cursor: pointer; + white-space: nowrap; + user-select: none; + &:hover { + background-color: lighten($o-brand-secondary, 30%); + } + } + + .o_pivot_measure_row { + @include o-pivot-header-cell; + } + + .o_pivot_header_cell { + white-space: nowrap; + user-select: none; + } + + .o_pivot_header_cell_closed { + @include o-pivot-header-cell; + &::before { + font-family: FontAwesome; + content: ""; + margin-right: 8px; + } + } + + .o_pivot_header_cell_opened { + @include o-pivot-header-cell; + &::before { + font-family: FontAwesome; + content: ""; + margin-right: 8px; + } + } + + .o_pivot_sort_order_asc::after { + @include o-caret-up; + margin-left: 5px; + } + + .o_pivot_sort_order_desc::after { + @include o-caret-down; + margin-left: 5px; + } + + .o_pivot_cell_value.o_cell_hover { + background-color: $table-hover-bg; + } + } + + .o_pivot_field_selection::after { + @include o-caret-right; + position: absolute; + right: 6px; + top: 9px; + } + + .o_pivot_field_menu .dropdown-item.disabled { + color: $text-muted; + cursor: default; + } +} diff --git a/addons/web/static/src/scss/popover.scss b/addons/web/static/src/scss/popover.scss new file mode 100644 index 00000000..5914fe51 --- /dev/null +++ b/addons/web/static/src/scss/popover.scss @@ -0,0 +1,152 @@ +@keyframes slide-top { + 0% { + opacity: 0; + transform: translateY(-5%); + } + + 100% { + opacity: 1; + transform: translateY(0); + } +} + +@keyframes slide-right { + 0% { + opacity: 0; + transform: translateX(5%); + } + + 100% { + opacity: 1; + transform: translateX(0); + } +} + +@keyframes slide-bottom { + 0% { + opacity: 0; + transform: translateY(5%); + } + + 100% { + opacity: 1; + transform: translateY(0); + } +} + +@keyframes slide-left { + 0% { + opacity: 0; + transform: translateX(-5%); + } + + 100% { + opacity: 1; + transform: translateX(0); + } +} + +.o_popover { + $border-color: gray('300'); + $bg-color: gray('200'); + $arrow-pad: 8px; + $arrow-size: 8px; + $animation-time: 0.2s; + max-width: 100vw; + + position: fixed; + z-index: 1060; + border: 1px solid $border-color; + background-color: #fff; + box-shadow: 0 1px 4px rgba(0, 0, 0, 0.2); + + .o_popover_header { + background-color: gray('200'); + font-size: 1.1rem; + padding: 0.5rem 0.75rem; + border-bottom: 1px solid $border-color; + &:empty { + display: none; + } + } + + &--top { + animation: $animation-time slide-top; + + &::before, + &::after { + content: ''; + position: absolute; + top: 100%; + left: 50%; + margin-left: -$arrow-size; + border: $arrow-size solid transparent; + border-top-color: $bg-color; + } + + &::before { + margin-top: 1px; + border-top-color: $border-color; + } + } + + &--right { + animation: $animation-time slide-right; + + &::before, + &::after { + content: ''; + position: absolute; + top: 50%; + right: 100%; + margin-top: -$arrow-size; + border: $arrow-size solid transparent; + border-right-color: $bg-color; + } + + &::before { + margin-right: 1px; + border-right-color: $border-color; + } + } + + &--bottom { + animation: $animation-time slide-bottom; + + &::before, + &::after { + content: ''; + position: absolute; + bottom: 100%; + left: 50%; + margin-left: -$arrow-size; + border: $arrow-size solid transparent; + border-bottom-color: $bg-color; + } + + &::before { + margin-bottom: 1px; + border-bottom-color: $border-color; + } + } + + &--left { + animation: $animation-time slide-left; + + &::before, + &::after { + content: ''; + position: absolute; + top: 50%; + left: 100%; + margin-top: -$arrow-size; + border: $arrow-size solid transparent; + border-left-color: $bg-color; + } + + &::before { + margin-left: 1px; + border-left-color: $border-color; + } + } +} diff --git a/addons/web/static/src/scss/primary_variables.scss b/addons/web/static/src/scss/primary_variables.scss new file mode 100644 index 00000000..81b6c45f --- /dev/null +++ b/addons/web/static/src/scss/primary_variables.scss @@ -0,0 +1,119 @@ +/// +/// This file regroups the variables that style odoo components. +/// They are available in every asset bundle. +/// + +// Font sizes +$o-root-font-size: 12px; +$o-font-size-base: 13rem * (1px / $o-root-font-size); +$o-line-height-base: 1.5; // This is BS default + +// Colors +$o-community-color: #7C7BAD; +$o-enterprise-color: #875A7B; +$o-enterprise-primary-color: #00A09D; + +$o-brand-odoo: $o-community-color; +$o-brand-primary: $o-community-color; + +$o-brand-secondary: #f0eeee; +$o-brand-lightsecondary: #e2e2e0; +$o-gray-100: #f8f9fa; // This is BS default + +$o-main-color-muted: #a8a8a8; +$o-main-text-color: #4c4c4c; + +$o-view-background-color: white; +$o-shadow-color: #303030; + +$o-form-lightsecondary: #ccc; + +$o-list-footer-bg-color: #eee; +$o-list-footer-font-weight: bold; + +$o-tooltip-background-color: white; +$o-tooltip-color: #666666; +$o-tooltip-arrow-color: white; +$o-tooltip-text-color: #777777; +$o-tooltip-title-text-color: black; +$o-tooltip-title-background-color: #F7F7F7; + +// Layout +// +// Extension of BS4. This is not redefining the BS4 variable directly as we only +// need the extra ones for media queries (not creating new breakpoint classes). +// Note: default BS4 values are hardcoded here while it should be possible to +// merge with the default BS variable (but we would have to take care of +// ordering & cie). +$o-extra-grid-breakpoints: ( + xs: 0, + vsm: 475px, + sm: 576px, + md: 768px, + lg: 992px, + xl: 1200px, + xxl: 1534px, +); + +$o-form-group-cols: 12; +$o-form-spacing-unit: 5px; +$o-horizontal-padding: 16px; +$o-innergroup-rpadding: 45px; +$o-dropdown-hpadding: 20px; + +$o-sheet-vpadding: 24px; + +$o-statbutton-height: 44px; +$o-statbutton-vpadding: 0px; +$o-statbutton-spacing: 6px; + +$o-modal-lg: 980px; +$o-modal-md: 650px; + +// Needed for having no spacing between sheet and mail body in mass_mailing: +// Different required cancel paddings between web and web_enterprise +$o-sheet-cancel-tpadding: 0px; + +$o-avatar-size: 90px; + +$o-statusbar-height: 33px; + +$o-label-font-size-factor: 0.8; +$o-navbar-height: 46px; + +$o-nb-calendar-colors: 24; + +$o-base-settings-mobile-tabs-height: 40px; +$o-base-settings-mobile-tabs-overflow-gap: 3%; + +$o-cp-breadcrumb-height: 30px; + +$o-datepicker-week-color: #8f8f8f; + +$o-card-body-bg-opacity: 0.9; + +// Kanban + +$o-kanban-default-record-width: 300px; +$o-kanban-small-record-width: 240px; + +$o-kanban-header-title-height: 50px; + +$o-kanban-image-width: 64px; +$o-kanban-image-fill-width: 95px; +$o-kanban-inside-vgutter: 8px; +$o-kanban-inside-hgutter: 8px; +$o-kanban-color-border-width: 3px; +$o-kanban-inner-hmargin: 5px; +$o-kanban-progressbar-height: 20px; + +$o-kanban-mobile-tabs-height: 40px; +$o-kanban-mobile-empty-height: $o-kanban-image-width; +// ------- Kanban dashboard variables ------- + +// Used to manage spacing in complex dropdown menu +$o-kanban-dashboard-dropdown-complex-gap: 5px; + +// Form view + +$o-form-view-sheet-max-width: 1140px !default; diff --git a/addons/web/static/src/scss/progress_bar.scss b/addons/web/static/src/scss/progress_bar.scss new file mode 100644 index 00000000..71af7708 --- /dev/null +++ b/addons/web/static/src/scss/progress_bar.scss @@ -0,0 +1,36 @@ +.o_progressbar { + + > div { + display: inline-block; + } + + .o_progressbar_title { + white-space: nowrap; + padding-right: 10px; + } + + .o_progress { + width: 100px; + height: 15px; + vertical-align: middle; + + border: 1px solid lighten($o-brand-secondary, 25%); + overflow: hidden; + + background-color: white; + &.o_progress_overflow { + background-color: $o-brand-secondary; + } + + .o_progressbar_complete { + background-color: $o-brand-primary; + height: 100%; + } + } + + .o_progressbar_value { + width: 100px; + white-space: nowrap; + padding-left: 10px; + } +} diff --git a/addons/web/static/src/scss/rainbow.scss b/addons/web/static/src/scss/rainbow.scss new file mode 100644 index 00000000..fa666fca --- /dev/null +++ b/addons/web/static/src/scss/rainbow.scss @@ -0,0 +1,255 @@ +/************** rainbow *****************/ + +.o_reward { + $reward-size: 400px; + $reward-size-mobile: 300px; + $reward-text-color: #727880; + $reward-base-time: 1.4s; + + will-change: transform; + z-index: $zindex-modal; + padding: 50px; + margin: -5% auto 0 (-$reward-size / 2); + @include media-breakpoint-down(sm) { + margin: -5% auto 0 (-$reward-size-mobile / 2); + } + background-image: -webkit-radial-gradient(#EDEFF4 30%, transparent 70%, transparent); + background-image: -o-radial-gradient(#EDEFF4 30%, transparent 70%, transparent); + background-image: radial-gradient(#EDEFF4 30%, transparent 70%, transparent); + + animation: reward-fading ($reward-base-time * 0.5) ease-in-out 0s 1 normal forwards; + @include o-position-absolute(20%, auto, auto, 50%); + @include size($reward-size, $reward-size); + @include media-breakpoint-down(sm) { + @include size($reward-size-mobile, $reward-size-mobile); + } + + &.o_reward_fading { + display: block; + animation: reward-fading-reverse ($reward-base-time * 0.4) ease-in-out 0s 1 normal forwards; + + .o_reward_face_group { + animation: reward-jump-reverse ($reward-base-time * 0.4) ease-in-out 0s 1 normal forwards; + } + + .o_reward_rainbow { + path { + animation: reward-rainbow-reverse ($reward-base-time * 0.5) ease-out 0s 1 normal forwards; + } + } + + } + + .o_reward_face, .o_reward_stars, .o_reward_shadow, .o_reward_thumbup { + margin: 0 auto; + } + + .o_reward_rainbow { + path { + stroke-dasharray: 600; + stroke-dashoffset: 0; + fill: none; + stroke-linecap: round; + stroke-width: 21px; + animation: reward-rainbow $reward-base-time ease-out 0s 1 normal forwards; + } + } + + .o_reward_face_group { + transform-origin: center; + animation: reward-jump $reward-base-time * 0.8 ease-in-out 0s 1 normal none running; + @include o-position-absolute(6%, 0, 0, 0); + @include size(100%, 60%); + } + + .o_reward_face { + display: block; + top: 42%; + position: relative; + border-radius: 100%; + background: center center / contain no-repeat; + animation: reward-float $reward-base-time ease-in-out $reward-base-time infinite alternate; + @include size(34%, 56.67%); + } + + .o_reward_stars { + display: block; + @include size($reward-size * 0.75, $reward-size / 2); + @include media-breakpoint-down(sm) { + @include size($reward-size-mobile * 0.75, $reward-size-mobile / 2); + } + @include o-position-absolute(18%, 7%); + + svg { + transform-origin: center center; + @include o-position-absolute(28%, $left:3%); + animation: reward-stars $reward-base-time ease-in-out 0s infinite alternate-reverse; + + &.star2, &.star4 { + animation: reward-stars ($reward-base-time*1.2) ease-in-out 0s infinite alternate; + } + + &.star2 { + left: 20%; + top: 2%; + } + + &.star3 { + left: 49%; + top: 6%; + } + + &.star4 { + left: 70%; + top: 27%; + } + + } + } + + .o_reward_thumbup { + width: 40px; + display: block; + animation: reward-scale ($reward-base-time * 0.5) ease-in-out 0s infinite alternate; + @include o-position-absolute(63%, auto, auto, 65%); + } + + .o_reward_msg_container { + will-change: transform; + padding-top: 11%; + width: 70%; + margin-left: 15%; + + // Translate before animate + transform: translateY(5px); + + animation: reward-float $reward-base-time ease-in-out $reward-base-time infinite alternate-reverse; + @include o-position-absolute(85%, auto, auto, 0%); + + .o_reward_thumb_right { + height: 40px; + z-index: 1; + @include o-position-absolute(0, auto, auto, 16%); + } + + .o_reward_msg { + margin-left: 7%; + margin-top: -9.5%; + padding: 25px 15px 20px; + background: white; + border: 1px solid #ecf1ff; + border-top-width: 0; + display: inline-block; + + // Reset margins for first and penultimate childs (the last one is shadow) + *:first-child { + margin-top: 0; + } + } + + .o_reward_msg_content { + position: relative; + font-family: sans-serif; + text-align: left; + color: $reward-text-color; + } + + .o_reward_shadow_container { + transform: translateY(0px) rotateZ(0); + animation: reward-float $reward-base-time ease-in-out $reward-base-time infinite alternate; + } + + .o_reward_shadow { + @include size(100%, 12px); + background-color: #e7eaf0; + border-radius: 100%; + transform: scale(0.8) rotateZ(0); + animation: reward-scale $reward-base-time ease-in-out $reward-base-time infinite alternate; + @include o-position-absolute(auto, auto, -40px, 0); + } + } +} + +@keyframes reward-fading { + 0% { + opacity: 0; + } + 100% { + opacity: 1; + } +} + +@keyframes reward-fading-reverse { + 100% { + opacity: 0; + } +} + +@keyframes reward-jump { + 0% { + transform: scale(0.5); + } + 50% { + transform: scale(1.05); + } + to { + transform: scale(1); + } +} + +@keyframes reward-jump-reverse { + 0% { + transform: scale(1); + } + 50% { + transform: scale(1.05); + } + to { + transform: scale(0.5); + } +} + +@keyframes reward-rainbow { + 0% { + stroke-dashoffset: -500; + } + to { + stroke-dashoffset: 0; + } +} + +@keyframes reward-rainbow-reverse { + to { + stroke-dashoffset: -500; + } +} + +@keyframes reward-float { + from { + transform: translateY(0px); + } + to { + transform: translateY(5px); + } +} + +@keyframes reward-stars { + from { + transform: scale(0.3) rotate(0deg); + } + 50% { + transform: scale(1.0) rotate(20deg); + } + to { + transform: scale(0.3) rotate(80deg); + } +} + +@keyframes reward-scale { + from { + transform: scale(0.8); + } + to { + transform: scale(1.0); + } +} diff --git a/addons/web/static/src/scss/report.scss b/addons/web/static/src/scss/report.scss new file mode 100644 index 00000000..8e29f0ce --- /dev/null +++ b/addons/web/static/src/scss/report.scss @@ -0,0 +1,106 @@ +$o-default-report-font: 'Lato' !default; +$o-default-report-primary-color: rgb(0, 0, 0) !default; +$o-default-report-secondary-color: rgb(0, 0, 0) !default; + +html, body { + height: 100%; + direction: ltr; +} + +body { + color: #000 !important; + word-wrap: break-word; + font-family: $o-default-report-font; +} + +span.o_force_ltr { + display: inline-block; +} +.o_force_ltr, .o_field_phone { + unicode-bidi: embed; // ensure element has level of embedding for direction + /*rtl:ignore*/ + direction: ltr; +} + +.border-black td, .border-black th { + border-top: 1px solid black !important; +} + +/* See https://github.com/wkhtmltopdf/wkhtmltopdf/issues/1524 */ +.table-sm { + > thead > tr > th { + border-bottom: none !important; + } + > tbody > tr { + page-break-inside: avoid !important; + border-top: none !important; + } +} +.zero_min_height { + min-height: 0px !important; +} + +/* To avoid broken snippets in report rendering */ +.jumbotron, .panel, .carousel, section{ + page-break-inside: avoid; +} + +/* Wkhtmltopdf doesn't support very well the media-print CSS (depends on the version) */ +.d-print-none{ + display: none; +} + +.o_bold { + font-weight: bolder; +} +.o_italic { + font-style: italic; +} +.o_underline { + text-decoration: underline; +} + +/*Total table*/ +div#total { + page-break-inside: avoid; + table { + tr { + &.o_subtotal, + &.o_total { + td { + border-top: 1px solid black !important; + } + &.o_border_bottom { + td { + border-bottom: 1px solid black !important; + } + } + } + } + } +} + +table { + thead { + &.o_black_border { + tr { + th { + border-bottom: 2px solid black !important; + } + } + } + } +} + +// Wkhtmltopdf doesn't handle flexbox properly, both the content +// of columns and columns themselves does not wrap over new lines +// when needed: the font of the pdf will reduce to make the content +// fit the page format. +// A (weak) solution is to force the content on one line and +// impose the width, so to have evenly size columns. +// This should work fine in most cases, but will yield ugly results +// when 6+ columns are rendered +.col-auto{ + -webkit-box-flex: 1 !important; + width: 100% !important; +} diff --git a/addons/web/static/src/scss/report_backend.scss b/addons/web/static/src/scss/report_backend.scss new file mode 100644 index 00000000..18ea620c --- /dev/null +++ b/addons/web/static/src/scss/report_backend.scss @@ -0,0 +1,14 @@ +.o_report_iframe { + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + width: 100%; + height: 100%; + border: none; +} + +.o_report_buttons { + display: inline-block; +} diff --git a/addons/web/static/src/scss/ribbon.scss b/addons/web/static/src/scss/ribbon.scss new file mode 100644 index 00000000..6837b0c7 --- /dev/null +++ b/addons/web/static/src/scss/ribbon.scss @@ -0,0 +1,74 @@ +.ribbon { + width: 150px; + height: 150px; + overflow: hidden; + position: absolute; + + &::before, &::after { + position: absolute; + z-index: -1; + content: ''; + display: block; + border: 5px solid #2980b9; + } + + & span { + z-index: 1; + position: absolute; + display: grid; + align-items: center; + width: 225px; + height: 48px; + padding: 0 44px; + box-shadow: 0 5px 10px rgba(0, 0, 0, .1); + color: #fff; + font: 700 18px/1 'Lato', sans-serif; + text-shadow: 0 1px 1px rgba(0, 0, 0, .2); + text-transform: uppercase; + text-align: center; + overflow: hidden; + user-select: none; + &.o_small { + font-size: 12px; + } + &.o_medium { + font-size: 15px; + } + } + + &-top-right { + margin-top: -$o-sheet-vpadding; + right: 0; + + &::before, &::after { + border-top-color: black; + border-right-color: black; + } + + &::before { + top: 0; + left: 0; + } + + &::after { + bottom: 0; + right: 0; + } + + & span { + left: -15px; + top: 30px; + transform: rotate(45deg); + } + } +} + +// after ribbon widget there can be field widgets or oe_title which may +// have widgets on right end, add margin-right so ribbon not overlap on it +.ribbon { + &:not(.o_invisible_modifier) { + ~ .oe_title, ~ .o_field_widget { + margin-right: 100px; + } + } +} diff --git a/addons/web/static/src/scss/search_panel.scss b/addons/web/static/src/scss/search_panel.scss new file mode 100644 index 00000000..0a25d53a --- /dev/null +++ b/addons/web/static/src/scss/search_panel.scss @@ -0,0 +1,144 @@ +// ------- View with SearchPanel ------- +$o-searchpanel-active-bg: rgba(108, 193, 237, 0.3); +$o-searchpanel-p: $o-horizontal-padding; +$o-searchpanel-p-small: $o-horizontal-padding*0.5; +$o-searchpanel-p-tiny: $o-searchpanel-p-small*0.5; + +$o-searchpanel-category-default-color: $o-brand-primary; +$o-searchpanel-filter-default-color: #D59244; + +.o_controller_with_searchpanel { + display: flex; + align-items: flex-start; + + .o_renderer_with_searchpanel { + flex: 1 1 100%; + overflow: auto; // make the renderer and search panel scroll individually + max-height: 100%; + position: relative; + } +} + +.o_search_panel { + flex: 0 0 220px; + overflow: auto; + height: 100%; + padding: $o-searchpanel-p-small $o-searchpanel-p-small $o-searchpanel-p*2 $o-searchpanel-p; + border-right: 1px solid $gray-300; + background-color: white; + + .o_search_panel_category .o_search_panel_section_icon { + color: $o-brand-odoo; + } + + .o_search_panel_filter .o_search_panel_section_icon { + color: $o-searchpanel-filter-default-color; + } + + .o_toggle_fold { + text-align: center; + width: 1.5rem; + } + + .o_search_panel_label { + align-items: center; + cursor: pointer; + display: flex; + justify-content: space-between; + user-select: none; + width: 100%; + } + + .o_search_panel_section_header { + cursor: default; + padding: $o-searchpanel-p-small 0; + } + + .list-group { + padding-bottom: $o-searchpanel-p-tiny; + } + + .list-group-item { + padding: 0; + + .list-group-item { + padding: 0 0 0 $custom-control-gutter; + margin-bottom: $o-searchpanel-p-tiny*0.5; + } + + .o_search_panel_label_title { + color: $headings-color; + width: 100%; + @include o-text-overflow; + } + + header.active { + background-color: $o-searchpanel-active-bg; + } + } + + .o_search_panel_category_value { + cursor: pointer; + + header { + align-items: center; + display: flex; + justify-content: space-between; + padding: 4px 6px 4px 0px; + } + + .o_search_panel_label.o_with_counters { + overflow: hidden; + } + + .o_search_panel_category_value { + margin-bottom: 0; + padding-left: $o-searchpanel-p; + position: relative; + + &:before, + &:after { + content: ''; + background: $gray-500; + margin-left: 4px; + @include o-position-absolute(0, $left: $o-searchpanel-p-tiny); + @include size(1px, 100%); + } + + &:after { + top: 12px; + @include size(8px, 1px); + } + + &:last-child:before { + height: 12px; + } + } + } + + .o_search_panel_group_header .custom-control { + width: 100%; + } + + .o_search_panel_filter_value, + .o_search_panel_filter_group { + cursor: pointer; + padding-bottom: $o-searchpanel-p-small; + + .o_search_panel_label, + .o_search_panel_label_title { + padding-right: 6px; + } + } + + .o_search_panel_filter_group { + + header { + display: flex; + } + + .o_search_panel_label.o_with_counters { + justify-content: flex-start; + } + } +} diff --git a/addons/web/static/src/scss/search_view.scss b/addons/web/static/src/scss/search_view.scss new file mode 100644 index 00000000..1f654ead --- /dev/null +++ b/addons/web/static/src/scss/search_view.scss @@ -0,0 +1,198 @@ +.o_searchview { + align-items: flex-end; + padding: 0 20px 1px 0; + position: relative; + + .o_searchview_input_container { + display: flex; + flex-flow: row wrap; + position: relative; + + .o_searchview_facet { + display: flex; + flex: 0 0 auto; + margin: 1px 3px 0 0; + max-width: 100%; + position: relative; + $o-searchview-facet-remove-width: 18px; + + .o_searchview_facet_label { + align-items: center; + color: white; + flex: 0 0 auto; + padding: 0 3px; + @include o-text-overflow($display: flex); + } + + .o_facet_values { + direction: ltr#{"/*rtl:ignore*/"}; + padding: 0 $o-searchview-facet-remove-width 0 5px; + + .o_facet_values_sep { + font-style: italic; + margin: 0 0.3rem; + } + } + + .o_facet_remove { + align-items: center; + cursor: pointer; + display: flex; + flex: 0 0 auto; + justify-content: center; + width: $o-searchview-facet-remove-width; + @include o-position-absolute(0, 0, 0); + } + } + + .o_searchview_input { + flex: 1 0 auto; + width: 75px; + } + + .o_searchview_autocomplete { + width: 100%; + @include o-position-absolute(100%, $left: auto); + + .o_menu_item { + align-items: center; + display: flex; + padding-left: 25px; + + &.o_indent { + padding-left: 50px; + } + + a { + &:hover { + background-color: inherit; + } + + &.o_expand { + display: flex; + justify-content: center; + width: 25px; + @include o-position-absolute($left: 0); + } + } + } + } + } +} + +.o_search_options .o_dropdown_toggler_btn:first-child { + // apply the padding to the first element so that when the `favorites` menu goes under + // the `filters` menu, they're still aligned + margin-right: 15px; +} + +// Dropdowns +.o_dropdown { + color: $o-main-text-color; + cursor: pointer; + display: inline-block; + user-select: none; // Prevent text selection when toggling the dropdowns + white-space: nowrap; // prevent the arrow from going under the dropdown when too little space + + .o_dropdown_title { + padding: 0 0.3rem; + } + + .o_dropdown_caret { + font-size: 12px; + width: 1rem; + } + + &:hover, + &.show { + + .o_dropdown_toggler_btn { + color: darken($o-main-text-color, 10%); + } + } + + .o_dropdown_menu { + + ul.o_embed_menu { + padding: 0; + } + + a.dropdown-item { + align-items: center; + display: flex; + position: relative; + + .o_item_description { + flex: 1; + } + + .o_icon_right { + @include o-position-absolute($right: $dropdown-item-padding-x * 0.5); + } + + .o_icon_right + .o_icon_right { + @include o-position-absolute($right: $dropdown-item-padding-x * 1.25); + } + } + + .o_menu_item_options { + list-style: none; + margin-bottom: 1rem; + padding: 0; + } + } +} + +// Open and closed carets in dropdowns +.o_closed_menu { + position: relative; + + &:before { + margin-top: (-$caret-width); + @include o-caret-right; + @include o-position-absolute($top: 50%, $left: 10px); + } +} +.o_open_menu { + position: relative; + + &:before { + margin-top: (-$caret-width); + @include o-caret-down; + @include o-position-absolute($top: 50%, $left: 10px); + } +} + +// Filter menu +.o_filter_menu { + + .o_filter_condition { + margin-bottom: 8px; + position: relative; + + .o_or_filter { // or between conditions + @include o-position-absolute($top: 3px, $left: 4px); + } + + .o_generator_menu_operator { + margin: 3px 0px; // vertical gap between selects + } + + .o_generator_menu_value { // date fields: datepicker position + .datepickerbutton { + cursor: pointer; + @include o-position-absolute(3px, -20px); + } + } + + .o_generator_menu_delete { // delete condition button position + cursor: pointer; + @include o-position-absolute($top: 6px, $right: 4px); + } + } + + .o_add_filter_menu { // apply and add buttons + display: flex; + justify-content: space-between; + } +} diff --git a/addons/web/static/src/scss/search_view_extra.scss b/addons/web/static/src/scss/search_view_extra.scss new file mode 100644 index 00000000..2530f060 --- /dev/null +++ b/addons/web/static/src/scss/search_view_extra.scss @@ -0,0 +1,36 @@ +.o_searchview { + background-color: white; + border: 1px solid #ccc; + border-radius: $border-radius; + padding: 1px 25px 3px 5px; + + .o_searchview_facet { + border: 1px solid gray('600'); + background: $o-brand-lightsecondary; + + .o_searchview_facet_label { + background-color: $o-brand-odoo; + } + + .o_facet_remove { + color: gray('600'); + &:hover { + color: gray('700'); + } + } + } + + .o_searchview_input { + border: none; + padding: 4px 0 0 0; + } + .o_searchview_icon { + @include o-position-absolute($top: 6px, $right: 5px); + } + .o_searchview_autocomplete li.o_selection_focus { + background-color: $o-brand-odoo; + > a { + color: white; + } + } +} diff --git a/addons/web/static/src/scss/secondary_variables.scss b/addons/web/static/src/scss/secondary_variables.scss new file mode 100644 index 00000000..aa3d2eeb --- /dev/null +++ b/addons/web/static/src/scss/secondary_variables.scss @@ -0,0 +1,44 @@ +$o-webclient-background-color: desaturate($o-gray-100, 100%) !default; +$o-control-panel-background-color: $o-view-background-color !default; + +$o-list-footer-color: $o-main-text-color !default; +$o-list-group-header-color: lighten($o-brand-lightsecondary, 10%) !default; + +// UI custom colors for tags, kanban records' colors, ...) +// Note: the first value is the old BS3 gray-light value +$o-colors: lighten(#000, 46.7%), #F06050, #F4A460, #F7CD1F, #6CC1ED, #814968, + #EB7E7F, #2C8397, #475577, #D6145F, #30C381, #9365B8 !default; + +$o-colors-secondary: #aa4b6b, #30C381, #97743a, #F7CD1F, #4285F4, #8E24AA, + #D6145F, #173e43, #348F50, #AA3A38, #795548, #5e0231, + #6be585, #999966, #e9d362, #b56969, #bdc3c7, #649173 !default; + +// UI custom colors, complete list +$o-colors-complete: join( + set-nth($o-colors, 1, #134E5E), + $o-colors-secondary +)!default; + +$o-sheet-cancel-hpadding: $o-horizontal-padding !default; +$o-sheet-cancel-bpadding: $o-horizontal-padding !default; + +$o-statusbar-arrow-width: $o-statusbar-height / 3 !default; +$o-statusbar-disabled-bg: lighten($o-brand-lightsecondary, 7%) !default; + +$o-navbar-inverse-link-hover-bg: darken($o-brand-odoo, 10%) !default; + +$o-datepicker-week-bg-color: lighten($o-datepicker-week-color, 30%) !default; + +// Kanban + +$o-kanban-record-margin: $o-horizontal-padding / 2 !default; +$o-kanban-group-padding: $o-horizontal-padding !default; // Cannot be higher than this + +$o-kanban-inside-hgutter-mobile: $o-horizontal-padding !default; + +// ------- Kanban dashboard variables ------- + +// Since rows and containers are used inside cards, we're +// forced to set the padding relative to the grid system. +$o-kanban-dashboard-hpadding: $o-horizontal-padding !default; +$o-kanban-dashboard-vpadding: $o-horizontal-padding / 2 !default; diff --git a/addons/web/static/src/scss/special_fields.scss b/addons/web/static/src/scss/special_fields.scss new file mode 100644 index 00000000..cabdf919 --- /dev/null +++ b/addons/web/static/src/scss/special_fields.scss @@ -0,0 +1,55 @@ +/* + IFRAME WRAPPER SCSS + */ + +$preview_height: 1123 + 32; +$preview_width: 794; +$preview_scale: 0.50; + +@mixin o_preview_iframe_styling($scale) { + + .o_preview_iframe_wrapper { + padding: 0; + overflow: hidden; + width: ($preview_width * $scale) + 0px; + height: ($preview_height * $scale) + 0px; + position: relative; + } + + .o_preview_iframe { + width: $preview_width + 0px; + height: $preview_height + 0px; + border: 2px solid lightgrey; + overflow: hidden; + + padding-top: 16px; + padding-bottom: 16px; + + -ms-zoom: 0.5; + -moz-transform: scale($scale); + -moz-transform-origin: 0 0; + -o-transform: scale($scale); + -o-transform-origin: 0 0; + -webkit-transform: scale($scale); + -webkit-transform-origin: 0 0; + } + + .o_iframe_wrapper_spinner { + position: absolute; + left: (($preview_width * $scale) / 2) - 40 + 0px; + top: (($preview_height * $scale) / 2) - 40 + 0px; + // -40 for width/2 and height/2 of the spinner + } + +} + + +@include o_preview_iframe_styling($preview_scale) + +@media (max-width: 1488px) { + @include o_preview_iframe_styling($preview_scale * 0.80) +} + +@media (max-width: 600px) { + @include o_preview_iframe_styling($preview_scale * 0.60) +} diff --git a/addons/web/static/src/scss/switch_company_menu.scss b/addons/web/static/src/scss/switch_company_menu.scss new file mode 100644 index 00000000..6bc4fa4d --- /dev/null +++ b/addons/web/static/src/scss/switch_company_menu.scss @@ -0,0 +1,24 @@ + +.o_main_navbar { + .o_switch_company_menu { + > a .oe_topbar_name { + @include o-text-overflow($max-width: 250px); + } + .dropdown-item { + cursor: pointer; + + &:hover { + background-color: white; + } + + .o_py { + padding-top: 0.2rem !important; + padding-bottom: 0.2rem !important; + + &:hover { + background-color: #e9ecef !important; + } + } + } + } +} diff --git a/addons/web/static/src/scss/tooltip.scss b/addons/web/static/src/scss/tooltip.scss new file mode 100644 index 00000000..fdf10fea --- /dev/null +++ b/addons/web/static/src/scss/tooltip.scss @@ -0,0 +1,115 @@ + +.tooltip { + $arrow-size: 6px; + &.show { + opacity: 1; + } + + .tooltip-inner { + max-width: 300px; // fallback for browsers which do not support "vw" unit + max-width: 100vw; + background-color: $o-tooltip-background-color; + color: $o-tooltip-color; + border-radius: $border-radius; + box-shadow: 0px 0px 1px 1px $o-brand-secondary; + margin: 5px; + border-color: $o-tooltip-color; + + padding: 5px; + + text-align: left; + + .oe_tooltip_string { + background-color: $o-tooltip-title-background-color; + font-weight: bold; + padding: 5px 8px; + } + .oe_tooltip_help { + white-space: pre-line; + padding: 8px; + margin-bottom: 0px; + } + .oe_tooltip_technical { + padding: 8px; + margin: 0 0 0 15px; + list-style-type: circle; + + .oe_tooltip_technical_title { + font-weight: bold; + } + } + .oe_tooltip_help + .oe_tooltip_technical { + padding-top: 0px; + } + } + + .arrow { + &::after { + content: ''; + position: absolute; + } + } + + &.bs-tooltip-bottom { + margin-top: $tooltip-arrow-height; + .arrow { + top: $tooltip-arrow-height; + &::before { + border: $arrow-size solid transparent; + border-bottom-color: $o-tooltip-color; + } + &::after { + bottom: -1px; + border: $arrow-size solid transparent; + border-bottom-color: $o-tooltip-title-background-color; + } + } + } + &.bs-tooltip-top { + margin-bottom: $tooltip-arrow-height; + .arrow { + bottom: $tooltip-arrow-height; + &::before { + border: $arrow-size solid transparent; + border-top-color: $o-tooltip-color; + } + &::after { + border: $arrow-size solid transparent; + border-top-color: $o-tooltip-arrow-color; + margin-top: -1px; + } + } + } + &.bs-tooltip-left { + margin-right: $tooltip-arrow-height; + .arrow { + right: $tooltip-arrow-height; + &::before { + border: $arrow-size solid transparent; + border-left-color: $o-tooltip-color; + } + &::after { + border: $arrow-size solid transparent; + border-left-color: $o-tooltip-arrow-color; + margin-left: -1px; + } + } + } + &.bs-tooltip-right { + margin-left: $tooltip-arrow-height; + .arrow { + left: $tooltip-arrow-height; + &::before, + &::after { + border: $arrow-size solid transparent; + border-right-color: $o-tooltip-color; + } + &::after { + border: $arrow-size solid transparent; + border-right-color: $o-tooltip-arrow-color; + margin-right: -1px; + right: 0px; + } + } + } +} diff --git a/addons/web/static/src/scss/translation_dialog.scss b/addons/web/static/src/scss/translation_dialog.scss new file mode 100644 index 00000000..477f9371 --- /dev/null +++ b/addons/web/static/src/scss/translation_dialog.scss @@ -0,0 +1,10 @@ +// translation dialog +.o_translation_dialog { + .o_language_current { + font-weight: bold; + } + + .row { + margin-bottom: 9px; + } +} diff --git a/addons/web/static/src/scss/ui.scss b/addons/web/static/src/scss/ui.scss new file mode 100644 index 00000000..6cf669bb --- /dev/null +++ b/addons/web/static/src/scss/ui.scss @@ -0,0 +1,179 @@ +.o_hidden { + display: none!important; +} + +.o_text_overflow { + @include o-text-overflow; +} + +.ui-autocomplete { + z-index: $zindex-modal + 1; + max-width: 600px; + .ui-menu-item > a { + display: block; + } +} + +[aria-hidden="true"], [aria-hidden="1"] { + display: none!important; +} + +.dropdown-toggle { + white-space: nowrap; + + &.o-no-caret { + &::before, &::after { + content: normal; + } + } +} + +// Allow to change data-toggle="collapse" text according to status +:not(.collapsed) > .o-collapsed-label, +.collapsed > .o-not-collapsed-label { + display: none; +} + +// This is rtl language specific fix +// It will fix the extra space in ui-autocomplete class +// and flip the next and previous symbols of jquery ui. +.o_rtl { + .ui-autocomplete { + direction: ltr; + right: 0; + left: auto; + } + .ui-datepicker-next, .ui-datepicker-prev { + -webkit-transform: rotate(180deg); + -o-transform: rotate(180deg); + transform: rotate(180deg); + } +} + +// Overwrite bts4 custom-checkbox design (un-checked state only) +.custom-control.custom-checkbox { + .custom-control-input:not(:checked):not(:indeterminate) { + ~ .custom-control-label:before { + background: none; + outline: 1px solid $o-main-text-color; + } + &:disabled ~ .custom-control-label:before { + background: $o-brand-lightsecondary; + opacity: 0.25; + } + } + @media print { + -webkit-print-color-adjust: exact !important; + color-adjust: exact !important; + } +} + +.o_catch_attention { + position: relative; + z-index: 1; + animation: catchAttention 200ms ease 0s infinite normal; +} + +// bounce effect +@keyframes catchAttention { + 0%, 20%, 40%, 60%, 80%, 100% { + transition-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); + } + 0% { + transform: translateY(-30%); + } + 20% { + transform: translateY(-25%); + } + 40% { + transform: translateY(-20%); + } + 60% { + transform: translateY(-15%); + } + 80% { + transform: translateY(-10%); + } + 100% { + transform: translateY(-5%); + } +} + +// Declare a similar class as 'disabled' but with no visual effect +.o_debounce_disabled { + pointer-events: none; +} + +span.o_force_ltr { + display: inline-block; +} +.o_force_ltr, .o_field_phone { + unicode-bidi: embed; // ensure element has level of embedding for direction + /*rtl:ignore*/ + direction: ltr; +} + +// To fill the available space while keeping aspect ratio (crop). +// Assuming the important part of the image is its center. +.o_object_fit_cover { + object-fit: cover; +} + +.o_image_24_cover { + width: 24px; + height: 24px; + object-fit: cover; +} + +.o_image_40_cover { + width: 40px; + height: 40px; + object-fit: cover; +} + +.o_image_64_cover { + width: 64px; + height: 64px; + object-fit: cover; +} + +// Keep ratio but avoid cropping (so part of the background becomes visible). +.o_image_64_contain { + width: 64px; + height: 64px; + object-fit: contain; +} + +// When having a square is not necessary, the image will take less space if any +// of its width or height is smaller than 64px. +.o_image_64_max { + max-width: 64px; + max-height: 64px; +} + +.o_image_128_max { + max-width: 128px !important; + max-height: 128px !important; +} + +.o_width_128 { + width: 128px; +} + +.o_web_accesskey_overlay { + @include o-position-absolute(0, 0); + display: flex; + justify-content: center; + align-items: center; + width: 100%; + height: 100%; + background-color: rgba(0, 0, 0, .6); + color: #FFFFFF; + font-size: 1rem; + + // Needed as the parent button could be using font-awesome. + // Can't be put to a BS variable here as we are in assets common (will be + // overridden for backend but left with 'sans-serif' for frontend to not + // have an useless frontend rule. + font-family: sans-serif; +} diff --git a/addons/web/static/src/scss/ui_extra.scss b/addons/web/static/src/scss/ui_extra.scss new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/addons/web/static/src/scss/ui_extra.scss diff --git a/addons/web/static/src/scss/utils.scss b/addons/web/static/src/scss/utils.scss new file mode 100644 index 00000000..0fcca801 --- /dev/null +++ b/addons/web/static/src/scss/utils.scss @@ -0,0 +1,437 @@ +/// +/// 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; +} diff --git a/addons/web/static/src/scss/views.scss b/addons/web/static/src/scss/views.scss new file mode 100644 index 00000000..ff843145 --- /dev/null +++ b/addons/web/static/src/scss/views.scss @@ -0,0 +1,96 @@ +/** + * This file regroups the rules which apply on elements which are shared between + * all renderers. For field default rules, see the fields.scss file. + */ + +// Invisible modifier (can be inside the view, the button area, ...) +.o_invisible_modifier { + display: none!important; +} + +// Status +// This should normally be put in fields.scss but these classes are used outside +// of `.o_field_widget` so it needs to be placed at an upper level. +.o_status { + display: inline-block; + height: 12px; + width: 12px; + border-radius: 6px; + vertical-align: middle; + + background-color: gray('300'); + + &.o_status_green { + background-color: theme-color('success'); + } + &.o_status_red { + background-color: theme-color('danger'); + } +} + +.o_btn-link-as-button { + padding: 2px; + font-size:12px; + + & > a { + margin-bottom: -4px !important; + margin-left: 3px; + } +} + +// No content helper +.o_view_nocontent { + @include o-position-absolute(30%, 0, 0, 0); + pointer-events: none; + z-index: 1; + + .o_nocontent_help { + @include o-nocontent-empty; + + .o_view_nocontent_smiling_face:before { + @extend %o-nocontent-init-image; + @include size(120px, 140px); + background: transparent url(/web/static/src/img/smiling_face.svg) no-repeat center; + } + + .o_view_nocontent_neutral_face:before { + @extend %o-nocontent-init-image; + @include size(120px, 140px); + background: transparent url(/web/static/src/img/neutral_face.svg) no-repeat center; + } + + .o_view_nocontent_empty_folder:before { + @extend %o-nocontent-empty-document; + } + + .o_empty_custom_dashboard { + min-height: 327px; + margin-left: -$grid-gutter-width/2; + margin-top: -$grid-gutter-width/2; + padding: 100px 0 0 137px; + background: transparent url(/web/static/src/img/graph_background.png) no-repeat 0 0; + } + } +} + +.o_view_sample_data { + .o_list_table { + cursor: default !important; + + & > thead .o_list_record_selector { + pointer-events: none; + } + } + + .o_nocontent_help { + border-radius: 20%; + background-color: #f9f9f9; + box-shadow: 0 0 120px 100px #f9f9f9; + } + + .o_sample_data_disabled { + opacity: 0.33; + pointer-events: none; + user-select: none; + } +} diff --git a/addons/web/static/src/scss/web.zoomodoo.scss b/addons/web/static/src/scss/web.zoomodoo.scss new file mode 100644 index 00000000..3d855c96 --- /dev/null +++ b/addons/web/static/src/scss/web.zoomodoo.scss @@ -0,0 +1,34 @@ + +.zoomodoo { + position: relative; + + /* 'Shrink-wrap' the element */ + display: inline-block; + *display: inline; + *zoom: 1; + + img { + vertical-align: bottom; + } +} + +.zoomodoo-flyout { + position:absolute; + z-index: 100; + overflow: hidden; + background: #FFF; + top: 0; + width: 100%; + height: 100%; + img { + max-width: 500%; + } +} + +.zoomodoo-hover .zoomodoo-flyout { + left: 0; +} + +.zoomodoo-next .zoomodoo-flyout { + left: 100%; +}
\ No newline at end of file diff --git a/addons/web/static/src/scss/web_calendar.scss b/addons/web/static/src/scss/web_calendar.scss new file mode 100644 index 00000000..fd411e02 --- /dev/null +++ b/addons/web/static/src/scss/web_calendar.scss @@ -0,0 +1,788 @@ +// Variables +$o-cw-color-today: nth($o-colors, 3); +$o-cw-color-today-accent: #FC3D39; +$o-cw-popup-avatar-size: 16px; +$o-cw-filter-avatar-size: 20px; + +// Animations +@keyframes backgroundfade { + from { background-color: rgba($info, 0.5); } + to { background-color: rgba($info, 0.1); } +} + +.o_calendar_container { + height: 100%; + display: flex; +} + +.o_calendar_view { + flex: 1 1 auto; + min-width: 0; + height: 100%; + background-color: gray('100'); + background: linear-gradient(-45deg, gray('100'), white); + + .fc-event { + margin: 0 1px; + border-style: solid; + border-width: 0 0 0 3px; + border-radius: 0; + box-sizing: border-box; + overflow: hidden; + background: none; + font-size: 11px; + line-height: 1; + + &:not([href]):not([tabindex]) { + color: $body-color; + } + + &.fc-dragging.fc-day-grid-event.dayGridMonth .fc-content { + @include text-truncate(); + margin: 4px 4px 3px; + } + + .fc-bg { + background-color: mix(theme-color('primary'), white); // Used for placeholder events only (on creation) + @include size(101%); // Compensate border + opacity: 0.9; + transition: opacity 0.2s; + } + + .fc-content { + white-space: normal; + margin: 8px 4px; + font-size: 1.1em; + font-weight: 500; + } + + // Try to show one full lien for short event + &.fc-short .fc-content { + margin-top: 1px; + } + + &.o_cw_custom_highlight { + z-index: 10!important; + + .fc-bg{ + opacity: 0.95; + } + } + } + + .o_calendar_widget { + height: 100%; + + > .fc-view-container { + height: 100%; + } + + // === Adapt calendar table borders === + // ===================================== + + td { + border-color: gray('200'); + } + + .fc-time-grid .fc-slats .fc-minor td { + border-top-color: gray('300'); + } + + .fc-widget-content { + border-left-color: transparent; + } + + .fc-widget-header { + border-color: transparent; + border-bottom-color: $gray-200; + padding: 3px 0 5px; + } + + hr.fc-widget-header { + padding: 1px; + border: 0; + background: gray('400'); + } + + .fc-timeGrid-view .fc-day-grid .fc-row .fc-content-skeleton { + padding: .5em; + } + + .fc-event-container { + color: white; + } + + .o_calendar_disabled { + background-color: $gray-200; + border-color: white !important; + } + + // ====== Specific agenda types ====== + // ==================================== + + // ====== Both Day and Week agenda + .fc-timeGridDay-view, .fc-timeGridWeek-view { + .fc-axis { + padding-left: $o-horizontal-padding; + } + + // Hide unnecessary borders + table td, div.fc-row.fc-week.fc-widget-content { + border-left-color: transparent; + border-right-color: transparent; + } + + // Reinfornce default border color + tbody td { + border-top-color: gray('400'); + } + + // Remove dotted borders (half-hours) + .fc-time-grid .fc-slats .fc-minor td { + border-top-style: none; + } + + // Align labels and timelines + .fc-axis.fc-time { + border-top-color: transparent; + + span { + max-width: 45px; + margin-top: -19px; + position: relative; + display: block; + } + } + + // Add a small gap on top to show the first time label (0:00) + .fc-scroller .fc-time-grid > .fc-slats, + .fc-scroller .fc-time-grid > .fc-bg { + padding-top: 15px; + } + + // Row containing "all day" events + div.fc-day-grid { + background-color: $o-view-background-color; + box-shadow: 0 6px 12px -6px rgba(black, 0.16); + + + hr.fc-widget-header { + padding: 1px 0 0; + background: gray('300'); + } + + .fc-content-skeleton tr:not(:first-child) .fc-h-event{ + margin-top: 3px + } + } + + // Create a 'preudo-border' for the first row. The actual border + // it's hidden because of border-collapse settings. + .fc-slats tr:first-child td.fc-widget-content:last-child { + box-shadow: inset 0 1px 0 gray('400'); + } + + .fc-day.fc-widget-content.fc-today { + border-left-color: rgba($o-cw-color-today, 0.3); + border-right-color: rgba($o-cw-color-today, 0.3); + background: rgba($o-cw-color-today, 0.05 ); + } + + .fc-event { + // Prevent events with similar color to visually overlap each other + box-shadow: 0 0 0 1px white; + + &.fc-event:not(.fc-h-event) { + border-width: 3px 0 0; + + &.fc-not-start { + border-width: 0 0 3px; + + &.fc-not-end { + border-width: 0; + } + } + } + } + + // Reset position to keep the "nowIndicator" line visible + .fc-content-col { + position: initial; + } + } + + // ====== Day only + .fc-timeGridDay-view .fc-event { + padding: 10px; + font-size: 14px; + + // Try to avoid showing no title for short event + &.fc-short { + padding-top: 0; + padding-bottom: 0; + } + } + + // ====== Week only + .fc-timeGridWeek-view { + .fc-now-indicator { + left: $o-horizontal-padding; + } + + // Expand tiny events on hover/select + .fc-event:not(.fc-h-event).o_cw_custom_highlight, .fc-event:not(.fc-h-event).o_cw_custom_hover { + transition: margin .1s .3s, left .1s .3s, right .1s .3s; + margin: 0!important; + right: 1px!important; + left: 1px!important; + } + } + + // ====== Month only + .fc-dayGridMonth-view { + padding-left: $o-horizontal-padding; + + .fc-widget-header { + padding: 3px 0; + } + + .fc-week-number { + background: none; + } + + .fc-day-number { + margin: 5px; + padding: 0.1rem 0.3rem 0.1rem 0; + font-size: 1.2rem; + color: gray('900'); + font-weight: 400; + line-height: 1; + } + + .fc-day-top.fc-other-month { + opacity: 0.8; + + .fc-day-number { + color: gray('500'); + } + } + + td:last-child { + border-right-color: transparent; + } + + .fc-bg .fc-today { + background: $o-view-background-color; + border-color: gray('300'); + } + + .fc-content-skeleton .fc-today .fc-day-number { + margin-top: 3px; + padding: 0.4em 0.4em 0.35em; + border-radius: 100%; + min-width: 1.1em; + background: $o-cw-color-today-accent; + text-align: center; + color: white; + font-size: 1.1rem; + } + + .fc-more-cell { + > div, .fc-more { + height: 100%; + display: flex; + align-items: center; + justify-content: center; + } + } + + .fc-event { + margin: 0 3px 2px; + + .fc-content { + @include text-truncate(); + margin: 4px 4px 3px; + } + + &.o_cw_nobg { + .fc-bg { + visibility: hidden; + } + + &.o_cw_custom_hover, &.o_cw_custom_highlight, &:hover { + .fc-bg { + visibility: visible; + } + } + } + + &.fc-not-start { + border-right-width: 3px; + + .fc-content { + padding-left: 6px; + } + } + + &.fc-not-end { + margin-right: 0; + + .fc-content { + padding-right: 6px; + } + } + } + } + // ====== Year only + .fc-dayGridYear-view { + border: none; + height: 100%; + padding-top: 1rem; + padding-left: $o-horizontal-padding; + box-sizing: border-box; + display: flex; + flex-wrap: wrap; + justify-content: space-evenly; + overflow-y: auto; + + > table { + height: 100%; + } + + table, tr, th, td { + border: none; + } + + &.fc-readonly-year-view { + .fc-day-top:not(.fc-has-event) { + cursor: default !important; + } + } + + &:not(.fc-readonly-year-view) { + .fc-day-top:hover > .fc-day-number { + font-weight: bold; + border-radius: 100%; + text-align: center; + background-color: rgba(#87c0d1, 0.5); + color: gray('900'); + } + } + + > .fc-month-container { + width: 25%; + min-width: 25rem; + box-sizing: border-box; + + > .fc-month { + width: 21rem; + margin: auto; + + > .fc-toolbar.fc-header-toolbar { + margin-top: 15px; + margin-bottom: 5px; + cursor: default; + + h2 { + font-size: 1.2rem; + color: gray('600'); + } + } + + .fc-widget-header { + padding: 2px 0; + cursor: default; + } + + .fc-dayGridMonth-view { + padding-left: unset; + } + + .fc-week.fc-row { + min-height: unset; + } + + .fc-disabled-day { + background-color: unset; + } + + .fc-day-top { + text-align: center; + padding: 5px; + cursor: pointer; + + > .fc-day-number { + display: block; + float: unset; + line-height: unset; + margin: auto; + padding: 0.1rem 0; + font-size: 1.2rem; + } + + &.fc-today > .fc-day-number { + font-weight: bold; + } + } + } + } + } + // ====== RTL layout(s) + &.fc-rtl { + .fc-timeGrid-view .fc-event { + border-width: 0 3px 0 0; + } + + .fc-dayGridMonth-view .fc-event { + border-width: 0 3px 0 0; + + &.fc-not-start { + margin: 0 0 1px 5px; + border-width: 0 0 0 3px; + + .fc-content { + padding-right: 6px; + padding-left: 0; + } + } + + &.fc-not-end { + margin: 0 5px 1px 0; + + .fc-content { + padding-left: 6px; + padding-right: 0; + } + } + } + } + } +} + +.o_calendar_sidebar_container { + flex: 0 0 auto; + position: relative; + @include o-webclient-padding($top: $o-horizontal-padding/2); + background-color: $o-view-background-color; + border-left: 1px solid darken($o-control-panel-background-color, 20%); + overflow-y:auto; + + .o_calendar_sidebar { + width: 200px; + font-size: 14px; + + @include media-breakpoint-up('xl') { + width: 250px; + } + } + + .ui-datepicker { + margin: 0; + width: 100%; + padding: 0; + + &, td, .ui-datepicker-header, td a, td span { + border: 0; + } + + th { + padding: .7em .2em; + width: 14%; + + > span { + color: #666666; + } + } + + td { + padding: 0; + + a, span { + padding: 5px 0; + background: none; + text-align: center; + vertical-align: middle; + font-size: 1.2rem; + color: gray('900'); + font-weight: 400; + } + + &.ui-datepicker-current-day a { + background: $info; + color: color-yiq($info); + font-weight: bold; + } + + &.ui-datepicker-today a { + margin: auto; + border-radius: 100%; + padding: .1em; + width: 25px; + background: mix($o-cw-color-today-accent, white, 80%); + color: white; + } + + &.ui-datepicker-current-day.ui-datepicker-today a { + background: $o-cw-color-today-accent; + } + } + + .ui-datepicker-header { + background: none; + } + + .ui-datepicker-header { + border-radius: 0; + + .ui-datepicker-title { + color: gray('600'); + font-size: 1.2rem; + font-weight: normal; + } + + .ui-icon { + background-image: none; + text-indent: 0; + color: transparent; + + &:before { + font: normal normal normal 13px/1 FontAwesome; + content: "\f053"; + color: gray('400'); + } + &.ui-icon-circle-triangle-e:before { + content: "\f054" + } + } + + .ui-state-hover.ui-datepicker-next-hover, .ui-state-hover.ui-datepicker-prev-hover { + background: none; + border: none; + cursor: pointer; + + span:before { + color: gray('800'); + } + } + } + + .o_selected_range.o_color:not(.ui-datepicker-unselectable) { + background-color: $info; + animation: backgroundfade 2s forwards; + } + } + + .o_calendar_filter { + font-size: 0.9em; + padding: 2em 0 1em; + + .o_cw_filter_collapse_icon { + transition: all 0.3s ease; + @include o-hover-opacity(); + font-size: 0.7em; + } + + .collapsed .o_cw_filter_collapse_icon { + transform: rotate(90deg); + opacity: 1; + } + + .o_calendar_filter_item { + cursor: pointer; + overflow: hidden; + + input { + z-index: -1; + opacity: 0; + } + + .o_cw_filter_input_bg { + @include size(1.3em); + border-width: 2px; + border-style: solid; + border-radius: 1px; + overflow: hidden; + + &.o_beside_avatar { + @include size($o-cw-filter-avatar-size); + border-radius: 2px; + } + } + + input:not(:checked) + label .o_cw_filter_input_bg { + background: transparent!important; + + i.fa { + visibility: hidden; + } + } + + + .o_cw_filter_avatar { + @include size($o-cw-filter-avatar-size); + border-radius: 2px; + + &.fa { + padding: 4px 3px; + } + } + + .o_cw_filter_title { + line-height: $o-line-height-base; + flex-grow: 1; + } + + button.o_remove { + @include o-position-absolute(0,0,0); + transform: translateX(100%); + transition: transform 0.2s; + } + + &:hover { + button.o_remove { + transform: translateX(0%); + } + } + } + + .o_field_many2one { + margin-top: 1rem; + width: 100%; + } + } +} + +.o_cw_popover { + min-width: 256px; + max-width: 328px; + font-size: $font-size-base; + + .card-header, .card-header .popover-header { + font-size: 1.05em; + font-weight: 500; + line-height: 1; + } + + .card-footer { + background: none; + } + + .o_cw_popover_close { + cursor: pointer; + } + + .o_calendar_avatars { + line-height: 1; + } + + .o_calendar_avatars img { + margin-right: 0.4rem; + @include size($o-cw-popup-avatar-size); + border-radius: 100%; + } + + .list-group-item { + padding: 0.5rem 1rem; + border: none; + } + + .o_cw_popover_fields_secondary { + max-height: 170px; // Fallback for old browsers + max-height: 25vh; + overflow-y: auto; + padding-bottom: 1px; // prevents the scrollbar to show when not needed + + &::-webkit-scrollbar { + background: gray('200'); + width: 6px; + } + &::-webkit-scrollbar-thumb { + background: gray('500'); + } + } + + .fc-rtl & { + text-align: right; + .o_calendar_avatars { + > div { + justify-content: flex-end; + } + img { + order: 2; + margin: 0 0 0 0.4rem; + } + } + } +} + +// =============== Generate color classes =============== +@for $i from 1 through length($o-colors-complete) { + $color: nth($o-colors-complete, $i); + + .o_calendar_view .fc-view { + .fc-bgevent.o_calendar_color_#{$i - 1} { + border-color: $color; + background-color: $color; + opacity: 0.2; + } + .fc-event.o_calendar_color_#{$i - 1} { + border-color: $color; + color: darken($color, 35%); + opacity: 0.8; + + &.o_event_hightlight { + opacity: 1; + + .fc-content { + font-weight: bold; + } + } + + .fc-bg { + background: mix($color, white); + } + + &.o_cw_custom_hover, &.o_cw_custom_highlight { + box-shadow: 0 12px 12px -5px rgba($color, 0.3); + color: color-yiq($color); + + .fc-bg { + background: $color; + } + } + } + } + + .o_cw_filter_color_#{$i - 1} { + .o_cw_filter_input_bg { + border-color: $color; + background: $color; + color: color-yiq($color); + } + } + + .o_cw_popover.o_calendar_color_#{$i - 1} { + $color-subdle: mix(white, $color, 90%); + + .card-header, .card-header .popover-header { + background-color: $color-subdle; + color: color-yiq($color-subdle); + } + + .card-header { + border-color: mix($card-border-color, mix(white, $color)); + } + } +} + +.modal { + .o_attendee_head { + width: 32px; + margin-right: 5px; + } +} + +.o_dashboard { + .o_calendar_container .o_calendar_sidebar_container { + display: none; + } +} diff --git a/addons/web/static/src/scss/webclient.scss b/addons/web/static/src/scss/webclient.scss new file mode 100644 index 00000000..19f107b4 --- /dev/null +++ b/addons/web/static/src/scss/webclient.scss @@ -0,0 +1,140 @@ +:root { + font-size: $o-root-font-size; +} + +// ------------------------------------------------------------------ +// General +// ------------------------------------------------------------------ +.o_web_client { + direction: ltr; + position: relative; // normally useless but required by bootstrap-datepicker + background-color: $o-webclient-background-color; +} + +// ------------------------------------------------------------------ +// Misc. widgets +// ------------------------------------------------------------------ + +// Buttons +.o_icon_button { + background-color: transparent; + border: 0; + padding: 0; + outline: none; +} + +// Boolean Toggle widget +div.o_boolean_toggle.custom-control.custom-checkbox { + $line-height-computed: $line-height-base * $font-size-base; + $slider-width: $line-height-computed * 1.5; + $circle-width: $line-height-computed * 0.6; + + display: inline-block; + padding-left: $slider-width + 0.25rem; + + > label.custom-control-label { + &::before, &::after { + content: ""; + top: 0; + left: -($slider-width + 0.25rem); + } + &::before { + width: $slider-width; + height: 100%; + background-color: #a0a0a0 !important; + border-radius: 100px; + outline: none !important; + } + &::after { + transform: translate($line-height-computed * 0.2, $line-height-computed * 0.2); + width: ceil($circle-width / 1rem * $o-root-font-size); + height: ceil($circle-width / 1rem * $o-root-font-size); + border-radius: 100px; + background-color: $white; + cursor: pointer; + } + } + > input.custom-control-input:checked + label.custom-control-label { + &::before { + background-color: $o-brand-primary !important; + } + &::after { + transform: translate($slider-width - $circle-width - $line-height-computed * 0.2, $line-height-computed * 0.2); + background-image: none; + } + } +} + +// Full bg colors (bootstrap extension) +.bg-success-full { + background-color: theme-color('success'); +} +.bg-warning-full { + background-color: theme-color('warning'); +} +.bg-danger-full { + background-color: theme-color('danger'); +} +.bg-info-full { + background-color: theme-color('info'); +} +.bg-muted-full { + background-color: #dee2e6; +} + +// Light version of contextual bg colors (e.g. bg-danger-light) +@each $name in ('primary', 'secondary', 'success', 'danger', 'warning', 'info') { + @include bg-variant('.bg-#{$name}-light', rgba(theme-color($name), .5), #333); +} + +.o_web_accesskey_overlay { + font-family: $font-family-sans-serif; +} + +// Decorations +.text-bf { + font-weight: bold; +} +.text-it { + font-style: italic; +} + +//== Badges +.badge { + margin: 1px 2px 1px 0; +} + +// Btn-link variations +.btn-link { + font-weight: $btn-font-weight; + + &.btn-secondary { + @include o-btn-link-variant($body-color, $headings-color); + } + &.btn-success, &.text-success { + @include o-btn-link-variant($body-color, theme-color('success')); + } + &.btn-warning, &.text-warning { + @include o-btn-link-variant($body-color, theme-color('warning')); + } + &.btn-danger, &.text-danger { + @include o-btn-link-variant($body-color, theme-color('danger')); + } + &.btn-info, &.text-info { + @include o-btn-link-variant($body-color, darken(theme-color('info'), 20%)); + } +} + +//== Printing improvements +@media print { + .table-responsive { + overflow-x: initial; + } +} + +//== Action manager +// ensure special links are styled as pointers even when they don't +// have an href +[type="action"], [type="toggle"] { + cursor: pointer !important; +} diff --git a/addons/web/static/src/scss/webclient_extra.scss b/addons/web/static/src/scss/webclient_extra.scss new file mode 100644 index 00000000..8e8e3630 --- /dev/null +++ b/addons/web/static/src/scss/webclient_extra.scss @@ -0,0 +1,51 @@ +//------------------------------------------------------------------------------ +// Inputs and selects (note: put the o_input class to have the style) +//------------------------------------------------------------------------------ +[type="text"], [type="password"], [type="number"], textarea, select { + width: 100%; + display: block; + outline: none; +} +.o_input { + border: 1px solid $o-form-lightsecondary; + border-radius: $border-radius; + padding: 2px 4px; + color: #1f1f1f; + + .o_input { + border: none!important; + padding: 0; + } +} +select { + background: white; +} + +//------------------------------------------------------------------------------ +// Loading +//------------------------------------------------------------------------------ + +.o_loading { + background-color: $o-brand-odoo; + color: white; + padding: 4px; +} + +//------------------------------------------------------------------------------ +// User menu in navbar +//------------------------------------------------------------------------------ +#oe_main_menu_navbar .oe_user_menu_placeholder .o_user_menu .oe_topbar_name { + @include o-text-overflow; + max-width: 150px; +} + +.o_rtl { + .navbar-right { + padding: 0; // fix for user agent stylesheet -webkit-padding-start: 40px in rtl language + } +} + +.btn-secondary { + // Bootstrap variables do not control the color for btn-secondary border + border-color: $border-color; +} diff --git a/addons/web/static/src/scss/webclient_layout.scss b/addons/web/static/src/scss/webclient_layout.scss new file mode 100644 index 00000000..4d2a7b44 --- /dev/null +++ b/addons/web/static/src/scss/webclient_layout.scss @@ -0,0 +1,84 @@ +// ------------------------------------------------------------------ +// Base layout rules, use the 'webclient.scss' file for styling +// ------------------------------------------------------------------ +html { + height: 100%; + + .o_web_client { + height: 100%; + display: flex; + flex-flow: column nowrap; + + > .o_action_manager { + direction: ltr; //Define direction attribute here so when rtlcss preprocessor run, it converts it to rtl + flex: 1 1 auto; + height: 100%; + overflow: hidden; + + > .o_action { + height: 100%; + display: flex; + flex-flow: column nowrap; + > .o_control_panel { + flex: 0 0 auto; + } + > .o_content { + flex: 1 1 auto; + position: relative; // Allow to redistribute the 100% height to its child + overflow: auto; + height: 100%; + } + } + } + + &.o_fullscreen { + .o_main_navbar { + display: none; + } + } + + .o_main_navbar { + flex: 0 0 auto; + } + + .o_control_panel { + flex: 0 0 auto; + } + + .o_content { + direction: ltr; //Define direction attribute here so when rtlcss preprocessor run, it converts it to rtl + flex: 1 1 auto; + position: relative; // Allow to redistribute the 100% height to its child + + > .o_view_controller { + position: absolute; // Get the 100% height of its flex parent + top: 0; + right: 0; + bottom: 0; + left: 0; + height: 100%; + direction: ltr; + } + } + } + + .o_loading { + position: fixed; + bottom: 0; + right: 0; + z-index: $zindex-modal + 1; + } +} + +@media print { + html .o_web_client { + .o_main_navbar { + display: none; + } + .o_content { + position: static; + overflow: visible; + height: auto; + } + } +} |
