summaryrefslogtreecommitdiff
path: root/addons/web/static/src/scss
diff options
context:
space:
mode:
authorstephanchrst <stephanchrst@gmail.com>2022-05-10 21:51:50 +0700
committerstephanchrst <stephanchrst@gmail.com>2022-05-10 21:51:50 +0700
commit3751379f1e9a4c215fb6eb898b4ccc67659b9ace (patch)
treea44932296ef4a9b71d5f010906253d8c53727726 /addons/web/static/src/scss
parent0a15094050bfde69a06d6eff798e9a8ddf2b8c21 (diff)
initial commit 2
Diffstat (limited to 'addons/web/static/src/scss')
-rw-r--r--addons/web/static/src/scss/animation.scss146
-rw-r--r--addons/web/static/src/scss/attachment_preview.scss113
-rw-r--r--addons/web/static/src/scss/banner.scss9
-rw-r--r--addons/web/static/src/scss/base_document_layout.scss43
-rw-r--r--addons/web/static/src/scss/base_frontend.scss29
-rw-r--r--addons/web/static/src/scss/base_settings.scss170
-rw-r--r--addons/web/static/src/scss/bootstrap_overridden.scss148
-rw-r--r--addons/web/static/src/scss/bootstrap_overridden_frontend.scss17
-rw-r--r--addons/web/static/src/scss/bootstrap_overridden_report.scss2
-rw-r--r--addons/web/static/src/scss/bootstrap_review.scss191
-rw-r--r--addons/web/static/src/scss/bs_mixins_overrides.scss74
-rw-r--r--addons/web/static/src/scss/color_picker.scss62
-rw-r--r--addons/web/static/src/scss/colorpicker.scss48
-rw-r--r--addons/web/static/src/scss/control_panel.scss137
-rw-r--r--addons/web/static/src/scss/data_export.scss53
-rw-r--r--addons/web/static/src/scss/datepicker.scss99
-rw-r--r--addons/web/static/src/scss/daterangepicker.scss69
-rw-r--r--addons/web/static/src/scss/debug_manager.scss8
-rw-r--r--addons/web/static/src/scss/domain_selector.scss204
-rw-r--r--addons/web/static/src/scss/dropdown.scss91
-rw-r--r--addons/web/static/src/scss/dropdown_extra.scss60
-rw-r--r--addons/web/static/src/scss/dropdown_menu.scss46
-rw-r--r--addons/web/static/src/scss/fields.scss521
-rw-r--r--addons/web/static/src/scss/fields_extra.scss59
-rw-r--r--addons/web/static/src/scss/file_upload.scss58
-rw-r--r--addons/web/static/src/scss/fontawesome_overridden.scss38
-rw-r--r--addons/web/static/src/scss/fonts.scss91
-rw-r--r--addons/web/static/src/scss/form_view.scss996
-rw-r--r--addons/web/static/src/scss/form_view_extra.scss109
-rw-r--r--addons/web/static/src/scss/graph_view.scss81
-rw-r--r--addons/web/static/src/scss/import_bootstrap.scss53
-rw-r--r--addons/web/static/src/scss/kanban_column_progressbar.scss147
-rw-r--r--addons/web/static/src/scss/kanban_dashboard.scss210
-rw-r--r--addons/web/static/src/scss/kanban_examples_dialog.scss97
-rw-r--r--addons/web/static/src/scss/kanban_view.scss648
-rw-r--r--addons/web/static/src/scss/keyboard.scss16
-rw-r--r--addons/web/static/src/scss/layout_background.scss99
-rw-r--r--addons/web/static/src/scss/layout_boxed.scss108
-rw-r--r--addons/web/static/src/scss/layout_clean.scss88
-rw-r--r--addons/web/static/src/scss/layout_standard.scss25
-rw-r--r--addons/web/static/src/scss/lazyloader.scss8
-rw-r--r--addons/web/static/src/scss/list_view.scss392
-rw-r--r--addons/web/static/src/scss/list_view_extra.scss42
-rw-r--r--addons/web/static/src/scss/mimetypes.scss73
-rw-r--r--addons/web/static/src/scss/modal.scss131
-rw-r--r--addons/web/static/src/scss/model_field_selector.scss134
-rw-r--r--addons/web/static/src/scss/name_and_signature.scss60
-rw-r--r--addons/web/static/src/scss/navbar.scss171
-rw-r--r--addons/web/static/src/scss/navbar_mobile.scss98
-rw-r--r--addons/web/static/src/scss/notification.scss25
-rw-r--r--addons/web/static/src/scss/pivot_view.scss104
-rw-r--r--addons/web/static/src/scss/popover.scss152
-rw-r--r--addons/web/static/src/scss/primary_variables.scss119
-rw-r--r--addons/web/static/src/scss/progress_bar.scss36
-rw-r--r--addons/web/static/src/scss/rainbow.scss255
-rw-r--r--addons/web/static/src/scss/report.scss106
-rw-r--r--addons/web/static/src/scss/report_backend.scss14
-rw-r--r--addons/web/static/src/scss/ribbon.scss74
-rw-r--r--addons/web/static/src/scss/search_panel.scss144
-rw-r--r--addons/web/static/src/scss/search_view.scss198
-rw-r--r--addons/web/static/src/scss/search_view_extra.scss36
-rw-r--r--addons/web/static/src/scss/secondary_variables.scss44
-rw-r--r--addons/web/static/src/scss/special_fields.scss55
-rw-r--r--addons/web/static/src/scss/switch_company_menu.scss24
-rw-r--r--addons/web/static/src/scss/tooltip.scss115
-rw-r--r--addons/web/static/src/scss/translation_dialog.scss10
-rw-r--r--addons/web/static/src/scss/ui.scss179
-rw-r--r--addons/web/static/src/scss/ui_extra.scss0
-rw-r--r--addons/web/static/src/scss/utils.scss437
-rw-r--r--addons/web/static/src/scss/views.scss96
-rw-r--r--addons/web/static/src/scss/web.zoomodoo.scss34
-rw-r--r--addons/web/static/src/scss/web_calendar.scss788
-rw-r--r--addons/web/static/src/scss/webclient.scss140
-rw-r--r--addons/web/static/src/scss/webclient_extra.scss51
-rw-r--r--addons/web/static/src/scss/webclient_layout.scss84
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;
+ }
+ }
+}