diff options
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> |
