@@ -1,3 +1,4 @@
|
||||
* Allow column resizing
|
||||
* Add simple chat widget on sidebar
|
||||
* Support color as type of icon and image
|
||||
* Support color widget on form view
|
||||
|
||||
+67
-4
@@ -708,6 +708,15 @@ img.icon {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-height: 0;
|
||||
|
||||
.tree-dragged-image {
|
||||
display: block;
|
||||
height: 0;
|
||||
left: 0;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
width: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.wizard-form {
|
||||
@@ -724,6 +733,11 @@ img.icon {
|
||||
> table.tree {
|
||||
width: 100%;
|
||||
table-layout: fixed;
|
||||
border: 1px solid transparent;
|
||||
|
||||
&.table-bordered {
|
||||
border-color: @table-border-color;
|
||||
}
|
||||
|
||||
col.selection-state, col.favorite, col.tree-menu {
|
||||
width: 30px;
|
||||
@@ -749,15 +763,37 @@ img.icon {
|
||||
}
|
||||
}
|
||||
> thead > tr > th {
|
||||
> label.sortable {
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
padding: 5px;
|
||||
|
||||
> label {
|
||||
display: inline-block;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
margin: 0;
|
||||
|
||||
&.sortable {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
> div.resizer {
|
||||
z-index: 1;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
inset-inline-end: -5px;
|
||||
width: 2px;
|
||||
height: 100%;
|
||||
cursor: ew-resize;
|
||||
padding: 0 4px;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
&.tree-menu {
|
||||
cursor: pointer;
|
||||
overflow: visible;
|
||||
}
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
text-align: center;
|
||||
&.char, &.text, &.many2one, &.one2one, &.date, &.time, &.selection,
|
||||
&.reference, &.url, &.email, &.callto, &.sip {
|
||||
@@ -775,8 +811,15 @@ img.icon {
|
||||
z-index: 100;
|
||||
|
||||
th {
|
||||
border: 1px solid transparent;
|
||||
border-bottom: 2px solid @table-border-color;
|
||||
background-color: @body-bg;
|
||||
}
|
||||
|
||||
&:hover th {
|
||||
border: 1px solid @table-border-color;
|
||||
border-bottom-width: 2px;
|
||||
}
|
||||
}
|
||||
> tfoot > tr {
|
||||
bottom: 0;
|
||||
@@ -920,6 +963,26 @@ img.icon {
|
||||
padding-top: 0;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
> table.table-bordered {
|
||||
border: 1px solid @table-border-color;
|
||||
> thead,
|
||||
> tbody,
|
||||
> tfoot {
|
||||
> tr:first-child {
|
||||
> th,
|
||||
> td {
|
||||
border: 1px solid @table-border-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
> thead > tr:first-child {
|
||||
> th,
|
||||
> td {
|
||||
border-bottom-width: 2px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.form, .board {
|
||||
|
||||
@@ -2219,6 +2219,7 @@
|
||||
} else if (~['tree', 'list-form'].indexOf(view.view_type)) {
|
||||
var paths;
|
||||
if (view.view_type == 'tree') {
|
||||
view.save_width();
|
||||
paths = view.get_expanded_paths();
|
||||
} else {
|
||||
paths = [];
|
||||
@@ -2345,5 +2346,6 @@
|
||||
});
|
||||
}
|
||||
});
|
||||
Sao.Screen.tree_column_width = {};
|
||||
Sao.Screen.tree_column_optional = {};
|
||||
}());
|
||||
|
||||
+130
-1
@@ -15,6 +15,35 @@
|
||||
});
|
||||
}
|
||||
|
||||
// circumvent https://bugzilla.mozilla.org/show_bug.cgi?id=505521
|
||||
let dragoverFixAdded = false;
|
||||
let lastWindowDragEvent = null;
|
||||
const fillEventScreenPosition = (e) => {
|
||||
e.screenPosition = {
|
||||
x: e.screenX,
|
||||
y: e.screenY,
|
||||
};
|
||||
|
||||
if (!navigator.userAgent.search("Firefox")) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!dragoverFixAdded) {
|
||||
window.addEventListener('dragover', (e) => {
|
||||
lastWindowDragEvent = e;
|
||||
});
|
||||
dragoverFixAdded = true;
|
||||
}
|
||||
|
||||
if (lastWindowDragEvent &&
|
||||
(e.timeStamp - lastWindowDragEvent.timeStamp) < 100) {
|
||||
e.screenPosition = {
|
||||
x: lastWindowDragEvent.screenX,
|
||||
y: lastWindowDragEvent.screenY,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
Sao.View.TreeXMLViewParser = Sao.class_(Sao.View.XMLViewParser, {
|
||||
_parse_tree: function(node, attributes) {
|
||||
for (const child of node.childNodes) {
|
||||
@@ -118,6 +147,9 @@
|
||||
this.treeview = jQuery('<div/>', {
|
||||
'class': 'treeview responsive'
|
||||
}).appendTo(this.el);
|
||||
this.dragged_image= jQuery('<span/>', {
|
||||
'class': 'tree-dragged-image',
|
||||
}).html(' ').appendTo(this.el);
|
||||
|
||||
// Synchronize both scrollbars
|
||||
this.treeview.scroll(() => {
|
||||
@@ -189,6 +221,7 @@
|
||||
});
|
||||
}
|
||||
|
||||
let idx = 2;
|
||||
for (const column of this.columns) {
|
||||
col = jQuery('<col/>', {
|
||||
'class': column.attributes.widget,
|
||||
@@ -197,6 +230,7 @@
|
||||
'class': column.attributes.widget,
|
||||
});
|
||||
th.uniqueId();
|
||||
th.get(0).dataset.column = idx;
|
||||
var label = jQuery('<label/>')
|
||||
.text(column.attributes.string)
|
||||
.attr('title', column.attributes.string);
|
||||
@@ -221,6 +255,52 @@
|
||||
label.addClass('sortable');
|
||||
}
|
||||
tr.append(th.append(label));
|
||||
let resizer = jQuery('<div/>', {
|
||||
'class': 'resizer',
|
||||
'draggable': true,
|
||||
}).appendTo(th);
|
||||
resizer.on('dragstart', (event) => {
|
||||
let th = event.target.parentNode;
|
||||
let headers = th.parentNode.childNodes;
|
||||
let cols = this.colgroup[0].childNodes;
|
||||
for (let i = 0; i < headers.length; i++) {
|
||||
let header = headers[i];
|
||||
let col = cols[i];
|
||||
if (header == th) {
|
||||
break;
|
||||
}
|
||||
if (col.style.display != 'none') {
|
||||
col.style.width = `${header.offsetWidth}px`;
|
||||
}
|
||||
}
|
||||
th.dataset.startPosition = event.screenX;
|
||||
th.dataset.originalWidth = th.offsetWidth;
|
||||
|
||||
this.table.addClass('table-bordered');
|
||||
event.originalEvent.dataTransfer.setDragImage(
|
||||
this.dragged_image.get(0), 0, 0);
|
||||
});
|
||||
// column_widths is used as a global for the callback of the
|
||||
// drag event so that when the setTimeout callback is triggered
|
||||
// the latest value of the width is used
|
||||
let column_widths = {};
|
||||
resizer.on('drag', (event) => {
|
||||
fillEventScreenPosition(event);
|
||||
let resized_th = event.target.parentNode;
|
||||
let col = this.colgroup[0].childNodes[resized_th.dataset.column];
|
||||
let width_offset = (event.screenPosition.x -
|
||||
resized_th.dataset.startPosition);
|
||||
if (Sao.i18n.rtl) {
|
||||
width_offset *= -1;
|
||||
}
|
||||
let width = Number(resized_th.dataset.originalWidth) + width_offset;
|
||||
column_widths[resized_th] = width;
|
||||
setTimeout(() => {
|
||||
let width = column_widths[resized_th];
|
||||
col.style.width = `${width}px`;
|
||||
});
|
||||
});
|
||||
|
||||
column.header = th;
|
||||
column.col = col;
|
||||
|
||||
@@ -238,7 +318,20 @@
|
||||
sum_row.append(total_cell);
|
||||
column.footers.push(total_cell);
|
||||
}
|
||||
idx += 1;
|
||||
}
|
||||
|
||||
this.table.on('dragover', (event) => {
|
||||
// This is necessary so that the 'drop' event is processed
|
||||
event.preventDefault();
|
||||
});
|
||||
this.table.on('drop', (event) => {
|
||||
event.preventDefault();
|
||||
if (!this.editable) {
|
||||
this.table.removeClass('table-bordered');
|
||||
}
|
||||
});
|
||||
|
||||
this.tbody = jQuery('<tbody/>');
|
||||
this.table.append(this.tbody);
|
||||
|
||||
@@ -352,6 +445,36 @@
|
||||
}
|
||||
Sao.Screen.tree_column_optional[this.view_id] = fields;
|
||||
},
|
||||
save_width: function() {
|
||||
var widths = {};
|
||||
for (let column of this.columns) {
|
||||
if (!column.get_visible() || !column.attributes.name ||
|
||||
column instanceof Sao.View.Tree.ButtonColumn) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Use the DOM element to retrieve the exact style set
|
||||
var width = column.col[0].style.width;
|
||||
if (width.endsWith('px')) {
|
||||
widths[column.attributes.name] = Number(width.slice(0, -2));
|
||||
}
|
||||
}
|
||||
|
||||
var model_name = this.screen.model_name;
|
||||
var TreeWidth = new Sao.Model('ir.ui.view_tree_width');
|
||||
TreeWidth.execute(
|
||||
'set_width',
|
||||
[model_name, widths],
|
||||
{});
|
||||
if (Object.prototype.hasOwnProperty.call(
|
||||
Sao.Screen.tree_column_width, model_name)) {
|
||||
Object.assign(
|
||||
Sao.Screen.tree_column_width[model_name],
|
||||
widths);
|
||||
} else {
|
||||
Sao.Screen.tree_column_width[model_name] = widths;
|
||||
}
|
||||
},
|
||||
reset: function() {
|
||||
this.display_size = null;
|
||||
},
|
||||
@@ -826,6 +949,8 @@
|
||||
domain = inversion.simplify(domain);
|
||||
var decoder = new Sao.PYSON.Decoder(this.screen.context);
|
||||
var min_width = [];
|
||||
let tree_column_width = (
|
||||
Sao.Screen.tree_column_width[this.screen.model_name] || {});
|
||||
var tree_column_optional = (
|
||||
Sao.Screen.tree_column_optional[this.view_id] || {});
|
||||
for (const column of this.columns) {
|
||||
@@ -875,7 +1000,11 @@
|
||||
!column.col.hasClass('selection-state') &&
|
||||
!column.col.hasClass('favorite')) {
|
||||
var width, c_width;
|
||||
if (column.attributes.width) {
|
||||
width = tree_column_width[name];
|
||||
if (width) {
|
||||
c_width = width;
|
||||
min_width.push(`${width}px`);
|
||||
} else if (column.attributes.width) {
|
||||
width = c_width = column.attributes.width;
|
||||
min_width.push(width + 'px');
|
||||
} else {
|
||||
|
||||
Reference in New Issue
Block a user