diff --git a/api/js/etemplate/Et2Nextmatch/ColumnSelection.ts b/api/js/etemplate/Et2Nextmatch/ColumnSelection.ts
new file mode 100644
index 0000000000..970e20287a
--- /dev/null
+++ b/api/js/etemplate/Et2Nextmatch/ColumnSelection.ts
@@ -0,0 +1,297 @@
+/**
+ * Column selector for nextmatch
+ */
+import {classMap, css, html, LitElement, PropertyValues, repeat, TemplateResult} from "@lion/core";
+import {Et2InputWidget} from "../Et2InputWidget/Et2InputWidget";
+import {et2_nextmatch_customfields} from "../et2_extension_nextmatch";
+import shoelace from "../Styles/shoelace";
+import {et2_dataview_column} from "../et2_dataview_model_columns";
+import {et2_customfields_list} from "../et2_extension_customfields";
+import Sortable from "sortablejs/modular/sortable.complete.esm";
+import {cssImage} from "../Et2Widget/Et2Widget";
+import {SlMenuItem} from "@shoelace-style/shoelace";
+import {Et2Select} from "../Et2Select/Et2Select";
+
+export class Et2ColumnSelection extends Et2InputWidget(LitElement)
+{
+ static get styles()
+ {
+ return [
+ // Parent (SlSelect) returns a single cssResult, not an array
+ super.styles,
+ shoelace,
+ css`
+ :host {
+ height: 100%;
+ display: flex;
+ flex-direction: column;
+ flex: 1 0 auto;
+ --icon-width: 20px;
+ }
+ .title {
+ font-size: var(--sl-font-size-large);
+ color: var(--sl-color-neutral-0);
+ background-color: var(--sl-color-primary-600);
+ }
+ .title sl-icon {
+ vertical-align: middle;
+ cursor: pointer;
+ }
+ sl-menu {
+ flex: 1 1 auto;
+ overflow-y: auto;
+ }
+ /* Drag handle on columns (not individual custom fields or search letter) */
+ sl-menu > .select_row::part(base) {
+ padding-left: 10px;
+ }
+ sl-menu > .column::part(base) {
+ background-image: ${cssImage("splitter_vert")};
+ background-position: 3px 1.5ex;
+ background-repeat: no-repeat;
+ cursor: grab;
+ }
+ /* Change vertical alignment of CF checkbox line to up with title, not middle */
+ .custom_fields::part(base) {
+ align-items: baseline;
+ }
+ `
+ ]
+ }
+
+ static get properties()
+ {
+ return {
+ /**
+ * List of currently selected columns
+ */
+ value: {type: Object},
+
+ autoRefresh: {type: Number}
+ }
+ }
+
+ private __columns = [];
+ private __autoRefresh : number | false = false;
+ private sort : Sortable;
+
+ constructor(...args : any[])
+ {
+ super(...args);
+
+ this.columnClickHandler = this.columnClickHandler.bind(this);
+ this.handleSelectAll = this.handleSelectAll.bind(this);
+ }
+
+ connectedCallback()
+ {
+ super.connectedCallback();
+
+ this.updateComplete.then(() =>
+ {
+ this.sort = Sortable.create(this.shadowRoot.querySelector('sl-menu'), {
+ ghostClass: 'ui-fav-sortable-placeholder',
+ draggable: 'sl-menu-item.column',
+ dataIdAttr: 'value',
+ direction: 'vertical',
+ delay: 25
+ });
+ });
+ }
+
+ protected firstUpdated(_changedProperties : PropertyValues)
+ {
+ super.firstUpdated(_changedProperties);
+
+ if(this._autoRefreshNode)
+ {
+ this._autoRefreshNode.select_options = {
+ // Cause [unknown] problems with mail
+ 30: "30 seconds",
+ //60: "1 Minute",
+ 180: "3 Minutes",
+ 300: "5 Minutes",
+ 900: "15 Minutes",
+ 1800: "30 Minutes"
+ };
+ }
+
+ if(this._preferenceNode)
+ {
+ this._preferenceNode.select_options = [
+ {value: 'default', label: 'Default', title: 'Set these columns as the default'},
+ {value: 'reset', label: 'Reset', title: "Reset all user's column preferences"},
+ {value: 'force', label: 'Force', title: 'Force column preference so users cannot change it'}
+ ];
+ }
+ }
+
+ protected render() : TemplateResult
+ {
+ return html`${this.headerTemplate()}
+
+ ${repeat(this.__columns, (column) => column.id, (column) => this.rowTemplate(column))}
+
+ `;
+ }
+
+ protected headerTemplate()
+ {
+ return html`
+
+
+ ${this.egw().lang("Select columns")}
+
+ `;
+ }
+
+ protected footerTemplate()
+ {
+ let autoRefresh = html`
+
+
+ `;
+ // Add default checkbox for admins
+ const apps = this.egw().user('apps');
+
+ return html`
+ ${this.__autoRefresh !== false ? autoRefresh : ''}
+ ${!apps['admin'] ? '' : html`
+
+ `
+ }
+ `;
+ }
+
+ /**
+ * Template for each individual column
+ *
+ * @param column
+ * @returns {TemplateResult}
+ * @protected
+ */
+ protected rowTemplate(column) : TemplateResult
+ {
+ let isCustom = column.widget?.instanceOf(et2_nextmatch_customfields) || false;
+ /* ?disabled=${column.visibility == et2_dataview_column.ET2_COL_VISIBILITY_DISABLED} */
+ return html`
+
+ ${column.caption}
+
+ ${isCustom ? this.customFieldsTemplate(column) : ''}
+ `;
+ }
+
+ /**
+ * Template for all custom fields
+ * Does not include "Custom fields", it's done as a regular column
+ *
+ * @param column
+ * @returns {TemplateResult}
+ * @protected
+ */
+ protected customFieldsTemplate(column) : TemplateResult
+ {
+ // Custom fields get listed separately
+ let widget = column.widget;
+ if(jQuery.isEmptyObject((widget).customfields))
+ {
+ // No customfields defined, don't show column
+ return html``;
+ }
+ return html`
+
+ ${repeat(Object.values(widget.customfields), (field) => field.name, (field) =>
+ {
+ return this.rowTemplate({
+ id: et2_customfields_list.PREFIX + field.name,
+ caption: field.label,
+ visibility: (widget.fields[field.name] ? et2_dataview_column.ET2_COL_VISIBILITY_VISIBLE : false)
+
+ });
+ })}
+ `;
+ }
+
+ columnClickHandler(event)
+ {
+ const item = event.detail.item;
+
+ // Toggle checked state
+ item.checked = !item.checked;
+ }
+
+ handleSelectAll(event)
+ {
+ let checked = (this.shadowRoot.querySelector("sl-menu-item")).checked || false;
+ this.shadowRoot.querySelectorAll('sl-menu-item').forEach((item) => {item.checked = !checked});
+ }
+
+ set columns(new_columns)
+ {
+ this.__columns = new_columns;
+ this.requestUpdate();
+ }
+
+ get value()
+ {
+ let value = [];
+
+ this.sort.toArray().forEach((val) =>
+ {
+ let column = this.__columns.find((col) => col.id == val);
+ let menuItem = this.shadowRoot.querySelector("[value='" + val + "']");
+ if(column && menuItem)
+ {
+ if(menuItem.checked)
+ {
+ value.push(val);
+ }
+ if(column.widget?.customfields)
+ {
+ menuItem.querySelectorAll("[value][checked]").forEach((cf : SlMenuItem) =>
+ {
+ value.push(cf.value);
+ })
+ }
+ }
+ });
+ return value;
+ }
+
+ private get _autoRefreshNode() : Et2Select
+ {
+ return (this.shadowRoot?.querySelector("#nm_autorefresh"));
+ }
+
+ private get _preferenceNode() : Et2Select
+ {
+ return (this.shadowRoot.querySelector("#default_preference"))
+ }
+
+ get autoRefresh() : number
+ {
+ return parseInt(this._autoRefreshNode?.value.toString()) || 0;
+ }
+
+ set autoRefresh(new_value : number)
+ {
+ this.__autoRefresh = new_value;
+ this.requestUpdate("autoRefresh");
+ }
+}
+
+customElements.define("et2-nextmatch-columnselection", Et2ColumnSelection);
\ No newline at end of file
diff --git a/api/js/etemplate/et2_extension_nextmatch.ts b/api/js/etemplate/et2_extension_nextmatch.ts
index 61889f5431..d8921d3b1a 100644
--- a/api/js/etemplate/et2_extension_nextmatch.ts
+++ b/api/js/etemplate/et2_extension_nextmatch.ts
@@ -70,13 +70,13 @@ import {et2_template} from "./et2_widget_template";
import {egw} from "../jsapi/egw_global";
import {et2_compileLegacyJS} from "./et2_core_legacyJSFunctions";
import {egwIsMobile} from "../egw_action/egw_action_common.js";
-import Sortable from 'sortablejs/modular/sortable.complete.esm.js';
import {Et2Dialog} from "./Et2Dialog/Et2Dialog";
import {Et2Select} from "./Et2Select/Et2Select";
import {Et2Button} from "./Et2Button/Et2Button";
import {loadWebComponent} from "./Et2Widget/Et2Widget";
import {Et2AccountFilterHeader} from "./Nextmatch/Headers/AccountFilterHeader";
import {Et2SelectCategory} from "./Et2Select/Et2SelectCategory";
+import {Et2ColumnSelection} from "./Et2Nextmatch/ColumnSelection";
//import {et2_selectAccount} from "./et2_widget_SelectAccount";
let keep_import : Et2AccountFilterHeader
@@ -1938,111 +1938,47 @@ export class et2_nextmatch extends et2_DOMWidget implements et2_IResizeable, et2
{
var col = columnMgr.columns[i];
const widget = this.columns[i].widget;
-
- if(col.visibility == et2_dataview_column.ET2_COL_VISIBILITY_DISABLED ||
- col.visibility == et2_dataview_column.ET2_COL_VISIBILITY_ALWAYS_NOSELECT)
- {
- continue;
- }
- if(col.caption)
- {
- columns.push({value: col.id, label: col.caption});
- if(col.visibility == et2_dataview_column.ET2_COL_VISIBILITY_VISIBLE) columns_selected.push(col.id);
- }
- // Custom fields get listed separately
- if(widget.instanceOf(et2_nextmatch_customfields))
- {
- if(jQuery.isEmptyObject((widget).customfields))
- {
- // No customfields defined, don't show column
- columns.pop();
- continue;
- }
- for(var field_name in (widget).customfields)
- {
- columns.push({
- value: et2_nextmatch_customfields.PREFIX + field_name, label: " - " +
- (widget).customfields[field_name].label
- });
- if(widget.options.fields[field_name])
- {
- columns_selected.push(et2_customfields_list.PREFIX + field_name);
- }
- }
- }
+ columns.push({...col, widget: widget});
}
// Letter search
if(this.options.settings.lettersearch)
{
- columns.push({value: LETTERS, label: egw.lang('Search letter')});
- if(this.header.lettersearch.is(':visible')) columns_selected.push(LETTERS);
+ columns.push({
+ id: LETTERS,
+ caption: this.egw().lang('Search letter'),
+ visibility: (this.header.lettersearch.is(':visible') ? et2_dataview_column.ET2_COL_VISIBILITY_VISIBLE : et2_dataview_column.ET2_COL_VISIBILITY_ALWAYS_NOSELECT)
+ });
}
// Build the popup
- if(!this.selectPopup)
+ let selectPopup = new Et2ColumnSelection();
+ selectPopup.setParent(this);
+ selectPopup.columns = columns;
+ if(!this.options.disable_autorefresh)
{
- const select = loadWebComponent("et2-select", {
- id: "columns",
- multiple: true,
- rows: 8,
- empty_label: this.egw().lang("select columns"),
- selected_first: false
- }, this);
- // Don't let options run through the loading stuff
- select.select_options = columns;
- select.value = columns_selected;
+ selectPopup.autoRefresh = parseInt(this._get_autorefresh());
+ }
- let autoRefresh;
- let sort;
- if(!this.options.disable_autorefresh)
+ const okButton = loadWebComponent("et2-button", {
+ image: "check",
+ label: this.egw().lang("ok"),
+ slot: "buttons"
+ }, this);
+ okButton.onclick = function()
+ {
+ // Update visibility
+ const visibility = {};
+ for(var i = 0; i < columnMgr.columns.length; i++)
{
- autoRefresh = loadWebComponent("et2-select", {
- empty_label: "Refresh",
- id: "nm_autorefresh",
- statustext: egw.lang("Automatically refresh list"),
- value: this._get_autorefresh()
- }, this);
- autoRefresh.select_options = {
- // Cause [unknown] problems with mail
- 30: "30 seconds",
- //60: "1 Minute",
- 180: "3 Minutes",
- 300: "5 Minutes",
- 900: "15 Minutes",
- 1800: "30 Minutes"
- };
- }
-
- const defaultCheck = loadWebComponent("et2-select", {
- id: "nm_col_preference",
- empty_label: "Preference",
- value: this.options.settings.columns_forced ? 'force' : ''
- }, this);
- defaultCheck.select_options = [
- {value: 'default', label: 'Default', title: 'Set these columns as the default'},
- {value: 'reset', label: 'Reset', title: "Reset all user's column preferences"},
- {value: 'force', label: 'Force', title: 'Force column preference so users cannot change it'}
- ];
-
- const okButton = loadWebComponent("et2-button", {
- image: "check",
- label: this.egw().lang("ok")
- }, this);
- okButton.onclick = function()
- {
- // Update visibility
- const visibility = {};
- for(var i = 0; i < columnMgr.columns.length; i++)
+ const col = columnMgr.columns[i];
+ if(col.caption && col.visibility !== et2_dataview_column.ET2_COL_VISIBILITY_ALWAYS_NOSELECT &&
+ col.visibility !== et2_dataview_column.ET2_COL_VISIBILITY_DISABLED)
{
- const col = columnMgr.columns[i];
- if(col.caption && col.visibility !== et2_dataview_column.ET2_COL_VISIBILITY_ALWAYS_NOSELECT &&
- col.visibility !== et2_dataview_column.ET2_COL_VISIBILITY_DISABLED)
- {
visibility[col.id] = {visible: false};
}
}
- const value = select.value;
+ const value = selectPopup.value;
// Update & remove letter filter
if(self.header.lettersearch)
@@ -2058,7 +1994,6 @@ export class et2_nextmatch extends et2_DOMWidget implements et2_IResizeable, et2
}
self._set_lettersearch(show_letters);
}
-
let column = 0;
for(var i = 0; i < value.length; i++)
{
@@ -2097,105 +2032,42 @@ export class et2_nextmatch extends et2_DOMWidget implements et2_IResizeable, et2
}
columnMgr.setColumnVisibilitySet(visibility);
- this.sortedColumnsList = [];
- if(sort)
- {
- sort.toArray().forEach((data_id) =>
- {
- const value = select.getValue();
- if(data_id.match(/^col_/) && value.indexOf(data_id) != -1)
- {
- const col_id = data_id.replace('col_', '');
- const col_widget = self.columns[col_id].widget;
- if(col_widget.customfields)
- {
- self.sortedColumnsList.push(col_widget.id);
- for(let field_name in col_widget.customfields)
- {
- if(jQuery.isEmptyObject(col_widget.options.fields) || col_widget.options.fields[field_name] == true)
- {
- self.sortedColumnsList.push(et2_customfields_list.PREFIX + field_name);
- }
- }
- }
- else
- {
- self.sortedColumnsList.push(self._getColumnName(col_widget));
- }
- }
- });
- }
-
// Hide popup
self.selectPopup.toggle();
self.dataview.updateColumns();
// Auto refresh
- self._set_autorefresh(autoRefresh ? autoRefresh.get_value() : 0);
+ self._set_autorefresh(selectPopup.autoRefresh);
- // Set default or clear forced
- if(show_letters)
- {
- self.activeFilters.selectcols.push('lettersearch');
- }
- self.getInstanceManager().submit();
-
- self.selectPopup = null;
- };
-
- const cancelButton = loadWebComponent("et2-button", {
- label: this.egw().lang("cancel"),
- image: "cancel"
- }, this);
- cancelButton.onclick = function()
+ // Set default or clear forced
+ if(show_letters)
{
- self.selectPopup.toggle();
- self.selectPopup = null;
- };
-
- select.updateComplete.then(() =>
- {
- window.setTimeout(() =>
- {
- sort = Sortable.create(select.shadowRoot.querySelector('.select__tags'), {
- ghostClass: 'ui-fav-sortable-placeholder',
- draggable: 'et2-tag',
- dataIdAttr: 'value',
- direction: 'vertical',
- delay: 25,
-
- });
- }, 100);
- });
-
- const $footerWrap = jQuery(document.createElement("div"))
- .addClass('dialogFooterToolbar')
- .append(okButton)
- .append(cancelButton);
- this.selectPopup = jQuery(document.createElement("div"))
- .addClass("colselection ui-dialog ui-widget-content")
- .append(select)
- .append($footerWrap)
- .appendTo(this.innerDiv);
-
- // Add autorefresh
- if(autoRefresh)
- {
- $footerWrap.append(autoRefresh);
+ self.activeFilters.selectcols.push('lettersearch');
}
+ self.getInstanceManager().submit();
- // Add default checkbox for admins
- const apps = this.egw().user('apps');
- if(apps['admin'])
- {
- $footerWrap.append(defaultCheck);
- }
- }
- else
+ self.selectPopup = null;
+ };
+
+ const cancelButton = loadWebComponent("et2-button", {
+ label: this.egw().lang("cancel"),
+ image: "cancel",
+ slot: "buttons"
+ }, this);
+ cancelButton.onclick = function()
{
- this.selectPopup.toggle();
- }
+ self.selectPopup.toggle();
+ };
+
+ selectPopup.append(okButton);
+ selectPopup.append(cancelButton);
+
+ this.selectPopup = jQuery(document.createElement("div"))
+ .addClass("colselection ui-dialog ui-widget-content")
+ .append(selectPopup)
+ .appendTo(this.innerDiv);
+
const t_position = jQuery(e.target).position();
const s_position = this.div.position();
const max_height = this.getDOMNode().getElementsByClassName('egwGridView_outer')[0]['tBodies'][0].clientHeight -
@@ -3791,10 +3663,12 @@ export class et2_nextmatch_header_bar extends et2_DOMWidget implements et2_INext
this.nextmatch.applyFilters(set);
});
}
+ // Sometimes the filter does not display the current value
+ // Call sync to try to get it to display
select.updateComplete.then(async() =>
{
await select.updateComplete;
- //select.syncValueFromItems();
+ select.syncItemsFromValue();
})
return select;
}
diff --git a/api/templates/default/etemplate2.css b/api/templates/default/etemplate2.css
index 2c23367612..284b078d0f 100644
--- a/api/templates/default/etemplate2.css
+++ b/api/templates/default/etemplate2.css
@@ -4003,10 +4003,6 @@ tr.disableIfNoEPL {
border: 2px dashed silver;
}
-.colselection [id*="columns"]::part(tags) {
- max-height: 10em;
-}
-
.colselection .dialogFooterToolbar {
display: flex;
flex-direction: row;