diff options
| author | qsm-odoo <qsm@odoo.com> | 2017-04-19 18:06:12 +0200 |
|---|---|---|
| committer | qsm-odoo <qsm@odoo.com> | 2017-05-04 12:07:30 +0200 |
| commit | 250e716c3a12e88acb4a199b427dc47d4ec55d5d (patch) | |
| tree | 0be849fd0af8d54f556af5f1274e641200ec82ef /addons/web/static/src | |
| parent | 8943aae665ea906aa03bc30cada884c571738b7c (diff) | |
[REF] web, *: restore views design
Since the new views merge, a lot of design elements were broken. This
was particularly impacting the fields in the editable list view; indeed
the editable list view is not using an inline form view anymore so the
fields in the list were not properly styled as the LESS was still
defined assuming the form view environment (for example the invalid
fields were red for o2m fields in form view but not in editable list
view even though they got the right CSS class).
This commit refactor the LESS following these rules:
- No more division of non-layout and layout rules. Dividing LESS rules
in x_layout.less and x.less was a mistake. Many rules can be
considered to be layout and not layout at the same time, developers
always have to switch from one file part to the other, many CSS
selectors (and rules!) are duplicated for nothing, ...
- Field style is extracted from x_view.less and put in the new
fields.less file. As before, the fields_extra.less will contain the
rules specific to community so that the enterprise repo can override
those by replacing the whole file.
- Many classes have been renamed so that o_form_x_y becomes o_x_y as
many classes can now be applied outside of form view. These classes
should not be used in templates anyway.
The commit also changes the DOM of fields so that it is more minimalist
(no useless parent div, etc).
Input elements are not automatically styled anymore, they have to get
the o_input class explicitely. This allows to fix lots of small style
bugs of previous versions (required monetary field had not the proper
style, readonly m2m tags appeared as editable, ...). This also improves
the LESS code.
The editable list view should also completely stop flickering on chrome
and firefox.
The commit also removes the orange outline on list view dirty cells.
The commit also removes deprecated static xml, LESS and other code.
Notice there are still styles to restore/fix and LESS to improve.
Diffstat (limited to 'addons/web/static/src')
33 files changed, 1210 insertions, 1678 deletions
diff --git a/addons/web/static/src/js/core/dom.js b/addons/web/static/src/js/core/dom.js index 4333061f924..2dad9eb912f 100644 --- a/addons/web/static/src/js/core/dom.js +++ b/addons/web/static/src/js/core/dom.js @@ -56,7 +56,12 @@ return { if ($textarea.data("auto_resize")) { return; } - function resize () { + + var $fixedTextarea; + var minHeight; + + function resize() { + $fixedTextarea.insertAfter($textarea); var heightOffset; var style = window.getComputedStyle($textarea[0], null); if (style.boxSizing === 'content-box') { @@ -64,28 +69,25 @@ return { } else { heightOffset = parseFloat(style.borderTopWidth) + parseFloat(style.borderBottomWidth); } - $fixed_text_area.width($textarea.width()); - $fixed_text_area.val($textarea.val()); - var height = $fixed_text_area[0].scrollHeight; - $textarea.css({height: Math.max(height + heightOffset, min_height)}); + $fixedTextarea.width($textarea.width()); + $fixedTextarea.val($textarea.val()); + var height = $fixedTextarea[0].scrollHeight; + $textarea.css({height: Math.max(height + heightOffset, minHeight)}); } options = options || {}; + minHeight = (options && options.min_height) || 50; - var $fixed_text_area; - var min_height = (options && options.min_height) || 50; - if (!$fixed_text_area) { - $fixed_text_area = $('<textarea disabled>').css({ - position: 'absolute', - opacity: 0, - height: 10, - top: -10000, - left: -10000, - }); - $fixed_text_area.addClass($textarea[0].className); - $fixed_text_area.insertAfter($textarea); - $fixed_text_area.data("auto_resize", true); - } + $fixedTextarea = $('<textarea disabled>', { + class: $textarea[0].className, + }).css({ + position: 'absolute', + opacity: 0, + height: 10, + top: -10000, + left: -10000, + }); + $fixedTextarea.data("auto_resize", true); var style = window.getComputedStyle($textarea[0], null); if (style.resize === 'vertical') { @@ -101,7 +103,6 @@ return { core.bus.on('DOM_updated', options.parent, resize); core.bus.on('view_shown', options.parent, resize); } - }, /** * Detaches widgets from the DOM and performs their on_detach_callback() diff --git a/addons/web/static/src/js/fields/abstract_field.js b/addons/web/static/src/js/fields/abstract_field.js index 4948a3e3c31..dd2caaa5dd1 100644 --- a/addons/web/static/src/js/fields/abstract_field.js +++ b/addons/web/static/src/js/fields/abstract_field.js @@ -34,7 +34,6 @@ var field_utils = require('web.field_utils'); var Widget = require('web.Widget'); var AbstractField = Widget.extend({ - className: 'o_field_widget', events: { 'keydown': '_onKeydown', }, @@ -151,6 +150,7 @@ var AbstractField = Widget.extend({ var self = this; return this._super.apply(this, arguments).then(function () { self.$el.attr('name', self.name); + self.$el.addClass('o_field_widget'); return self._render(); }); }, diff --git a/addons/web/static/src/js/fields/basic_fields.js b/addons/web/static/src/js/fields/basic_fields.js index e600954b967..7b48574fb44 100644 --- a/addons/web/static/src/js/fields/basic_fields.js +++ b/addons/web/static/src/js/fields/basic_fields.js @@ -184,7 +184,7 @@ var InputField = DebouncedField.extend({ */ _prepareInput: function ($input) { this.$input = $input || $("<input/>"); - this.$input.addClass('o_form_input'); + this.$input.addClass('o_input'); this.$input.attr({ type: 'text', placeholder: this.attrs.placeholder || "", @@ -261,12 +261,13 @@ var InputField = DebouncedField.extend({ }); var FieldChar = InputField.extend({ - supportedFieldTypes: ['char'], + className: 'o_field_char', tagName: 'span', + supportedFieldTypes: ['char'], }); var FieldDate = InputField.extend({ - className: "o_form_field_date", + className: "o_field_date", tagName: "span", supportedFieldTypes: ['date'], @@ -411,7 +412,8 @@ var FieldDateTime = FieldDate.extend({ }); var FieldMonetary = InputField.extend({ - className: 'o_form_field_monetary o_list_number', + className: 'o_field_monetary o_field_number', + tagName: 'span', supportedFieldTypes: ['float', 'monetary'], resetOnAnyFieldChange: true, // Have to listen to currency changes @@ -436,6 +438,7 @@ var FieldMonetary = InputField.extend({ if (this.mode === 'edit' && this.currency) { this.tagName = 'div'; + this.className += ' o_input'; } }, @@ -554,28 +557,18 @@ var FieldBoolean = AbstractField.extend({ /** * The actual checkbox is designed in css to have full control over its * appearance, as opposed to letting the browser and the os decide how - * a checkbox should look. The actual input is disabled and hidden. + * a checkbox should look. The actual input is disabled and hidden. In + * readonly mode, the checkbox is disabled. * * @override * @private */ - _renderReadonly: function () { + _render: function () { var $checkbox = this._formatValue(this.value); this.$input = $checkbox.find('input'); - this.$el.empty().append($checkbox); - }, - - /** - * The actual checkbox is designed in css to have full control over its - * appearance, as opposed to letting the browser and the os decide how - * a checkbox should look. The actual input is hidden. - * - * @override - * @private - */ - _renderEdit: function () { - this._renderReadonly(); - this.$input.prop('disabled', false); + this.$input.prop('disabled', this.mode === 'readonly'); + this.$el.addClass($checkbox.attr('class')); + this.$el.empty().append($checkbox.contents()); }, //-------------------------------------------------------------------------- @@ -591,7 +584,6 @@ var FieldBoolean = AbstractField.extend({ _onChange: function () { this._setValue(this.$input[0].checked); }, - /** * Implement keyboard movements. Mostly useful for its environment, such * as a list view. @@ -624,6 +616,8 @@ var FieldBoolean = AbstractField.extend({ }); var FieldInteger = InputField.extend({ + className: 'o_field_integer o_field_number', + tagName: 'span', supportedFieldTypes: ['integer'], //-------------------------------------------------------------------------- @@ -669,6 +663,8 @@ var FieldInteger = InputField.extend({ }); var FieldFloat = InputField.extend({ + className: 'o_field_float o_field_number', + tagName: 'span', supportedFieldTypes: ['float'], /** @@ -696,22 +692,6 @@ var FieldFloat = InputField.extend({ isSet: function () { return this.value === 0 || this._super.apply(this, arguments); }, - - //-------------------------------------------------------------------------- - // Private - //-------------------------------------------------------------------------- - - /** - * Format value according to precision parameter. - * - * @override - * @private - */ - _renderReadonly: function () { - var value = this._formatValue(this.value); - var $span = $('<span>').addClass('o_form_field o_form_field_number').text(value); - this.$el.html($span); - }, }); var FieldFloatTime = FieldFloat.extend({ @@ -743,21 +723,28 @@ var FieldFloatTime = FieldFloat.extend({ }); var FieldText = DebouncedField.extend({ + className: 'o_field_text', supportedFieldTypes: ['text'], /** - * In edit mode, the text widget contains a textarea. We append it in - * start() instead of _renderEdit() to keep the same textarea even - * if several _renderEdit are done. This allows to keep the cursor - * position and to autoresize only once. + * @constructor + */ + init: function () { + this._super.apply(this, arguments); + + if (this.mode === 'edit') { + this.tagName = 'textarea'; + this.className += ' o_input'; + } + }, + /** + * As it it done in the start function, the autoresize is done only once. * * @override */ start: function () { - this.$el.addClass('o_list_text o_form_textarea'); - if (this.mode === 'edit') { - this.$textarea = $('<textarea>').appendTo(this.$el); + this.$textarea = this.$el; if (this.attrs.placeholder) { this.$textarea.attr('placeholder', this.attrs.placeholder); } @@ -816,13 +803,14 @@ var FieldText = DebouncedField.extend({ * Displays a handle to modify the sequence. */ var HandleWidget = AbstractField.extend({ - tagName: 'span', className: 'o_row_handle fa fa-arrows ui-sortable-handle', + tagName: 'span', description: "", supportedFieldTypes: ['integer'], }); -var EmailWidget = InputField.extend({ +var FieldEmail = InputField.extend({ + className: 'o_field_email', prefix: 'mailto', supportedFieldTypes: ['char'], @@ -853,7 +841,8 @@ var EmailWidget = InputField.extend({ } }); -var FieldPhone = EmailWidget.extend({ +var FieldPhone = FieldEmail.extend({ + className: 'o_field_phone', prefix: 'tel', /** @@ -907,6 +896,7 @@ var FieldPhone = EmailWidget.extend({ }); var UrlWidget = InputField.extend({ + className: 'o_field_url', supportedFieldTypes: ['char'], /** @@ -939,9 +929,9 @@ var UrlWidget = InputField.extend({ var AbstractFieldBinary = AbstractField.extend({ events: _.extend({}, AbstractField.prototype.events, { - 'change .o_form_input_file': 'on_file_change', + 'change .o_input_file': 'on_file_change', 'click .o_select_file_button': function () { - this.$('.o_form_input_file').click(); + this.$('.o_input_file').click(); }, 'click .o_clear_file_button': 'on_clear', }), @@ -1084,8 +1074,8 @@ var FieldBinaryFile = AbstractFieldBinary.extend({ this.on_save_as(event); } }, - 'click .o_form_input': function () { // eq[0] - this.$('.o_form_input_file').click(); + 'click .o_input': function () { // eq[0] + this.$('.o_input_file').click(); }, }), supportedFieldTypes: ['binary'], @@ -1106,7 +1096,7 @@ var FieldBinaryFile = AbstractFieldBinary.extend({ if (this.value) { this.$el.children().removeClass('o_hidden'); this.$('.o_select_file_button').first().addClass('o_hidden'); - this.$('.o_form_input').eq(0).val(this.filename_value || this.value); + this.$('.o_input').eq(0).val(this.filename_value || this.value); } else { this.$el.children().addClass('o_hidden'); this.$('.o_select_file_button').first().removeClass('o_hidden'); @@ -1727,8 +1717,8 @@ var FieldToggleBoolean = AbstractField.extend({ * @private */ _render: function () { - var class_name = this.value ? 'o_toggle_button_success' : 'text-muted'; - this.$('i').attr('class', ('fa fa-circle ' + class_name)); + var className = this.value ? 'o_toggle_button_success' : 'text-muted'; + this.$('i').addClass('fa fa-circle ' + className); var title = this.value ? this.attrs.options.active : this.attrs.options.inactive; this.$el.attr('title', title); }, @@ -1747,7 +1737,6 @@ var FieldToggleBoolean = AbstractField.extend({ event.stopPropagation(); this._setValue(!this.value); }, - }); var JournalDashboardGraph = AbstractField.extend({ @@ -1891,7 +1880,7 @@ var FieldDomain = AbstractField.extend({ events: _.extend({}, AbstractField.prototype.events, { "click .o_domain_show_selection_button": "_onShowSelectionButtonClick", - "click .o_form_field_domain_dialog_button": "_onDialogEditButtonClick", + "click .o_field_domain_dialog_button": "_onDialogEditButtonClick", }), custom_events: { "domain_changed": "_onDomainSelectorValueChange", @@ -1907,7 +1896,7 @@ var FieldDomain = AbstractField.extend({ this.inDialog = !!this.nodeOptions.in_dialog; this.fsFilters = this.nodeOptions.fs_filters || {}; - this.className = "o_form_field_domain"; + this.className = "o_field_domain"; if (this.mode === "edit") { this.className += " o_edit_mode"; } @@ -2190,7 +2179,7 @@ var AceEditor = DebouncedField.extend({ return { DebouncedField: DebouncedField, - EmailWidget: EmailWidget, + FieldEmail: FieldEmail, FieldBinaryFile: FieldBinaryFile, FieldBinaryImage: FieldBinaryImage, FieldBoolean: FieldBoolean, diff --git a/addons/web/static/src/js/fields/field_registry.js b/addons/web/static/src/js/fields/field_registry.js index 41889fcfa85..dcd8f7acd48 100644 --- a/addons/web/static/src/js/fields/field_registry.js +++ b/addons/web/static/src/js/fields/field_registry.js @@ -29,7 +29,7 @@ registry .add('float', basic_fields.FieldFloat) .add('char', basic_fields.FieldChar) .add('handle', basic_fields.HandleWidget) - .add('email', basic_fields.EmailWidget) + .add('email', basic_fields.FieldEmail) .add('phone', basic_fields.FieldPhone) .add('url', basic_fields.UrlWidget) .add('image', basic_fields.FieldBinaryImage) diff --git a/addons/web/static/src/js/fields/field_utils.js b/addons/web/static/src/js/fields/field_utils.js index afc15b7a882..508d97b131c 100644 --- a/addons/web/static/src/js/fields/field_utils.js +++ b/addons/web/static/src/js/fields/field_utils.js @@ -35,13 +35,16 @@ var _t = core._t; * @returns {jQueryElement} */ function formatBoolean(value) { - var $input = $('<input type="checkbox">') - .prop('checked', value) - .prop('disabled', true); - return $('<div>') - .addClass('o_checkbox') - .append($input) - .append($('<span>')); + var $input = $('<input/>', { + type: 'checkbox', + }).prop({ + checked: value, + disabled: true, + }); + var $div = $('<div/>', { + class: 'o_checkbox', + }); + return $div.append($input, '<span/>'); } /** @@ -342,7 +345,7 @@ function parseDateTime(value, field, options) { var pattern2 = datePatternWoZero + ' ' + timePatternWoZero; var datetime; if (options && options.isUTC) { - // phatomjs crash if we don't use this format + // phatomjs crash if we don't use this format datetime = moment.utc(value.replace(' ', 'T') + 'Z'); } else { datetime = moment.utc(value, [pattern1, pattern2, moment.ISO_8601], true); diff --git a/addons/web/static/src/js/fields/relational_fields.js b/addons/web/static/src/js/fields/relational_fields.js index 28d790b16c4..d7e65f5c4bf 100644 --- a/addons/web/static/src/js/fields/relational_fields.js +++ b/addons/web/static/src/js/fields/relational_fields.js @@ -81,9 +81,9 @@ var FieldMany2One = AbstractField.extend({ 'search_create_popup': '_onSearchCreatePopup', }, events: _.extend({}, AbstractField.prototype.events, { - 'click .o_form_input': '_onInputClick', - 'focusout .o_form_input': '_onInputFocusout', - 'keyup .o_form_input': '_onInputKeyup', + 'click input': '_onInputClick', + 'focusout input': '_onInputFocusout', + 'keyup input': '_onInputKeyup', 'click .o_external_button': '_onExternalButtonClick', 'click': '_onClick', }), @@ -106,7 +106,7 @@ var FieldMany2One = AbstractField.extend({ // what is 'this.floating' for? add a comment! this.floating = false; - this.$input = this.$('.o_form_input'); + this.$input = this.$('input'); this.$external_button = this.$('.o_external_button'); return this._super.apply(this, arguments); }, @@ -713,7 +713,7 @@ var FieldX2Many = AbstractField.extend({ onFailure: def.reject.bind(def), }); } else { - self.renderer.setRowMode(recordID, 'readonly', true); + self.renderer.setRowMode(recordID, 'readonly'); def.resolve(); } }); @@ -819,7 +819,7 @@ var FieldX2Many = AbstractField.extend({ }); var FieldOne2Many = FieldX2Many.extend({ - className: 'o_form_field_one2many', + className: 'o_field_one2many', supportedFieldTypes: ['one2many'], //-------------------------------------------------------------------------- @@ -937,7 +937,7 @@ var FieldOne2Many = FieldX2Many.extend({ }); var FieldMany2Many = FieldX2Many.extend({ - className: 'o_form_field_many2many', + className: 'o_field_many2many', supportedFieldTypes: ['many2many'], /** @@ -1019,7 +1019,7 @@ var FieldMany2ManyBinaryMultiFiles = AbstractField.extend({ events: { 'click .o_attach': '_onAttach', 'click .oe_delete': '_onDelete', - 'change .o_form_input_file': '_onFileChanged', + 'change .o_input_file': '_onFileChanged', }, /** * @constructor @@ -1102,7 +1102,7 @@ var FieldMany2ManyBinaryMultiFiles = AbstractField.extend({ _onAttach: function () { // This widget uses a hidden form to upload files. Clicking on 'Attach' // will simulate a click on the related input. - this.$('.o_form_input_file').click(); + this.$('.o_input_file').click(); }, /** * @private @@ -1196,7 +1196,7 @@ var FieldMany2ManyBinaryMultiFiles = AbstractField.extend({ var FieldMany2ManyTags = AbstractField.extend({ tag_template: "FieldMany2ManyTag", - className: "o_form_field o_form_field_many2manytags", + className: "o_field_many2manytags", supportedFieldTypes: ['many2many'], custom_events: { field_changed: '_onFieldChanged', @@ -1204,13 +1204,24 @@ var FieldMany2ManyTags = AbstractField.extend({ }, events: _.extend({}, AbstractField.prototype.events, { 'click .o_delete': '_onDeleteTag', - 'keydown .o_form_field_many2one input': '_onKeyDown', + 'keydown .o_field_many2one input': '_onKeyDown', }), fieldsToFetch: { color: {type: 'integer'}, display_name: {type: 'char'}, }, + /** + * @constructor + */ + init: function () { + this._super.apply(this, arguments); + + if (this.mode === 'edit') { + this.className += ' o_input'; + } + }, + //-------------------------------------------------------------------------- // Public //-------------------------------------------------------------------------- @@ -1310,7 +1321,7 @@ var FieldMany2ManyTags = AbstractField.extend({ this.many2one._getSearchBlacklist = function () { return self.value.res_ids; }; - this.many2one.appendTo(this.$el); + return this.many2one.appendTo(this.$el); }, /** * @private @@ -1445,7 +1456,7 @@ var FormFieldMany2ManyTags = FieldMany2ManyTags.extend({ var KanbanFieldMany2ManyTags = FieldMany2ManyTags.extend({ _render: function () { var self = this; - this.$el.addClass('o_form_field_many2manytags o_kanban_tags'); + this.$el.addClass('o_field_many2manytags o_kanban_tags'); _.each(this.value.data, function (m2m) { // 10th color is invisible if ('color' in m2m.data && m2m.data.color !== 10) { diff --git a/addons/web/static/src/js/views/basic/basic_renderer.js b/addons/web/static/src/js/views/basic/basic_renderer.js index e150b0b2dd0..25ba02530be 100644 --- a/addons/web/static/src/js/views/basic/basic_renderer.js +++ b/addons/web/static/src/js/views/basic/basic_renderer.js @@ -52,7 +52,7 @@ var BasicRenderer = AbstractRenderer.extend({ if (!canBeSaved) { invalidFields.push(widget.name); } - widget.$el.toggleClass('o_form_invalid', !canBeSaved); + widget.$el.toggleClass('o_field_invalid', !canBeSaved); }); return invalidFields; }, @@ -249,9 +249,9 @@ var BasicRenderer = AbstractRenderer.extend({ } // Toggle modifiers CSS classes if necessary - element.$el.toggleClass("o_form_invisible", !!modifiers.invisible); - element.$el.toggleClass("o_readonly", !!modifiers.readonly); - element.$el.toggleClass("o_form_required", !!modifiers.required); + element.$el.toggleClass("o_invisible_modifier", !!modifiers.invisible); + element.$el.toggleClass("o_readonly_modifier", !!modifiers.readonly); + element.$el.toggleClass("o_required_modifier", !!modifiers.required); // Call associated callback if (element.callback) { @@ -495,7 +495,15 @@ var BasicRenderer = AbstractRenderer.extend({ // Update the modifiers registration by associating the widget and by // giving the modifiers options now (as the potential callback is // associated to new widget) - this._registerModifiers(node, record, widget, modifiersOptions); + this._registerModifiers(node, record, widget, _.extend({ + callback: (function (element, modifiers, record) { + element.$el.toggleClass('o_field_empty', !!( + record.data.id + && (modifiers.readonly || this.mode === 'readonly') + && !element.widget.isSet() + )); + }).bind(this), + }, modifiersOptions || {})); return widget; }, diff --git a/addons/web/static/src/js/views/form/form_renderer.js b/addons/web/static/src/js/views/form/form_renderer.js index 20244f12e95..f6bcbd49eeb 100644 --- a/addons/web/static/src/js/views/form/form_renderer.js +++ b/addons/web/static/src/js/views/form/form_renderer.js @@ -8,10 +8,6 @@ var core = require('web.core'); var _t = core._t; var qweb = core.qweb; -var FIELD_CLASSES = { - 'one2many': 'o_field_one2many', -}; - var FormRenderer = BasicRenderer.extend({ className: "o_form_view", /** @@ -27,7 +23,7 @@ var FormRenderer = BasicRenderer.extend({ //-------------------------------------------------------------------------- /** - * Extend the method so that labels also receive the 'o_form_invalid' class + * Extend the method so that labels also receive the 'o_field_invalid' class * if necessary. * * @override @@ -40,14 +36,14 @@ var FormRenderer = BasicRenderer.extend({ var fieldNames = this._super.apply(this, arguments); var $labels = this.$('label'); - $labels.removeClass('o_form_invalid'); + $labels.removeClass('o_field_invalid'); _.each(fieldNames, function (fieldName) { var idForLabel = self.idsForLabels[fieldName]; if (idForLabel) { $labels .filter('[for=' + idForLabel + ']') - .addClass('o_form_invalid'); + .addClass('o_field_invalid'); } }); return fieldNames; @@ -66,7 +62,7 @@ var FormRenderer = BasicRenderer.extend({ _.each(resetWidgets, function (widget) { self._setIDForLabel(widget, self.idsForLabels[widget.name]); }); - return resetWidgets + return resetWidgets; }); }, /** @@ -105,7 +101,7 @@ var FormRenderer = BasicRenderer.extend({ var name = $notebook.data('name'); if (name in state) { var $page = $notebook.find('> ul > li').eq(state[name]); - if (!$page.hasClass('o_form_invisible')) { + if (!$page.hasClass('o_invisible_modifier')) { $page.find('a[data-toggle="tab"]').click(); } } @@ -138,15 +134,6 @@ var FormRenderer = BasicRenderer.extend({ //-------------------------------------------------------------------------- /** - * Adds the adequate classnames to a field widget's $el. - * - * @private - * @param {Object} widget a field widget - */ - _addFieldClassNames: function (widget) { - widget.$el.addClass('o_form_field'); // TODO will be removed in the CSS update - }, - /** * @private * @param {jQueryElement} $el * @param {Object} node @@ -207,7 +194,7 @@ var FormRenderer = BasicRenderer.extend({ } }); var buttons_partition = _.partition(buttons, function ($button) { - return $button.is('.o_form_invisible'); + return $button.is('.o_invisible_modifier'); }); var invisible_buttons = buttons_partition[0]; var visible_buttons = buttons_partition[1]; @@ -261,22 +248,8 @@ var FormRenderer = BasicRenderer.extend({ * @returns {AbstractField} */ _renderFieldWidget: function (node, record, options, modifiersOptions) { - var self = this; - modifiersOptions = _.extend({ - callback: function (element, modifiers, record) { - element.$el.toggleClass('o_form_field_empty', !!( // FIXME condition is evaluated twice (label AND widget...) - record.data.id - && (modifiers.readonly || self.mode === 'readonly') - && !element.widget.isSet() - )); - }, - }, modifiersOptions || {}); - - var widget = this._super(node, record, options, modifiersOptions); + var widget = this._super.apply(this, arguments); this._setIDForLabel(widget, this._getIDForLabel(node.attrs.name)); - - widget.$el.addClass(FIELD_CLASSES[record.fields[node.attrs.name].type]); - this._addFieldClassNames(widget); this._handleAttributes(widget.$el, node); if (JSON.parse(node.attrs.default_focus || "0")) { this.defaultFocusField = widget; @@ -344,7 +317,7 @@ var FormRenderer = BasicRenderer.extend({ }); this._registerModifiers(node, this.state, $label, { callback: function (element, modifiers, record) { - element.$el.toggleClass('o_form_label_empty', !!( + element.$el.toggleClass('o_form_label_empty', !!( // FIXME condition is evaluated twice (label AND widget...) record.data.id && (modifiers.readonly || self.mode === 'readonly') && !widget.isSet() // getting like this because it could have been re-rendered... @@ -656,7 +629,7 @@ var FormRenderer = BasicRenderer.extend({ if (modifiers.invisible && element.$el.hasClass('active')) { element.$el.removeClass('active'); tab.$page.removeClass('active'); - var $firstVisibleTab = $headers.find('li:not(.o_form_invisible):first()'); + var $firstVisibleTab = $headers.find('li:not(.o_invisible_modifier):first()'); $firstVisibleTab.addClass('active'); $pages.find($firstVisibleTab.find('a').attr('href')).addClass('active'); } diff --git a/addons/web/static/src/js/views/list/list_editable_renderer.js b/addons/web/static/src/js/views/list/list_editable_renderer.js index 3d34b80f846..1b662fa46af 100644 --- a/addons/web/static/src/js/views/list/list_editable_renderer.js +++ b/addons/web/static/src/js/views/list/list_editable_renderer.js @@ -29,7 +29,7 @@ ListRenderer.include({ 'click tbody tr:not(.o_data_row)': '_onEmptyRowClick', 'click tfoot': '_onFooterClick', 'click tr .o_list_record_delete': '_onTrashIconClick', - 'click .o_form_field_x2many_list_row_add a': '_onAddRecord', + 'click .o_field_x2many_list_row_add a': '_onAddRecord', }), /** * @override @@ -90,31 +90,6 @@ ListRenderer.include({ return this._super(recordID); }, /** - * @see BasicRenderer.confirmChange - * - * We also react to changes by adding the 'o_field_dirty' css class, - * which changes its style to a visible orange outline. It allows the user - * to have a visual feedback on which fields changed, and on which fields - * have been saved. - * - * @todo maybe should be a basic behavior - * - * @override - * @param {Object} state - a record resource (the new full state) - * @param {string} id - the local id for the changed record - * @param {string[]} fields - list of modified fields - * @param {OdooEvent} e - the event that triggered the change - * @returns {Deferred} - */ - confirmChange: function (state, id, fields, e) { - return this._super.apply(this, arguments).then(function (resetWidgets) { - _.each(resetWidgets, function (widget) { - var $td = widget.$el.parent(); - $td.addClass('o_field_dirty'); - }); - }); - }, - /** * This method is called by the controller when the user has clicked 'save', * and the model as actually saved the current changes. We then need to * set the correct values for each cell. The difference with confirmChange @@ -172,9 +147,8 @@ ListRenderer.include({ * * @param {string} recordID * @param {string} mode - * @param {boolean} [keepDirtyFlag=false] */ - setRowMode: function (recordID, mode, keepDirtyFlag) { + setRowMode: function (recordID, mode) { var self = this; var rowIndex = _.findIndex(this.state.data, {id: recordID}); if (rowIndex < 0) { @@ -185,12 +159,18 @@ ListRenderer.include({ this.currentRow = editMode ? rowIndex : null; var $row = this.$('.o_data_row:nth(' + rowIndex + ')'); - $row.toggleClass('o_selected_row', editMode); if (editMode) { // Instantiate column widgets and destroy potential readonly ones var oldWidgets = _.clone(this.allFieldWidgets[record.id]); var $tds = $row.children('.o_data_cell'); + // Force the size of each cell to its current value so that it + // won't change if its content changes, to prevent the view from + // flickering + $tds.each(function () { + var $td = $(this); + $td.css({width: $td.outerWidth()}); + }); _.each(this.columns, function (node, colIndex) { var $td = $tds.eq(colIndex); var $newTd = self._renderBodyCell(record, node, colIndex, { @@ -203,18 +183,16 @@ ListRenderer.include({ self._unregisterModifiersElement(node, record, $td.children()); } - // Force the width of each cell to its current value so that it - // won't change if its content changes, to prevent the view from - // flickering - $td.width($td.width()); $td.empty().append($newTd.contents()); }); _.each(oldWidgets, this._destroyFieldWidget.bind(this, record)); } else { for (var colIndex = 0; colIndex < this.columns.length; colIndex++) { - this._setCellValue(rowIndex, colIndex, keepDirtyFlag || false); + this._setCellValue(rowIndex, colIndex); } } + + $row.toggleClass('o_selected_row', editMode); // Toggle here so that style is applied at the end }, //-------------------------------------------------------------------------- @@ -310,7 +288,7 @@ ListRenderer.include({ var $a = $('<a href="#">').text(_t("Add an item")); var $td = $('<td>') .attr('colspan', this._getNumberOfCols()) - .addClass('o_form_field_x2many_list_row_add') + .addClass('o_field_x2many_list_row_add') .append($a); var $tr = $('<tr>').append($td); $rows.push($tr); @@ -398,17 +376,13 @@ ListRenderer.include({ * * @param {integer} rowIndex * @param {integer} colIndex - * @param {boolean} keepDirtyFlag */ - _setCellValue: function (rowIndex, colIndex, keepDirtyFlag) { + _setCellValue: function (rowIndex, colIndex) { var record = this.state.data[rowIndex]; var node = this.columns[colIndex]; var $oldTd = this.$('.o_data_row:nth(' + rowIndex + ') > .o_data_cell:nth(' + colIndex + ')'); var $newTd = this._renderBodyCell(record, node, colIndex, {mode: 'readonly'}); - if (keepDirtyFlag && $oldTd.hasClass('o_field_dirty')) { - $newTd.addClass('o_field_dirty'); - } this._unregisterModifiersElement(node, record, $oldTd); $oldTd.replaceWith($newTd); diff --git a/addons/web/static/src/js/widgets/switch_company_menu.js b/addons/web/static/src/js/widgets/switch_company_menu.js index 8bea91a5a97..c27a6d82603 100644 --- a/addons/web/static/src/js/widgets/switch_company_menu.js +++ b/addons/web/static/src/js/widgets/switch_company_menu.js @@ -34,9 +34,9 @@ var SwitchCompanyMenu = Widget.extend({ _.each(session.user_companies.allowed_companies, function(company) { var a = ''; if (company[0] === session.user_companies.current_company[0]) { - a = '<i class="fa fa-check o_current_company"></i>'; + a = '<i class="fa fa-check mr8"></i>'; } else { - a = '<span class="o_company"/>'; + a = '<span class="mr24"/>'; } companies_list += '<li><a href="#" data-menu="company" data-company-id="' + company[0] + '">' + a + company[1] + '</a></li>'; }); diff --git a/addons/web/static/src/less/animation.less b/addons/web/static/src/less/animation.less index 4aeabc4acc4..c9fe2ae7af4 100644 --- a/addons/web/static/src/less/animation.less +++ b/addons/web/static/src/less/animation.less @@ -1,4 +1,4 @@ -/************** DEFINITION of ANIMATIONS *************/ +/************** DEFINITION of ANIMATIONS **************/ // 'marked' effects @keyframes markAnim { 0% { @@ -145,51 +145,32 @@ } } -/************** MIXINS for ANIMATIONS *************/ +/************** MIXINS for ANIMATIONS **************/ .o-display-marked(@content, @top, @left, @color) { - position: absolute; - top: @top; - left: @left; + .o-position-absolute(@top: @top, @left: @left); font-size: 300px; font-family: "FontAwesome"; content: @content; color: @color; - animation: markAnim ease-in-out 1s; - animation-iteration-count: 1; - transform-origin: 50% 50%; - animation-fill-mode:forwards; /*when the spec is finished*/ - -webkit-animation: markAnim ease-in-out 1s; - -webkit-animation-iteration-count: 1; - -webkit-transform-origin: 50% 50%; - -webkit-animation-fill-mode:forwards; /*Chrome 16+, Safari 4+*/ - -moz-animation: markAnim ease-in-out 1s; - -moz-animation-iteration-count: 1; - -moz-transform-origin: 50% 50%; - -moz-animation-fill-mode:forwards; /*FF 5+*/ - -o-animation: markAnim ease-in-out 1s; - -o-animation-iteration-count: 1; - -o-transform-origin: 50% 50%; - -o-animation-fill-mode:forwards; /*Not implemented yet*/ - -ms-animation: markAnim ease-in-out 1s; - -ms-animation-iteration-count: 1; - -ms-transform-origin: 50% 50%; - -ms-animation-fill-mode:forwards; /*IE 10+*/ + .o-transform-origin(); + .o-animation(markAnim, 1s, ease-in-out, 0s, 1, normal, forwards); - @media (max-width: 992px) { + @media (max-width: @screen-sm-max) { left: 40%; font-size: 30vw; } } - -/************** USAGE of ANIMATIONS *************/ +/************** USAGE of ANIMATIONS **************/ // Animation on wow button +// TODO the only difference here is the "content" property, just make an +// 'o_wow' class for the other properties to avoid generating useless CSS .o_wow_thumbs:after { - .o-display-marked("\f164", 20%, 40%, #21b799); + .o-display-marked("\f164", 20%, 40%, @odoo-brand-optional); } .o_wow_peace:after { - .o-display-marked("\f118", 20%, 40%, #21b799); + .o-display-marked("\f118", 20%, 40%, @odoo-brand-optional); } .o_wow_heart:after { - .o-display-marked("\f004", 20%, 40%, #21b799); + .o-display-marked("\f004", 20%, 40%, @odoo-brand-optional); } diff --git a/addons/web/static/src/less/control_panel.less b/addons/web/static/src/less/control_panel.less index e2635af43d4..83048112815 100644 --- a/addons/web/static/src/less/control_panel.less +++ b/addons/web/static/src/less/control_panel.less @@ -1,63 +1,109 @@ +@breadcrumb-height: 30px; + .o_control_panel { + .o-flex-display(); + .o-flex-flow(row, wrap); + border-bottom: 1px solid @odoo-main-color-muted; .o-webclient-padding(@top: 10px, @bottom: 10px); background-color: @odoo-control-panel-background-color; - border-bottom: 1px solid @odoo-main-color-muted; - .breadcrumb { - font-size: 18px; + + > .breadcrumb { + width: 50%; margin: 0px; - > li > a { - color: @odoo-brand-optional; - cursor: pointer; - &:hover { - color: darken(@odoo-brand-optional, 10%); - text-decoration: none; + font-size: 18px; + + > li { + .o-text-overflow(@max-width: 90%); + + > a { + color: @odoo-brand-optional; + cursor: pointer; + + &:hover { + color: darken(@odoo-brand-optional, 10%); + text-decoration: none; + } } } } - // Sidebar - .o_cp_sidebar { - padding-right: 10px; + &.o_breadcrumb_full > .breadcrumb { + width: 100%; + } - .o_hidden_input_file { - position: relative; - input.o_form_input_file { - position: absolute; - top: 1px; - opacity: 0; - width: 100%; - height: 26px; - } - .o_form_binary_form span { - padding: 3px 25px; - color: @btn-default-color; + > .o_cp_searchview { + width: 50%; + min-height: @breadcrumb-height; + } + + .btn.o_enable_searchview { + width: 10%; + margin-left: 2%; + padding: 0px; + } + + > .o_cp_left { + .o-flex-display(); + .o-justify-content(space-between); + width: 50%; + margin-top: 5px; + + > .o_cp_sidebar { + 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: @btn-default-color; + } + .o_form_binary_form:hover { + background-color: @table-bg-hover; + } } - .o_form_binary_form:hover { - background-color: @table-bg-hover; + .o_sidebar_delete_attachment { + padding: 0px; + position: absolute; + top: 5px; + right: 10px; } } - .o_sidebar_delete_attachment { - padding: 0px; - position: absolute; - top: 5px; - right: 10px; - } } - // Pager - .o_cp_pager { - text-align: center; - padding-left: 5px; - .user-select(none); - } + > .o_cp_right { + .o-flex-display(); + width: 50%; + margin-top: 5px; + + > .o_cp_pager { + margin: auto 0 auto auto; + padding-left: 5px; + text-align: center; + .user-select(none); + } - // View Switcher - .o_cp_switch_buttons { - padding-left: 5px; + > .o_cp_switch_buttons { + padding-left: 5px; + } } } .o_x2m_control_panel { + .o-flex-display(); + .o-flex-flow(row, wrap); + + .o_cp_buttons, .o_cp_pager { + .o-flex-display(); + margin-right: auto; + } + .o_cp_buttons > div { margin-top: 5px; } @@ -68,7 +114,14 @@ display: inline-block; width: 60px; text-align: right; + &:focus { outline: none; } } + +@media print { + .o_control_panel { + display: none; + } +} diff --git a/addons/web/static/src/less/control_panel_layout.less b/addons/web/static/src/less/control_panel_layout.less deleted file mode 100644 index 92f8397bca4..00000000000 --- a/addons/web/static/src/less/control_panel_layout.less +++ /dev/null @@ -1,70 +0,0 @@ -@breadcrumb-height: 30px; - -.o_control_panel { - .o-flex-display(); - .o-flex-flow(row, wrap); - - .breadcrumb { - width: 50%; - - > li { - .o_text_overflow; - max-width: 90%; - display: inline-block; - } - } - - &.o_breadcrumb_full { - .breadcrumb { - width: 100%; - } - } - - .o_cp_searchview { - width: 50%; - min-height: @breadcrumb-height; - } - - .btn.o_enable_searchview { - width: 10%; - margin-left: 2%; - padding: 0px; - } - - .o_cp_left { - width: 50%; - margin-top: 5px; - .o-flex-display(); - .o-justify-content(space-between); - } - - .o_cp_right { - width: 50%; - margin-top: 5px; - .o-flex-display(); - - .o_cp_pager { - margin: auto 0 auto auto; - } - } -} - -.o_x2m_control_panel { - .o-flex-display(); - .o-flex-flow(row, wrap); - - .o_cp_buttons { - .o-flex-display(); - margin-right: auto; - } - .o_cp_pager { - .o-flex-display(); - margin-left: auto; - } -} - -@media print { - .o_control_panel { - display: none; - } -} diff --git a/addons/web/static/src/less/fields.less b/addons/web/static/src/less/fields.less new file mode 100644 index 00000000000..329b4f62fbd --- /dev/null +++ b/addons/web/static/src/less/fields.less @@ -0,0 +1,367 @@ +// +// 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: @brand-danger!important; + border-color: @brand-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 { + display: inline-block; + + // Block fields + &.note-editor, &.oe_form_field_html, &.oe_form_field_html_text, &.oe_form_field_text, &.o_view_manager_content, &.o_field_domain { + 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 { + @media (min-width: @screen-sm-min) { + .o-inline-flex-display(); + } + > span, > button { + .o-flex(0, 0, auto); + } + } + + // Dropdowns input (many2one, ...) + .o_input_dropdown { + .o-flex(1, 0, auto); + position: relative; + width: 100%; + + > input { + height: 100%; + padding-right: 15px; + cursor: pointer; + } + + .o_dropdown_button { + .o-position-absolute(0, 0); + pointer-events: none; + &:after { + .o-caret-down; + } + } + } + + // Input in field + input[type="text"] { + display: inline-block; + } + + // Text + &.o_field_text, &.oe_form_field_text .oe_form_text_content { + width: 100%; + white-space: pre-wrap; + } + + // Monetary + &.o_field_monetary { + &.o_input { + .o-align-items(baseline); + + > input { + width: 100px; + .o-flex(1, 0, auto); + } + } + } + + // Many2many tags + &.o_field_many2manytags { + .o-flex-flow(row, wrap); + .o-align-items(baseline); + + > .o_field_widget { + .o-flex(1, 0, 100px); + position: initial; + max-width: 100%; + } + + > .badge { + .o-flex(0, 0, auto); + border: 0; + font-size: 12px; + color: white; + cursor: pointer; + + &.o_tag_color_0 { background-color: #4D4D4D; } + &.o_tag_color_1 { background-color: #61BD4F; } + &.o_tag_color_2 { background-color: #F2D600; } + &.o_tag_color_3 { background-color: #FFAB4A; } + &.o_tag_color_4 { background-color: #EB5A46; } + &.o_tag_color_5 { background-color: #875A7B; } + &.o_tag_color_6 { background-color: #0079BF; } + &.o_tag_color_7 { background-color: #00C2E0; } + &.o_tag_color_8 { background-color: #4CD98E; } + &.o_tag_color_9 { background-color: #FF80CE; } + &.o_tag_color_10 { background-color: #B6BBBF; } + + > .o_badge_text { + .o-text-overflow(inline-block); + max-width: 200px; + } + + > ul { + @colored-tag-width: 45px; + width: 5*@colored-tag-width + 3px; + > li { + float: left; + color: black; + > span { + border-radius: 6px; + width: @colored-tag-width; + height: 30px; + display: inline-block; + border: 2px solid white; + cursor: pointer; + padding: 0; + + &:hover { + opacity: 0.8; + } + } + } + > li:last-child { + margin-top: 7px; + cursor: auto; + } + } + + .o_delete { + cursor: pointer; + } + } + } + + // Stars + &.o_priority { + display: inline-block; + padding: 0; + margin: 0; + vertical-align: middle; + > .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: @odoo-main-color-muted; + } + &.fa-star { + color: gold; + } + } + } + + // Status + &.o_selection .o_status { + display: inline-block; + height: 12px; + width: 12px; + border-radius: 6px; + vertical-align: middle; + + background-color: @odoo-brand-secondary; + + &.o_status_green { + background-color: @brand-success; + } + &.o_status_red { + background-color: @brand-danger; + } + } + + // Specific success color for toggle_button widget + .fa.o_toggle_button_success { + color: @brand-success; + } + + // Many2One + &.o_field_many2one { + .o-inline-flex-display(); + > span, > button { + .o-flex(0, 0, auto); + } + } + + // Handle widget + &.o_row_handle { + cursor: ns-resize; + } + + // Radio buttons + &.o_field_radio { + .o_radio_input { + outline: none; + } + + .o_radio_item { + .o-flex(0, 0, auto); + .o-flex-display(); + .o-align-items(center); + + > input { + .o-flex(0, 0, auto); + margin: 0 @odoo-form-spacing-unit 0 0; + } + > label { + margin: 0; + } + } + + &.o_horizontal { + .o_radio_item { + margin-right: @odoo-form-spacing-unit * 2; + } + } + + &.o_vertical { + .o-flex-flow(column, nowrap); + .o_radio_item { + margin-bottom: @odoo-form-spacing-unit; + } + } + } + + // Percent pie + &.o_field_percent_pie { + .o-align-items(center); + + > .o_pie { + position: relative; + margin-right: 10px; + border-radius: 100%; + overflow: hidden; + background-color: @odoo-brand-optional; + + &:after { // Outside pie border to go over border-radius irregularities + content: " "; + .o-position-absolute(0, 0); + width: 100%; + height: 100%; + border-radius: 100%; + } + + .o_mask { + .o-position-absolute(0, 0); + .o-transform-origin(0); + width: 50%; + height: 100%; + background-color: @odoo-view-background-color; + &.o_full { + background-color: inherit; + } + } + + .o_pie_value { + .o-flex-display(); + .o-justify-content(center); + .o-align-items(center); + + border-radius: 100%; + + font-weight: 700; + color: @odoo-brand-primary; + } + } + } + + // 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 { + .o-position-absolute(0, 0); + width: 100%; + + color: white; + background-color: @odoo-brand-optional; + opacity: 0; + transition: opacity ease 400ms; + + > .fa { + margin: 5px; + cursor: pointer; + } + } + @media (max-width: @screen-xs-min) { + .o_form_image_controls{ + position: initial; + opacity: 1; + > .fa{ + width: 50%; + padding: 6px; + margin: 0px; + text-align: center; + &.o_select_file_button{ + background: @odoo-brand-optional; + } + &.o_clear_file_button{ + background: @brand-danger; + } + } + } + } + + &:hover .o_form_image_controls { + opacity: 0.8; + } + } + + // 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 { + .o-position-absolute(0, 0); + margin-top: 0; + } + } + } +} diff --git a/addons/web/static/src/less/fields_extra.less b/addons/web/static/src/less/fields_extra.less new file mode 100644 index 00000000000..79d9628116d --- /dev/null +++ b/addons/web/static/src/less/fields_extra.less @@ -0,0 +1,58 @@ +// +// 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; + } +} + +//------------------------------------------------------------------------------ +// Fields +//------------------------------------------------------------------------------ + +.o_field_widget { + // Dropdowns input carret + .o_input_dropdown .o_dropdown_button { + .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 @odoo-brand-primary; + } + + .o_pie_value { + .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; + } + } + } +} diff --git a/addons/web/static/src/less/form_view.less b/addons/web/static/src/less/form_view.less index f8a221672d8..f7c5616342a 100644 --- a/addons/web/static/src/less/form_view.less +++ b/addons/web/static/src/less/form_view.less @@ -1,10 +1,7 @@ .o_form_view { background-color: @odoo-view-background-color; - // Utility classes (+ oe_edit_only, oe_read_only, o_label_nowrap, o_text_overflow) - .o_form_invisible, .o_form_field_empty { - display: none!important; - } + // Utility classes .oe_form_box_info { .bg-info; .o-webclient-padding(@top: 5px, @bottom: 5px); @@ -21,11 +18,72 @@ .oe_grey { opacity: 0.5; } + .oe_inline { + width: auto!important; + @media (min-width: @screen-xs-min) { + &.o_inner_group { + width: 1px!important; + } + } + } + .oe_left { + .oe_inline; + float: left!important; + } + .oe_right { + .oe_inline; + float: right!important; + } + + @media (min-width: @screen-xs-min) { + .o_row { + &, &.o_field_widget { // Some field may want to use o_row as root and these rules must prevalue + .o-flex-display(); + width: auto!important; + } + + .o-align-items(baseline); + min-width: 50px; + margin: 0 -@odoo-form-spacing-unit/2; + + > div, > span, > button, > label, > a, > input { // > * did not add a level of priority to the rule + .o-flex(0, 0, auto); + width: auto!important; + margin-right: @odoo-form-spacing-unit/2; + margin-left: @odoo-form-spacing-unit/2; + } + + > .o_row { + margin: 0; + } + > .btn { + padding-top: 0; + padding-bottom: 0; + } + > .o_field_boolean { + .o-align-self(center); + } + } + + .o_row .o_field_many2one, .o_field_many2one.oe_inline { + &.o_with_button:not(:last-child) { // If invisible element as last child, this line has no effect but that's not very important + margin-right: (35px + (@odoo-form-spacing-unit/2)); + } + } + } + + .o_row > div > .o_field_widget { + width: 100%; + } // Readonly specific rules &.o_form_readonly { .oe_edit_only { - .o_form_invisible; + display: none!important; + } + + .o_row, .o_row > div { + display: inline-block; } .o_form_uri { // FIXME maybe global? @@ -47,42 +105,63 @@ // Editable specific rules &.o_form_editable { .oe_read_only { - .o_form_invisible; + display: none!important; } - .o_form_field_many2manytags { - .o-input(); - - > .o_form_field { - .o-flex(1, 0, 100px); - position: initial; - max-width: 100%; - - > .o_form_input_dropdown > .o_form_input { - border-style: none!important; - } - } + .oe_title { + max-width: @container-sm - (2 * @odoo-horizontal-padding); } - .o_form_field_monetary { - .o-input(); - .o-align-items(baseline); + @media (min-width: @screen-xs-min) { + .o_row { + > .o_field_widget, > div { + .o-flex(1, 1, auto); + width: 0!important; // 3rd flex argument does not work with input (must be auto and real width 0) - > .o_form_input { - border-style: none!important; - width: 100px; - .o-flex(1, 0, auto); + &.o_field_boolean, &.o_priority { + .o-flex(0, 0, auto); + width: auto!important; + } + } } } } - // Header containing status/workflow buttons + // Statusbar .o_form_statusbar { - background-color: @odoo-view-background-color; + .o-flex(0, 0, auto); position: relative; // Needed for the "More" dropdown + .o-flex-display(); + .o-justify-content(space-between); + padding-left: @odoo-horizontal-padding; border-bottom: 1px solid @odoo-brand-secondary; + background-color: @odoo-view-background-color; + + > .o_statusbar_buttons, > .o_statusbar_status { + .o-flex-display(); + .o-align-items(center); + .o-align-content(space-around); + } + + > .o_field_widget { + .o-align-self(center); + margin-bottom: 0px; + } + + > .o_statusbar_buttons { + .o-flex-flow(row, wrap); + + > .btn { + margin: 2px 3px 2px 0; + } + } > .o_statusbar_status { + .o-flex-flow(row-reverse, wrap-reverse); + .o-align-self(stretch); + .o-align-items(stretch); + overflow-x: hidden; // to prevent horizontal scroll due to last arrow + > .o_arrow_button { min-height: @odoo-statusbar-height; font-size: 11px; @@ -152,6 +231,7 @@ } } } + .o-status-more { .o-position-absolute(100%, 0); min-width: 100px; @@ -161,6 +241,26 @@ } } } + + // Touch device mode + > .o_statusbar_buttons > .btn-group { + > .dropdown-toggle { + &:after { + .o-caret-down(); + margin-left: @odoo-form-spacing-unit; + } + } + + > .dropdown-menu { + padding: 5px 0 2px 0; + min-width: 100px; + + > li > .btn { + min-width: 100%; + margin-bottom: 3px; + } + } + } } // Button box @@ -177,6 +277,12 @@ // 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; + } .o_priority > .o_priority_star { font-size: inherit; } @@ -184,25 +290,62 @@ // Groups .o_group { - &.o_label_nowrap { - .o_form_label { - white-space: nowrap; + display: inline-block; + width: 100%; + margin: 10px 0; + + // o_group contains nested groups + .o-generate-groups(@odoo-form-group-cols); + .o-generate-groups(@n, @i: 1) when (@i =< @n) { + .o_group_col_@{i} { + display: inline-block; + width: floor(100% / 12 * @i); + vertical-align: top; + } + .o-generate-groups(@n, @i + 1); + } + + &.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 { + .o-flex(0, 0, auto); + padding: 0 10px; + } + } + + &.o_label_nowrap .o_form_label { + white-space: nowrap; + } + .o_td_label .o_form_label { font-weight: bold; margin-right: 0px; } } - &.oe_form_configuration { - .o_group .o_form_label { - white-space: nowrap; - } - } // Separators .o_horizontal_separator { @@ -213,78 +356,33 @@ } } - // Labels - .o_form_label { - font-size: @font-size-base; // The label muse have the same size whatever their position - line-height: @line-height-base; - margin-right: @odoo-form-spacing-unit; - &.o_form_invalid { - color: @brand-danger; - } - } - - // ace editor RO - .oe_form_field_text .oe_form_text_content { - white-space: pre-wrap; - } - - // Tags - .o_form_field_many2manytags { - .badge { - cursor: pointer; - color: white; - font-size: 12px; - border: 0; - - > ul { - @colored-tag-width: 45px; - width: 5*@colored-tag-width + 3px; - > li { - float: left; - color: black; - > span { - border-radius: 6px; - width: @colored-tag-width; - height: 30px; - display: inline-block; - border: 2px solid white; - cursor: pointer; - padding: 0; - - &:hover { - opacity: 0.8; - } - } - } - > li:last-child { - margin-top: 7px; - cursor: auto; - } - } - } - } + // Notebooks + .o_notebook { + clear: both; // For the notebook not to have alongside floating elements + margin-top: @odoo-form-spacing-unit * 2; - .o_form_invalid { - &.o_form_input, .o_form_input, &.o_form_textarea, .o_form_textarea { - border-color: @brand-danger!important; // enterprise o_form_required rule overrides this without !important + .nav-tabs > .tab-pane { + min-height: 100px; } } - // Boolean - .o_form_field_boolean { + // Labels + .o_form_label { margin: 0 @odoo-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; } - // Numbers - .o_form_field_number { - white-space: nowrap; + // Form fields + .o_field_widget { + margin-bottom: @odoo-form-spacing-unit; } - - // Radio buttons - .o_form_field_radio .o_form_radio { - outline: none; + .o_field_widget, .btn { + .o_field_widget { + margin-bottom: 0px; + } } - + // Translate icon .o_field_translate { padding: 0; @@ -299,160 +397,32 @@ display: inline-block; } } - .o_form_field.o_form_invisible + .o_field_translate { + .o_field_widget.o_invisible_modifier + .o_field_translate { display: none; } - // Dropdowns input (many2one, ...) - .o_form_input_dropdown { - .o-flex(1, 0, auto); - position: relative; - - > input { - padding-right: 15px; - cursor: pointer; - } - - .o_dropdown_button { - pointer-events: none; - &:after { - .o-caret-down; - } + // Text field with oe_inline class + .o_field_text.oe_inline { + width: 100%!important; + @media (min-width: @screen-xs-min) { + width: 45%!important; } } - // Many2many tags - .badge .o_delete { - cursor: pointer; - } - - // Image - .o_form_field_image { - position: relative; - - .o_form_image_controls { - .o-position-absolute(0, 0); + // One2Many, Many2Many outside of group + .o_field_widget { + &.o_view_manager_content, &.o_field_one2many, &.o_field_many2many { width: 100%; - - color: white; - background-color: @odoo-brand-optional; - opacity: 0; - transition: opacity ease 400ms; - - > .fa { - margin: 5px; - cursor: pointer; - } - } - @media (max-width: @screen-xs-min) { - .o_form_image_controls{ - position: initial; - opacity: 1; - > .fa{ - width: 50%; - padding: 6px; - margin: 0px; - text-align: center; - &.o_select_file_button{ - background: @odoo-brand-optional; - } - &.o_clear_file_button{ - background: @brand-danger; - } - } - } - } - - &:hover .o_form_image_controls { - opacity: 0.8; - } - } - - // Input loading/file - .o_form_binary_progress, .o_hidden_input_file .o_form_input_file { - display: none; - } - - // Timezone widget warning - .o_tz_warning { - color: @brand-danger; - cursor: help; - position: absolute; - margin-left: -40px; - margin-top: 5px; - } - - // One2Many Kanban views - .o_form_field .o_kanban_view.o_kanban_ungrouped { - padding: 0; - .o_kanban_record { - box-shadow: none; - } - } - - // One2Many List views - .o_form_field .o_list_view { - margin-bottom: 10px; - - > tfoot > tr > td { - padding: 3px; - color: @odoo-main-text-color; - } - } - &.o_form_readonly .o_form_field .o_list_view .o_row_handle { - display: none; // Hide the handler in non-edit mode - } - - // Percent pie field - .o_form_field_percent_pie { - .o_pie { - position: relative; - border-radius: 100%; - overflow: hidden; - background-color: @odoo-brand-optional; - - &:after { // Outside pie border to go over border-radius irregularities - content: " "; - .o-position-absolute(0, 0); + > div { width: 100%; - height: 100%; - border-radius: 100%; - } - - .o_mask { - .o-position-absolute(0, 0); - .o-transform-origin(0); - width: 50%; - height: 100%; - background-color: @odoo-view-background-color; - &.o_full { - background-color: inherit; - } - } - - .o_pie_value { - .o-flex-display(); - .o-justify-content(center); - .o-align-items(center); - - border-radius: 100%; - - font-weight: 700; - color: @odoo-brand-primary; } } } - // Attachments - .oe_attachment { - display: inline-block; - border: 1px solid @odoo-brand-lightsecondary; - margin: 0 @odoo-form-spacing-unit @odoo-form-spacing-unit 0; - padding: @odoo-form-spacing-unit; - } - // Specific style classes .o_group.o_inner_group.oe_subtotal_footer { + .oe_right; + > tbody > tr > td { padding: 0; @@ -470,7 +440,7 @@ } } - .o_form_field { + .o_field_widget { text-align: right; width: 100%; } @@ -518,7 +488,7 @@ margin-right: 0; } } - > span.o_form_field { + > span.o_field_widget { width: auto; } } @@ -556,12 +526,55 @@ } } - // Notebooks - .o_notebook { - .nav-tabs { - > .tab-pane { - min-height: 100px; - } + // Boolean + .o_field_boolean { + margin: 0 @odoo-form-spacing-unit 0 0; + } + + // Timezone widget warning + .o_tz_warning { + color: @brand-danger; + cursor: help; + position: absolute; + margin-left: -40px; + 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: @odoo-main-text-color; + } + } + &.o_form_readonly .o_field_widget .o_list_view .o_row_handle { + display: none; // Hide the handler in non-edit mode + } + + // Attachments + .oe_attachment { + display: inline-block; + border: 1px solid @odoo-brand-lightsecondary; + margin: 0 @odoo-form-spacing-unit @odoo-form-spacing-unit 0; + padding: @odoo-form-spacing-unit; + } + + &.oe_form_configuration { + .o_group .o_form_label { + white-space: nowrap; + } + h2 { + margin-top: 32px !important; } } } @@ -569,16 +582,30 @@ // Overriden style when form view in modal .modal .modal-dialog { .o_form_view { - .o_form_sheet { - box-shadow: none; + .o_form_sheet_bg { + padding: 0; + + > .o_form_sheet { + box-shadow: none; + width: 100%; + margin: 0 auto; + max-width: none; + } + } + .oe_chatter { + display: none; + } + } + &:not(.modal-lg) .o_form_view { + .o_group { width: 100%; - margin: 0 auto; - max-width: none; } } +} - .o_form_invisible { - display: none!important; +@media print { + .oe_button_box, .o_form_statusbar { + display: none !important; } } @@ -619,10 +646,10 @@ label.o_light_label, .o_light_label label { margin-left: 30px; border-left: 1px solid #bbbbbb; padding-left: 10px; - .o_form_input_dropdown > .o_form_input { + .o_input_dropdown > .o_input { width: 100%; } - .o_form_field { + .o_field_widget { width: 50%; } button.btn-link { @@ -631,7 +658,3 @@ label.o_light_label, .o_light_label label { } } } - -.oe_form_configuration h2 { - margin-top: 32px !important; -} diff --git a/addons/web/static/src/less/form_view_extra.less b/addons/web/static/src/less/form_view_extra.less index 710b668c053..6e48c173c6e 100644 --- a/addons/web/static/src/less/form_view_extra.less +++ b/addons/web/static/src/less/form_view_extra.less @@ -1,10 +1,30 @@ +@sheet-max-width: 860px; +@sheet-min-width: 650px; +@sheet-padding: 16px; + .o_form_view { .o_form_sheet_bg { - background: url(/web/static/src/img/form_sheetbg.png); + position: relative; + padding: @sheet-padding 0; border-bottom: 1px solid #ddd; + background: url(/web/static/src/img/form_sheetbg.png); + .o_form_sheet { + min-width: @sheet-min-width; + max-width: @sheet-max-width; + min-height: 330px; + margin: 0 auto; + padding: 16px; border: 1px solid #c8c8d3; box-shadow: 0 4px 20px rgba(0,0,0,0.15); + background: white; + + .ui-tabs { + margin: 0 -16px; + } + .oe_notebook_page { + padding: 0 16px; + } } } @@ -16,26 +36,59 @@ .btn-default; } } - &.o_form_nosheet > header { - margin: -16px -16px 0 -16px; + &.o_form_nosheet { + padding: 16px; + &.oe_form_nomargin { + margin: 0; + } + > header { + margin: -16px -16px 0 -16px; + } } // Button box .oe_button_box { + width: 400px; + text-align: right; + float: right; + + &, & + .oe_avatar { + + .oe_title { + width: 400px; + } + } + .oe_stat_button { + text-align: left; + width: 132px !important; + height: 40px; + margin: 0px -1px -1px 0px; + padding: 0; border-radius: 0; border-color: #ccc; background: white; outline: none; + &:hover { background-color: #e6e6e6; } + .o_button_icon { + display: inline-block; + width: 37px; + padding: 0px 3px; + text-align: center; + vertical-align: middle; color: #7C7BAD; font-size: 24px; } - .o_stat_info { // contains the value and text + // Some buttons only display text and directly use the WidgetButton template instead of StatInfo + .o_stat_info, > span { // contains the value and text + display: inline-block; + max-width: 70%; + line-height: 1.2; + .o_stat_value { .o-text-overflow(block); font-weight: 700; @@ -50,19 +103,34 @@ } } } - - // Some buttons only display text and directly use the WidgetButton template instead of StatInfo > span { font-size: small; white-space: normal; // text on several lines if needed } + + > div { + text-align: left; + padding: 0; + line-height: 120%; + vertical-align: middle; + } } } // Avatar - .oe_avatar > img { - box-shadow: 0 1px 4px rgba(0, 0, 0, 0.4); - border: none; + .oe_avatar { + float: left; + > img { + max-height: 90px; + max-width: 90px; + margin-bottom: 10px; + box-shadow: 0 1px 4px rgba(0, 0, 0, 0.4); + border: none; + } + + .oe_title { + padding-left: 95px; + margin-left: 5px; + } } // Groups @@ -70,31 +138,15 @@ .o_td_label { border-right: 1px solid #ddd; } - } - - // All required form fields - .o_form_required { - &.o_form_input, .o_form_input, &.o_form_textarea, .o_form_textarea { - background-color: #D2D2FF; + .o_td_label + td { + padding: 2px 36px 2px 8px; } - } - - .o_form_field.o_form_field_many2one .o_external_button { - padding: 0; - margin-left: 2px; - font-size: 19px; - color: #7C7BAD; - border: none; - &:hover { - background-color: transparent; + .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 } } - // Dropdowns input carret - .o_form_input_dropdown .o_dropdown_button { - .o-position-absolute(2px, 4px); - } - // Separators .o_horizontal_separator { color: @odoo-brand-optional; @@ -106,30 +158,6 @@ margin-left: -15px; } - // Percent pie field - .o_form_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 @odoo-brand-primary; - } - - .o_pie_value { - .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; - } - } - } - // Specific style classes .o_group.o_inner_group.oe_subtotal_footer { .oe_subtotal_footer_separator { @@ -139,6 +167,15 @@ border-right: none; } } + + // Chatter + .oe_chatter { + display: block; + min-width: @sheet-min-width + 2* @sheet-padding; + max-width: @sheet-max-width + 2* @sheet-padding; + margin: 0 auto; + padding: 16px 16px 48px; + } } // Overriden style when form view in modal diff --git a/addons/web/static/src/less/form_view_layout.less b/addons/web/static/src/less/form_view_layout.less deleted file mode 100644 index 1f3cb374036..00000000000 --- a/addons/web/static/src/less/form_view_layout.less +++ /dev/null @@ -1,373 +0,0 @@ -.o_form_view { - .oe_inline { - width: auto!important; - @media (min-width: @screen-xs-min) { - &.o_inner_group { - width: 1px!important; - } - } - } - .oe_left { - .oe_inline; - float: left!important; - } - .oe_right { - .oe_inline; - float: right!important; - } - - @media (min-width: @screen-xs-min) { - .o_row { - &, &.o_form_field { // Some field may want to use o_row as root and these rules must prevalue - .o-flex-display(); - width: auto!important; - } - - .o-align-items(baseline); - min-width: 50px; - margin: 0 -@odoo-form-spacing-unit/2; - - > div, > span, > button, > label, > a, > input { // > * did not add a level of priority to the rule - .o-flex(0, 0, auto); - width: auto!important; - margin-right: @odoo-form-spacing-unit/2; - margin-left: @odoo-form-spacing-unit/2; - } - - > .o_row { - margin: 0; - } - > .btn { - padding-top: 0; - padding-bottom: 0; - } - > .o_form_field_boolean { - .o-align-self(center); - } - } - - .o_row .o_form_field_many2one, .o_form_field_many2one.oe_inline { - &.o_with_button:not(:last-child) { // If invisible element as last child, this line has no effect but that's not very important - margin-right: (35px + (@odoo-form-spacing-unit/2)); - } - } - } - - .o_row > div > .o_form_field { - width: 100%; - } - - // Readonly specific rules - &.o_form_readonly { - .o_row { - display: inline-block; - > div { - display: inline-block; - } - } - } - - // Editable specific rules - &.o_form_editable { - @media (min-width: @screen-xs-min) { - .o_row { - > .o_form_field, > div { - .o-flex(1, 1, auto); - width: 0!important; // 3rd flex argument does not work with input (must be auto and real width 0) - - &.o_form_field_boolean, &.o_priority { - .o-flex(0, 0, auto); - width: auto!important; - } - } - } - } - - .oe_title { - max-width: @container-sm - (2 * @odoo-horizontal-padding); - } - } - - // Header containing status/workflow buttons - .o_form_statusbar { - .o-flex(0, 0, auto); - .o-flex-display(); - .o-justify-content(space-between); - - padding-left: @odoo-horizontal-padding; - - > .o_statusbar_buttons, > .o_statusbar_status { - .o-flex-display(); - .o-align-items(center); - .o-align-content(space-around); - } - - > .o_form_field { - .o-align-self(center); - margin-bottom: 0px; - } - - > .o_statusbar_buttons { - .o-flex-flow(row, wrap); - - > .btn { - margin: 2px 3px 2px 0; - } - } - - > .o_statusbar_status { - .o-flex-flow(row-reverse, wrap-reverse); - .o-align-self(stretch); - .o-align-items(stretch); - overflow-x: hidden; // to prevent horizontal scroll due to last arrow - } - - // Touch device mode - > .o_statusbar_buttons > .btn-group { - > .dropdown-toggle { - &:after { - .o-caret-down(); - margin-left: @odoo-form-spacing-unit; - } - } - - > .dropdown-menu { - padding: 5px 0 2px 0; - min-width: 100px; - - > li > .btn { - min-width: 100%; - margin-bottom: 3px; - } - } - } - } - - // 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; - } - } - - // Groups - .o_group { - display: inline-block; - width: 100%; - margin: 10px 0; - - // o_group contains nested groups - .o-generate-groups(@odoo-form-group-cols); - .o-generate-groups(@n, @i: 1) when (@i =< @n) { - .o_group_col_@{i} { - display: inline-block; - width: floor(100% / 12 * @i); - vertical-align: top; - } - .o-generate-groups(@n, @i + 1); - } - - &.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_form_field_boolean, .oe_avatar, .o_form_uri { - &.o_form_field { - width: auto; - } - } - } - } - - .o_form_field { - width: 100%; - - > .btn { - .o-flex(0, 0, auto); - padding: 0 10px; - } - } - } - - // Notebooks - .o_notebook { - clear: both; // For the notebook not to have alongside floating elements - margin-top: @odoo-form-spacing-unit * 2; - } - - // Labels - .o_form_label { - margin: 0; - } - - // Form fields - .o_form_field { - display: inline-block; - margin-bottom: @odoo-form-spacing-unit; - } - - // Block fields - .note-editor, .oe_form_field_html, .oe_form_field_html_text, .oe_form_field_text, .o_view_manager_content, .o_form_field_domain { - display: block; - } - - // Flex fields - .o_form_field_many2one, .o_form_field_radio, .o_form_field_many2manytags, .o_form_field_percent_pie, .o_form_field_monetary, .o_form_field_binary_file { - @media (min-width: @screen-sm-min) { - .o-inline-flex-display(); - } - > span, > button { - .o-flex(0, 0, auto); - } - } - - .o_form_field, .btn { - .o_form_field { - margin-bottom: 0px; - } - } - - // Input in field - .o_form_field { - .o_form_input_dropdown { - width: 100%; - } - .o_form_input { - display: inline-block; - } - } - - // Textarea - .o_form_textarea { - white-space: pre-wrap; - width: 100%; - &.oe_inline { - width: 100%!important; - @media (min-width: @screen-xs-min) { - width: 45%!important; - } - } - } - - // Radio buttons - .o_form_field_radio { - .o_radio_item { - .o-flex(0, 0, auto); - .o-flex-display(); - .o-align-items(center); - - > input { - .o-flex(0, 0, auto); - margin: 0 @odoo-form-spacing-unit 0 0; - } - > label { - margin: 0; - } - } - - &.o_horizontal { - .o_radio_item { - margin-right: @odoo-form-spacing-unit * 2; - } - } - - &.o_vertical { - .o-flex-flow(column, nowrap); - .o_radio_item { - margin-bottom: @odoo-form-spacing-unit; - } - } - } - - // One2Many, Many2Many outside of group - .o_form_field { - &.o_view_manager_content, &.o_form_field_one2many, &.o_form_field_many2many { - width: 100%; - > div { - width: 100%; - } - } - } - - // Many2many tags - .o_form_field_many2manytags { - .o-flex-flow(row, wrap); - .o-align-items(baseline); - .badge { - .o-flex(0, 0, auto); - > .o_badge_text { - .o-text-overflow(inline-block); - max-width: 200px; - } - } - } - - // Percent pie - .o_form_field_percent_pie { - .o-align-items(center); - > .o_pie { - margin-right: 10px; - } - } - - // Specific style classes - .o_group.o_inner_group.oe_subtotal_footer { - .oe_right; - } - - //ace - .o_ace_view_editor { - width: 100%; - .ace-view-editor { - height: 300px; - padding: 0 1em; - } - } - - .o_form_field_domain { - > .o_form_field_domain_panel { - margin-top: 8px; - } - &.o_inline_mode.o_edit_mode { - position: relative; - - > .o_form_field_domain_panel { - .o-position-absolute(0, 0); - margin-top: 0; - } - } - } -} - -// Overriden style when form view in modal -.modal .modal-dialog { - .o_form_view { - .o_form_sheet_bg { - padding: 0; - } - .oe_chatter { - display: none; - } - } - &:not(.modal-lg) .o_form_view { - .o_group { - width: 100%; - } - } -} - -@media print { - .oe_button_box { - display: none !important; - } -} diff --git a/addons/web/static/src/less/form_view_layout_extra.less b/addons/web/static/src/less/form_view_layout_extra.less deleted file mode 100644 index 51c1c3f731e..00000000000 --- a/addons/web/static/src/less/form_view_layout_extra.less +++ /dev/null @@ -1,109 +0,0 @@ -@sheet-max-width: 860px; -@sheet-min-width: 650px; -@sheet-padding: 16px; - -.o_form_view { - &.o_form_nosheet { - padding: 16px; - &.oe_form_nomargin { - margin: 0; - } - } - .o_form_sheet_bg { - position: relative; - padding: @sheet-padding 0; - .o_form_sheet { - min-width: @sheet-min-width; - max-width: @sheet-max-width; - min-height: 330px; - background: white; - padding: 16px; - margin: 0 auto; - .ui-tabs { - margin: 0 -16px; - } - .oe_notebook_page { - padding: 0 16px; - } - } - } - - // Button box - .oe_button_box { - width: 400px; - text-align: right; - float: right; - .oe_stat_button { - text-align: left; - width: 132px !important; - height: 40px; - margin: 0px -1px -1px 0px; - padding: 0; - > div { - text-align: left; - padding: 0; - line-height: 120%; - vertical-align: middle; - } - .o_button_icon { - display: inline-block; - width: 30%; - padding: 0px 3px; - width: 37px; - text-align: center; - vertical-align: middle; - } - - .o_stat_info { // contains the value and text - display: inline-block; - max-width: 70%; - line-height: 1.2; - } - - // Some buttons only display text and directly use the WidgetButton template instead of StatInfo - > span { - .o_stat_info; // layout - } - } - - &, & + .oe_avatar { - + .oe_title { - width: 400px; - } - } - } - - // Groups - .o_group { - .o_td_label + td { - padding: 2px 36px 2px 8px; - } - .o_form_field.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 - } - } - - // Avatar - .oe_avatar { - float: left; - > img { - max-height: 90px; - max-width: 90px; - margin-bottom: 10px; - } - + .oe_title { - padding-left: 95px; - margin-left: 5px; - } - } - - // Chatter - .oe_chatter { - display: block; - min-width: @sheet-min-width + 2* @sheet-padding; - max-width: @sheet-max-width + 2* @sheet-padding; - margin: 0 auto; - padding: 16px 16px 48px; - } -} diff --git a/addons/web/static/src/less/kanban_view.less b/addons/web/static/src/less/kanban_view.less index 9b2f914d3b4..d1e39c8b18e 100644 --- a/addons/web/static/src/less/kanban_view.less +++ b/addons/web/static/src/less/kanban_view.less @@ -123,7 +123,7 @@ } } } - .o_form_field_many2manytags { + .o_field_many2manytags { display: block; } .oe_kanban_text_red { @@ -552,3 +552,8 @@ color: #639B89; } } + +// Introduced with new views (TODO check) +.o_kanban { + height: 100%; // required for grouped kanban views +} diff --git a/addons/web/static/src/less/list_view.less b/addons/web/static/src/less/list_view.less index bad4e370961..8fad7a04527 100644 --- a/addons/web/static/src/less/list_view.less +++ b/addons/web/static/src/less/list_view.less @@ -1,5 +1,7 @@ @odoo-list-group-header-color: lighten(@odoo-brand-lightsecondary, 10%); + .o_list_view { + position: relative; cursor: pointer; background-color: @odoo-view-background-color; margin-bottom: 0px; @@ -12,7 +14,6 @@ thead { color: @odoo-main-text-color; - background-color: #eee; border-bottom: 1px solid #cacaca; > tr > th { border-left: 1px solid #dfdfdf; @@ -46,23 +47,8 @@ } } - .o_list_record_selector { + .o_list_record_selector, .o_list_record_delete, .o_handle_cell { width: 1px; // to prevent the column to expand - > input { - margin-left: 3px; - .hidden-xs(); - } - } - - .o_list_record_delete { - width: 13px; - } - - .o_handle_cell { - width: 1px; // to prevent the column to expand - .o_row_handle { - cursor: ns-resize; - } } // Decoration of the row @@ -80,8 +66,6 @@ tbody > tr.o_group_header { height: 31px; - .o-vertical-gradient(#fcfcfc, #dedede); - .o_group_name { white-space: nowrap; } @@ -106,108 +90,77 @@ border-top: none; // Override bootstrap for grouped list views } - .o_list_number { - text-align: right; - white-space: nowrap; - } - .o_list_text { - white-space: pre-wrap; - } -} - -.o_list_editable { - position: relative; - - .o_temp_visible { - opacity: 0; - display: block!important; - - &.o_form_invisible { - display: block!important; + // Values styling + .o_data_cell { + &.o_list_number, &.o_field_integer, &.o_field_float, &.o_field_monetary { + text-align: right; + white-space: nowrap; + } + &.o_list_text, &.o_field_text { + white-space: pre-wrap; } } - .o_list_view { - .o_row_edition > td { - visibility: hidden; // Hide row's content during edition - &.o_list_record_selector, &.o_list_record_delete, &.o_list_button { - visibility: visible; - } - - &.o_list_record_delete { - opacity: 0.5; - pointer-events: none; - } + //-------------------------------------------------------------------------- + // Edition + //-------------------------------------------------------------------------- + .o_data_row.o_selected_row > .o_data_cell:not(.o_readonly_modifier) { + position: relative; + padding: 0; - &.o_readonly { - visibility: visible; - color: transparent; + // Field widgets override + > .o_field_widget { + .o-position-absolute(0, 0, auto, 0); + min-height: 100%; - > .o_checkbox { - visibility: hidden; - } + &:not(.o_field_text) { + white-space: nowrap; } - } - } - .o_form_view.o_list_editable_form { - padding: 0; - > .o_form_field { - padding: 0; - position: absolute; - white-space: nowrap; - &.o_form_textarea { - white-space: pre-wrap; - } - .o_form_field, .badge { // Many2Many tags + .o_field_widget, .badge { // Many2Many tags display: block; float: left; white-space: normal; max-width: 100%; // Fix a chrome bug where badge would not break on multiple lines } - - &.o_readonly:not(.o_checkbox) { - background-color: transparent; - padding: 5px; - } } > .o_field_translate { position: absolute; width: 16px; // to have some space on the right } - .o_form_input_dropdown { - height: 100%; - } - .o_form_input { - height: 100%; - padding-left: 5px; - padding-right: 5px; + .o_input { + padding: 4px; // bootstrap cell - 1px of border + background-color: white; + + .o_input { + padding: 0!important; + } } - .o_form_input, .o_form_textarea, .o_form_field_many2manytags, .o_form_field_monetary { - border-style: solid; + .o_input_dropdown { + min-height: 100%; } - .o_form_field_monetary .o_form_input { + .o_field_monetary input { width: 0; } .o_checkbox { padding: 7px 0 0 5px; background: transparent; } - .o_form_textarea { + .o_field_widget { min-width: 0; } - .o_with_button .o_form_input_dropdown { + .o_with_button .o_input_dropdown { width: 1%; // Hack so that the sibling button is not overlapped } - .o_form_input_dropdown, .o_datepicker { - > .o_form_input { + .o_input_dropdown, .o_datepicker { + > .o_input { padding-right: 15px; // To prevent caret overlapping } > .o_dropdown_button, .o_datepicker_button { .o-position-absolute(20%, 5px); } } - .o_form_field > .o_external_button { + .o_field_widget > .o_external_button { padding: 0; background-color: inherit; margin-left: 3px; @@ -215,24 +168,15 @@ .o_row_handle { visibility: hidden; // Hide sequence when editing } - .o_form_field_boolean { + .o_field_boolean { width: auto!important; // Override the width computed in JS (needed for Chrome) } - .o_form_field_number { + .o_field_number { text-align: right; } } } -// Workaround a bug in safari mobile where the inputs are not displayed correctly. -.modal { - .o_list_editable { - .o_form_view.o_list_editable_form { - -webkit-overflow-scrolling: auto; - } - } -} - // Buttons in ControlPanel .o_list_buttons { .o_list_button_save, .o_list_button_discard { diff --git a/addons/web/static/src/less/list_view_extra.less b/addons/web/static/src/less/list_view_extra.less new file mode 100644 index 00000000000..9a9413c0dd7 --- /dev/null +++ b/addons/web/static/src/less/list_view_extra.less @@ -0,0 +1,10 @@ + +.o_list_view { + thead { + background-color: #eee; + } + + tbody > tr.o_group_header { + .o-vertical-gradient(#fcfcfc, #dedede); + } +} diff --git a/addons/web/static/src/less/model_field_selector.less b/addons/web/static/src/less/model_field_selector.less index 095e8b699f9..124c15cb8e5 100644 --- a/addons/web/static/src/less/model_field_selector.less +++ b/addons/web/static/src/less/model_field_selector.less @@ -28,8 +28,6 @@ } &.o_edit_mode { - .o-input(); - > .o_field_selector_controls::after { .o-caret-down(); } diff --git a/addons/web/static/src/less/navbar.less b/addons/web/static/src/less/navbar.less index 5f3aff0b98f..4e709ed7f9b 100644 --- a/addons/web/static/src/less/navbar.less +++ b/addons/web/static/src/less/navbar.less @@ -1,10 +1,10 @@ .o_main_navbar { + position: relative; height: @odoo-navbar-height; border: 1px solid @odoo-navbar-inverse-link-hover-bg; background-color: @odoo-brand-primary; color: white; - .box-shadow(0px 0px 8px @odoo-main-text-color); > a, > button { float: left; @@ -29,6 +29,24 @@ } } + > .o_menu_brand { + display: block; + float: left; + margin-right: 35px; + .user-select(none); + color: white; + font-size: 22px; + font-weight: 500; + line-height: @odoo-navbar-height - 2; + + @media (max-width: @screen-xs-max) { + float: none; + margin: 0; + border-bottom: 1px solid @odoo-navbar-inverse-link-hover-bg; + color: transparent; + } + } + > .o_menu_toggle { margin-right: 5px; } @@ -60,6 +78,37 @@ outline: none; } } + + &.o_extra_menu_items { + > a { + width: @odoo-navbar-height - 2; + text-align: center; + } + &.open > ul { + padding: 0; + + > li { + > a { + background-color: @odoo-brand-primary; + color: white; + border-bottom: 1px solid @odoo-brand-lightsecondary; + + &.dropdown-toggle { + background-color: lighten(@odoo-brand-primary, 15%); + pointer-events: none; // hack to prevent clicking on it because dropdown always opened + } + } + > ul { // 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 { @@ -71,10 +120,6 @@ } } - .o_menu_brand { - display: none; - } - .open .dropdown-menu { max-height: 90vh; // the dropdown should not overstep the viewport overflow: auto; @@ -89,6 +134,10 @@ .o-menu-indentation(3); } + .oe_topbar_avatar { + height: 15px; + } + @media (max-width: @screen-xs-max) { .o-transition(height, 200ms, linear); diff --git a/addons/web/static/src/less/new_views.less b/addons/web/static/src/less/new_views.less deleted file mode 100644 index f8c0ca2e7b6..00000000000 --- a/addons/web/static/src/less/new_views.less +++ /dev/null @@ -1,83 +0,0 @@ -.o_kanban { - height: 100%; // required for grouped kanban views -} - -.o_list_view { - tr.o_selected_row { - outline: 1px solid lightgray; - - > td:not(.o_readonly):not(.o_list_button) { - position: relative; - background-color: white; - - > .o_field_widget, > .o_form_field_many2one { - .o-position-absolute(0, 0, 0, 0); - } - - .o_form_field_many2one .o_form_input_dropdown { - .o-flex(1, 1, auto); - - .o_form_input { - height: 100%; - } - - .o_dropdown_button { - display: none; - } - } - - .o_form_field_many2manytags { - .o_form_field_many2one { - height: 100%; - display: inline-block; - } - } - - .o_field_boolean { - .o-flex-display(); - .o-align-items(center); - } - } - } - - td.o_field_dirty { - outline: 1px solid orange; - } -} - -//////////////////////////////////////////////////////////////////////////////// -// Rules on fields moved from form_view(_layout).less (community or enterprise) -// so that they also apply outside a form_view - -// Many2One -.o_form_field_many2one { - .o-inline-flex-display(); - > span, > button { - .o-flex(0, 0, auto); - } -} -// Many2ManyTags -.o_form_field_many2manytags .badge { - cursor: pointer; - color: white; - font-size: 12px; - border: 0; -} -// Dropdowns input (many2one, ...) -.o_form_input_dropdown { - .o-flex(1, 0, auto); - position: relative; - - > input { - padding-right: 15px; - cursor: pointer; - } - - .o_dropdown_button { - pointer-events: none; - &:after { - .o-caret-down; - } - .o-position-absolute(0, 0); // ENTERPRISE ONLY - } -} diff --git a/addons/web/static/src/less/search_view_extra.less b/addons/web/static/src/less/search_view_extra.less index 0ef7b8dd1dd..9c263976e4d 100644 --- a/addons/web/static/src/less/search_view_extra.less +++ b/addons/web/static/src/less/search_view_extra.less @@ -11,7 +11,7 @@ .o_searchview_facet_label { background-color: @gray-light; } - + .o_facet_remove { color: @gray-light; &:hover { @@ -40,5 +40,4 @@ input[type="text"], select { .form-control(); } - } diff --git a/addons/web/static/src/less/switch_company_menu.less b/addons/web/static/src/less/switch_company_menu.less deleted file mode 100644 index 08774e24d49..00000000000 --- a/addons/web/static/src/less/switch_company_menu.less +++ /dev/null @@ -1,6 +0,0 @@ -.o_company { - margin-right:23px; -} -.o_current_company { - margin-right:10px; -}
\ No newline at end of file diff --git a/addons/web/static/src/less/tree_view.less b/addons/web/static/src/less/tree_view.less deleted file mode 100644 index 66db28f58a8..00000000000 --- a/addons/web/static/src/less/tree_view.less +++ /dev/null @@ -1,50 +0,0 @@ -.o_treeview_table { - width: 100%; - background-color: white; - border-collapse: collapse; - - thead { - color: @odoo-main-text-color; - background-color: @odoo-brand-lightsecondary; - > tr > th { - padding: 10px; - } - } - - tbody { - > tr { - border: 1px solid @odoo-brand-lightsecondary; - &:hover { - background-color: lighten(@odoo-brand-lightsecondary, 15%); - } - > td { - border: 1px solid @odoo-brand-lightsecondary; - cursor: pointer; - height: 40px; - padding: 0 4px; - &.oe_number { - text-align: right; - } - } - } - .treeview-tr, .treeview-td { - &.o_treeview_first > span { - padding-left: 16px; - } - } - .treeview-tr.o_treeview_first { - position: relative; - &:before { - position: absolute; - top: 13px; - .o-caret-right(6px); - } - } - .o_open .treeview-tr.o_treeview_first { - &:before { - top: 16px; - .o-caret-down(6px); - } - } - } -}
\ No newline at end of file diff --git a/addons/web/static/src/less/utils.less b/addons/web/static/src/less/utils.less index 8d85eb0d977..19068dd3545 100644 --- a/addons/web/static/src/less/utils.less +++ b/addons/web/static/src/less/utils.less @@ -209,9 +209,9 @@ // ------------------------------------------------------------------ // Text overflow // ------------------------------------------------------------------ -.o-text-overflow(@display: inline-block) { +.o-text-overflow(@display: inline-block, @max-width: 100%) { display: @display; - max-width: 100%; + max-width: @max-width; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; diff --git a/addons/web/static/src/less/views.less b/addons/web/static/src/less/views.less index f2f9a97ee1b..db5fe8f814c 100644 --- a/addons/web/static/src/less/views.less +++ b/addons/web/static/src/less/views.less @@ -1,87 +1,7 @@ -// -------------------------------------------------------------------------- -// This file contains rules applying on elements shared between views -// -------------------------------------------------------------------------- - -// Many2many tags -.o_form_field_many2manytags { - .o_tag_color_0 { - background-color: #4D4D4D; - } - .o_tag_color_1 { - background-color: #61BD4F; - } - .o_tag_color_2 { - background-color: #F2D600; - } - .o_tag_color_3 { - background-color: #FFAB4A; - } - .o_tag_color_4 { - background-color: #EB5A46; - } - .o_tag_color_5 { - background-color: #875A7B; - } - .o_tag_color_6 { - background-color: #0079BF; - } - .o_tag_color_7 { - background-color: #00C2E0; - } - .o_tag_color_8 { - background-color: #4CD98E; - } - .o_tag_color_9 { - background-color: #FF80CE; - } - .o_tag_color_10 { - background-color: #B6BBBF; - } -} - -// Stars -.o_priority { - display: inline-block; - padding: 0; - margin: 0; - vertical-align: middle; - > .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: @odoo-main-color-muted; - } - &.fa-star { - color: gold; - } - } -} - -.o_status { - display: inline-block; - height: 12px; - width: 12px; - border-radius: 6px; - vertical-align: middle; - - background-color: @odoo-brand-secondary; - - &.o_status_green { - background-color: @brand-success; - } - &.o_status_red { - background-color: @brand-danger; - } -} +/** + * This file regroups the rules which apply on elements which are shared between + * all renderers. For field default rules, see the fields.less file. + */ // No content helper .oe_view_nocontent { @@ -92,15 +12,12 @@ display: inline!important; } } -// Hide view_nocontent in x2many fields -.o_form_field .oe_view_nocontent { - display: none; -} +.o_field_widget .oe_view_nocontent, .o_cannot_create .oe_view_nocontent_create { display: none; } -// Specific success color for toggle_button widget -.fa.o_toggle_button_success { - color: @brand-success; +// Invisible modifier (can be inside the view, the button area, ...) +.o_invisible_modifier { + display: none!important; } diff --git a/addons/web/static/src/less/web_calendar.less b/addons/web/static/src/less/web_calendar.less index b23b64bcfa7..ed25f8f8dd0 100644 --- a/addons/web/static/src/less/web_calendar.less +++ b/addons/web/static/src/less/web_calendar.less @@ -194,7 +194,7 @@ .o_calendar_filter { padding: 8px; border-top: 1px solid lightgray; - .o_form_field_many2one { + .o_field_many2one { width: 100%; } } diff --git a/addons/web/static/src/less/webclient_extra.less b/addons/web/static/src/less/webclient_extra.less index 43d316ec3a0..7c337352618 100644 --- a/addons/web/static/src/less/webclient_extra.less +++ b/addons/web/static/src/less/webclient_extra.less @@ -1,6 +1,6 @@ -// ------------------------------------------------------------------ +//------------------------------------------------------------------------------ // Checkboxes -// ------------------------------------------------------------------ +//------------------------------------------------------------------------------ div.o_checkbox { display: inline-block; vertical-align: sub; @@ -9,29 +9,32 @@ div.o_checkbox { } } -// ------------------------------------------------------------------ -// Inputs and selects -// ------------------------------------------------------------------ -.o-input() { +//------------------------------------------------------------------------------ +// 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 @odoo-form-lightsecondary; border-radius: @border-radius-base; padding: 2px 4px; color: #1f1f1f; -} -input[type="text"], input[type="password"], input[type="number"], textarea, select { - .o-input(); - width: 100%; - outline: none; - display: block; + .o_input { + border: none!important; + padding: 0; + } } select { background: white; } -// ------------------------------------------------------------------ +//------------------------------------------------------------------------------ // Loading -// ------------------------------------------------------------------ +//------------------------------------------------------------------------------ .o_loading { position: fixed; top: 0; @@ -45,9 +48,9 @@ select { border-bottom-left-radius: 8px; } -// ------------------------------------------------------------------ +//------------------------------------------------------------------------------ // User menu in navbar -// ------------------------------------------------------------------ +//------------------------------------------------------------------------------ #oe_main_menu_navbar .oe_user_menu_placeholder .o_user_menu .oe_topbar_name { .o-text-overflow(); max-width: 150px; diff --git a/addons/web/static/src/xml/base.xml b/addons/web/static/src/xml/base.xml index 6ffe8fca2db..d89d11f0cd5 100644 --- a/addons/web/static/src/xml/base.xml +++ b/addons/web/static/src/xml/base.xml @@ -2,7 +2,9 @@ <templates id="template" xml:space="preserve"> <div t-name="EmptyComponent"/> + <div t-name="Loading" class="o_loading"/> + <t t-name="Notification"> <div t-attf-class="o_notification #{className}" t-translation="off"> <a t-if="widget.sticky" class="fa fa-times o_close" href="#"/> @@ -49,15 +51,15 @@ <table class="o_group o_inner_group o_label_nowrap"> <tr> <td class="o_td_label"><label for="old_pwd" class="o_form_label">Old Password</label></td> - <td width="100%"><input type="password" class="o_form_field o_form_input" name="old_pwd" minlength="1" autofocus="autofocus" autocomplete="current-password"/></td> + <td width="100%"><input type="password" class="o_field_widget o_input" name="old_pwd" minlength="1" autofocus="autofocus" autocomplete="current-password"/></td> </tr> <tr> <td class="o_td_label"><label for="new_password" class="o_form_label">New Password</label></td> - <td width="100%"><input type="password" class="o_form_field o_form_input" name="new_password" minlength="1" autocomplete="new-password"/></td> + <td width="100%"><input type="password" class="o_field_widget o_input" name="new_password" minlength="1" autocomplete="new-password"/></td> </tr> <tr> <td class="o_td_label"><label for="confirm_pwd" class="o_form_label">Confirm New Password</label></td> - <td width="100%"><input type="password" class="o_form_field o_form_input" name="confirm_pwd" minlength="1" autocomplete="new-password"/></td> + <td width="100%"><input type="password" class="o_field_widget o_input" name="confirm_pwd" minlength="1" autocomplete="new-password"/></td> </tr> </table> @@ -83,7 +85,6 @@ </div> </div> </t> - <t t-name="X2ManyControlPanel"> <div class="o_x2m_control_panel"> <div class="o_cp_buttons"/> @@ -108,7 +109,6 @@ <ul class="dropdown-menu o_debug_dropdown" role="menu"/> </li> </t> - <t t-name="WebClient.DebugManager.Global"> <li><a href="#" data-action="perform_js_tests">Run JS Tests</a></li> <li><a href="#" data-action="select_view">Open View</a></li> @@ -255,48 +255,6 @@ </t> </t> -<t t-name="TreeView"> - <select t-if="toolbar" style="width: 30%"/> - <table class="o_treeview_table"> - <thead> - <tr> - <th t-foreach="fields_view" t-as="field" - t-if="!field.attrs.modifiers.tree_invisible" - class="treeview-header"> - <t t-esc="field_value.attrs.string || fields[field.attrs.name].string" /> - </th> - </tr> - </thead> - <tbody> - </tbody> - </table> -</t> -<tr t-name="TreeView.rows" - t-foreach="records" t-as="record" - t-att-id="'treerow_' + record.id" - t-att-data-id="record.id" t-att-data-level="level + 1" - t-att-data-row-parent-id="row_parent_id"> - <t t-set="children" t-value="record[children_field]"/> - <t t-set="class" t-value="children and children.length ? 'treeview-tr' : 'treeview-td'"/> - <t t-set="rank" t-value="'o_treeview_first'"/> - <t t-set="style" t-value="'background-position: ' + 19*(level) + 'px; padding-left: ' + (4 + 19*(level)) + 'px;'"/> - - <td t-foreach="fields_view" t-as="field" - t-if="!field.attrs.modifiers.tree_invisible" - t-att-data-id="record.id" - t-att-style="color_for(record) + style " - t-attf-class="#{class} #{rank} #{(fields[field.attrs.name].type === 'float') or (fields[field.attrs.name].type === 'integer') or (fields[field.attrs.name].type === 'monetary')? 'oe_number' : ''}"> - - <span t-if="!field.attrs.modifiers.invisible" > - <t t-esc="render(record[field.attrs.name], fields[field.attrs.name])" /> - </span> - - <t t-set="class" t-value="'treeview-td'"/> - <t t-set="rank" t-value="''"/> - <t t-set="style" t-value="''"/> - </td> -</tr> - <div t-name="ListView" class="table-responsive"> <table class="o_list_view table table-condensed table-striped"> <t t-set="columns_count" t-value="visible_columns.length + (options.selectable ? 1 : 0) + (options.deletable ? 1 : 0)"/> @@ -332,7 +290,6 @@ </tfoot> </table> </div> - <t t-name="ListView.buttons"> <div class="o_list_buttons"> <t t-if="widget.is_action_enabled('create')"> @@ -349,60 +306,6 @@ </div> </t> -<t t-name="ListView.rows" t-foreach="records.length" t-as="index"> - <t t-call="ListView.row"> - <t t-set="record" t-value="records.at(index)"/> - </t> -</t> - -<tr t-name="ListView.row" - t-att-data-id="record.get('id')" - t-attf-style="#{(view.fonts || view.colors) ? view.style_for(record) : ''}" - t-attf-class="#{view.compute_decoration_classnames(record)}"> - <t t-set="asData" t-value="record.toForm().data"/> - <t t-foreach="columns" t-as="column"> - <td t-if="column.meta"> </td> - </t> - <td t-if="options.selectable" class="o_list_record_selector"> - <t t-set="checked" t-value="options.select_view_id == record.get('id') ? 'checked' : null"/> - <input t-if="options.radio" type="radio" name="radiogroup" t-att-checked="checked"/> - <div t-if="!options.radio" class="o_checkbox"> - <input type="checkbox" name="radiogroup" t-att-checked="checked"/><span/> - </div> - </td> - <t t-foreach="columns" t-as="column"> - <t t-set="number" t-value="column.type === 'integer' or column.type == 'float' or column.type == 'monetary'"/> - <t t-set="text" t-value="column.type === 'text'"/> - <t t-set="modifiers" t-value="column.modifiers_for(asData)"/> - <td t-if="!column.meta and column.invisible !== '1'" t-att-title="column.help" - t-attf-class="#{modifiers.readonly ? 'o_readonly' : ''} #{number ? 'o_list_number' : ''} #{text ? 'o_list_text' : ''} #{column.id === 'sequence' ? 'o_handle_cell' : ''} #{column.tag === 'button' ? 'o_list_button' : ''}" - t-att-data-field="column.id" - ><t t-raw="render_cell(record, column)"/></td> - </t> - <td t-if="options.deletable" class='o_list_record_delete'> - <span name="delete" class="fa fa-trash-o"/> - </td> -</tr> - -<button t-name="ListView.row.text_button" type="button" - t-att-title="widget.string" t-att-disabled="disabled || undefined" - t-att-class="disabled ? 'oe_list_button_disabled btn_txt oe_link' : 'btn_txt oe_link'" - ><t t-esc="widget.string"/></button> -<button t-name="ListView.row.button" type="button" - t-att-title="widget.string" t-att-disabled="disabled || undefined" - t-attf-class="o_icon_button #{disabled ? 'oe_list_button_disabled' : ''}" - ><i t-attf-class="fa #{widget.icon}" t-att-title="widget.string"/> -</button> -<t t-extend="ListView.row"> - <!-- adds back padding to row being rendered after edition, if necessary - (if not deletable add back padding), otherwise the row being added is - missing columns - --> - <t t-jquery="> :last" t-operation="after"> - <td t-if="edited and !options.deletable" class="oe-listview-padding"/> - </t> -</t> - <t t-name="FormView.buttons"> <div class="o_form_buttons_view"> <button t-if="widget.is_action_enabled('edit')" type="button" @@ -546,7 +449,6 @@ <button class="btn btn-default fa fa-pie-chart o_graph_button" title="Pie Chart" data-mode="pie"/> </div> </t> - <div t-name="GraphView.error" class="oe_view_nocontent" > <p><strong><t t-esc="title"/></strong></p> <p><t t-esc="description"/></p> @@ -555,7 +457,6 @@ <div t-name="PivotView" class="o_pivot"> <div class="o_field_selection"/> </div> - <t t-name="PivotView.buttons"> <div class="btn-group btn-group-sm" role="group"> <button class="btn btn-primary btn-sm dropdown-toggle" data-toggle="dropdown"> @@ -575,7 +476,6 @@ <button class="btn btn-default fa fa-download o_pivot_download" title="Download xls"/> </div> </t> - <t t-name="PivotView.FieldSelection"> <ul class="dropdown-menu o_pivot_field_menu" role="menu"> <t t-foreach="fields" t-as="field"> @@ -601,7 +501,6 @@ </t> </ul> </t> - <div t-name="PivotView.nodata" class="oe_view_nocontent" > <p><strong>No data to display.</strong></p> <p> @@ -610,24 +509,10 @@ </p> </div> - <t t-name="Widget"> Unhandled widget <t t-js="dict">console.warn('Unhandled widget', dict.widget);</t> </t> -<t t-name="FieldChar"> - <span t-if="widget.get('effective_readonly')"/> - - <input t-if="!widget.get('effective_readonly')" class="o_form_input" - t-att-barcode_events="widget.options.barcode_events" - t-att-type="widget.password ? 'password' : 'text'" - t-att-id="widget.idForLabel" - t-att-tabindex="widget.node.attrs.tabindex" - t-att-autofocus="widget.node.attrs.autofocus" - t-att-placeholder="widget.node.attrs.placeholder" - t-att-autocomplete="widget.password ? 'new-password' : widget.node.attrs.autocomplete" - t-att-maxlength="widget.field.size"/> -</t> <t t-name="FormSelection"> <div class="btn-group o_selection"> <a href="#" data-toggle="dropdown"><span class="o_status"/></a> @@ -643,29 +528,8 @@ </a> </li> </t> -<t t-name="FieldEmail" t-extend="FieldChar"> - <t t-jquery="span" t-operation="replace"> - <t t-if="widget.get('effective_readonly')"> - <a t-if="widget.clickable" class="o_form_uri o_text_overflow" href="#" target="_blank"/> - <span t-if="!widget.clickable" class="o_text_overflow"/> - </t> - </t> -</t> -<t t-name="FieldText"> - <span t-if="widget.get('effective_readonly')" class="o_form_textarea"/> - - <textarea t-if="!widget.get('effective_readonly')" - class="o_form_textarea" - rows="6" - t-att-name="widget.name" - t-att-tabindex="widget.node.attrs.tabindex" - t-att-autofocus="widget.node.attrs.autofocus" - t-att-placeholder="!widget.get('effective_readonly') ? widget.node.attrs.placeholder : ''" - t-att-maxlength="widget.field.size"/> -</t> - <t t-name="FieldDomain.content"> - <div t-if="hasModel" class="o_form_field_domain_panel"> + <div t-if="hasModel" class="o_field_domain_panel"> <i class="fa fa-arrow-right"/> <button t-if="isValid" class="btn btn-xs btn-default o_domain_show_selection_button" type="button"> @@ -673,11 +537,10 @@ </button> <span t-else="" class="text-warning"><i class="fa fa-exclamation-triangle"/> Invalid domain</span> - <button t-if="inDialogEdit" class="btn btn-xs btn-primary o_form_field_domain_dialog_button">Edit domain</button> + <button t-if="inDialogEdit" class="btn btn-xs btn-primary o_field_domain_dialog_button">Edit domain</button> </div> <div t-else="">Select a model to add a filter.</div> </t> - <t t-name="DomainNode.ControlPanel"> <div t-if="!widget.readonly && !widget.noControlPanel" class="o_domain_node_control_panel"> <button class="btn o_domain_delete_node_button"><i class="fa fa-times"/></button> @@ -800,8 +663,7 @@ </t> </div> </div> - -<div t-name="ModelFieldSelector" t-attf-class="o_field_selector#{!widget.options.readonly ? ' o_edit_mode' : ''}"> +<div t-name="ModelFieldSelector" t-attf-class="o_field_selector#{!widget.options.readonly ? ' o_edit_mode o_input' : ''}"> <div class="o_field_selector_value" tabindex="0"/> <div class="o_field_selector_controls" tabindex="0"> <i class="fa fa-exclamation-triangle o_field_selector_warning hidden" title="Invalid field chain"/> @@ -816,7 +678,7 @@ <ul class="o_field_selector_page"/> </div> <div t-if="widget.options.debugMode" class="o_field_selector_popover_footer"> - <input type="text"/> + <input type="text" class="o_input"/> </div> </div> </div> @@ -842,12 +704,11 @@ </li> </t> </ul> - <t t-name="web.datepicker"> <t t-set="placeholder" t-value="widget.getParent().node and widget.getParent().node.attrs.placeholder"/> <div class="o_datepicker"> <input type="text" - class="o_datepicker_input" + class="o_datepicker_input o_input" t-att-name="widget.name" t-att-placeholder="placeholder"/> <span class="o_datepicker_button"/> @@ -857,7 +718,7 @@ <span t-if="widget.mode === 'readonly'"/> <select t-if="widget.mode !== 'readonly'" - class="o_form_input" + class="o_input" t-att-name="widget.name" t-att-tabindex="widget.attrs.tabindex" t-att-autofocus="widget.attrs.autofocus" @@ -866,9 +727,9 @@ <t t-name="FieldRadio"> <span t-if="widget.mode === 'readonly'"/> - <div t-if="widget.mode !== 'readonly'" t-attf-class="o_form_field_radio #{widget.nodeOptions.horizontal ? 'o_horizontal' : 'o_vertical'}"> + <div t-if="widget.mode !== 'readonly'" t-attf-class="o_field_radio #{widget.nodeOptions.horizontal ? 'o_horizontal' : 'o_vertical'}"> <div t-foreach="widget.values" t-as="value" class="o_radio_item"> - <input type="radio" class="o_form_radio" t-att-id="widget.unique_id + '_' + value[0]" t-att-name="widget.unique_id" t-att-data-value="value[0]"/> + <input type="radio" class="o_radio_input" t-att-id="widget.unique_id + '_' + value[0]" t-att-name="widget.unique_id" t-att-data-value="value[0]"/> <label t-if="!widget.nodeOptions.no_radiolabel" class="o_form_label" t-att-for="widget.unique_id + '_' + value[0]"><t t-esc="value[1]"/></label> </div> </div> @@ -878,9 +739,9 @@ <a t-if="!widget.nodeOptions.no_open" class="o_form_uri" href="#"/> <span t-if="widget.nodeOptions.no_open"/> </t> - <div t-if="widget.mode === 'edit'" class="o_form_field o_form_field_many2one"> - <div class="o_form_input_dropdown"> - <input type="text" class="o_form_input" + <div t-if="widget.mode === 'edit'" class="o_field_widget o_field_many2one"> + <div class="o_input_dropdown"> + <input type="text" class="o_input" t-att-barcode_events="widget.nodeOptions.barcode_events" t-att-tabindex="widget.attrs.tabindex" t-att-autofocus="widget.attrs.autofocus" @@ -891,9 +752,6 @@ <button type="button" t-if="!widget.nodeOptions.no_open" class="fa fa-external-link btn btn-default o_external_button" tabindex="-1" draggable="false"/> </div> </t> -<t t-name="Many2OneButton.cell"> - <i t-attf-class="fa #{widget.icon}" t-att-title="widget.string"/> -</t> <t t-name="FieldMany2ManyTag"> <t t-foreach="elements" t-as="el"> <span t-attf-class="badge dropdown o_tag_color_#{el.color || 10}" t-att-data-color="el.color || 10" t-att-data-index="el_index" t-att-data-id="el.id"> @@ -915,19 +773,6 @@ </li> </ul> </t> -<t t-name="FieldBoolean"> - <div class="o_checkbox o_form_field_boolean"> - <input type="checkbox" - t-att-id="widget.idForLabel" - t-att-name="widget.name" - t-att-tabindex="widget.node.attrs.tabindex" - t-att-autofocus="widget.node.attrs.autofocus"/> - <span/> - </div> -</t> -<div t-name="FieldUpgradeBoolean" class="o_form_field o_form_field_boolean"> - <t t-call="FieldBoolean"/> -</div> <t t-name="ProgressBar"> <div class="o_progressbar"> <div t-if="widget.title" class="o_progressbar_title"><t t-esc="widget.title"/></div><div class="o_progress"> @@ -936,7 +781,7 @@ </div> </t> <t t-name="FieldPercentPie"> - <div class="o_form_field_percent_pie"> + <div class="o_field_percent_pie"> <div class="o_pie"> <div class="o_mask"/> <div class="o_mask"/> @@ -966,7 +811,7 @@ </button> </t> <t t-name="FieldBinaryImage"> - <div class="o_form_field_image"> + <div class="o_field_image"> <t t-if="widget.mode !== 'readonly'"> <div class="o_form_image_controls"> <span class="fa fa-pencil fa-lg pull-left o_select_file_button" title="Edit"/> @@ -997,8 +842,8 @@ <t t-name="FieldBinaryFile"> <a t-if="widget.mode === 'readonly'" href="javascript:void(0)" class="o_form_uri"/> - <div t-if="widget.mode !== 'readonly'" class="o_form_field_binary_file"> - <input type="text" class="o_form_input" + <div t-if="widget.mode !== 'readonly'" class="o_field_binary_file"> + <input type="text" class="o_input" readonly="readonly" t-att-name="widget.name" t-att-tabindex="widget.attrs.tabindex" @@ -1022,8 +867,8 @@ <input type="hidden" name="csrf_token" t-att-value="csrf_token"/> <input type="hidden" name="session_id" value="" t-if="widget.getSession().override_session"/> <input type="hidden" name="callback" t-att-value="fileupload_id"/> - <input t-if="widget.image_only" type="file" class="o_form_input_file" name="ufile" accept="image/*"/> - <input t-if="!widget.image_only" type="file" class="o_form_input_file" name="ufile" t-att="{'multiple': multi_upload ? 'multiple' : null}"/> + <input t-if="widget.image_only" type="file" class="o_input_file" name="ufile" accept="image/*"/> + <input t-if="!widget.image_only" type="file" class="o_input_file" name="ufile" t-att="{'multiple': multi_upload ? 'multiple' : null}"/> <t t-raw="0"/> </form> <iframe t-att-id="fileupload_id" t-att-name="fileupload_id" style="display: none"/> @@ -1089,7 +934,6 @@ <t t-name="SearchView.autocomplete"> <ul class="dropdown-menu o_searchview_autocomplete" role="menu"></ul> </t> - <div t-name="SearchView.FilterMenu" class="btn-group o_dropdown"> <button class="o_dropdown_toggler_btn btn btn-sm dropdown-toggle" data-toggle="dropdown"> <span class="fa fa-filter"/> Filters <span class="caret"/> @@ -1198,7 +1042,6 @@ </option> </select> </t> - <div t-name="SearchView.GroupByMenu" class="btn-group hidden-xs o_dropdown"> <button class="o_dropdown_toggler_btn btn btn-sm dropdown-toggle" data-toggle="dropdown"> <span class="fa fa-bars"/> Group By <span class="caret"/> @@ -1220,7 +1063,6 @@ <button type="button" class="btn btn-primary btn-sm o_add_group o_select_group">Apply</button> </li> </t> - <div t-name="SearchView.FavoriteMenu" class="btn-group o_dropdown"> <button class="o_dropdown_toggler_btn btn btn-sm dropdown-toggle" data-toggle="dropdown"> <span class="fa fa-star"/> Favorites <span class="caret"/> @@ -1334,13 +1176,6 @@ Name: <input class="oe_form_m2o_input_name" type="text"/> </div> </t> -<t t-name="FieldMonetary" t-extend="FieldChar"> - <t t-jquery="input"> - var $div = $(document.createElement('div')).attr('t-if', this.attr('t-if')).addClass('o_form_field_monetary'); - this.clone(true).appendTo($div); - this.replaceWith($div); - </t> -</t> <t t-name="FieldMany2ManyCheckBoxes"> <div> <div t-foreach="widget.m2mValues" t-as="m2m_value"> @@ -1352,31 +1187,16 @@ </div> </div> </t> -<t t-name="X2ManyCounter"> - <a href="javascript:void(0)"><t t-esc="text"/></a> -</t> <t t-name="StatInfo"> <span class="o_stat_value"><t t-esc="value"/></span> <span class="o_stat_text"><t t-esc="text"/></span> </t> - <t t-name="toggle_button"> <button type="button" class="o_icon_button" t-att-title="widget.string"> <i t-attf-class="fa #{widget.icon}" t-att-title="widget.string"/> </button> </t> -<t t-name="BooleanButton"> - <t t-if="widget.get_value()"> - <span class="o_stat_text o_not_hover text-success"><t t-esc="widget.string_true"/></span> - <span class="o_stat_text o_hover text-danger"><t t-esc="widget.hover_true"/></span> - </t> - <t t-if="!widget.get_value()"> - <span class="o_stat_text o_not_hover text-danger"><t t-esc="widget.string_false"/></span> - <span class="o_stat_text o_hover text-success"><t t-esc="widget.hover_false"/></span> - </t> -</t> - <div t-name="Pager"> <span class="o_pager_counter"> <span class="o_pager_value"></span> / <span class="o_pager_limit"></span> |
