[REF] web: kanban: adapt code to new style

This commit adapts some kanban files to the new style and
guidelines.
This commit is contained in:
Aaron Bohy
2017-04-11 19:44:38 +02:00
parent c369b6ec2c
commit d4fdbad99a
5 changed files with 412 additions and 248 deletions
@@ -4,36 +4,33 @@ odoo.define('web.KanbanColumn', function (require) {
var config = require('web.config');
var core = require('web.core');
var Dialog = require('web.Dialog');
var view_dialogs = require('web.view_dialogs');
var quick_create = require('web.kanban_quick_create');
var kanban_quick_create = require('web.kanban_quick_create');
var KanbanRecord = require('web.KanbanRecord');
var view_dialogs = require('web.view_dialogs');
var Widget = require('web.Widget');
var _t = core._t;
var QWeb = core.qweb;
var RecordQuickCreate = quick_create.RecordQuickCreate;
var RecordQuickCreate = kanban_quick_create.RecordQuickCreate;
var KanbanColumn = Widget.extend({
template: "KanbanView.Group",
events: {
'click .o_column_edit': 'edit_column',
'click .o_column_delete': 'delete_column',
'click .o_column_archive': 'archive_records',
'click .o_column_unarchive': 'unarchive_records',
'click .o_kanban_quick_add': 'add_quick_create',
'click .o_kanban_load_more': 'load_more',
'click .o_kanban_toggle_fold': 'toggle_fold',
},
template: 'KanbanView.Group',
custom_events: {
'kanban_record_delete': 'delete_record',
'cancel_quick_create': 'cancel_quick_create',
'quick_create_add_record': 'quick_create_add_record',
cancel_quick_create: '_onCancelQuickCreate',
kanban_record_delete: '_onDeleteRecord',
quick_create_add_record: '_onQuickCreateAddRecord',
},
events: {
'click .o_column_edit': '_onEditColumn',
'click .o_column_delete': '_onDeleteColumn',
'click .o_column_archive': '_onArchiveRecords',
'click .o_column_unarchive': '_onUnarchiveRecords',
'click .o_kanban_quick_add': '_onAddQuickCreate',
'click .o_kanban_load_more': '_onLoadMore',
'click .o_kanban_toggle_fold': '_onToggleFold',
},
/**
* @param {any} parent
* @param {any} data
* @param {any} options
* @param {any} recordOptions
* @override
*/
init: function (parent, data, options, recordOptions) {
this._super(parent);
@@ -81,14 +78,14 @@ var KanbanColumn = Widget.extend({
}
},
/**
* @returns {Deferred}
* @override
*/
start: function () {
var self = this;
this.$header = this.$('.o_kanban_header');
for (var i = 0; i < this.data_records.length; i++) {
this.add_record(this.data_records[i], {no_update: true});
this.addRecord(this.data_records[i], {no_update: true});
}
this.$header.tooltip();
@@ -105,7 +102,7 @@ var KanbanColumn = Widget.extend({
cursor: 'move',
over: function () {
self.$el.addClass('o_kanban_hover');
self.update_column();
self._update();
},
out: function () {
self.$el.removeClass('o_kanban_hover');
@@ -118,34 +115,61 @@ var KanbanColumn = Widget.extend({
if (index >= 0) {
if ($.contains(self.$el[0], record.$el[0])) {
// resequencing records
self.trigger_up('kanban_column_resequence', {ids: self.get_ids()});
self.trigger_up('kanban_column_resequence', {ids: self._getIDs()});
}
} else {
// adding record to this column
self.trigger_up('kanban_column_add_record', {record: record, ids: self.get_ids()});
self.trigger_up('kanban_column_add_record', {record: record, ids: self._getIDs()});
}
}
});
}
this.$el.click(function (event) {
if (self.folded) {
self.toggle_fold(event);
self._onToggleFold(event);
}
});
this.update_column();
this._update();
return this._super.apply(this, arguments);
},
is_empty: function () {
return !this.records.length;
},
//--------------------------------------------------------------------------
// Public
//--------------------------------------------------------------------------
add_record: function (data, options) {
var record = new KanbanRecord(this, data, this.record_options);
/**
* Adds the quick create record to the top of the column.
*/
addQuickCreate: function () {
if (this.quickCreateWidget) {
return;
}
var self = this;
var width = this.records.length ? this.records[0].$el.innerWidth() : this.$el.width() - 8;
this.quickCreateWidget = new RecordQuickCreate(this, width);
this.quickCreateWidget.insertAfter(this.$header);
this.quickCreateWidget.$el.focusout(function () {
var hasFocus = (self.quickCreateWidget.$(':focus').length > 0);
if (! hasFocus && self.quickCreateWidget) {
self._cancelQuickCreate();
}
});
},
/**
* Adds a record in the column.
*
* @param {Object} recordState
* @param {Object} options
* @params {string} options.position 'before' to add the record at the top,
* added at the bottom by default
* @params {Boolean} options.no_update set to true not to update the column
*/
addRecord: function (recordState, options) {
var record = new KanbanRecord(this, recordState, this.record_options);
this.records.push(record);
if (options.position === 'before') {
record.insertAfter(this.quick_create_widget ? this.quick_create_widget.$el : this.$header);
record.insertAfter(this.quickCreateWidget ? this.quickCreateWidget.$el : this.$header);
} else {
var $load_more = this.$('.o_kanban_load_more');
if ($load_more.length) {
@@ -155,19 +179,43 @@ var KanbanColumn = Widget.extend({
}
}
if (!options.no_update) {
this.update_column();
this._update();
}
},
/**
* @returns {Boolean} true iff the column is empty
*/
isEmpty: function () {
return !this.records.length;
},
get_ids: function () {
//--------------------------------------------------------------------------
// Private
//--------------------------------------------------------------------------
/**
* Destroys the QuickCreate widget.
*
* @private
*/
_cancelQuickCreate: function () {
this.quickCreateWidget.destroy();
this.quickCreateWidget = undefined;
},
/**
* @returns {integer[]} the res_ids of the records in the column
*/
_getIDs: function () {
var ids = [];
this.$('.o_kanban_record').each(function (index, r) {
ids.push($(r).data('record').id);
});
return ids;
},
update_column: function () {
/**
* @private
*/
_update: function () {
var title = this.folded ? this.title + ' (' + this.size + ')' : this.title;
this.$header.find('.o_column_title').text(title);
this.$header.find('.o-kanban-count').text(this.records.length);
@@ -183,29 +231,42 @@ var KanbanColumn = Widget.extend({
}
},
archive_records: function (event) {
//--------------------------------------------------------------------------
// Handlers
//--------------------------------------------------------------------------
/**
* @private
*/
_onAddQuickCreate: function () {
this.addQuickCreate();
},
/**
* @private
* @param {MouseEvent} event
*/
_onArchiveRecords: function (event) {
event.preventDefault();
this.trigger_up('kanban_column_archive_records', {archive: true});
},
unarchive_records: function (event) {
event.preventDefault();
this.trigger_up('kanban_column_archive_records', {archive: false});
/**
* @private
*/
_onCancelQuickCreate: function () {
this.cancelQuickCreate();
},
toggle_fold: function (event) {
event.preventDefault();
this.trigger_up('column_toggle_fold');
},
delete_column: function (event) {
/**
* @private
* @param {MouseEvent} event
*/
_onDeleteColumn: function (event) {
event.preventDefault();
var buttons = [
{
text: _t("Ok"),
classes: 'btn-primary',
close: true,
click: this.trigger_up.bind(this, 'kanban_column_delete')
click: this.trigger_up.bind(this, 'kanban_column_delete'),
},
{text: _t("Cancel"), close: true}
];
@@ -217,8 +278,24 @@ var KanbanColumn = Widget.extend({
}),
}).open();
},
edit_column: function (event) {
/**
* @private
* @param {OdooEvent} event
*/
_onDeleteRecord: function (event) {
var self = this;
event.data.parent_id = this.db_id;
event.data.after = function cleanup() {
var index = self.records.indexOf(event.data.record);
self.records.splice(index, 1);
self._update();
};
},
/**
* @private
* @param {MouseEvent} event
*/
_onEditColumn: function (event) {
event.preventDefault();
new view_dialogs.FormViewDialog(this, {
res_model: this.relation,
@@ -227,47 +304,37 @@ var KanbanColumn = Widget.extend({
on_saved: this.trigger_up.bind(this, 'reload'),
}).open();
},
delete_record: function (event) {
var self = this;
event.data.parent_id = this.db_id;
event.data.after = function cleanup() {
var index = self.records.indexOf(event.data.record);
self.records.splice(index, 1);
self.update_column();
};
},
add_quick_create: function () {
if (this.quick_create_widget) {
return;
}
var self = this;
var width = this.records.length ? this.records[0].$el.innerWidth() : this.$el.width() - 8;
this.quick_create_widget = new RecordQuickCreate(this, width);
this.quick_create_widget.insertAfter(this.$header);
this.quick_create_widget.$el.focusout(function () {
var hasFocus = (self.quick_create_widget.$(':focus').length > 0);
if (! hasFocus && self.quick_create_widget) {
self.cancel_quick_create();
}
});
},
cancel_quick_create: function () {
this.quick_create_widget.destroy();
this.quick_create_widget = undefined;
},
quick_create_add_record: function (event) {
this.trigger_up('quick_create_record', event.data);
},
load_more: function (event) {
/**
* @private
* @param {MouseEvent} event
*/
_onLoadMore: function (event) {
event.preventDefault();
this.trigger_up('kanban_load_more');
},
/**
* @private
* @param {OdooEvent} event
*/
_onQuickCreateAddRecord: function (event) {
this.trigger_up('quick_create_record', event.data);
},
/**
* @private
* @param {MouseEvent} event
*/
_onToggleFold: function (event) {
event.preventDefault();
this.trigger_up('column_toggle_fold');
},
/**
* @private
* @param {MouseEvent} event
*/
_onUnarchiveRecords: function (event) {
event.preventDefault();
this.trigger_up('kanban_column_archive_records', {archive: false});
},
});
return KanbanColumn;
@@ -59,7 +59,7 @@ var KanbanController = BasicController.extend({
var data = self.model.get(self.handle, {raw: true});
if (data.groupedBy.length > 0 && data.count > 0 && self.on_create === 'quick_create') {
// Activate the quick create in the first column
self.renderer.add_quick_create();
self.renderer.addQuickCreate();
} else if (self.on_create && self.on_create !== 'quick_create') {
// Execute the given action
self.do_action(self.on_create, {
@@ -101,7 +101,7 @@ var KanbanController = BasicController.extend({
* @param {string} id local id from the basic record data
*/
_confirmSave: function (id) {
this.renderer.update_record(this.model.get(id));
this.renderer.updateRecord(this.model.get(id));
},
/**
* @todo this is dead code right now. resurrect this?
@@ -181,7 +181,7 @@ var KanbanController = BasicController.extend({
.then(function () {
_.each(column_db_ids, function (db_id) {
var data = self.model.get(db_id);
self.renderer.update_column(db_id, data);
self.renderer.updateColumn(db_id, data);
});
});
}).fail(this.reload.bind(this));
@@ -207,7 +207,7 @@ var KanbanController = BasicController.extend({
.toggleActive(record_ids, active_value, column.db_id)
.then(function (db_id) {
var data = self.model.get(db_id);
self.renderer.update_column(db_id, data);
self.renderer.updateColumn(db_id, data);
});
}
},
@@ -228,8 +228,8 @@ var KanbanController = BasicController.extend({
this.model
.deleteRecords([column.db_id], relatedModelName, this.handle)
.done(function () {
if (column.is_empty()) {
self.renderer.remove_widget(column);
if (column.isEmpty()) {
self.renderer.removeWidget(column);
self._updateButtons();
} else {
self.reload();
@@ -255,7 +255,7 @@ var KanbanController = BasicController.extend({
var column = event.target;
this.model.load_more(column.db_id).then(function (db_id) {
var data = self.model.get(db_id);
self.renderer.update_column(db_id, data);
self.renderer.updateColumn(db_id, data);
});
},
/**
@@ -297,7 +297,7 @@ var KanbanController = BasicController.extend({
return self.model
.addRecordToGroup(data.id, records[0])
.then(function (db_id) {
column.add_record(self.model.get(db_id), {position: 'before'});
column.addRecord(self.model.get(db_id), {position: 'before'});
});
}
data.context['default_' + data.groupedBy[0]] = column.id;
@@ -347,7 +347,7 @@ var KanbanController = BasicController.extend({
var column = event.target;
this.model.toggleGroup(column.db_id).then(function (db_id) {
var data = self.model.get(db_id);
self.renderer.update_column(db_id, data);
self.renderer.updateColumn(db_id, data);
});
},
/**
@@ -1,26 +1,30 @@
odoo.define('web.KanbanModel', function (require) {
"use strict";
/**
* The KanbanModel extends the BasicModel to add Kanban specific features like
* moving a record from a group to another, resequencing records...
*/
var BasicModel = require('web.BasicModel');
return BasicModel.extend({
load: function (params) {
this.defaultGroupedBy = params.groupBy;
params.groupedBy = params.groupedBy.length ? params.groupedBy : this.defaultGroupedBy;
return this._super(params);
},
reload: function (id, options) {
// if the groupBy is given in the options and if it is an empty array,
// fallback on the default groupBy
if (options && options.groupBy && !options.groupBy.length) {
options.groupBy = this.defaultGroupedBy;
}
return this._super(id, options);
},
addRecordToGroup: function (group_id, res_id) {
var group = this.localData[group_id];
var KanbanModel = BasicModel.extend({
//--------------------------------------------------------------------------
// Public
//--------------------------------------------------------------------------
/**
* Adds a record to a group in the localData, and fetch the record.
*
* @param {string} groupID localID of the group
* @param {integer} resId id of the record
* @returns {Deferred<string>} resolves to the local id of the new record
*/
addRecordToGroup: function (groupID, resId) {
var group = this.localData[groupID];
var new_record = this._makeDataPoint({
res_id: res_id,
res_id: resId,
modelName: group.model,
fields: group.fields,
fieldNames: group.fieldNames,
@@ -34,60 +38,13 @@ return BasicModel.extend({
return result.id;
});
},
moveRecord: function (record_id, group_id, parent_id) {
var self = this;
var parent = this.localData[parent_id];
var new_group = this.localData[group_id];
var changes = {};
var groupedFieldName = parent.groupedBy[0];
var groupedField = parent.fields[groupedFieldName];
if (groupedField.type === 'many2one') {
changes[groupedFieldName] = {
id: new_group.res_id,
display_name: new_group.value,
};
} else {
changes[groupedFieldName] = new_group.value;
}
return this.notifyChanges(record_id, changes).then(function () {
return self.save(record_id);
}).then(function (result) {
// Remove record from its current group
var old_group;
for (var i = 0; i < parent.count; i++) {
old_group = self.localData[parent.data[i]];
var index = _.indexOf(old_group.data, record_id);
if (index >= 0) {
old_group.data.splice(index, 1);
old_group.count--;
break;
}
}
// Add record to its new group
new_group.data.push(result.id);
new_group.count++;
return [old_group.id, new_group.id];
});
},
resequence: function (modelName, res_ids, parent_id) {
if ((res_ids.length <= 1)) {
return $.when(parent_id); // there is nothing to sort
}
var self = this;
var data = self.localData[parent_id];
var def;
if (data.fields.sequence) {
def = this._rpc('/web/dataset/resequence')
.params({model: modelName, ids: res_ids})
.exec();
}
return $.when(def).then(function () {
data.data = _.sortBy(data.data, function (d) {
return _.indexOf(res_ids, d.res_id);
});
return parent_id;
});
},
/**
* Creates a new group from a name (performs a name_create).
*
* @param {string} name
* @param {string} parentID localID of the parent of the group
* @returns {Deferred<string>} resolves to the local id of the new group
*/
createGroup: function (name, parentID) {
var self = this;
var parent = this.localData[parentID];
@@ -121,6 +78,97 @@ return BasicModel.extend({
return newGroup.id;
});
},
/**
* @override
*/
load: function (params) {
this.defaultGroupedBy = params.groupBy;
params.groupedBy = params.groupedBy.length ? params.groupedBy : this.defaultGroupedBy;
return this._super(params);
},
/**
* Moves a record from a group to another.
*
* @param {string} recordID localID of the record
* @param {string} groupID localID of the new group of the record
* @param {string} parentID localID of the parent
* @returns {Deferred<string[]>} resolves to a pair [oldGroupID, newGroupID]
*/
moveRecord: function (recordID, groupID, parentID) {
var self = this;
var parent = this.localData[parentID];
var new_group = this.localData[groupID];
var changes = {};
var groupedFieldName = parent.groupedBy[0];
var groupedField = parent.fields[groupedFieldName];
if (groupedField.type === 'many2one') {
changes[groupedFieldName] = {
id: new_group.res_id,
display_name: new_group.value,
};
} else {
changes[groupedFieldName] = new_group.value;
}
return this.notifyChanges(recordID, changes).then(function () {
return self.save(recordID);
}).then(function (result) {
// Remove record from its current group
var old_group;
for (var i = 0; i < parent.count; i++) {
old_group = self.localData[parent.data[i]];
var index = _.indexOf(old_group.data, recordID);
if (index >= 0) {
old_group.data.splice(index, 1);
old_group.count--;
break;
}
}
// Add record to its new group
new_group.data.push(result.id);
new_group.count++;
return [old_group.id, new_group.id];
});
},
/**
* @override
*/
reload: function (id, options) {
// if the groupBy is given in the options and if it is an empty array,
// fallback on the default groupBy
if (options && options.groupBy && !options.groupBy.length) {
options.groupBy = this.defaultGroupedBy;
}
return this._super(id, options);
},
/**
* Resequences records.
*
* @param {string} modelName
* @param {Array[integer]} resIDs the new sequence of ids
* @param {string]} parentID the localID of the parent
* @returns {Deferred<string>} resolves to the local id of the parent
*/
resequence: function (modelName, resIDs, parentID) {
if ((resIDs.length <= 1)) {
return $.when(parentID); // there is nothing to sort
}
var self = this;
var data = self.localData[parentID];
var def;
if (data.fields.sequence) {
def = this._rpc('/web/dataset/resequence')
.params({model: modelName, ids: resIDs})
.exec();
}
return $.when(def).then(function () {
data.data = _.sortBy(data.data, function (d) {
return _.indexOf(resIDs, d.res_id);
});
return parentID;
});
},
});
return KanbanModel;
});
@@ -14,14 +14,12 @@ var _t = core._t;
var KanbanRecord = Widget.extend({
template: 'KanbanView.record',
events: {
'click .oe_kanban_action': 'on_kanban_action_clicked',
'click .o_kanban_manage_toggle_button': 'toggle_manage_pane',
},
custom_events: {
'kanban_update_record': 'update_record',
kanban_update_record: 'update_record',
},
init: function (parent, state, options) {
@@ -15,7 +15,7 @@ var ColumnQuickCreate = quick_create.ColumnQuickCreate;
var qweb = core.qweb;
function find_in_node(node, predicate) {
function findInNode(node, predicate) {
if (predicate(node)) {
return node;
}
@@ -23,25 +23,25 @@ function find_in_node(node, predicate) {
return undefined;
}
for (var i = 0; i < node.children.length; i++) {
if (find_in_node(node.children[i], predicate)) {
if (findInNode(node.children[i], predicate)) {
return node.children[i];
}
}
}
function qweb_add_if(node, condition) {
function qwebAddIf(node, condition) {
if (node.attrs[qweb.prefix + '-if']) {
condition = _.str.sprintf("(%s) and (%s)", node.attrs[qweb.prefix + '-if'], condition);
}
node.attrs[qweb.prefix + '-if'] = condition;
}
function transform_qweb_template(node, fields) {
function transformQwebTemplate(node, fields) {
// Process modifiers
if (node.tag && node.attrs.modifiers) {
var modifiers = JSON.parse(node.attrs.modifiers || "{}");
if (modifiers.invisible) {
qweb_add_if(node, _.str.sprintf("!kanban_compute_domain(%s)", JSON.stringify(modifiers.invisible)));
qwebAddIf(node, _.str.sprintf("!kanban_compute_domain(%s)", JSON.stringify(modifiers.invisible)));
}
}
switch (node.tag) {
@@ -108,116 +108,136 @@ function transform_qweb_template(node, fields) {
}
if (node.children) {
for (var i = 0, ii = node.children.length; i < ii; i++) {
transform_qweb_template(node.children[i], fields);
transformQwebTemplate(node.children[i], fields);
}
}
}
return BasicRenderer.extend({
className: "o_kanban_view",
var KanbanRenderer = BasicRenderer.extend({
className: 'o_kanban_view',
/**
* @override
*/
init: function (parent, state, params) {
this._super.apply(this, arguments);
this.qweb = new QWeb(session.debug, {_s: session.origin});
var templates = find_in_node(this.arch, function (n) { return n.tag === 'templates';});
transform_qweb_template(templates, state.fields);
var templates = findInNode(this.arch, function (n) { return n.tag === 'templates';});
transformQwebTemplate(templates, state.fields);
this.qweb.add_template(utils.json_node_to_xml(templates));
this.record_options = _.extend({}, params.record_options, { qweb: this.qweb });
this.column_options = _.extend({}, params.column_options, { qweb: this.qweb });
this.recordOptions = _.extend({}, params.record_options, { qweb: this.qweb });
this.columnOptions = _.extend({}, params.column_options, { qweb: this.qweb });
},
update: function (state, fields) {
this.fields = fields;
return this._super(state);
//--------------------------------------------------------------------------
// Public
//--------------------------------------------------------------------------
/**
* Displays the quick create record in the first column.
*/
addQuickCreate: function () {
this.widgets[0].addQuickCreate();
},
update_record: function (record_state) {
var is_grouped = !!this.state.groupedBy.length;
/**
* Removes a widget (record if ungrouped, column if grouped) from the view.
*
* @param {Widget} widget the instance of the widget to remove
*/
removeWidget: function (widget) {
this.widgets.splice(this.widgets.indexOf(widget), 1);
widget.destroy();
},
/**
* Updates a given column with its new state.
*
* @param {string} localID the column id
* @param {Object} columnState
*
* @returns {Deferred}
*/
updateColumn: function (localID, columnState) {
var column = _.findWhere(this.widgets, {db_id: localID});
this.widgets.splice(_.indexOf(this.widgets, column), 1); // remove column from widgets' list
var newColumn = new KanbanColumn(this, columnState, this.columnOptions, this.recordOptions);
this.widgets.push(newColumn);
return newColumn.insertAfter(column.$el).then(column.destroy.bind(column));
},
/**
* Updates a given record with its new state.
*
* @param {Object} recordState
*/
updateRecord: function (recordState) {
var isGrouped = !!this.state.groupedBy.length;
var record;
if (is_grouped) {
// if grouped, this.widgets are kanban columns
// so we need to find the kanban record inside
if (isGrouped) {
// if grouped, this.widgets are kanban columns so we need to find
// the kanban record inside
_.each(this.widgets, function (widget) {
record = record || _.findWhere(widget.records, {db_id: record_state.id});
record = record || _.findWhere(widget.records, {
db_id: recordState.id,
});
});
} else {
record = _.findWhere(this.widgets, {db_id: record_state.id});
record = _.findWhere(this.widgets, {db_id: recordState.id});
}
if (record) {
record.update(record_state);
record.update(recordState);
}
},
update_column: function (column_db_id, data) {
var column = _.findWhere(this.widgets, {db_id: column_db_id});
this.widgets.splice(_.indexOf(this.widgets, column), 1); // remove column from widgets' list
var new_column = new KanbanColumn(this, data, this.column_options, this.record_options);
this.widgets.push(new_column);
return new_column.insertAfter(column.$el).then(column.destroy.bind(column));
},
_renderView: function () {
var is_grouped = !!this.state.groupedBy.length;
this.$el.toggleClass('o_kanban_grouped', is_grouped);
this.$el.toggleClass('o_kanban_ungrouped', !is_grouped);
this.$el.empty();
var fragment = document.createDocumentFragment();
if (is_grouped) {
this._renderGrouped(fragment);
} else {
this._render_ungrouped(fragment);
}
this.$el.append(fragment);
return this._super.apply(this, arguments);
},
_render_ungrouped: function (fragment) {
var self = this;
_.each(this.state.data, function (record) {
var kanban_record = new KanbanRecord(self, record, self.record_options);
self.widgets.push(kanban_record);
kanban_record.appendTo(fragment);
});
//--------------------------------------------------------------------------
// Private
//--------------------------------------------------------------------------
// append ghost divs to ensure that all kanban records are left aligned
this._appendGhostDivs(fragment, 6);
},
/**
* Appends empty invisible divs to a document fragment.
* Renders empty invisible divs in a document fragment.
*
* @private
* @param {DocumentFragment} fragment
* @param {integer} nbDivs the number of divs to append
*/
_appendGhostDivs: function (fragment, nbDivs) {
_renderGhostDivs: function (fragment, nbDivs) {
for (var $ghost, i = 0; i < nbDivs; i++) {
$ghost = $('<div>').addClass('o_kanban_record o_kanban_ghost');
$ghost.appendTo(fragment);
}
},
/**
* Renders an grouped kanban view in a fragment.
*
* @private
* @param {DocumentFragment} fragment
*/
_renderGrouped: function (fragment) {
var self = this;
var group_by_field_attrs = this.state.fields[this.state.groupedBy[0]];
var groupByFieldAttrs = this.state.fields[this.state.groupedBy[0]];
// Deactivate the drag'n'drop if the groupedBy field:
// - is a date or datetime since we group by month or
// - is readonly
var draggable = true;
if (group_by_field_attrs) {
if (group_by_field_attrs.type === "date" || group_by_field_attrs.type === "datetime") {
if (groupByFieldAttrs) {
if (groupByFieldAttrs.type === "date" || groupByFieldAttrs.type === "datetime") {
draggable = false;
} else if (group_by_field_attrs.readonly !== undefined) {
draggable = !(group_by_field_attrs.readonly);
} else if (groupByFieldAttrs.readonly !== undefined) {
draggable = !(groupByFieldAttrs.readonly);
}
}
var grouped_by_m2o = group_by_field_attrs && (group_by_field_attrs.type === 'many2one');
var grouped_by_field = grouped_by_m2o && group_by_field_attrs.relation;
this.column_options = _.extend(this.column_options, {
var groupedByM2O = groupByFieldAttrs && (groupByFieldAttrs.type === 'many2one');
var grouped_by_field = groupedByM2O && groupByFieldAttrs.relation;
this.columnOptions = _.extend(this.columnOptions, {
draggable: draggable,
grouped_by_m2o: grouped_by_m2o,
grouped_by_m2o: groupedByM2O,
relation: grouped_by_field,
});
// Render columns
_.each(this.state.data, function (group) {
var column = new KanbanColumn(self, group, self.column_options, self.record_options);
var column = new KanbanColumn(self, group, self.columnOptions, self.recordOptions);
if (!group.value) {
column.prependTo(fragment); // display the 'Undefined' group first
self.widgets.unshift(column);
@@ -247,18 +267,49 @@ return BasicRenderer.extend({
});
// Column quick create
if (this.column_options.group_creatable && grouped_by_m2o) {
this.column_quick_create = new ColumnQuickCreate(this);
this.column_quick_create.appendTo(fragment);
if (this.columnOptions.group_creatable && groupedByM2O) {
var quickCreate = new ColumnQuickCreate(this);
quickCreate.appendTo(fragment);
}
},
add_quick_create: function () {
this.widgets[0].add_quick_create();
/**
* Renders an ungrouped kanban view in a fragment.
*
* @private
* @param {DocumentFragment} fragment
*/
_renderUngrouped: function (fragment) {
var self = this;
_.each(this.state.data, function (record) {
var kanbanRecord = new KanbanRecord(self, record, self.recordOptions);
self.widgets.push(kanbanRecord);
kanbanRecord.appendTo(fragment);
});
// append ghost divs to ensure that all kanban records are left aligned
this._renderGhostDivs(fragment, 6);
},
/**
* @override
* @private
*/
_renderView: function () {
var isGrouped = !!this.state.groupedBy.length;
this.$el.toggleClass('o_kanban_grouped', isGrouped);
this.$el.toggleClass('o_kanban_ungrouped', !isGrouped);
this.$el.empty();
var fragment = document.createDocumentFragment();
if (isGrouped) {
this._renderGrouped(fragment);
} else {
this._renderUngrouped(fragment);
}
this.$el.append(fragment);
return this._super.apply(this, arguments);
},
remove_widget: function (widget) {
this.widgets.splice(this.widgets.indexOf(widget), 1);
widget.destroy();
}
});
return KanbanRenderer;
});