diff --git a/addons/fleet/views/fleet_vehicle_model_views.xml b/addons/fleet/views/fleet_vehicle_model_views.xml index f85a81649bd..184f753f7b6 100644 --- a/addons/fleet/views/fleet_vehicle_model_views.xml +++ b/addons/fleet/views/fleet_vehicle_model_views.xml @@ -19,13 +19,12 @@ - + -
- +
diff --git a/addons/im_livechat/views/im_livechat_channel_views.xml b/addons/im_livechat/views/im_livechat_channel_views.xml index 4281ef830c1..f4341bea2ef 100644 --- a/addons/im_livechat/views/im_livechat_channel_views.xml +++ b/addons/im_livechat/views/im_livechat_channel_views.xml @@ -105,8 +105,7 @@ - diff --git a/addons/web/static/src/js/core/dialog.js b/addons/web/static/src/js/core/dialog.js index 9cca9c1127a..811fd9fd0de 100644 --- a/addons/web/static/src/js/core/dialog.js +++ b/addons/web/static/src/js/core/dialog.js @@ -31,6 +31,8 @@ var Dialog = Widget.extend({ * @param {string} [options.title=Odoo] * @param {string} [options.subtitle] * @param {string} [options.size=large] - 'large', 'medium' or 'small' + * @param {boolean} [options.fullscreen=false] - whether or not the dialog + * should be open in fullscreen mode (the main usecase is mobile) * @param {string} [options.dialogClass] - class to add to the modal-body * @param {jQuery} [options.$content] * Element which will be the $el, replace the .modal-body and get the @@ -56,6 +58,7 @@ var Dialog = Widget.extend({ options = _.defaults(options || {}, { title: _t('Odoo'), subtitle: '', size: 'large', + fullscreen: true, dialogClass: '', $content: false, buttons: [{text: _t("Ok"), close: true}], @@ -65,6 +68,7 @@ var Dialog = Widget.extend({ this.$content = options.$content; this.title = options.title; this.subtitle = options.subtitle; + this.fullscreen = options.fullscreen; this.dialogClass = options.dialogClass; this.size = options.size; this.buttons = options.buttons; @@ -81,6 +85,7 @@ var Dialog = Widget.extend({ return this._super.apply(this, arguments).then(function () { // Render modal once xml dependencies are loaded self.$modal = $(QWeb.render('Dialog', { + fullscreen: self.fullscreen, title: self.title, subtitle: self.subtitle, technical: self.technical, diff --git a/addons/web/static/src/js/fields/relational_fields.js b/addons/web/static/src/js/fields/relational_fields.js index 65ec9f33ddf..5aecf7b592f 100644 --- a/addons/web/static/src/js/fields/relational_fields.js +++ b/addons/web/static/src/js/fields/relational_fields.js @@ -1315,6 +1315,7 @@ var FieldOne2Many = FieldX2Many.extend({ fields_view: this.attrs.views && this.attrs.views.form, parentID: this.value.id, viewInfo: this.view, + deletable: this.activeActions.delete, })); }, @@ -1375,9 +1376,9 @@ var FieldOne2Many = FieldX2Many.extend({ */ _onOpenRecord: function (ev) { // we don't want interference with the components upstream. + var self = this; ev.stopPropagation(); - var self = this; var id = ev.data.id; var onSaved = function (record) { if (_.some(self.value.data, {id: record.id})) { @@ -1398,6 +1399,10 @@ var FieldOne2Many = FieldX2Many.extend({ this._openFormDialog({ id: id, on_saved: onSaved, + on_remove: function () { + self._setValue({operation: 'DELETE', ids: [id]}); + }, + deletable: this.activeActions.delete, readonly: this.mode === 'readonly', }); }, @@ -1464,7 +1469,11 @@ var FieldMany2Many = FieldX2Many.extend({ self._setValue({operation: 'TRIGGER_ONCHANGE'}, {forceChange: true}); self.trigger_up('reload', {db_id: ev.data.id}); }, + on_remove: function () { + self._setValue({operation: 'FORGET', ids: [ev.data.id]}); + }, readonly: this.mode === 'readonly', + deletable: this.activeActions.delete, string: this.string, }); }, diff --git a/addons/web/static/src/js/views/form/form_controller.js b/addons/web/static/src/js/views/form/form_controller.js index d0ca5c3d09f..5e669e0aa57 100644 --- a/addons/web/static/src/js/views/form/form_controller.js +++ b/addons/web/static/src/js/views/form/form_controller.js @@ -597,8 +597,10 @@ var FormController = BasicController.extend({ fields_view: data.fields_view, model: this.model, on_saved: data.on_saved, + on_remove: data.on_remove, parentID: data.parentID, readonly: data.readonly, + deletable: data.deletable, recordID: record && record.id, res_id: record && record.res_id, res_model: data.field.relation, @@ -620,7 +622,9 @@ var FormController = BasicController.extend({ context: event.data.context, fields_view: event.data.fields_view, on_saved: event.data.on_saved, + on_remove: event.data.on_remove, readonly: event.data.readonly, + deletable: event.data.deletable, res_id: record.res_id, res_model: record.model, title: _t("Open: ") + event.data.string, diff --git a/addons/web/static/src/js/views/view_dialogs.js b/addons/web/static/src/js/views/view_dialogs.js index e99f81ccb08..b8eadf4fe69 100644 --- a/addons/web/static/src/js/views/view_dialogs.js +++ b/addons/web/static/src/js/views/view_dialogs.js @@ -1,6 +1,7 @@ odoo.define('web.view_dialogs', function (require) { "use strict"; +var config = require('web.config'); var core = require('web.core'); var data = require('web.data'); var Dialog = require('web.Dialog'); @@ -35,6 +36,7 @@ var ViewDialog = Dialog.extend({ */ init: function (parent, options) { options = options || {}; + options.fullscreen = config.device.isMobile; options.dialogClass = options.dialogClass || '' + ' o_act_window'; this._super(parent, $.extend(true, {}, options)); @@ -81,9 +83,15 @@ var FormViewDialog = ViewDialog.extend({ * @param {Object} [options.fields_view] optional form fields_view * @param {boolean} [options.readonly=false] only applicable when not in * creation mode + * @param {boolean} [options.deletable=false] whether or not the record can + * be deleted + * @param {boolean} [options.disable_multiple_selection=false] set to true + * to remove the possibility to create several records in a row * @param {function} [options.on_saved] callback executed after saving a * record. It will be called with the record data, and a boolean which * indicates if something was changed + * @param {function} [options.on_remove] callback executed when the user + * clicks on the 'Remove' button * @param {BasicModel} [options.model] if given, it will be used instead of * a new form view model * @param {string} [options.recordID] if given, the model has to be given as @@ -93,20 +101,24 @@ var FormViewDialog = ViewDialog.extend({ */ init: function (parent, options) { var self = this; + options = options || {}; this.res_id = options.res_id || null; this.on_saved = options.on_saved || (function () {}); + this.on_remove = options.on_remove || (function () {}); this.context = options.context; this.model = options.model; this.parentID = options.parentID; this.recordID = options.recordID; this.shouldSaveLocally = options.shouldSaveLocally; + this.readonly = options.readonly; + this.deletable = options.deletable; + this.disable_multiple_selection = options.disable_multiple_selection; var multi_select = !_.isNumber(options.res_id) && !options.disable_multiple_selection; var readonly = _.isNumber(options.res_id) && options.readonly; - if (!options || !options.buttons) { - options = options || {}; + if (!options.buttons) { options.buttons = [{ text: (readonly ? _t("Close") : _t("Discard")), classes: "btn-secondary o_form_button_cancel", @@ -138,6 +150,15 @@ var FormViewDialog = ViewDialog.extend({ }, }); } + + var multi = options.disable_multiple_selection; + if (!multi && this.deletable) { + options.buttons.push({ + text: _t("Remove"), + classes: 'btn-secondary o_btn_remove', + click: this._remove.bind(this), + }); + } } } this._super(parent, options); @@ -209,6 +230,7 @@ var FormViewDialog = ViewDialog.extend({ //-------------------------------------------------------------------------- // Private //-------------------------------------------------------------------------- + /** * @override */ @@ -216,13 +238,26 @@ var FormViewDialog = ViewDialog.extend({ this.trigger_up('form_dialog_discarded'); return true; }, + + /** + * @private + */ + _remove: function () { + this.on_remove(); + this.close(); + }, + + /** + * @private + * @returns {Deferred} + */ _save: function () { var self = this; return this.form_view.saveRecord(this.form_view.handle, { - stayInEdit: true, - reload: false, - savePoint: this.shouldSaveLocally, - viewType: 'form', + stayInEdit: true, + reload: false, + savePoint: this.shouldSaveLocally, + viewType: 'form', }).then(function (changedFields) { // record might have been changed by the save (e.g. if this was a new record, it has an // id now), so don't re-use the copy obtained before the save diff --git a/addons/web/static/src/scss/modal.scss b/addons/web/static/src/scss/modal.scss index a71f8aa0ff2..c525050e1ec 100644 --- a/addons/web/static/src/scss/modal.scss +++ b/addons/web/static/src/scss/modal.scss @@ -83,6 +83,32 @@ } } } + + @include media-breakpoint-down(xs) { + &.o_modal_full { + .modal-dialog { + margin: 0px; + height: 100%; + + .modal-content { + height: 100%; + border: none; + + .modal-header { + background: $o-brand-odoo; + .modal-title, .o_subtitle, button.close { + color: white; + } + } + + .modal-body { + height: 100%; + overflow-y: auto; + } + } + } + } + } } // Temporary fix for modals which are not instantiated thanks to the Dialog diff --git a/addons/web/static/src/xml/dialog.xml b/addons/web/static/src/xml/dialog.xml index 955deb0e7c8..d5f6025d968 100644 --- a/addons/web/static/src/xml/dialog.xml +++ b/addons/web/static/src/xml/dialog.xml @@ -3,7 +3,7 @@ -