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

View File

@@ -15008,7 +15008,8 @@ function eval_pyson(value){
},
_parse_form: function(node, attributes) {
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.parse_child(node, container);
if (this._containers.length > 0) {
@@ -15205,7 +15206,8 @@ function eval_pyson(value){
return;
}
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.parse_child(node, container);
var page = new Sao.View.Form.Page(
@@ -15216,7 +15218,8 @@ function eval_pyson(value){
},
_parse_group: function(node, attributes) {
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.parse_child(node, group);
@@ -15259,7 +15262,8 @@ function eval_pyson(value){
_parse_child: function(node, attributes) {
var paned = this.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.parse_child(node, container);
@@ -15528,9 +15532,10 @@ function eval_pyson(value){
});
Sao.View.Form.Container = Sao.class_(Object, {
init: function(col=4) {
init: function(col=4, col_widths=null) {
if (col < 0) col = 0;
this.col = col;
this.col_widths = this._parse_col_widths(col_widths);
this.el = jQuery('<div/>', {
'class': 'form-container'
});
@@ -15547,6 +15552,21 @@ function eval_pyson(value){
this._grid_cols = [];
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() {
this._col = 1;
this._row += 1;
@@ -15659,7 +15679,15 @@ function eval_pyson(value){
var i;
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++) {
if (this._xexpand.has(i)) {
this._grid_cols.push(`minmax(min-content, ${col}fr)`);
@@ -22691,6 +22719,29 @@ function eval_pyson(value){
this._toggle_filter_logic_menu();
})
.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/>', {
'type': 'file',
'accept': 'application/json,.json',
@@ -22774,17 +22825,8 @@ function eval_pyson(value){
});
Sao.View.AGGridTree._super.init.call(this, view_id, screen, xml);
if (this.is_embedded_one2many) {
this._layout_store = this._normalize_layout_store(null);
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 = this._load_persisted_state();
if (this._persisted_state &&
this._persisted_state.valueFilters) {
this._apply_sao_value_filter_state(
this._persisted_state.valueFilters);
@@ -22792,6 +22834,7 @@ function eval_pyson(value){
this._apply_saved_sort_to_screen_order();
this._update_layout_button();
this._update_sao_value_filter_mode_button();
this._render_active_filter_chips();
this._update_copy_state();
this._init_grid();
this._install_resize_observer();
@@ -22921,7 +22964,33 @@ function eval_pyson(value){
});
},
_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() {
return 'layout_' + String(Date.now()) + '_' +
@@ -22973,7 +23042,8 @@ function eval_pyson(value){
return this._normalize_layout_store(null);
}
try {
var raw_state = localStorage.getItem(this._get_storage_key());
var raw_state = localStorage.getItem(
this._get_layout_storage_key());
if (!raw_state) {
return this._normalize_layout_store(null);
}
@@ -23018,7 +23088,7 @@ function eval_pyson(value){
}
try {
localStorage.setItem(
this._get_storage_key(),
this._get_layout_storage_key(),
JSON.stringify(store));
} catch (error) {
Sao.Logger.warn(
@@ -23142,9 +23212,6 @@ function eval_pyson(value){
window.addEventListener('resize', this._window_resize_handler);
},
_persist_grid_state: function(options) {
if (this.is_embedded_one2many) {
return;
}
options = options || {};
var state = this._capture_grid_state();
state.valueFilters = this._merge_restored_value_filter_state(
@@ -23344,6 +23411,7 @@ function eval_pyson(value){
this._sao_value_filter_groups = state.groups ?
jQuery.extend(true, {}, state.groups) : null;
this._update_sao_value_filter_mode_button();
this._render_active_filter_chips();
},
_apply_saved_sort_to_screen_order: function() {
if (!this._persisted_state || !this._persisted_state.columns) {
@@ -24827,6 +24895,7 @@ function eval_pyson(value){
this._schedule_state_save();
this._sync_selection_header_checkbox();
this._update_summary_bar();
this._render_active_filter_chips();
},
onFirstDataRendered: () => {
window.requestAnimationFrame(() => this._update_summary_bar());
@@ -25003,6 +25072,10 @@ function eval_pyson(value){
}
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(
column, params.data.__record);
},
@@ -25023,11 +25096,16 @@ function eval_pyson(value){
if (!params.data.__record) {
return '';
}
var rendered;
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);
}
return this._render_cell(column, params.data.__record);
return this._make_filter_draggable_cell(
rendered, column, params.data.__record);
},
tooltipValueGetter: params => {
if (!params.data || !params.data.__record ||
@@ -25230,9 +25308,6 @@ function eval_pyson(value){
case 'numeric':
return 'agNumberColumnFilter';
case 'date':
if (this._get_distinct_filter_values(column)) {
return Sao.View.AGGridValueSetFilter;
}
return 'agDateColumnFilter';
default:
if (this._get_distinct_filter_values(column)) {
@@ -25242,6 +25317,9 @@ function eval_pyson(value){
}
},
_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);
if (!values) {
return null;
@@ -25260,6 +25338,135 @@ function eval_pyson(value){
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) {
var filter = this._sao_value_filters &&
this._sao_value_filters[col_id];
@@ -25285,7 +25492,113 @@ function eval_pyson(value){
},
_is_sao_value_filter_user_source: function(source) {
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) {
var remove_filter = value_filters => {
@@ -25358,6 +25671,7 @@ function eval_pyson(value){
}
this._apply_sao_value_filters();
this._render_filter_logic_panel();
this._render_active_filter_chips();
this._schedule_state_save();
},
_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')
.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() {
return Object.keys(this._sao_value_filters || {})
.filter(col_id => this._is_sao_value_filter_active(
@@ -26229,6 +26745,47 @@ function eval_pyson(value){
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) {
if (!column || !column.attributes) {
return false;
@@ -35527,7 +36084,8 @@ function eval_pyson(value){
Sao.View.BoardXMLViewParser = Sao.class_(Sao.View.FormXMLViewParser, {
_parse_board: function(node, attributes) {
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.parse_child(node, container);
if (this._containers.length > 0) {