This commit is contained in:
2026-06-07 06:41:15 +02:00
parent 28efdfef36
commit 0d1b6303b8
4 changed files with 1590 additions and 64 deletions

209
dist/tryton-sao.css vendored
View File

@@ -11167,6 +11167,78 @@ html[theme="default"] .screen-container .nav-tabs > li.active > a:hover {
box-shadow: 0 2px 8px rgba(53, 81, 84, 0.12); box-shadow: 0 2px 8px rgba(53, 81, 84, 0.12);
} }
.ag-filter-apply-panel {
display: flex;
justify-content: flex-end;
gap: 5px;
padding: 6px 8px 8px;
}
.ag-filter-apply-panel button,
.ag-filter-apply-panel .ag-button,
.ag-filter-apply-panel .ag-standard-button,
.ag-popup .ag-filter-apply-panel button,
.ag-popup .ag-filter-apply-panel .ag-button,
.ag-popup .ag-filter-apply-panel .ag-standard-button,
.ag-theme-alpine .ag-filter-apply-panel button,
.ag-theme-alpine .ag-filter-apply-panel .ag-button,
.ag-theme-alpine .ag-filter-apply-panel .ag-standard-button {
display: inline-flex;
align-items: center;
justify-content: center;
min-height: 27px;
padding: 3px 8px;
border: 1px solid #d8e2df !important;
border-radius: 6px !important;
background: #ffffff !important;
color: #355154 !important;
font-size: 11.5px;
font-weight: 600;
letter-spacing: 0;
line-height: 1.2;
text-transform: none !important;
box-shadow: none !important;
transition: background-color 0.18s ease, border-color 0.18s ease,
box-shadow 0.18s ease, transform 0.18s ease;
}
.ag-filter-apply-panel button:hover,
.ag-filter-apply-panel .ag-button:hover,
.ag-filter-apply-panel .ag-standard-button:hover,
.ag-filter-apply-panel button:focus,
.ag-filter-apply-panel .ag-button:focus,
.ag-filter-apply-panel .ag-standard-button:focus,
.ag-popup .ag-filter-apply-panel button:hover,
.ag-popup .ag-filter-apply-panel .ag-button:hover,
.ag-popup .ag-filter-apply-panel .ag-standard-button:hover,
.ag-popup .ag-filter-apply-panel button:focus,
.ag-popup .ag-filter-apply-panel .ag-button:focus,
.ag-popup .ag-filter-apply-panel .ag-standard-button:focus,
.ag-theme-alpine .ag-filter-apply-panel button:hover,
.ag-theme-alpine .ag-filter-apply-panel .ag-button:hover,
.ag-theme-alpine .ag-filter-apply-panel .ag-standard-button:hover,
.ag-theme-alpine .ag-filter-apply-panel button:focus,
.ag-theme-alpine .ag-filter-apply-panel .ag-button:focus,
.ag-theme-alpine .ag-filter-apply-panel .ag-standard-button:focus {
background: linear-gradient(180deg, #f2faf7 0%, #e7f2ed 100%) !important;
border-color: #bfd5cd !important;
color: #28484b !important;
box-shadow: 0 1px 1px rgba(53, 81, 84, 0.05) !important;
}
.ag-filter-apply-panel button:active,
.ag-filter-apply-panel .ag-button:active,
.ag-filter-apply-panel .ag-standard-button:active,
.ag-popup .ag-filter-apply-panel button:active,
.ag-popup .ag-filter-apply-panel .ag-button:active,
.ag-popup .ag-filter-apply-panel .ag-standard-button:active,
.ag-theme-alpine .ag-filter-apply-panel button:active,
.ag-theme-alpine .ag-filter-apply-panel .ag-button:active,
.ag-theme-alpine .ag-filter-apply-panel .ag-standard-button:active {
transform: translateY(1px);
box-shadow: 0 2px 8px rgba(53, 81, 84, 0.12) !important;
}
.ag-grid-tree-columns-label, .ag-grid-tree-columns-label,
.ag-grid-tree-grouping-label, .ag-grid-tree-grouping-label,
.ag-grid-tree-layouts-label, .ag-grid-tree-layouts-label,
@@ -11183,6 +11255,133 @@ html[theme="default"] .screen-container .nav-tabs > li.active > a:hover {
color: #0f6f6d; color: #0f6f6d;
} }
.ag-grid-active-filter-chips {
display: inline-flex;
align-items: center;
gap: 4px;
min-width: 0;
max-width: min(520px, 38vw);
min-height: 32px;
overflow: hidden;
}
.ag-grid-active-filter-chips.drag-active {
min-width: 180px;
padding: 2px 6px;
border: 1px dashed #8fc3bd;
border-radius: 8px;
background: #f6fbf9;
}
.ag-grid-active-filter-chips.drag-over {
border-color: #0f817d;
background: #eef8f6;
}
.ag-grid-active-filter-drop-hint {
color: #0f6f6d;
font-size: 11px;
font-weight: 700;
white-space: nowrap;
}
.ag-grid-filter-draggable-cell {
cursor: grab;
}
.ag-grid-filter-draggable-cell:active {
cursor: grabbing;
}
.ag-grid-active-filter-chip {
display: grid;
grid-template-columns: minmax(0, 1fr) 14px;
grid-template-rows: auto auto;
column-gap: 6px;
row-gap: 1px;
align-items: center;
max-width: 190px;
min-height: 32px;
padding: 3px 5px 3px 8px;
border: 1px solid #c9ded8;
border-radius: 7px;
background: #f8fbfa;
color: #285256;
font-size: 10.5px;
font-weight: 600;
line-height: 1.2;
box-shadow: none;
}
.ag-grid-active-filter-chip:hover,
.ag-grid-active-filter-chip:focus {
border-color: #92bfb8;
background: #eff8f5;
color: #0f6f6d;
}
.ag-grid-active-filter-chip-name,
.ag-grid-active-filter-chip-detail {
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.ag-grid-active-filter-chip-name {
grid-column: 1;
grid-row: 1;
color: #637b87;
font-size: 10px;
font-weight: 700;
}
.ag-grid-active-filter-chip-value {
display: inline-flex;
grid-column: 1;
grid-row: 2;
min-width: 0;
gap: 4px;
color: #0f4f60;
}
.ag-grid-active-filter-chip-operator {
flex: 0 0 auto;
color: #0f817d;
font-weight: 800;
}
.ag-grid-active-filter-chip-detail {
font-weight: 700;
}
.ag-grid-active-filter-chip-remove {
display: inline-flex;
grid-column: 2;
grid-row: 1 / span 2;
align-items: center;
justify-content: center;
width: 14px;
height: 14px;
border-radius: 50%;
color: #6e8588;
font-size: 11px;
line-height: 1;
}
.ag-grid-active-filter-chip-remove:hover {
background: #dcebe7;
color: #0f6f6d;
}
.ag-grid-active-filter-chip-more {
display: inline-flex;
max-width: none;
min-height: 27px;
padding: 2px 7px;
border-radius: 999px;
}
.ag-grid-tree-grouping-panel .btn, .ag-grid-tree-grouping-panel .btn,
.ag-grid-tree-layouts-panel .btn, .ag-grid-tree-layouts-panel .btn,
.ag-grid-filter-logic-panel .btn { .ag-grid-filter-logic-panel .btn {
@@ -12774,7 +12973,10 @@ html[data-theme-mode="dark"] .ag-grid-tree-grouping > .btn,
html[data-theme-mode="dark"] .ag-grid-tree-layouts > .btn, html[data-theme-mode="dark"] .ag-grid-tree-layouts > .btn,
html[data-theme-mode="dark"] .ag-grid-tree-grouping-panel .btn, html[data-theme-mode="dark"] .ag-grid-tree-grouping-panel .btn,
html[data-theme-mode="dark"] .ag-grid-tree-layouts-panel .btn, html[data-theme-mode="dark"] .ag-grid-tree-layouts-panel .btn,
html[data-theme-mode="dark"] .ag-grid-filter-logic-panel .btn { html[data-theme-mode="dark"] .ag-grid-filter-logic-panel .btn,
html[data-theme-mode="dark"] .ag-theme-alpine .ag-filter-apply-panel button,
html[data-theme-mode="dark"] .ag-theme-alpine .ag-filter-apply-panel .ag-button,
html[data-theme-mode="dark"] .ag-theme-alpine .ag-filter-apply-panel .ag-standard-button {
background: linear-gradient(180deg, #213130 0%, #1a2726 100%); background: linear-gradient(180deg, #213130 0%, #1a2726 100%);
border-color: #35504d; border-color: #35504d;
color: #dce7e2; color: #dce7e2;
@@ -12789,7 +12991,10 @@ html[data-theme-mode="dark"] .ag-grid-tree-grouping > .btn:hover,
html[data-theme-mode="dark"] .ag-grid-tree-layouts > .btn:hover, html[data-theme-mode="dark"] .ag-grid-tree-layouts > .btn:hover,
html[data-theme-mode="dark"] .ag-grid-tree-grouping-panel .btn:hover, html[data-theme-mode="dark"] .ag-grid-tree-grouping-panel .btn:hover,
html[data-theme-mode="dark"] .ag-grid-tree-layouts-panel .btn:hover, html[data-theme-mode="dark"] .ag-grid-tree-layouts-panel .btn:hover,
html[data-theme-mode="dark"] .ag-grid-filter-logic-panel .btn:hover { html[data-theme-mode="dark"] .ag-grid-filter-logic-panel .btn:hover,
html[data-theme-mode="dark"] .ag-theme-alpine .ag-filter-apply-panel button:hover,
html[data-theme-mode="dark"] .ag-theme-alpine .ag-filter-apply-panel .ag-button:hover,
html[data-theme-mode="dark"] .ag-theme-alpine .ag-filter-apply-panel .ag-standard-button:hover {
background: linear-gradient(180deg, #27403d 0%, #203230 100%); background: linear-gradient(180deg, #27403d 0%, #203230 100%);
border-color: #4f716c; border-color: #4f716c;
color: #f0faf6; color: #f0faf6;

618
dist/tryton-sao.js vendored
View File

@@ -15008,7 +15008,8 @@ function eval_pyson(value){
}, },
_parse_form: function(node, attributes) { _parse_form: function(node, attributes) {
var container = new Sao.View.Form.Container( var container = new Sao.View.Form.Container(
Number(node.getAttribute('col') || 4)); Number(node.getAttribute('col') || 4),
attributes.col_widths);
this.view.containers.push(container); this.view.containers.push(container);
this.parse_child(node, container); this.parse_child(node, container);
if (this._containers.length > 0) { if (this._containers.length > 0) {
@@ -15205,7 +15206,8 @@ function eval_pyson(value){
return; return;
} }
var container = new Sao.View.Form.Container( var container = new Sao.View.Form.Container(
Number(node.getAttribute('col') || 4)); Number(node.getAttribute('col') || 4),
attributes.col_widths);
this.view.containers.push(container); this.view.containers.push(container);
this.parse_child(node, container); this.parse_child(node, container);
var page = new Sao.View.Form.Page( var page = new Sao.View.Form.Page(
@@ -15216,7 +15218,8 @@ function eval_pyson(value){
}, },
_parse_group: function(node, attributes) { _parse_group: function(node, attributes) {
var group = new Sao.View.Form.Container( var group = new Sao.View.Form.Container(
Number(node.getAttribute('col') || 4)); Number(node.getAttribute('col') || 4),
attributes.col_widths);
this.view.containers.push(group); this.view.containers.push(group);
this.parse_child(node, group); this.parse_child(node, group);
@@ -15259,7 +15262,8 @@ function eval_pyson(value){
_parse_child: function(node, attributes) { _parse_child: function(node, attributes) {
var paned = this.container; var paned = this.container;
var container = new Sao.View.Form.Container( var container = new Sao.View.Form.Container(
Number(node.getAttribute('col') || 4)); Number(node.getAttribute('col') || 4),
attributes.col_widths);
this.view.containers.push(container); this.view.containers.push(container);
this.parse_child(node, container); this.parse_child(node, container);
@@ -15528,9 +15532,10 @@ function eval_pyson(value){
}); });
Sao.View.Form.Container = Sao.class_(Object, { Sao.View.Form.Container = Sao.class_(Object, {
init: function(col=4) { init: function(col=4, col_widths=null) {
if (col < 0) col = 0; if (col < 0) col = 0;
this.col = col; this.col = col;
this.col_widths = this._parse_col_widths(col_widths);
this.el = jQuery('<div/>', { this.el = jQuery('<div/>', {
'class': 'form-container' 'class': 'form-container'
}); });
@@ -15547,6 +15552,21 @@ function eval_pyson(value){
this._grid_cols = []; this._grid_cols = [];
this._grid_rows = []; this._grid_rows = [];
}, },
_parse_col_widths: function(col_widths) {
if (!col_widths) {
return [];
}
return String(col_widths).split(',').map(width => {
width = String(width || '').trim();
if (!width) {
return null;
}
if (/^\d+(\.\d+)?$/.test(width)) {
return width + 'px';
}
return width;
});
},
add_row: function() { add_row: function() {
this._col = 1; this._col = 1;
this._row += 1; this._row += 1;
@@ -15659,7 +15679,15 @@ function eval_pyson(value){
var i; var i;
var col = this.col <= 0 ? this._col : this.col; var col = this.col <= 0 ? this._col : this.col;
if (this._xexpand.size) { if (this.col_widths.length) {
for (i = 1; i <= col; i++) {
this._grid_cols.push(
this.col_widths[i - 1] ||
(this._xexpand.has(i) ?
`minmax(min-content, ${col}fr)` :
'min-content'));
}
} else if (this._xexpand.size) {
for (i = 1; i <= col; i++) { for (i = 1; i <= col; i++) {
if (this._xexpand.has(i)) { if (this._xexpand.has(i)) {
this._grid_cols.push(`minmax(min-content, ${col}fr)`); this._grid_cols.push(`minmax(min-content, ${col}fr)`);
@@ -22691,6 +22719,29 @@ function eval_pyson(value){
this._toggle_filter_logic_menu(); this._toggle_filter_logic_menu();
}) })
.appendTo(this.toolbar); .appendTo(this.toolbar);
this.active_filter_chips = jQuery('<div/>', {
'class': 'ag-grid-active-filter-chips',
'aria-live': 'polite',
}).hide().appendTo(this.toolbar);
this.active_filter_chips.on('dragover', evt => {
if (!this._filter_drag_payload) {
return;
}
evt.preventDefault();
evt.originalEvent.dataTransfer.dropEffect = 'copy';
this.active_filter_chips.addClass('drag-over');
});
this.active_filter_chips.on('dragleave', evt => {
if (evt.currentTarget.contains(evt.relatedTarget)) {
return;
}
this.active_filter_chips.removeClass('drag-over');
});
this.active_filter_chips.on('drop', evt => {
evt.preventDefault();
this.active_filter_chips.removeClass('drag-over');
this._apply_filter_drop(evt.originalEvent);
});
this.import_layout_input = jQuery('<input/>', { this.import_layout_input = jQuery('<input/>', {
'type': 'file', 'type': 'file',
'accept': 'application/json,.json', 'accept': 'application/json,.json',
@@ -22774,17 +22825,8 @@ function eval_pyson(value){
}); });
Sao.View.AGGridTree._super.init.call(this, view_id, screen, xml); Sao.View.AGGridTree._super.init.call(this, view_id, screen, xml);
if (this.is_embedded_one2many) { this._persisted_state = this._load_persisted_state();
this._layout_store = this._normalize_layout_store(null); if (this._persisted_state &&
this._persisted_state = null;
this._sao_value_filters = {};
this._restored_value_filter_state = null;
this._sao_value_filter_restore_guard = false;
} else {
this._persisted_state = this._load_persisted_state();
}
if (!this.is_embedded_one2many &&
this._persisted_state &&
this._persisted_state.valueFilters) { this._persisted_state.valueFilters) {
this._apply_sao_value_filter_state( this._apply_sao_value_filter_state(
this._persisted_state.valueFilters); this._persisted_state.valueFilters);
@@ -22792,6 +22834,7 @@ function eval_pyson(value){
this._apply_saved_sort_to_screen_order(); this._apply_saved_sort_to_screen_order();
this._update_layout_button(); this._update_layout_button();
this._update_sao_value_filter_mode_button(); this._update_sao_value_filter_mode_button();
this._render_active_filter_chips();
this._update_copy_state(); this._update_copy_state();
this._init_grid(); this._init_grid();
this._install_resize_observer(); this._install_resize_observer();
@@ -22921,7 +22964,33 @@ function eval_pyson(value){
}); });
}, },
_get_layout_store_cache_key: function() { _get_layout_store_cache_key: function() {
return this._get_storage_key(); return this._get_layout_storage_key();
},
_get_layout_storage_key: function() {
var key = this._get_storage_key();
if (!this.is_embedded_one2many) {
return key;
}
var attributes = this.screen && this.screen.attributes || {};
var context_parts = [
'embedded_one2many',
attributes.name,
attributes.field,
attributes.relation_field,
attributes.one2many,
attributes.parent_name,
attributes.child_name,
this.screen && this.screen.exclude_field,
this.screen && this.screen.group &&
this.screen.group.parent_name,
this.screen && this.screen.group &&
this.screen.group.child_name,
].map(function(value) {
return String(value || '').replace(/[:\s]+/g, '_');
}).filter(function(value) {
return Boolean(value);
});
return [key].concat(context_parts).join(':');
}, },
_new_layout_id: function() { _new_layout_id: function() {
return 'layout_' + String(Date.now()) + '_' + return 'layout_' + String(Date.now()) + '_' +
@@ -22973,7 +23042,8 @@ function eval_pyson(value){
return this._normalize_layout_store(null); return this._normalize_layout_store(null);
} }
try { try {
var raw_state = localStorage.getItem(this._get_storage_key()); var raw_state = localStorage.getItem(
this._get_layout_storage_key());
if (!raw_state) { if (!raw_state) {
return this._normalize_layout_store(null); return this._normalize_layout_store(null);
} }
@@ -23018,7 +23088,7 @@ function eval_pyson(value){
} }
try { try {
localStorage.setItem( localStorage.setItem(
this._get_storage_key(), this._get_layout_storage_key(),
JSON.stringify(store)); JSON.stringify(store));
} catch (error) { } catch (error) {
Sao.Logger.warn( Sao.Logger.warn(
@@ -23142,9 +23212,6 @@ function eval_pyson(value){
window.addEventListener('resize', this._window_resize_handler); window.addEventListener('resize', this._window_resize_handler);
}, },
_persist_grid_state: function(options) { _persist_grid_state: function(options) {
if (this.is_embedded_one2many) {
return;
}
options = options || {}; options = options || {};
var state = this._capture_grid_state(); var state = this._capture_grid_state();
state.valueFilters = this._merge_restored_value_filter_state( state.valueFilters = this._merge_restored_value_filter_state(
@@ -23344,6 +23411,7 @@ function eval_pyson(value){
this._sao_value_filter_groups = state.groups ? this._sao_value_filter_groups = state.groups ?
jQuery.extend(true, {}, state.groups) : null; jQuery.extend(true, {}, state.groups) : null;
this._update_sao_value_filter_mode_button(); this._update_sao_value_filter_mode_button();
this._render_active_filter_chips();
}, },
_apply_saved_sort_to_screen_order: function() { _apply_saved_sort_to_screen_order: function() {
if (!this._persisted_state || !this._persisted_state.columns) { if (!this._persisted_state || !this._persisted_state.columns) {
@@ -24827,6 +24895,7 @@ function eval_pyson(value){
this._schedule_state_save(); this._schedule_state_save();
this._sync_selection_header_checkbox(); this._sync_selection_header_checkbox();
this._update_summary_bar(); this._update_summary_bar();
this._render_active_filter_chips();
}, },
onFirstDataRendered: () => { onFirstDataRendered: () => {
window.requestAnimationFrame(() => this._update_summary_bar()); window.requestAnimationFrame(() => this._update_summary_bar());
@@ -25003,6 +25072,10 @@ function eval_pyson(value){
} }
return ''; return '';
} }
if (this._is_date_filter_column(column)) {
return this._get_column_date_filter_value(
column, params.data.__record);
}
return this._get_column_text_value( return this._get_column_text_value(
column, params.data.__record); column, params.data.__record);
}, },
@@ -25023,11 +25096,16 @@ function eval_pyson(value){
if (!params.data.__record) { if (!params.data.__record) {
return ''; return '';
} }
var rendered;
if (badge_column) { if (badge_column) {
return this._render_badge_cell( rendered = this._render_badge_cell(
column, params.data.__record);
} else {
rendered = this._render_cell(
column, params.data.__record); column, params.data.__record);
} }
return this._render_cell(column, params.data.__record); return this._make_filter_draggable_cell(
rendered, column, params.data.__record);
}, },
tooltipValueGetter: params => { tooltipValueGetter: params => {
if (!params.data || !params.data.__record || if (!params.data || !params.data.__record ||
@@ -25230,9 +25308,6 @@ function eval_pyson(value){
case 'numeric': case 'numeric':
return 'agNumberColumnFilter'; return 'agNumberColumnFilter';
case 'date': case 'date':
if (this._get_distinct_filter_values(column)) {
return Sao.View.AGGridValueSetFilter;
}
return 'agDateColumnFilter'; return 'agDateColumnFilter';
default: default:
if (this._get_distinct_filter_values(column)) { if (this._get_distinct_filter_values(column)) {
@@ -25242,6 +25317,9 @@ function eval_pyson(value){
} }
}, },
_get_column_filter_params: function(column) { _get_column_filter_params: function(column) {
if (this._is_date_filter_column(column)) {
return this._get_date_column_filter_params(column);
}
var values = this._get_distinct_filter_values(column); var values = this._get_distinct_filter_values(column);
if (!values) { if (!values) {
return null; return null;
@@ -25260,6 +25338,135 @@ function eval_pyson(value){
searchPlaceholder: Sao.i18n.gettext('Search'), searchPlaceholder: Sao.i18n.gettext('Search'),
}; };
}, },
_is_date_filter_column: function(column) {
return Boolean(column && column.attributes &&
column.attributes.widget == 'date');
},
_get_date_column_filter_params: function(column) {
var relative_options = this._get_relative_date_filter_options();
return {
browserDatePicker: true,
buttons: ['reset', 'apply'],
closeOnApply: true,
maxNumConditions: 1,
defaultOption: 'empty',
filterOptions: ['empty'].concat(relative_options).concat([
'inRange',
'equals',
'lessThan',
'greaterThan',
'blank',
'notBlank',
]),
inRangeInclusive: true,
comparator: function(filter_date, cell_value) {
if (!cell_value) {
return 0;
}
var cell_date = cell_value instanceof Date ? cell_value :
new Date(cell_value);
if (isNaN(cell_date.getTime())) {
return 0;
}
var cell_midnight = new Date(
cell_date.getFullYear(),
cell_date.getMonth(),
cell_date.getDate());
if (cell_midnight < filter_date) {
return -1;
}
if (cell_midnight > filter_date) {
return 1;
}
return 0;
},
};
},
_get_relative_date_filter_options: function() {
var make_option = (key, label, range_getter) => {
return {
displayKey: 'sao' + key,
displayName: Sao.i18n.gettext(label),
numberOfInputs: 0,
predicate: (filter_values, cell_value) => {
var cell_date = this._normalize_date_filter_value(cell_value);
if (!cell_date) {
return false;
}
var range = range_getter();
return cell_date >= range.start && cell_date < range.end;
},
};
};
return [
make_option('Today', 'Today', () => {
var start = this._start_of_today();
return {
start: start,
end: this._add_days(start, 1),
};
}),
make_option('Last7Days', 'Last 7 Days', () => {
var today = this._start_of_today();
return {
start: this._add_days(today, -7),
end: this._add_days(today, 1),
};
}),
make_option('Last30Days', 'Last 30 Days', () => {
var today = this._start_of_today();
return {
start: this._add_days(today, -30),
end: this._add_days(today, 1),
};
}),
make_option('LastMonth', 'Last Month', () => {
var today = this._start_of_today();
var start = new Date(
today.getFullYear(), today.getMonth() - 1, 1);
var end = new Date(today.getFullYear(), today.getMonth(), 1);
return {
start: start,
end: end,
};
}),
make_option('ThisMonth', 'This Month', () => {
var today = this._start_of_today();
var start = new Date(today.getFullYear(), today.getMonth(), 1);
var end = new Date(
today.getFullYear(), today.getMonth() + 1, 1);
return {
start: start,
end: end,
};
}),
make_option('YearToDate', 'Year to Date', () => {
var today = this._start_of_today();
return {
start: new Date(today.getFullYear(), 0, 1),
end: this._add_days(today, 1),
};
}),
];
},
_start_of_today: function() {
var now = new Date();
return new Date(now.getFullYear(), now.getMonth(), now.getDate());
},
_add_days: function(date, days) {
return new Date(
date.getFullYear(), date.getMonth(), date.getDate() + days);
},
_normalize_date_filter_value: function(value) {
if (!value) {
return null;
}
var date = value instanceof Date ? value : new Date(value);
if (isNaN(date.getTime())) {
return null;
}
return new Date(date.getFullYear(), date.getMonth(), date.getDate());
},
_get_sao_value_filter_state: function(col_id) { _get_sao_value_filter_state: function(col_id) {
var filter = this._sao_value_filters && var filter = this._sao_value_filters &&
this._sao_value_filters[col_id]; this._sao_value_filters[col_id];
@@ -25285,7 +25492,113 @@ function eval_pyson(value){
}, },
_is_sao_value_filter_user_source: function(source) { _is_sao_value_filter_user_source: function(source) {
return ~['checkbox-change', 'all-button', 'none-button', return ~['checkbox-change', 'all-button', 'none-button',
'clear-button'].indexOf(source); 'clear-button', 'drag-drop'].indexOf(source);
},
_make_filter_draggable_cell: function(element, column, record) {
if (!element || !column || !column.attributes ||
!column.attributes.name || !record) {
return element;
}
var value = this._get_column_text_value(column, record);
if (!value) {
return element;
}
var wrapped = jQuery(element);
wrapped.attr('draggable', 'true')
.addClass('ag-grid-filter-draggable-cell')
.on('dragstart', evt => {
var payload = {
colId: column.attributes.name,
label: this._get_column_label(column.attributes.name),
value: String(value),
};
this._filter_drag_payload = payload;
evt.originalEvent.dataTransfer.effectAllowed = 'copy';
evt.originalEvent.dataTransfer.setData(
'application/x-sao-ag-grid-filter',
JSON.stringify(payload));
evt.originalEvent.dataTransfer.setData(
'text/plain',
payload.label + ' = ' + payload.value);
this._set_filter_dropzone_active(true, payload);
})
.on('dragend', () => {
this._filter_drag_payload = null;
this._set_filter_dropzone_active(false);
});
return wrapped[0];
},
_set_filter_dropzone_active: function(active, payload) {
if (!this.active_filter_chips) {
return;
}
this.active_filter_chips
.toggleClass('drag-active', Boolean(active))
.removeClass('drag-over');
if (active && !this._get_active_filter_chip_entries().length) {
this.active_filter_chips.empty();
jQuery('<span/>', {
'class': 'ag-grid-active-filter-drop-hint',
'text': payload ?
Sao.i18n.gettext('Drop to filter %1')
.replace('%1', payload.label) :
Sao.i18n.gettext('Drop to filter'),
}).appendTo(this.active_filter_chips);
this.active_filter_chips.css('display', 'inline-flex');
} else if (!active) {
this._render_active_filter_chips();
}
},
_get_filter_drop_payload: function(event) {
var payload = this._filter_drag_payload;
if (!payload && event && event.dataTransfer) {
try {
payload = JSON.parse(event.dataTransfer.getData(
'application/x-sao-ag-grid-filter') || 'null');
} catch (error) {
payload = null;
}
}
if (!payload || !payload.colId || !payload.value) {
return null;
}
return payload;
},
_apply_filter_drop: function(event) {
var payload = this._get_filter_drop_payload(event);
this._filter_drag_payload = null;
if (!payload) {
this._set_filter_dropzone_active(false);
return;
}
var column = this._find_column_by_id(payload.colId);
if (!column) {
this._set_filter_dropzone_active(false);
return;
}
var empty = Sao.i18n.gettext('(Empty)');
var value = String(payload.value || '') || empty;
var all_values = this._get_live_filter_values(
column, payload.colId).map(item => String(item || empty));
if (!~all_values.indexOf(value)) {
all_values.push(value);
}
var existing = this._sao_value_filters &&
this._sao_value_filters[payload.colId];
var selected = existing && existing.selected ?
Array.from(existing.selected).map(item => String(item)) : [];
if (!selected.length) {
selected = [value];
} else if (!~selected.indexOf(value)) {
selected.push(value);
}
this._set_sao_value_filter(
payload.colId,
selected,
all_values,
'drag-drop',
{value: value});
this._set_filter_dropzone_active(false);
}, },
_forget_sao_value_filter_saved_state: function(col_id) { _forget_sao_value_filter_saved_state: function(col_id) {
var remove_filter = value_filters => { var remove_filter = value_filters => {
@@ -25358,6 +25671,7 @@ function eval_pyson(value){
} }
this._apply_sao_value_filters(); this._apply_sao_value_filters();
this._render_filter_logic_panel(); this._render_filter_logic_panel();
this._render_active_filter_chips();
this._schedule_state_save(); this._schedule_state_save();
}, },
_trace_sao_value_filter_change: function(reason, col_id, selected, all, _trace_sao_value_filter_change: function(reason, col_id, selected, all,
@@ -25599,6 +25913,208 @@ function eval_pyson(value){
.find('.ag-grid-value-filter-mode-label') .find('.ag-grid-value-filter-mode-label')
.text(' ' + Sao.i18n.gettext(any ? 'Any filter' : 'All filters')); .text(' ' + Sao.i18n.gettext(any ? 'Any filter' : 'All filters'));
}, },
_get_column_label: function(col_id) {
var column = this._find_column_by_id(col_id);
return column && column.attributes ?
column.attributes.string || column.attributes.name || col_id :
col_id;
},
_describe_grid_filter_model: function(model) {
if (!model) {
return {
operator: '',
value: '',
};
}
var type = model.type || model.operator || '';
var value = model.filter;
if (value === undefined || value === null || value === '') {
value = model.dateFrom || model.filterTo || '';
}
if (Array.isArray(model.values)) {
return {
operator: '=',
value: model.values.length <= 2 ?
model.values.join(', ') :
Sao.i18n.gettext('%1 values').replace('%1', model.values.length),
};
}
if (model.condition1 || model.condition2) {
return {
operator: '',
value: Sao.i18n.gettext('complex'),
};
}
var relative_date_labels = {
saoToday: 'Today',
saoLast7Days: 'Last 7 Days',
saoLast30Days: 'Last 30 Days',
saoLastMonth: 'Last Month',
saoThisMonth: 'This Month',
saoYearToDate: 'Year to Date',
};
if (relative_date_labels[type]) {
return {
operator: '',
value: Sao.i18n.gettext(relative_date_labels[type]),
};
}
var operators = {
equals: '=',
notEqual: '!=',
contains: 'contains',
notContains: 'not contains',
startsWith: 'starts',
endsWith: 'ends',
lessThan: '<',
lessThanOrEqual: '<=',
greaterThan: '>',
greaterThanOrEqual: '>=',
inRange: 'between',
};
return {
operator: operators[type] || type || '=',
value: value,
};
},
_get_active_filter_chip_entries: function() {
var entries = [];
Object.keys(this._sao_value_filters || {}).forEach(col_id => {
var filter = this._sao_value_filters[col_id];
if (!this._is_sao_value_filter_active(filter)) {
return;
}
var selected = Array.from(filter.selected || []).sort();
var all = Array.from(filter.all || []).sort();
var selected_lookup = new Set(selected);
var excluded = all.filter(value => !selected_lookup.has(value));
var display_values = excluded.length &&
excluded.length < selected.length ? excluded : selected;
var operator = display_values === excluded ? '<>' : '=';
var detail = display_values.length <= 2 ?
display_values.join(', ') :
Sao.i18n.gettext('%1 values').replace(
'%1', display_values.length);
entries.push({
type: 'sao',
colId: col_id,
label: this._get_column_label(col_id),
operator: operator,
detail: detail,
});
});
var filter_model = this.gridApi && this.gridApi.getFilterModel ?
this._strip_sao_value_set_filter_model(
this.gridApi.getFilterModel()) : {};
Object.keys(filter_model || {}).forEach(col_id => {
var description =
this._describe_grid_filter_model(filter_model[col_id]);
entries.push({
type: 'grid',
colId: col_id,
label: this._get_column_label(col_id),
operator: description.operator,
detail: description.value,
});
});
return entries;
},
_open_filter_for_chip: function(entry) {
if (entry.type == 'sao') {
this._show_filter_logic_menu();
return;
}
if (!this.gridApi) {
return;
}
if (this.gridApi.showColumnFilter) {
this.gridApi.showColumnFilter(entry.colId);
} else if (this.gridApi.showColumnMenu) {
this.gridApi.showColumnMenu(entry.colId);
}
},
_remove_filter_chip: function(entry) {
if (entry.type == 'sao') {
delete this._sao_value_filters[entry.colId];
this._forget_sao_value_filter_saved_state(entry.colId);
this._apply_sao_value_filters();
this._render_filter_logic_panel();
this._schedule_state_save();
this._render_active_filter_chips();
return;
}
if (!this.gridApi || !this.gridApi.getFilterModel ||
!this.gridApi.setFilterModel) {
return;
}
var model = jQuery.extend(true, {}, this.gridApi.getFilterModel() || {});
delete model[entry.colId];
this.gridApi.setFilterModel(model);
if (this.gridApi.onFilterChanged) {
this.gridApi.onFilterChanged();
}
},
_render_active_filter_chips: function() {
if (!this.active_filter_chips) {
return;
}
var entries = this._get_active_filter_chip_entries();
this.active_filter_chips.empty();
if (!entries.length) {
this.active_filter_chips.hide();
return;
}
var visible = entries.slice(0, 4);
visible.forEach(entry => {
var text = entry.label + (entry.operator ? ' ' +
entry.operator : '') + (entry.detail ? ' ' +
entry.detail : '');
var chip = jQuery('<button/>', {
'class': 'ag-grid-active-filter-chip',
'type': 'button',
'title': text,
}).click(evt => {
evt.preventDefault();
this._open_filter_for_chip(entry);
}).appendTo(this.active_filter_chips);
jQuery('<span/>', {
'class': 'ag-grid-active-filter-chip-name',
'text': entry.label,
}).appendTo(chip);
var value = jQuery('<span/>', {
'class': 'ag-grid-active-filter-chip-value',
}).appendTo(chip);
jQuery('<span/>', {
'class': 'ag-grid-active-filter-chip-operator',
'text': entry.operator || '=',
}).appendTo(value);
jQuery('<span/>', {
'class': 'ag-grid-active-filter-chip-detail',
'text': entry.detail || '',
}).appendTo(value);
jQuery('<span/>', {
'class': 'ag-grid-active-filter-chip-remove',
'text': 'x',
'title': Sao.i18n.gettext('Remove filter'),
}).click(evt => {
evt.preventDefault();
evt.stopPropagation();
this._remove_filter_chip(entry);
}).appendTo(chip);
});
if (entries.length > visible.length) {
jQuery('<button/>', {
'class': 'ag-grid-active-filter-chip ag-grid-active-filter-chip-more',
'type': 'button',
'text': '+' + String(entries.length - visible.length),
'title': Sao.i18n.gettext('Show filter logic'),
}).click(evt => {
evt.preventDefault();
this._show_filter_logic_menu();
}).appendTo(this.active_filter_chips);
}
this.active_filter_chips.css('display', 'inline-flex');
},
_get_active_sao_value_filter_entries: function() { _get_active_sao_value_filter_entries: function() {
return Object.keys(this._sao_value_filters || {}) return Object.keys(this._sao_value_filters || {})
.filter(col_id => this._is_sao_value_filter_active( .filter(col_id => this._is_sao_value_filter_active(
@@ -26229,6 +26745,47 @@ function eval_pyson(value){
return ''; return '';
} }
}, },
_get_column_date_filter_value: function(column, record) {
if (!column || !record || !column.attributes.name) {
return null;
}
var name = column.attributes.name;
var value = null;
try {
var field = record.model && record.model.fields &&
record.model.fields[name];
if (field && field.get_client) {
value = field.get_client(record);
}
} catch (error) {
value = null;
}
if (!value && record._values &&
Object.prototype.hasOwnProperty.call(record._values, name)) {
value = record._values[name];
}
if (!value) {
return null;
}
if (value instanceof Date) {
return new Date(
value.getFullYear(), value.getMonth(), value.getDate());
}
if (value.isDate || value.isDateTime) {
return new Date(value.year(), value.month(), value.date());
}
if (typeof value == 'string') {
var date = moment(value, [
'YYYY-MM-DD',
Sao.common.date_format(
this.screen && this.screen.context &&
this.screen.context.date_format),
], true);
return date.isValid() ?
new Date(date.year(), date.month(), date.date()) : null;
}
return null;
},
_is_badge_column: function(column) { _is_badge_column: function(column) {
if (!column || !column.attributes) { if (!column || !column.attributes) {
return false; return false;
@@ -35527,7 +36084,8 @@ function eval_pyson(value){
Sao.View.BoardXMLViewParser = Sao.class_(Sao.View.FormXMLViewParser, { Sao.View.BoardXMLViewParser = Sao.class_(Sao.View.FormXMLViewParser, {
_parse_board: function(node, attributes) { _parse_board: function(node, attributes) {
var container = new Sao.View.Form.Container( var container = new Sao.View.Form.Container(
Number(node.getAttribute('col') || 4)); Number(node.getAttribute('col') || 4),
attributes.col_widths);
this.view.el.append(container.el); this.view.el.append(container.el);
this.parse_child(node, container); this.parse_child(node, container);
if (this._containers.length > 0) { if (this._containers.length > 0) {

View File

@@ -11172,6 +11172,78 @@ html[theme="default"] .screen-container .nav-tabs > li.active > a:hover {
box-shadow: 0 2px 8px rgba(53, 81, 84, 0.12); box-shadow: 0 2px 8px rgba(53, 81, 84, 0.12);
} }
.ag-filter-apply-panel {
display: flex;
justify-content: flex-end;
gap: 5px;
padding: 6px 8px 8px;
}
.ag-filter-apply-panel button,
.ag-filter-apply-panel .ag-button,
.ag-filter-apply-panel .ag-standard-button,
.ag-popup .ag-filter-apply-panel button,
.ag-popup .ag-filter-apply-panel .ag-button,
.ag-popup .ag-filter-apply-panel .ag-standard-button,
.ag-theme-alpine .ag-filter-apply-panel button,
.ag-theme-alpine .ag-filter-apply-panel .ag-button,
.ag-theme-alpine .ag-filter-apply-panel .ag-standard-button {
display: inline-flex;
align-items: center;
justify-content: center;
min-height: 27px;
padding: 3px 8px;
border: 1px solid #d8e2df !important;
border-radius: 6px !important;
background: #ffffff !important;
color: #355154 !important;
font-size: 11.5px;
font-weight: 600;
letter-spacing: 0;
line-height: 1.2;
text-transform: none !important;
box-shadow: none !important;
transition: background-color 0.18s ease, border-color 0.18s ease,
box-shadow 0.18s ease, transform 0.18s ease;
}
.ag-filter-apply-panel button:hover,
.ag-filter-apply-panel .ag-button:hover,
.ag-filter-apply-panel .ag-standard-button:hover,
.ag-filter-apply-panel button:focus,
.ag-filter-apply-panel .ag-button:focus,
.ag-filter-apply-panel .ag-standard-button:focus,
.ag-popup .ag-filter-apply-panel button:hover,
.ag-popup .ag-filter-apply-panel .ag-button:hover,
.ag-popup .ag-filter-apply-panel .ag-standard-button:hover,
.ag-popup .ag-filter-apply-panel button:focus,
.ag-popup .ag-filter-apply-panel .ag-button:focus,
.ag-popup .ag-filter-apply-panel .ag-standard-button:focus,
.ag-theme-alpine .ag-filter-apply-panel button:hover,
.ag-theme-alpine .ag-filter-apply-panel .ag-button:hover,
.ag-theme-alpine .ag-filter-apply-panel .ag-standard-button:hover,
.ag-theme-alpine .ag-filter-apply-panel button:focus,
.ag-theme-alpine .ag-filter-apply-panel .ag-button:focus,
.ag-theme-alpine .ag-filter-apply-panel .ag-standard-button:focus {
background: linear-gradient(180deg, #f2faf7 0%, #e7f2ed 100%) !important;
border-color: #bfd5cd !important;
color: #28484b !important;
box-shadow: 0 1px 1px rgba(53, 81, 84, 0.05) !important;
}
.ag-filter-apply-panel button:active,
.ag-filter-apply-panel .ag-button:active,
.ag-filter-apply-panel .ag-standard-button:active,
.ag-popup .ag-filter-apply-panel button:active,
.ag-popup .ag-filter-apply-panel .ag-button:active,
.ag-popup .ag-filter-apply-panel .ag-standard-button:active,
.ag-theme-alpine .ag-filter-apply-panel button:active,
.ag-theme-alpine .ag-filter-apply-panel .ag-button:active,
.ag-theme-alpine .ag-filter-apply-panel .ag-standard-button:active {
transform: translateY(1px);
box-shadow: 0 2px 8px rgba(53, 81, 84, 0.12) !important;
}
.ag-grid-tree-columns-label, .ag-grid-tree-columns-label,
.ag-grid-tree-grouping-label, .ag-grid-tree-grouping-label,
.ag-grid-tree-layouts-label, .ag-grid-tree-layouts-label,
@@ -11188,6 +11260,133 @@ html[theme="default"] .screen-container .nav-tabs > li.active > a:hover {
color: #0f6f6d; color: #0f6f6d;
} }
.ag-grid-active-filter-chips {
display: inline-flex;
align-items: center;
gap: 4px;
min-width: 0;
max-width: min(520px, 38vw);
min-height: 32px;
overflow: hidden;
}
.ag-grid-active-filter-chips.drag-active {
min-width: 180px;
padding: 2px 6px;
border: 1px dashed #8fc3bd;
border-radius: 8px;
background: #f6fbf9;
}
.ag-grid-active-filter-chips.drag-over {
border-color: #0f817d;
background: #eef8f6;
}
.ag-grid-active-filter-drop-hint {
color: #0f6f6d;
font-size: 11px;
font-weight: 700;
white-space: nowrap;
}
.ag-grid-filter-draggable-cell {
cursor: grab;
}
.ag-grid-filter-draggable-cell:active {
cursor: grabbing;
}
.ag-grid-active-filter-chip {
display: grid;
grid-template-columns: minmax(0, 1fr) 14px;
grid-template-rows: auto auto;
column-gap: 6px;
row-gap: 1px;
align-items: center;
max-width: 190px;
min-height: 32px;
padding: 3px 5px 3px 8px;
border: 1px solid #c9ded8;
border-radius: 7px;
background: #f8fbfa;
color: #285256;
font-size: 10.5px;
font-weight: 600;
line-height: 1.2;
box-shadow: none;
}
.ag-grid-active-filter-chip:hover,
.ag-grid-active-filter-chip:focus {
border-color: #92bfb8;
background: #eff8f5;
color: #0f6f6d;
}
.ag-grid-active-filter-chip-name,
.ag-grid-active-filter-chip-detail {
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.ag-grid-active-filter-chip-name {
grid-column: 1;
grid-row: 1;
color: #637b87;
font-size: 10px;
font-weight: 700;
}
.ag-grid-active-filter-chip-value {
display: inline-flex;
grid-column: 1;
grid-row: 2;
min-width: 0;
gap: 4px;
color: #0f4f60;
}
.ag-grid-active-filter-chip-operator {
flex: 0 0 auto;
color: #0f817d;
font-weight: 800;
}
.ag-grid-active-filter-chip-detail {
font-weight: 700;
}
.ag-grid-active-filter-chip-remove {
display: inline-flex;
grid-column: 2;
grid-row: 1 / span 2;
align-items: center;
justify-content: center;
width: 14px;
height: 14px;
border-radius: 50%;
color: #6e8588;
font-size: 11px;
line-height: 1;
}
.ag-grid-active-filter-chip-remove:hover {
background: #dcebe7;
color: #0f6f6d;
}
.ag-grid-active-filter-chip-more {
display: inline-flex;
max-width: none;
min-height: 27px;
padding: 2px 7px;
border-radius: 999px;
}
.ag-grid-view-transfer-button { .ag-grid-view-transfer-button {
font-size: 11.5px; font-size: 11.5px;
font-weight: 600; font-weight: 600;
@@ -12883,7 +13082,10 @@ html[data-theme-mode="dark"] .ag-grid-tree-layouts > .btn,
html[data-theme-mode="dark"] .ag-grid-view-transfer-button, html[data-theme-mode="dark"] .ag-grid-view-transfer-button,
html[data-theme-mode="dark"] .ag-grid-tree-grouping-panel .btn, html[data-theme-mode="dark"] .ag-grid-tree-grouping-panel .btn,
html[data-theme-mode="dark"] .ag-grid-tree-layouts-panel .btn, html[data-theme-mode="dark"] .ag-grid-tree-layouts-panel .btn,
html[data-theme-mode="dark"] .ag-grid-filter-logic-panel .btn { html[data-theme-mode="dark"] .ag-grid-filter-logic-panel .btn,
html[data-theme-mode="dark"] .ag-theme-alpine .ag-filter-apply-panel button,
html[data-theme-mode="dark"] .ag-theme-alpine .ag-filter-apply-panel .ag-button,
html[data-theme-mode="dark"] .ag-theme-alpine .ag-filter-apply-panel .ag-standard-button {
background: linear-gradient(180deg, #213130 0%, #1a2726 100%); background: linear-gradient(180deg, #213130 0%, #1a2726 100%);
border-color: #35504d; border-color: #35504d;
color: #dce7e2; color: #dce7e2;
@@ -12899,7 +13101,10 @@ html[data-theme-mode="dark"] .ag-grid-tree-layouts > .btn:hover,
html[data-theme-mode="dark"] .ag-grid-view-transfer-button:hover, html[data-theme-mode="dark"] .ag-grid-view-transfer-button:hover,
html[data-theme-mode="dark"] .ag-grid-tree-grouping-panel .btn:hover, html[data-theme-mode="dark"] .ag-grid-tree-grouping-panel .btn:hover,
html[data-theme-mode="dark"] .ag-grid-tree-layouts-panel .btn:hover, html[data-theme-mode="dark"] .ag-grid-tree-layouts-panel .btn:hover,
html[data-theme-mode="dark"] .ag-grid-filter-logic-panel .btn:hover { html[data-theme-mode="dark"] .ag-grid-filter-logic-panel .btn:hover,
html[data-theme-mode="dark"] .ag-theme-alpine .ag-filter-apply-panel button:hover,
html[data-theme-mode="dark"] .ag-theme-alpine .ag-filter-apply-panel .ag-button:hover,
html[data-theme-mode="dark"] .ag-theme-alpine .ag-filter-apply-panel .ag-standard-button:hover {
background: linear-gradient(180deg, #27403d 0%, #203230 100%); background: linear-gradient(180deg, #27403d 0%, #203230 100%);
border-color: #4f716c; border-color: #4f716c;
color: #f0faf6; color: #f0faf6;

View File

@@ -15008,7 +15008,8 @@ function eval_pyson(value){
}, },
_parse_form: function(node, attributes) { _parse_form: function(node, attributes) {
var container = new Sao.View.Form.Container( var container = new Sao.View.Form.Container(
Number(node.getAttribute('col') || 4)); Number(node.getAttribute('col') || 4),
attributes.col_widths);
this.view.containers.push(container); this.view.containers.push(container);
this.parse_child(node, container); this.parse_child(node, container);
if (this._containers.length > 0) { if (this._containers.length > 0) {
@@ -15205,7 +15206,8 @@ function eval_pyson(value){
return; return;
} }
var container = new Sao.View.Form.Container( var container = new Sao.View.Form.Container(
Number(node.getAttribute('col') || 4)); Number(node.getAttribute('col') || 4),
attributes.col_widths);
this.view.containers.push(container); this.view.containers.push(container);
this.parse_child(node, container); this.parse_child(node, container);
var page = new Sao.View.Form.Page( var page = new Sao.View.Form.Page(
@@ -15216,7 +15218,8 @@ function eval_pyson(value){
}, },
_parse_group: function(node, attributes) { _parse_group: function(node, attributes) {
var group = new Sao.View.Form.Container( var group = new Sao.View.Form.Container(
Number(node.getAttribute('col') || 4)); Number(node.getAttribute('col') || 4),
attributes.col_widths);
this.view.containers.push(group); this.view.containers.push(group);
this.parse_child(node, group); this.parse_child(node, group);
@@ -15259,7 +15262,8 @@ function eval_pyson(value){
_parse_child: function(node, attributes) { _parse_child: function(node, attributes) {
var paned = this.container; var paned = this.container;
var container = new Sao.View.Form.Container( var container = new Sao.View.Form.Container(
Number(node.getAttribute('col') || 4)); Number(node.getAttribute('col') || 4),
attributes.col_widths);
this.view.containers.push(container); this.view.containers.push(container);
this.parse_child(node, container); this.parse_child(node, container);
@@ -15528,9 +15532,10 @@ function eval_pyson(value){
}); });
Sao.View.Form.Container = Sao.class_(Object, { Sao.View.Form.Container = Sao.class_(Object, {
init: function(col=4) { init: function(col=4, col_widths=null) {
if (col < 0) col = 0; if (col < 0) col = 0;
this.col = col; this.col = col;
this.col_widths = this._parse_col_widths(col_widths);
this.el = jQuery('<div/>', { this.el = jQuery('<div/>', {
'class': 'form-container' 'class': 'form-container'
}); });
@@ -15547,6 +15552,21 @@ function eval_pyson(value){
this._grid_cols = []; this._grid_cols = [];
this._grid_rows = []; this._grid_rows = [];
}, },
_parse_col_widths: function(col_widths) {
if (!col_widths) {
return [];
}
return String(col_widths).split(',').map(width => {
width = String(width || '').trim();
if (!width) {
return null;
}
if (/^\d+(\.\d+)?$/.test(width)) {
return width + 'px';
}
return width;
});
},
add_row: function() { add_row: function() {
this._col = 1; this._col = 1;
this._row += 1; this._row += 1;
@@ -15659,7 +15679,15 @@ function eval_pyson(value){
var i; var i;
var col = this.col <= 0 ? this._col : this.col; var col = this.col <= 0 ? this._col : this.col;
if (this._xexpand.size) { if (this.col_widths.length) {
for (i = 1; i <= col; i++) {
this._grid_cols.push(
this.col_widths[i - 1] ||
(this._xexpand.has(i) ?
`minmax(min-content, ${col}fr)` :
'min-content'));
}
} else if (this._xexpand.size) {
for (i = 1; i <= col; i++) { for (i = 1; i <= col; i++) {
if (this._xexpand.has(i)) { if (this._xexpand.has(i)) {
this._grid_cols.push(`minmax(min-content, ${col}fr)`); this._grid_cols.push(`minmax(min-content, ${col}fr)`);
@@ -22691,6 +22719,29 @@ function eval_pyson(value){
this._toggle_filter_logic_menu(); this._toggle_filter_logic_menu();
}) })
.appendTo(this.toolbar); .appendTo(this.toolbar);
this.active_filter_chips = jQuery('<div/>', {
'class': 'ag-grid-active-filter-chips',
'aria-live': 'polite',
}).hide().appendTo(this.toolbar);
this.active_filter_chips.on('dragover', evt => {
if (!this._filter_drag_payload) {
return;
}
evt.preventDefault();
evt.originalEvent.dataTransfer.dropEffect = 'copy';
this.active_filter_chips.addClass('drag-over');
});
this.active_filter_chips.on('dragleave', evt => {
if (evt.currentTarget.contains(evt.relatedTarget)) {
return;
}
this.active_filter_chips.removeClass('drag-over');
});
this.active_filter_chips.on('drop', evt => {
evt.preventDefault();
this.active_filter_chips.removeClass('drag-over');
this._apply_filter_drop(evt.originalEvent);
});
this.import_layout_input = jQuery('<input/>', { this.import_layout_input = jQuery('<input/>', {
'type': 'file', 'type': 'file',
'accept': 'application/json,.json', 'accept': 'application/json,.json',
@@ -22774,17 +22825,8 @@ function eval_pyson(value){
}); });
Sao.View.AGGridTree._super.init.call(this, view_id, screen, xml); Sao.View.AGGridTree._super.init.call(this, view_id, screen, xml);
if (this.is_embedded_one2many) { this._persisted_state = this._load_persisted_state();
this._layout_store = this._normalize_layout_store(null); if (this._persisted_state &&
this._persisted_state = null;
this._sao_value_filters = {};
this._restored_value_filter_state = null;
this._sao_value_filter_restore_guard = false;
} else {
this._persisted_state = this._load_persisted_state();
}
if (!this.is_embedded_one2many &&
this._persisted_state &&
this._persisted_state.valueFilters) { this._persisted_state.valueFilters) {
this._apply_sao_value_filter_state( this._apply_sao_value_filter_state(
this._persisted_state.valueFilters); this._persisted_state.valueFilters);
@@ -22792,6 +22834,7 @@ function eval_pyson(value){
this._apply_saved_sort_to_screen_order(); this._apply_saved_sort_to_screen_order();
this._update_layout_button(); this._update_layout_button();
this._update_sao_value_filter_mode_button(); this._update_sao_value_filter_mode_button();
this._render_active_filter_chips();
this._update_copy_state(); this._update_copy_state();
this._init_grid(); this._init_grid();
this._install_resize_observer(); this._install_resize_observer();
@@ -22921,7 +22964,33 @@ function eval_pyson(value){
}); });
}, },
_get_layout_store_cache_key: function() { _get_layout_store_cache_key: function() {
return this._get_storage_key(); return this._get_layout_storage_key();
},
_get_layout_storage_key: function() {
var key = this._get_storage_key();
if (!this.is_embedded_one2many) {
return key;
}
var attributes = this.screen && this.screen.attributes || {};
var context_parts = [
'embedded_one2many',
attributes.name,
attributes.field,
attributes.relation_field,
attributes.one2many,
attributes.parent_name,
attributes.child_name,
this.screen && this.screen.exclude_field,
this.screen && this.screen.group &&
this.screen.group.parent_name,
this.screen && this.screen.group &&
this.screen.group.child_name,
].map(function(value) {
return String(value || '').replace(/[:\s]+/g, '_');
}).filter(function(value) {
return Boolean(value);
});
return [key].concat(context_parts).join(':');
}, },
_new_layout_id: function() { _new_layout_id: function() {
return 'layout_' + String(Date.now()) + '_' + return 'layout_' + String(Date.now()) + '_' +
@@ -22973,7 +23042,8 @@ function eval_pyson(value){
return this._normalize_layout_store(null); return this._normalize_layout_store(null);
} }
try { try {
var raw_state = localStorage.getItem(this._get_storage_key()); var raw_state = localStorage.getItem(
this._get_layout_storage_key());
if (!raw_state) { if (!raw_state) {
return this._normalize_layout_store(null); return this._normalize_layout_store(null);
} }
@@ -23018,7 +23088,7 @@ function eval_pyson(value){
} }
try { try {
localStorage.setItem( localStorage.setItem(
this._get_storage_key(), this._get_layout_storage_key(),
JSON.stringify(store)); JSON.stringify(store));
} catch (error) { } catch (error) {
Sao.Logger.warn( Sao.Logger.warn(
@@ -23142,9 +23212,6 @@ function eval_pyson(value){
window.addEventListener('resize', this._window_resize_handler); window.addEventListener('resize', this._window_resize_handler);
}, },
_persist_grid_state: function(options) { _persist_grid_state: function(options) {
if (this.is_embedded_one2many) {
return;
}
options = options || {}; options = options || {};
var state = this._capture_grid_state(); var state = this._capture_grid_state();
state.valueFilters = this._merge_restored_value_filter_state( state.valueFilters = this._merge_restored_value_filter_state(
@@ -23344,6 +23411,7 @@ function eval_pyson(value){
this._sao_value_filter_groups = state.groups ? this._sao_value_filter_groups = state.groups ?
jQuery.extend(true, {}, state.groups) : null; jQuery.extend(true, {}, state.groups) : null;
this._update_sao_value_filter_mode_button(); this._update_sao_value_filter_mode_button();
this._render_active_filter_chips();
}, },
_apply_saved_sort_to_screen_order: function() { _apply_saved_sort_to_screen_order: function() {
if (!this._persisted_state || !this._persisted_state.columns) { if (!this._persisted_state || !this._persisted_state.columns) {
@@ -24827,6 +24895,7 @@ function eval_pyson(value){
this._schedule_state_save(); this._schedule_state_save();
this._sync_selection_header_checkbox(); this._sync_selection_header_checkbox();
this._update_summary_bar(); this._update_summary_bar();
this._render_active_filter_chips();
}, },
onFirstDataRendered: () => { onFirstDataRendered: () => {
window.requestAnimationFrame(() => this._update_summary_bar()); window.requestAnimationFrame(() => this._update_summary_bar());
@@ -25003,6 +25072,10 @@ function eval_pyson(value){
} }
return ''; return '';
} }
if (this._is_date_filter_column(column)) {
return this._get_column_date_filter_value(
column, params.data.__record);
}
return this._get_column_text_value( return this._get_column_text_value(
column, params.data.__record); column, params.data.__record);
}, },
@@ -25023,11 +25096,16 @@ function eval_pyson(value){
if (!params.data.__record) { if (!params.data.__record) {
return ''; return '';
} }
var rendered;
if (badge_column) { if (badge_column) {
return this._render_badge_cell( rendered = this._render_badge_cell(
column, params.data.__record);
} else {
rendered = this._render_cell(
column, params.data.__record); column, params.data.__record);
} }
return this._render_cell(column, params.data.__record); return this._make_filter_draggable_cell(
rendered, column, params.data.__record);
}, },
tooltipValueGetter: params => { tooltipValueGetter: params => {
if (!params.data || !params.data.__record || if (!params.data || !params.data.__record ||
@@ -25230,9 +25308,6 @@ function eval_pyson(value){
case 'numeric': case 'numeric':
return 'agNumberColumnFilter'; return 'agNumberColumnFilter';
case 'date': case 'date':
if (this._get_distinct_filter_values(column)) {
return Sao.View.AGGridValueSetFilter;
}
return 'agDateColumnFilter'; return 'agDateColumnFilter';
default: default:
if (this._get_distinct_filter_values(column)) { if (this._get_distinct_filter_values(column)) {
@@ -25242,6 +25317,9 @@ function eval_pyson(value){
} }
}, },
_get_column_filter_params: function(column) { _get_column_filter_params: function(column) {
if (this._is_date_filter_column(column)) {
return this._get_date_column_filter_params(column);
}
var values = this._get_distinct_filter_values(column); var values = this._get_distinct_filter_values(column);
if (!values) { if (!values) {
return null; return null;
@@ -25260,6 +25338,135 @@ function eval_pyson(value){
searchPlaceholder: Sao.i18n.gettext('Search'), searchPlaceholder: Sao.i18n.gettext('Search'),
}; };
}, },
_is_date_filter_column: function(column) {
return Boolean(column && column.attributes &&
column.attributes.widget == 'date');
},
_get_date_column_filter_params: function(column) {
var relative_options = this._get_relative_date_filter_options();
return {
browserDatePicker: true,
buttons: ['reset', 'apply'],
closeOnApply: true,
maxNumConditions: 1,
defaultOption: 'empty',
filterOptions: ['empty'].concat(relative_options).concat([
'inRange',
'equals',
'lessThan',
'greaterThan',
'blank',
'notBlank',
]),
inRangeInclusive: true,
comparator: function(filter_date, cell_value) {
if (!cell_value) {
return 0;
}
var cell_date = cell_value instanceof Date ? cell_value :
new Date(cell_value);
if (isNaN(cell_date.getTime())) {
return 0;
}
var cell_midnight = new Date(
cell_date.getFullYear(),
cell_date.getMonth(),
cell_date.getDate());
if (cell_midnight < filter_date) {
return -1;
}
if (cell_midnight > filter_date) {
return 1;
}
return 0;
},
};
},
_get_relative_date_filter_options: function() {
var make_option = (key, label, range_getter) => {
return {
displayKey: 'sao' + key,
displayName: Sao.i18n.gettext(label),
numberOfInputs: 0,
predicate: (filter_values, cell_value) => {
var cell_date = this._normalize_date_filter_value(cell_value);
if (!cell_date) {
return false;
}
var range = range_getter();
return cell_date >= range.start && cell_date < range.end;
},
};
};
return [
make_option('Today', 'Today', () => {
var start = this._start_of_today();
return {
start: start,
end: this._add_days(start, 1),
};
}),
make_option('Last7Days', 'Last 7 Days', () => {
var today = this._start_of_today();
return {
start: this._add_days(today, -7),
end: this._add_days(today, 1),
};
}),
make_option('Last30Days', 'Last 30 Days', () => {
var today = this._start_of_today();
return {
start: this._add_days(today, -30),
end: this._add_days(today, 1),
};
}),
make_option('LastMonth', 'Last Month', () => {
var today = this._start_of_today();
var start = new Date(
today.getFullYear(), today.getMonth() - 1, 1);
var end = new Date(today.getFullYear(), today.getMonth(), 1);
return {
start: start,
end: end,
};
}),
make_option('ThisMonth', 'This Month', () => {
var today = this._start_of_today();
var start = new Date(today.getFullYear(), today.getMonth(), 1);
var end = new Date(
today.getFullYear(), today.getMonth() + 1, 1);
return {
start: start,
end: end,
};
}),
make_option('YearToDate', 'Year to Date', () => {
var today = this._start_of_today();
return {
start: new Date(today.getFullYear(), 0, 1),
end: this._add_days(today, 1),
};
}),
];
},
_start_of_today: function() {
var now = new Date();
return new Date(now.getFullYear(), now.getMonth(), now.getDate());
},
_add_days: function(date, days) {
return new Date(
date.getFullYear(), date.getMonth(), date.getDate() + days);
},
_normalize_date_filter_value: function(value) {
if (!value) {
return null;
}
var date = value instanceof Date ? value : new Date(value);
if (isNaN(date.getTime())) {
return null;
}
return new Date(date.getFullYear(), date.getMonth(), date.getDate());
},
_get_sao_value_filter_state: function(col_id) { _get_sao_value_filter_state: function(col_id) {
var filter = this._sao_value_filters && var filter = this._sao_value_filters &&
this._sao_value_filters[col_id]; this._sao_value_filters[col_id];
@@ -25285,7 +25492,113 @@ function eval_pyson(value){
}, },
_is_sao_value_filter_user_source: function(source) { _is_sao_value_filter_user_source: function(source) {
return ~['checkbox-change', 'all-button', 'none-button', return ~['checkbox-change', 'all-button', 'none-button',
'clear-button'].indexOf(source); 'clear-button', 'drag-drop'].indexOf(source);
},
_make_filter_draggable_cell: function(element, column, record) {
if (!element || !column || !column.attributes ||
!column.attributes.name || !record) {
return element;
}
var value = this._get_column_text_value(column, record);
if (!value) {
return element;
}
var wrapped = jQuery(element);
wrapped.attr('draggable', 'true')
.addClass('ag-grid-filter-draggable-cell')
.on('dragstart', evt => {
var payload = {
colId: column.attributes.name,
label: this._get_column_label(column.attributes.name),
value: String(value),
};
this._filter_drag_payload = payload;
evt.originalEvent.dataTransfer.effectAllowed = 'copy';
evt.originalEvent.dataTransfer.setData(
'application/x-sao-ag-grid-filter',
JSON.stringify(payload));
evt.originalEvent.dataTransfer.setData(
'text/plain',
payload.label + ' = ' + payload.value);
this._set_filter_dropzone_active(true, payload);
})
.on('dragend', () => {
this._filter_drag_payload = null;
this._set_filter_dropzone_active(false);
});
return wrapped[0];
},
_set_filter_dropzone_active: function(active, payload) {
if (!this.active_filter_chips) {
return;
}
this.active_filter_chips
.toggleClass('drag-active', Boolean(active))
.removeClass('drag-over');
if (active && !this._get_active_filter_chip_entries().length) {
this.active_filter_chips.empty();
jQuery('<span/>', {
'class': 'ag-grid-active-filter-drop-hint',
'text': payload ?
Sao.i18n.gettext('Drop to filter %1')
.replace('%1', payload.label) :
Sao.i18n.gettext('Drop to filter'),
}).appendTo(this.active_filter_chips);
this.active_filter_chips.css('display', 'inline-flex');
} else if (!active) {
this._render_active_filter_chips();
}
},
_get_filter_drop_payload: function(event) {
var payload = this._filter_drag_payload;
if (!payload && event && event.dataTransfer) {
try {
payload = JSON.parse(event.dataTransfer.getData(
'application/x-sao-ag-grid-filter') || 'null');
} catch (error) {
payload = null;
}
}
if (!payload || !payload.colId || !payload.value) {
return null;
}
return payload;
},
_apply_filter_drop: function(event) {
var payload = this._get_filter_drop_payload(event);
this._filter_drag_payload = null;
if (!payload) {
this._set_filter_dropzone_active(false);
return;
}
var column = this._find_column_by_id(payload.colId);
if (!column) {
this._set_filter_dropzone_active(false);
return;
}
var empty = Sao.i18n.gettext('(Empty)');
var value = String(payload.value || '') || empty;
var all_values = this._get_live_filter_values(
column, payload.colId).map(item => String(item || empty));
if (!~all_values.indexOf(value)) {
all_values.push(value);
}
var existing = this._sao_value_filters &&
this._sao_value_filters[payload.colId];
var selected = existing && existing.selected ?
Array.from(existing.selected).map(item => String(item)) : [];
if (!selected.length) {
selected = [value];
} else if (!~selected.indexOf(value)) {
selected.push(value);
}
this._set_sao_value_filter(
payload.colId,
selected,
all_values,
'drag-drop',
{value: value});
this._set_filter_dropzone_active(false);
}, },
_forget_sao_value_filter_saved_state: function(col_id) { _forget_sao_value_filter_saved_state: function(col_id) {
var remove_filter = value_filters => { var remove_filter = value_filters => {
@@ -25358,6 +25671,7 @@ function eval_pyson(value){
} }
this._apply_sao_value_filters(); this._apply_sao_value_filters();
this._render_filter_logic_panel(); this._render_filter_logic_panel();
this._render_active_filter_chips();
this._schedule_state_save(); this._schedule_state_save();
}, },
_trace_sao_value_filter_change: function(reason, col_id, selected, all, _trace_sao_value_filter_change: function(reason, col_id, selected, all,
@@ -25599,6 +25913,208 @@ function eval_pyson(value){
.find('.ag-grid-value-filter-mode-label') .find('.ag-grid-value-filter-mode-label')
.text(' ' + Sao.i18n.gettext(any ? 'Any filter' : 'All filters')); .text(' ' + Sao.i18n.gettext(any ? 'Any filter' : 'All filters'));
}, },
_get_column_label: function(col_id) {
var column = this._find_column_by_id(col_id);
return column && column.attributes ?
column.attributes.string || column.attributes.name || col_id :
col_id;
},
_describe_grid_filter_model: function(model) {
if (!model) {
return {
operator: '',
value: '',
};
}
var type = model.type || model.operator || '';
var value = model.filter;
if (value === undefined || value === null || value === '') {
value = model.dateFrom || model.filterTo || '';
}
if (Array.isArray(model.values)) {
return {
operator: '=',
value: model.values.length <= 2 ?
model.values.join(', ') :
Sao.i18n.gettext('%1 values').replace('%1', model.values.length),
};
}
if (model.condition1 || model.condition2) {
return {
operator: '',
value: Sao.i18n.gettext('complex'),
};
}
var relative_date_labels = {
saoToday: 'Today',
saoLast7Days: 'Last 7 Days',
saoLast30Days: 'Last 30 Days',
saoLastMonth: 'Last Month',
saoThisMonth: 'This Month',
saoYearToDate: 'Year to Date',
};
if (relative_date_labels[type]) {
return {
operator: '',
value: Sao.i18n.gettext(relative_date_labels[type]),
};
}
var operators = {
equals: '=',
notEqual: '!=',
contains: 'contains',
notContains: 'not contains',
startsWith: 'starts',
endsWith: 'ends',
lessThan: '<',
lessThanOrEqual: '<=',
greaterThan: '>',
greaterThanOrEqual: '>=',
inRange: 'between',
};
return {
operator: operators[type] || type || '=',
value: value,
};
},
_get_active_filter_chip_entries: function() {
var entries = [];
Object.keys(this._sao_value_filters || {}).forEach(col_id => {
var filter = this._sao_value_filters[col_id];
if (!this._is_sao_value_filter_active(filter)) {
return;
}
var selected = Array.from(filter.selected || []).sort();
var all = Array.from(filter.all || []).sort();
var selected_lookup = new Set(selected);
var excluded = all.filter(value => !selected_lookup.has(value));
var display_values = excluded.length &&
excluded.length < selected.length ? excluded : selected;
var operator = display_values === excluded ? '<>' : '=';
var detail = display_values.length <= 2 ?
display_values.join(', ') :
Sao.i18n.gettext('%1 values').replace(
'%1', display_values.length);
entries.push({
type: 'sao',
colId: col_id,
label: this._get_column_label(col_id),
operator: operator,
detail: detail,
});
});
var filter_model = this.gridApi && this.gridApi.getFilterModel ?
this._strip_sao_value_set_filter_model(
this.gridApi.getFilterModel()) : {};
Object.keys(filter_model || {}).forEach(col_id => {
var description =
this._describe_grid_filter_model(filter_model[col_id]);
entries.push({
type: 'grid',
colId: col_id,
label: this._get_column_label(col_id),
operator: description.operator,
detail: description.value,
});
});
return entries;
},
_open_filter_for_chip: function(entry) {
if (entry.type == 'sao') {
this._show_filter_logic_menu();
return;
}
if (!this.gridApi) {
return;
}
if (this.gridApi.showColumnFilter) {
this.gridApi.showColumnFilter(entry.colId);
} else if (this.gridApi.showColumnMenu) {
this.gridApi.showColumnMenu(entry.colId);
}
},
_remove_filter_chip: function(entry) {
if (entry.type == 'sao') {
delete this._sao_value_filters[entry.colId];
this._forget_sao_value_filter_saved_state(entry.colId);
this._apply_sao_value_filters();
this._render_filter_logic_panel();
this._schedule_state_save();
this._render_active_filter_chips();
return;
}
if (!this.gridApi || !this.gridApi.getFilterModel ||
!this.gridApi.setFilterModel) {
return;
}
var model = jQuery.extend(true, {}, this.gridApi.getFilterModel() || {});
delete model[entry.colId];
this.gridApi.setFilterModel(model);
if (this.gridApi.onFilterChanged) {
this.gridApi.onFilterChanged();
}
},
_render_active_filter_chips: function() {
if (!this.active_filter_chips) {
return;
}
var entries = this._get_active_filter_chip_entries();
this.active_filter_chips.empty();
if (!entries.length) {
this.active_filter_chips.hide();
return;
}
var visible = entries.slice(0, 4);
visible.forEach(entry => {
var text = entry.label + (entry.operator ? ' ' +
entry.operator : '') + (entry.detail ? ' ' +
entry.detail : '');
var chip = jQuery('<button/>', {
'class': 'ag-grid-active-filter-chip',
'type': 'button',
'title': text,
}).click(evt => {
evt.preventDefault();
this._open_filter_for_chip(entry);
}).appendTo(this.active_filter_chips);
jQuery('<span/>', {
'class': 'ag-grid-active-filter-chip-name',
'text': entry.label,
}).appendTo(chip);
var value = jQuery('<span/>', {
'class': 'ag-grid-active-filter-chip-value',
}).appendTo(chip);
jQuery('<span/>', {
'class': 'ag-grid-active-filter-chip-operator',
'text': entry.operator || '=',
}).appendTo(value);
jQuery('<span/>', {
'class': 'ag-grid-active-filter-chip-detail',
'text': entry.detail || '',
}).appendTo(value);
jQuery('<span/>', {
'class': 'ag-grid-active-filter-chip-remove',
'text': 'x',
'title': Sao.i18n.gettext('Remove filter'),
}).click(evt => {
evt.preventDefault();
evt.stopPropagation();
this._remove_filter_chip(entry);
}).appendTo(chip);
});
if (entries.length > visible.length) {
jQuery('<button/>', {
'class': 'ag-grid-active-filter-chip ag-grid-active-filter-chip-more',
'type': 'button',
'text': '+' + String(entries.length - visible.length),
'title': Sao.i18n.gettext('Show filter logic'),
}).click(evt => {
evt.preventDefault();
this._show_filter_logic_menu();
}).appendTo(this.active_filter_chips);
}
this.active_filter_chips.css('display', 'inline-flex');
},
_get_active_sao_value_filter_entries: function() { _get_active_sao_value_filter_entries: function() {
return Object.keys(this._sao_value_filters || {}) return Object.keys(this._sao_value_filters || {})
.filter(col_id => this._is_sao_value_filter_active( .filter(col_id => this._is_sao_value_filter_active(
@@ -26229,6 +26745,47 @@ function eval_pyson(value){
return ''; return '';
} }
}, },
_get_column_date_filter_value: function(column, record) {
if (!column || !record || !column.attributes.name) {
return null;
}
var name = column.attributes.name;
var value = null;
try {
var field = record.model && record.model.fields &&
record.model.fields[name];
if (field && field.get_client) {
value = field.get_client(record);
}
} catch (error) {
value = null;
}
if (!value && record._values &&
Object.prototype.hasOwnProperty.call(record._values, name)) {
value = record._values[name];
}
if (!value) {
return null;
}
if (value instanceof Date) {
return new Date(
value.getFullYear(), value.getMonth(), value.getDate());
}
if (value.isDate || value.isDateTime) {
return new Date(value.year(), value.month(), value.date());
}
if (typeof value == 'string') {
var date = moment(value, [
'YYYY-MM-DD',
Sao.common.date_format(
this.screen && this.screen.context &&
this.screen.context.date_format),
], true);
return date.isValid() ?
new Date(date.year(), date.month(), date.date()) : null;
}
return null;
},
_is_badge_column: function(column) { _is_badge_column: function(column) {
if (!column || !column.attributes) { if (!column || !column.attributes) {
return false; return false;
@@ -35527,7 +36084,8 @@ function eval_pyson(value){
Sao.View.BoardXMLViewParser = Sao.class_(Sao.View.FormXMLViewParser, { Sao.View.BoardXMLViewParser = Sao.class_(Sao.View.FormXMLViewParser, {
_parse_board: function(node, attributes) { _parse_board: function(node, attributes) {
var container = new Sao.View.Form.Container( var container = new Sao.View.Form.Container(
Number(node.getAttribute('col') || 4)); Number(node.getAttribute('col') || 4),
attributes.col_widths);
this.view.el.append(container.el); this.view.el.append(container.el);
this.parse_child(node, container); this.parse_child(node, container);
if (this._containers.length > 0) { if (this._containers.length > 0) {