From ba35be1d86871d5a5df53dd8bb952f4c41c750d9 Mon Sep 17 00:00:00 2001 From: ralf Date: Sun, 7 Aug 2022 10:10:33 +0200 Subject: [PATCH] Change Et2Checkbox to behave closer to legacy et2_widget_checkbox and thereby fixing all sorts of errors with it. Fix vfsSelectUI to be able to select files again with click on the row and only submit selected files. Also change Et2Widget.iterateOver and legacy et2_widget.interateOver to be called with new web-component classes like Et2Checkbox. Also make Et2Widget matching all widgets by default like et2_widget. Remove legacy et2_widget_checkbox code. --- api/js/etemplate/Et2Checkbox/Et2Checkbox.ts | 40 +-- api/js/etemplate/Et2Widget/Et2Widget.ts | 4 +- api/js/etemplate/et2_core_inheritance.ts | 8 +- api/js/etemplate/et2_widget_checkbox.ts | 283 +------------------- api/js/etemplate/et2_widget_toolbar.ts | 4 +- api/js/etemplate/etemplate2.ts | 1 - api/js/etemplate/vfsSelectUI.ts | 9 +- 7 files changed, 41 insertions(+), 308 deletions(-) diff --git a/api/js/etemplate/Et2Checkbox/Et2Checkbox.ts b/api/js/etemplate/Et2Checkbox/Et2Checkbox.ts index 5bb2c2be69..a93cc947fd 100644 --- a/api/js/etemplate/Et2Checkbox/Et2Checkbox.ts +++ b/api/js/etemplate/Et2Checkbox/Et2Checkbox.ts @@ -17,6 +17,11 @@ import shoelace from "../Styles/shoelace"; export class Et2Checkbox extends Et2InputWidget(SlCheckbox) { + /** + * Value to set checkbox in (third) indeterminate state + */ + static readonly INDETERMINATE = '***undefined***'; + static get styles() { return [ @@ -53,6 +58,9 @@ export class Et2Checkbox extends Et2InputWidget(SlCheckbox) constructor() { super(); + + this.selectedValue = 'true'; + this.unselectedValue = ''; } connectedCallback() @@ -81,40 +89,40 @@ export class Et2Checkbox extends Et2InputWidget(SlCheckbox) } } - get value() + get value() : string | boolean { - if(this.checked && this.selectedValue) - { - return this.selectedValue; - } - if(!this.checked && this.unselectedValue) - { - return this.unselectedValue; - } - return this.checked + ""; + return this.indeterminate ? undefined : + (this.checked ? this.selectedValue : this.unselectedValue); } set value(new_value : string | boolean) { this.requestUpdate("checked"); this.indeterminate = false; - if(typeof new_value === "boolean" || !this.selectedValue) + + if(typeof new_value === "boolean") { - this.checked = new_value; - return; + this.checked = new_value; } - if(this.selectedValue && new_value == this.selectedValue) + else if(new_value == this.selectedValue) { this.checked = true; } - else if(this.unselectedValue && new_value == this.unselectedValue) + else if(new_value == this.unselectedValue) { this.checked = false; } - else + // concept of an indeterminate value did not exist in eT2 and set value gets called with all kind of truthy of falsy values + // therefore we can NOT set everything not matching our (un)selectedValue to indeterminate! + // For now, we only do that for an explicit Et2Checkbox.INDETERMINATE value + else if (new_value === Et2Checkbox.INDETERMINATE) { this.indeterminate = true; } + else + { + this.checked = !!new_value; + } } private get _labelNode() diff --git a/api/js/etemplate/Et2Widget/Et2Widget.ts b/api/js/etemplate/Et2Widget/Et2Widget.ts index 4f0ad216f1..d1c5c624a5 100644 --- a/api/js/etemplate/Et2Widget/Et2Widget.ts +++ b/api/js/etemplate/Et2Widget/Et2Widget.ts @@ -799,7 +799,9 @@ const Et2WidgetMixin = (superClass : T) => iterateOver(_callback : Function, _context, _type) { - if(typeof _type == "undefined" || _type == et2_widget || et2_implements_registry[_type] && et2_implements_registry[_type](this)) + if (typeof _type === "undefined" || _type === et2_widget || _type === Et2Widget || + typeof _type === 'function' && this instanceof _type || + et2_implements_registry[_type] && et2_implements_registry[_type](this)) { _callback.call(_context, this); } diff --git a/api/js/etemplate/et2_core_inheritance.ts b/api/js/etemplate/et2_core_inheritance.ts index cf1a3c50ae..d0e785feeb 100644 --- a/api/js/etemplate/et2_core_inheritance.ts +++ b/api/js/etemplate/et2_core_inheritance.ts @@ -15,6 +15,7 @@ import {egw, IegwAppLocal} from "../jsapi/egw_global"; import {et2_checkType, et2_no_init, et2_validateAttrib} from "./et2_core_common"; import {et2_implements_registry} from "./et2_core_interfaces"; +import {Et2Widget} from "./Et2Widget/Et2Widget"; export class ClassWithInterfaces { @@ -49,6 +50,10 @@ export class ClassWithInterfaces { return this.implements(_class_or_interfacename); } + if (_class_or_interfacename === Et2Widget) + { + return true; + } return this instanceof _class_or_interfacename; } } @@ -273,5 +278,4 @@ export class ClassWithAttributes extends ClassWithInterfaces return attributes; } -} - +} \ No newline at end of file diff --git a/api/js/etemplate/et2_widget_checkbox.ts b/api/js/etemplate/et2_widget_checkbox.ts index 14f338e916..6388dd6f35 100644 --- a/api/js/etemplate/et2_widget_checkbox.ts +++ b/api/js/etemplate/et2_widget_checkbox.ts @@ -9,286 +9,9 @@ * @copyright Nathan Gray 2011 */ -/*egw:uses - /vendor/bower-asset/jquery/dist/jquery.js; - et2_core_inputWidget; - et2_core_valueWidget; -*/ - -import {et2_register_widget, WidgetConfig} from "./et2_core_widget"; -import {et2_inputWidget} from "./et2_core_inputWidget"; -import {ClassWithAttributes} from "./et2_core_inheritance"; -import {et2_IDetachedDOM} from "./et2_core_interfaces"; +import {Et2Checkbox} from "./Et2Checkbox/Et2Checkbox"; /** - * Class which implements the "checkbox" XET-Tag - * - * @augments et2_inputWidget + * @deprecated use Et2Checkbox */ -export class et2_checkbox extends et2_inputWidget -{ - static readonly _attributes : any = { - "selected_value": { - "name": "Set value", - "type": "string", - "default": "true", - "description": "Value when checked" - }, - "unselected_value": { - "name": "Unset value", - "type": "string", - "default": "", - "description": "Value when not checked" - }, - "ro_true": { - "name": "Read only selected", - "type": "string", - "default": "X ", - "description": "What should be displayed when readonly and selected" - }, - "ro_false": { - "name": "Read only unselected", - "type": "string", - "default": "", - "description": "What should be displayed when readonly and not selected" - }, - "value": { - // Stop framework from messing with value - "type": "any" - }, - "toggle_on": { - "name": "Toggle on caption", - "type": "string", - "default": "", - "description": "String caption to show for ON status", - "translate": true - }, - "toggle_off": { - "name": "Toggle off caption", - "type": "string", - "default": "", - "description": "String caption to show OFF status", - "translate": true - } - }; - - public static readonly legacyOptions : string[] = ["selected_value", "unselected_value", "ro_true", "ro_false"]; - input : JQuery = null; - toggle : JQuery = null; - value : string | boolean; - - /** - * Constructor - * - * @memberOf et2_checkbox - */ - constructor(_parent, _attrs? : WidgetConfig, _child? : object) - { - // Call the inherited constructor - super(_parent, _attrs, ClassWithAttributes.extendAttributes(et2_checkbox._attributes, _child || {})); - this.input = null; - this.createInputWidget(); - } - - createInputWidget() - { - this.input = jQuery(document.createElement("input")).attr("type", "checkbox"); - - this.input.addClass("et2_checkbox"); - - if (this.options.toggle_on || this.options.toggle_off) - { - let self = this; - // checkbox container - this.toggle = jQuery(document.createElement('span')) - .addClass('et2_checkbox_slideSwitch') - .append(this.input); - // update switch status on change - this.input.change(function(){ - self.getValue(); - return true; - }); - // switch container - let area = jQuery(document.createElement('span')).addClass('slideSwitch_container').appendTo(this.toggle); - // on span tag - let on = jQuery(document.createElement('span')).addClass('on').appendTo(area); - // off span tag - let off = jQuery(document.createElement('span')).addClass('off').appendTo(area); - on.text(this.options.toggle_on); - off.text(this.options.toggle_off); - - // handle a tag - jQuery(document.createElement('a')).appendTo(area); - this.setDOMNode(this.toggle[0]); - } - else - { - this.setDOMNode(this.input[0]); - } - } - - /** - * Override default to place checkbox before label, if there is no %s in the label - * - * @param {string} label - */ - set_label(label) { - if(label.length && label.indexOf('%s') < 0) - { - label = '%s'+label; - } - super.set_label(label); - jQuery(this.getSurroundings().getWidgetSurroundings()).addClass('et2_checkbox_label'); - } - - /** - * Override default to match against set/unset value - * - * @param {string|boolean} _value - */ - set_value(_value : string | boolean) - { - // in php, our database storage and et2_checkType(): "0" == false - if (_value === "0" && this.options.selected_value != "0") - { - _value = false; - } - if(_value != this.value) { - if(_value == this.options.selected_value || - _value && this.options.selected_value == this.attributes["selected_value"]["default"] && - _value != this.options.unselected_value) { - if (this.options.toggle_on || this.options.toggle_off) this.toggle.addClass('switchOn'); - this.input.prop("checked", true); - } else { - this.input.prop("checked", false); - if (this.options.toggle_on || this.options.toggle_off) this.toggle.removeClass('switchOn'); - } - } - } - - /** - * Disable checkbox on runtime - * - * @param {boolean} _ro - */ - set_readonly(_ro) - { - jQuery(this.getDOMNode()).attr('disabled', _ro); - this.input.prop('disabled', _ro); - } - - /** - * Override default to return unchecked value - */ - getValue() - { - if(this.input.prop("checked")) { - if (this.options.toggle_on || this.options.toggle_off) this.toggle.addClass('switchOn'); - return this.options.selected_value; - } else { - if (this.options.toggle_on || this.options.toggle_off) this.toggle.removeClass('switchOn'); - return this.options.unselected_value; - } - } - - set_disabled(_value) - { - let parentNode = jQuery(this.getDOMNode()).parent(); - if (parentNode[0] && parentNode[0].nodeName == "label" && parentNode.hasClass('.et2_checkbox_label')) - { - if (_value) - { - parentNode.hide(); - } - else - { - parentNode.show(); - } - } - super.set_disabled(_value); - } -} -et2_register_widget(et2_checkbox, ["checkbox"]); - -/** -* et2_checkbox_ro is the dummy readonly implementation of the checkbox -* @augments et2_checkbox -*/ -export class et2_checkbox_ro extends et2_checkbox implements et2_IDetachedDOM -{ - /** - * Ignore unset value - */ - static readonly _attributes : any = { - "unselected_value": { - "ignore": true - } - }; - - span : JQuery = null; - - /** - * Constructor - * - * @memberOf et2_checkbox_ro - */ - constructor(_parent, _attrs? : WidgetConfig, _child? : object) - { - // Call the inherited constructor - super(_parent, _attrs, ClassWithAttributes.extendAttributes(et2_checkbox_ro._attributes, _child || {})); - - this.value = ""; - this.span = jQuery(document.createElement("span")) - .addClass("et2_checkbox_ro"); - - this.setDOMNode(this.span[0]); - } - - /** - * note: checkbox is checked if even there is a value but not only if the _value is only "true" - * it's an exceptional validation for cases that we pass non boolean values as checkbox _value - * - * @param {string|boolean} _value - */ - set_value(_value) - { - if(_value == this.options.selected_value ||_value && this.options.selected_value == this.attributes["selected_value"]["default"] && - _value != this.options.unselected_value) { - this.span.text(this.options.ro_true); - this.value = _value; - } else { - this.span.text(this.options.ro_false); - } - } - - /** - * Code for implementing et2_IDetachedDOM - * - * @param {array} _attrs - */ - getDetachedAttributes(_attrs) - { - _attrs.push("value", "class"); - } - - getDetachedNodes() - { - return [this.span[0]]; - } - - setDetachedAttributes(_nodes, _values) - { - // Update the properties - if (typeof _values["value"] != "undefined") - { - this.span = jQuery(_nodes[0]); - this.set_value(_values["value"]); - } - - if (typeof _values["class"] != "undefined") - { - _nodes[0].setAttribute("class", _values["class"]); - } - } -} -et2_register_widget(et2_checkbox_ro, ["checkbox_ro"]); \ No newline at end of file +export class et2_checkbox extends Et2Checkbox {} \ No newline at end of file diff --git a/api/js/etemplate/et2_widget_toolbar.ts b/api/js/etemplate/et2_widget_toolbar.ts index c7ac21b111..5da455f398 100644 --- a/api/js/etemplate/et2_widget_toolbar.ts +++ b/api/js/etemplate/et2_widget_toolbar.ts @@ -15,10 +15,9 @@ */ import {et2_DOMWidget} from "./et2_core_DOMWidget"; -import {et2_createWidget, et2_register_widget, WidgetConfig} from "./et2_core_widget"; +import {et2_register_widget, WidgetConfig} from "./et2_core_widget"; import {ClassWithAttributes} from "./et2_core_inheritance"; import {egw_getObjectManager, egwActionObject, egwActionObjectManager} from '../egw_action/egw_action.js'; -import {et2_checkbox} from "./et2_widget_checkbox"; import {et2_IInput} from "./et2_core_interfaces"; import {egw} from "../jsapi/egw_global"; import {egwIsMobile} from "../egw_action/egw_action_common.js"; @@ -26,7 +25,6 @@ import {Et2Dialog} from "./Et2Dialog/Et2Dialog"; import {Et2DropdownButton} from "./Et2DropdownButton/Et2DropdownButton"; import {loadWebComponent} from "./Et2Widget/Et2Widget"; import interact from "@interactjs/interactjs"; -import Sortable from "sortablejs/modular/sortable.complete.esm.js"; import {Et2Button} from "./Et2Button/Et2Button"; import {Et2Checkbox} from "./Et2Checkbox/Et2Checkbox"; diff --git a/api/js/etemplate/etemplate2.ts b/api/js/etemplate/etemplate2.ts index 22eb5233d5..4eebbcec0b 100644 --- a/api/js/etemplate/etemplate2.ts +++ b/api/js/etemplate/etemplate2.ts @@ -110,7 +110,6 @@ import './et2_widget_textbox'; import './et2_widget_number'; import './et2_widget_url'; import './et2_widget_selectbox'; -import './et2_widget_checkbox'; import './et2_widget_radiobox'; import './et2_widget_date'; import './et2_widget_dialog'; diff --git a/api/js/etemplate/vfsSelectUI.ts b/api/js/etemplate/vfsSelectUI.ts index 64df53430a..30d9bf2d68 100644 --- a/api/js/etemplate/vfsSelectUI.ts +++ b/api/js/etemplate/vfsSelectUI.ts @@ -16,11 +16,10 @@ import {EgwApp} from "../jsapi/egw_app"; import {et2_vfs, et2_vfsPath, et2_vfsSelect} from "./et2_widget_vfs"; import {egw} from "../jsapi/egw_global"; import {et2_file} from "./et2_widget_file"; -import {Et2Textbox} from "./Et2Textbox/Et2Textbox"; import {Et2Button} from "./Et2Button/Et2Button"; import {Et2Select} from "./Et2Select/Et2Select"; -import {et2_checkbox} from "./et2_widget_checkbox"; import {Et2Dialog} from "./Et2Dialog/Et2Dialog"; +import {Et2Checkbox} from "./Et2Checkbox/Et2Checkbox"; /** @@ -266,11 +265,11 @@ export class vfsSelectUI extends EgwApp let file = widget.value.name; widget.getParent().iterateOver(function(widget) { - if(widget.options.selected_value == file) + if(widget.options.selectedValue === file) { - widget.set_value(widget.get_value() == file ? widget.options.unselected_value : file); + widget.set_value(widget.get_value() === file ? widget.options.unselectedValue : file); } - }, null, et2_checkbox); + }, null, Et2Checkbox); } // Stop event or it will toggle back off