summaryrefslogtreecommitdiff
path: root/addons/web/static/src/js
diff options
context:
space:
mode:
authorRomain Estievenart <res@odoo.com>2019-02-19 10:42:37 +0000
committerPierre Paridans <pierre@paridans.org>2019-11-25 09:29:39 +0000
commit715c136cd20c9e9969eeb45db993917defb8c0b2 (patch)
tree1a7c101ca67c0fd4c7ba6103a74d909731d7dc54 /addons/web/static/src/js
parent40e8834b00d07f62f25b427214c0c71bd37d40af (diff)
[IMP] web: add column creation support for kanban view on mobile
On mobile, there was no way to create a new column in the kanban view. This was specifically annoying in the Project app (the user was able to create the project but couldn't set its stages). This commit enables this feature by adding a `+` icon at the end of the tabs. This button opens a quick-create column widget. If there is no existing column (for example in a newly created project), this quick-create column widget is opened by default. Finally, this commit improves the processing needed for the swipe animation between the columns. Task ID: 1893137 closes odoo/odoo#40722 Signed-off-by: Pierre Paridans <pparidans@users.noreply.github.com> Co-authored-by: Ankit Sathvara <asa@odoo.com> Co-authored-by: Mohammed Shekha <msh@openerp.com> Co-authored-by: Romain Estievenart <res@odoo.com>
Diffstat (limited to 'addons/web/static/src/js')
-rw-r--r--addons/web/static/src/js/views/kanban/kanban_column_quick_create.js8
-rw-r--r--addons/web/static/src/js/views/kanban/kanban_controller.js1
-rw-r--r--addons/web/static/src/js/views/kanban/kanban_renderer_mobile.js168
-rw-r--r--addons/web/static/src/js/views/kanban/kanban_view.js2
4 files changed, 122 insertions, 57 deletions
diff --git a/addons/web/static/src/js/views/kanban/kanban_column_quick_create.js b/addons/web/static/src/js/views/kanban/kanban_column_quick_create.js
index b2a38d4e53e..3fe3a556782 100644
--- a/addons/web/static/src/js/views/kanban/kanban_column_quick_create.js
+++ b/addons/web/static/src/js/views/kanban/kanban_column_quick_create.js
@@ -6,6 +6,7 @@ odoo.define('web.kanban_column_quick_create', function (require) {
* create kanban columns directly from the Kanban view.
*/
+var config = require('web.config');
var core = require('web.core');
var Dialog = require('web.Dialog');
var Widget = require('web.Widget');
@@ -34,6 +35,7 @@ var ColumnQuickCreate = Widget.extend({
this._super.apply(this, arguments);
this.examples = options.examples;
this.folded = true;
+ this.isMobile = config.device.isMobile;
},
/**
* @override
@@ -101,9 +103,11 @@ var ColumnQuickCreate = Widget.extend({
*/
_cancel: function () {
if (!this.folded) {
- this.folded = true;
this.$input.val('');
- this._update();
+ if (!this.isMobile) {
+ this.folded = true;
+ this._update();
+ }
}
},
/**
diff --git a/addons/web/static/src/js/views/kanban/kanban_controller.js b/addons/web/static/src/js/views/kanban/kanban_controller.js
index 66d473095be..95012eeebfc 100644
--- a/addons/web/static/src/js/views/kanban/kanban_controller.js
+++ b/addons/web/static/src/js/views/kanban/kanban_controller.js
@@ -238,6 +238,7 @@ var KanbanController = BasicController.extend({
}).then(function () {
self._updateButtons();
self.renderer.quickCreateToggleFold();
+ self.renderer.trigger_up("quick_create_column_created");
});
});
},
diff --git a/addons/web/static/src/js/views/kanban/kanban_renderer_mobile.js b/addons/web/static/src/js/views/kanban/kanban_renderer_mobile.js
index 377131c16ba..43de802741a 100644
--- a/addons/web/static/src/js/views/kanban/kanban_renderer_mobile.js
+++ b/addons/web/static/src/js/views/kanban/kanban_renderer_mobile.js
@@ -19,8 +19,12 @@ if (!config.device.isMobile) {
}
KanbanRenderer.include({
+ custom_events: _.extend({}, KanbanRenderer.prototype.custom_events || {}, {
+ quick_create_column_created: '_onColumnAdded',
+ }),
events: _.extend({}, KanbanRenderer.prototype.events, {
'click .o_kanban_mobile_tab': '_onMobileTabClicked',
+ 'click .o_kanban_mobile_add_column': '_onMobileQuickCreateClicked',
}),
ANIMATE: true, // allows to disable animations for the tests
/**
@@ -73,12 +77,18 @@ KanbanRenderer.include({
/**
* Displays the quick create record in the active column
+ * override to open quick create record in current active column
*
+ * @override
* @returns {Promise}
*/
addQuickCreate: function () {
+ if(this._canCreateColumn() && !this.quickCreate.folded) {
+ this._onMobileQuickCreateClicked();
+ }
return this.widgets[this.activeColumnIndex].addQuickCreate();
},
+
/**
* Overrides to restore the left property and the scrollTop on the updated
* column, and to enable the swipe handlers
@@ -86,21 +96,13 @@ KanbanRenderer.include({
* @override
*/
updateColumn: function (localID) {
- var self = this;
var index = _.findIndex(this.widgets, {db_id: localID});
var $column = this.widgets[index].$el;
- var left = $column.css('left');
- var right = $column.css('right');
var scrollTop = $column.scrollTop();
- return this._super.apply(this, arguments).then(function () {
- $column = self.widgets[index].$el;
- if (_t.database.parameters.direction === 'rtl') {
- $column.css({right: right});
- } else {
- $column.css({left: left});
- }
- $column.scrollTop(scrollTop); // required when clicking on 'Load More'
- });
+ return this._super.apply(this, arguments)
+ .then(() => this._layoutUpdate(false))
+ // required when clicking on 'Load More'
+ .then(() => $column.scrollTop(scrollTop));
},
//--------------------------------------------------------------------------
@@ -108,6 +110,15 @@ KanbanRenderer.include({
//--------------------------------------------------------------------------
/**
+ * Check if we use the quick create on mobile
+ * @returns {boolean}
+ * @private
+ */
+ _canCreateColumn: function() {
+ return this.quickCreateEnabled && this.quickCreate && this.widgets.length;
+ },
+
+ /**
* Update the columns positions
*
* @private
@@ -115,45 +126,28 @@ KanbanRenderer.include({
*/
_computeColumnPosition: function (animate) {
if (this.widgets.length) {
- var self = this;
- var moveToIndex = this.activeColumnIndex;
- var updateFunc = animate ? 'animate' : 'css';
- var rtl = _t.database.parameters.direction === 'rtl';
- _.each(this.widgets, function (column, index) {
- var columnID = column.id || column.db_id;
- var $column = self.$('.o_kanban_group[data-id="' + columnID + '"]');
- if (index === moveToIndex - 1) {
- if (rtl) {
- $column[updateFunc]({right: '-100%'});
- } else {
- $column[updateFunc]({left: '-100%'});
- }
- } else if (index === moveToIndex + 1) {
- if (rtl) {
- $column[updateFunc]({right: '100%'});
- } else {
- $column[updateFunc]({left: '100%'});
- }
- } else if (index === moveToIndex) {
- if (rtl) {
- $column[updateFunc]({right: '0%'});
- } else {
- $column[updateFunc]({left: '0%'});
- }
- } else if (index < moveToIndex) {
- if (rtl) {
- $column.css({right: '-100%'});
- } else {
- $column.css({left: '-100%'});
- }
- } else if (index > moveToIndex) {
- if (rtl) {
- $column.css({right: '100%'});
- } else {
- $column.css({left: '100%'});
- }
- }
- });
+ // check rtl to compute correct css value
+ const rtl = _t.database.parameters.direction === 'rtl';
+
+ // display all o_kanban_group
+ this.$('.o_kanban_group').show();
+
+ const $columnAfter = this._toNode(this.widgets.filter((widget, index) => index > this.activeColumnIndex));
+ const promiseAfter = this._updateColumnCss($columnAfter, rtl ? {right: '100%'} : {left: '100%'}, animate);
+
+ const $columnBefore = this._toNode(this.widgets.filter((widget, index) => index < this.activeColumnIndex));
+ const promiseBefore = this._updateColumnCss($columnBefore, rtl ? {right: '-100%'} : {left: '-100%'}, animate);
+
+ const $columnCurrent = this._toNode(this.widgets.filter((widget, index) => index === this.activeColumnIndex));
+ const promiseCurrent = this._updateColumnCss($columnCurrent, rtl ? {right: '0%'} : {left: '0%'}, animate);
+
+ promiseAfter
+ .then(promiseBefore)
+ .then(promiseCurrent)
+ .then(() => {
+ $columnAfter.hide();
+ $columnBefore.hide();
+ });
}
},
@@ -227,7 +221,7 @@ KanbanRenderer.include({
}, 0);
// Apply a space around between child if the parent length is higher then the sum of the child width
var $tabs = this.$('.o_kanban_mobile_tabs');
- $tabs.toggleClass('justify-content-around', $tabs.outerWidth() >= widthChilds);
+ $tabs.toggleClass('justify-content-between', $tabs.outerWidth() >= widthChilds);
}
},
@@ -308,14 +302,18 @@ KanbanRenderer.include({
this._enableSwipe();
return Promise.resolve();
},
-
+ /**
+ * override to avoid display of example background
+ */
+ _renderExampleBackground: function () {},
/**
* @override
* @private
*/
_renderGrouped: function (fragment) {
var self = this;
- this._super.apply(this, arguments);
+ var newFragment = document.createDocumentFragment();
+ this._super.apply(this, [newFragment]);
this.defs.push(Promise.all(this.defs).then(function () {
var data = [];
_.each(self.state.data, function (group) {
@@ -327,8 +325,13 @@ KanbanRenderer.include({
}
});
+ var kanbanColumnContainer = document.createElement('div');
+ kanbanColumnContainer.classList.add('o_kanban_columns_content');
+ kanbanColumnContainer.appendChild(newFragment);
+ fragment.appendChild(kanbanColumnContainer);
$(qweb.render('KanbanView.MobileTabs', {
data: data,
+ quickCreateEnabled: self._canCreateColumn()
})).prependTo(fragment);
}));
},
@@ -343,19 +346,76 @@ KanbanRenderer.include({
if (self.state.groupedBy.length) {
// force first column for kanban view, because the groupedBy can be changed
return self._moveToGroup(0);
+ } else {
+ if(self._canCreateColumn()) {
+ self._onMobileQuickCreateClicked();
+ }
+ return Promise.resolve();
}
});
},
+ /**
+ * Retrieve the Jquery node (.o_kanban_group) for a list of a given widgets
+ *
+ * @private
+ * @param widgets
+ * @returns {jQuery} the matching .o_kanban_group widgets
+ */
+ _toNode: function (widgets) {
+ const selectorCss = widgets
+ .map(widget => '.o_kanban_group[data-id="' + (widget.id || widget.db_id) + '"]')
+ .join(', ');
+ return this.$(selectorCss);
+ },
+
+ /**
+ * Update the given column to the updated positions
+ *
+ * @private
+ * @param $column The jquery column
+ * @param cssProperties Use to update column
+ * @param {boolean} [animate=false] set to true to animate
+ * @return {Promise}
+ */
+ _updateColumnCss: function ($column, cssProperties, animate) {
+ if (animate) {
+ return new Promise(resolve => $column.animate(cssProperties, 'fast', resolve));
+ } else {
+ $column.css(cssProperties);
+ return Promise.resolve();
+ }
+ },
+
//--------------------------------------------------------------------------
// Handlers
//--------------------------------------------------------------------------
/**
* @private
+ */
+ _onColumnAdded: function () {
+ this._computeTabPosition();
+ if(this._canCreateColumn() && !this.quickCreate.folded) {
+ this.quickCreate.toggleFold();
+ }
+ },
+
+ /**
+ * @private
+ */
+ _onMobileQuickCreateClicked: function() {
+ this.$('.o_kanban_group').toggle();
+ this.quickCreate.toggleFold();
+ },
+ /**
+ * @private
* @param {MouseEvent} event
*/
_onMobileTabClicked: function (event) {
+ if(this._canCreateColumn() && !this.quickCreate.folded) {
+ this.quickCreate.toggleFold();
+ }
this._moveToGroup($(event.currentTarget).index(), true);
},
});
diff --git a/addons/web/static/src/js/views/kanban/kanban_view.js b/addons/web/static/src/js/views/kanban/kanban_view.js
index cc0de646d2d..3a3dd2a1cd6 100644
--- a/addons/web/static/src/js/views/kanban/kanban_view.js
+++ b/addons/web/static/src/js/views/kanban/kanban_view.js
@@ -61,7 +61,7 @@ var KanbanView = BasicView.extend({
editable: activeActions.group_edit,
deletable: activeActions.group_delete,
archivable: archAttrs.archivable ? !!JSON.parse(archAttrs.archivable) : true,
- group_creatable: activeActions.group_create && !config.device.isMobile,
+ group_creatable: activeActions.group_create,
quickCreateView: archAttrs.quick_create_view || null,
recordsDraggable: archAttrs.records_draggable ? !!JSON.parse(archAttrs.records_draggable) : true,
hasProgressBar: !!progressBar,