From 12151139ffda4e04c550ca599e962a9c22d1b293 Mon Sep 17 00:00:00 2001 From: nathan Date: Wed, 7 Dec 2022 13:45:38 -0700 Subject: [PATCH] Et2Dialog: Better non-modal handling, now with less internal conflicts with SlDialog --- api/js/etemplate/Et2Dialog/Et2Dialog.ts | 17 +- .../etemplate/Et2Dialog/Et2DialogContent.ts | 89 ------ .../etemplate/Et2Dialog/Et2DialogOverlay.ts | 268 ------------------ api/js/etemplate/et2_widget_dialog.ts | 11 +- 4 files changed, 20 insertions(+), 365 deletions(-) delete mode 100644 api/js/etemplate/Et2Dialog/Et2DialogContent.ts delete mode 100644 api/js/etemplate/Et2Dialog/Et2DialogOverlay.ts diff --git a/api/js/etemplate/Et2Dialog/Et2Dialog.ts b/api/js/etemplate/Et2Dialog/Et2Dialog.ts index 31acd21df7..34555781e3 100644 --- a/api/js/etemplate/Et2Dialog/Et2Dialog.ts +++ b/api/js/etemplate/Et2Dialog/Et2Dialog.ts @@ -224,11 +224,11 @@ export class Et2Dialog extends Et2Widget(SlotMixin(SlDialog)) } /* Non-modal dialogs don't have an overlay */ - :host(:not([modal])) .dialog, :host(:not([modal])) .dialog__overlay { + :host(:not([isModal])) .dialog, :host(:not([isModal])) .dialog__overlay { pointer-events: none; background: transparent; } - :host(:not([modal])) .dialog__panel { + :host(:not([isModal])) .dialog__panel { pointer-events: auto; } ` @@ -243,8 +243,9 @@ export class Et2Dialog extends Et2Widget(SlotMixin(SlDialog)) /** * Allow other controls to be accessed while the dialog is visible + * while not conflicting with internal attribute */ - modal: {type: Boolean, reflect: true}, + isModal: {type: Boolean, reflect: true}, /** * Title for the dialog, goes in the header @@ -362,7 +363,7 @@ export class Et2Dialog extends Et2Widget(SlotMixin(SlDialog)) { this._setApiInstance(parent_egw); } - this.modal = true; + this.isModal = false; this.dialog_type = Et2Dialog.PLAIN_MESSAGE; this.destroyOnClose = true; this.hideOnEscape = this.hideOnEscape === false ? false : true; @@ -396,7 +397,7 @@ export class Et2Dialog extends Et2Widget(SlotMixin(SlDialog)) // Prevent close if they click the overlay when the dialog is modal this.addEventListener('sl-request-close', event => { - if(this.modal && event.detail.source === 'overlay') + if(this.isModal && event.detail.source === 'overlay') { event.preventDefault(); } @@ -999,7 +1000,7 @@ export class Et2Dialog extends Et2Widget(SlotMixin(SlDialog)) listeners: { move: this._onMoveResize }, - modifiers: (this.modal ? [] : [ + modifiers: (this.isModal ? [] : [ interact.modifiers.restrict({ restriction: 'parent', endOnly: true @@ -1069,6 +1070,7 @@ export class Et2Dialog extends Et2Widget(SlotMixin(SlDialog)) message: _message, title: _title || dialog.egw().lang('Confirmation required'), buttons: typeof _buttons != 'undefined' ? _buttons : Et2Dialog.BUTTONS_YES_NO, + isModal: true, dialog_type: typeof _type != 'undefined' ? _type : Et2Dialog.QUESTION_MESSAGE, icon: _icon, value: _value @@ -1096,6 +1098,7 @@ export class Et2Dialog extends Et2Widget(SlotMixin(SlDialog)) message: _message, title: _title, buttons: Et2Dialog.BUTTONS_OK, + isModal: true, dialog_type: _type || Et2Dialog.INFORMATION_MESSAGE }); @@ -1131,6 +1134,7 @@ export class Et2Dialog extends Et2Widget(SlotMixin(SlDialog)) }, title: _title || 'Input required', buttons: _buttons || Et2Dialog.BUTTONS_OK_CANCEL, + isModal: true, value: { content: { value: _value, @@ -1252,6 +1256,7 @@ export class Et2Dialog extends Et2Widget(SlotMixin(SlDialog)) } }, title: _title || 'please wait...', + isModal: true, buttons: buttons }); dialog.egw().window.document.body.appendChild(dialog); diff --git a/api/js/etemplate/Et2Dialog/Et2DialogContent.ts b/api/js/etemplate/Et2Dialog/Et2DialogContent.ts deleted file mode 100644 index d4805a925c..0000000000 --- a/api/js/etemplate/Et2Dialog/Et2DialogContent.ts +++ /dev/null @@ -1,89 +0,0 @@ -import {css, CSSResultArray, html, LitElement} from "@lion/core"; -import {Et2Widget} from "../Et2Widget/Et2Widget"; -import shoelace from "../Styles/shoelace"; - -/** - * Widget for the actual content of a dialog, used when we're not doing a template - * - */ -export class Et2DialogContent extends Et2Widget(LitElement) -{ - static styles : CSSResultArray = [ - shoelace, - css` - :host { - display: block; - min-width: 300px; - min-height: 32px; - } - .dialog { - } - .dialog_icon { - margin-right: 2ex; - vertical-align: middle; - } - ` - ]; - - - get properties() - { - return { - ...super.properties(), - - message: String, - dialogType: Number, - icon: String, - value: Object - } - } - - /** - * Details for dialog type options - */ - private readonly _dialogTypes : any = [ - //PLAIN_MESSAGE: 0 - "", - //INFORMATION_MESSAGE: 1, - "dialog_info", - //QUESTION_MESSAGE: 2, - "dialog_help", - //WARNING_MESSAGE: 3, - "dialog_warning", - //ERROR_MESSAGE: 4, - "dialog_error" - ]; - - constructor() - { - super(); - - this.icon = ""; - this.dialogType = 0; - } - - /** - * Block until after the paint - This is needed to deal with children not fully "done" before the OverlayController - * tries to do things with them - * - * @returns {Promise} - */ - async performUpdate() - { - await new Promise((resolve) => setTimeout(() => resolve())); - return super.performUpdate(); - } - - render() - { - let icon = this.icon || this.parentNode.egw().image(this._dialogTypes[this.dialogType]) || ""; - return html` -
- - Empty dialog - add some content -
- `; - } -} - -customElements.define("et2-dialog-content", Et2DialogContent); \ No newline at end of file diff --git a/api/js/etemplate/Et2Dialog/Et2DialogOverlay.ts b/api/js/etemplate/Et2Dialog/Et2DialogOverlay.ts deleted file mode 100644 index db2052ea9c..0000000000 --- a/api/js/etemplate/Et2Dialog/Et2DialogOverlay.ts +++ /dev/null @@ -1,268 +0,0 @@ -import {css, html, ifDefined, LitElement, repeat, SlotMixin} from '@lion/core'; -import {DialogButton, Et2Dialog} from "./Et2Dialog"; -import {et2_template} from "../et2_widget_template"; -import {Et2DialogContent} from "./Et2DialogContent"; -import shoelace from "../Styles/shoelace"; - -/** - * This handles the visible portion of the dialog, including the title & close button. - * - * Note we can't extend Et2Widget. If I try, something in the render / creation breaks and calling open() gives an - * error with modal: true - */ -export class Et2DialogOverlay extends SlotMixin(LitElement) -{ - - protected buttons : DialogButton[]; - - protected _dialog : Et2Dialog; - - - /** - * I don't know what's going on with styles here, but if we define Et2DialogOverlay.styles it breaks - * dialog display in Firefox. - * The styles are added in render(), which is bad from a performance standpoint, but good in that it works. - */ - static get _styles() - { - return [shoelace, - css` - :host { - display: inline-block; - background: white; - position: relative; - border: 1px solid silver; - box-shadow: -2px 1px 9px 3px #b4b4b4; - min-width: 250px; - touch-action: none; - box-sizing: border-box; - } - - :host([hidden]) { - display: none; - } - - .overlay { - display: flex; - flex-direction: column; - height: 100%; - } - .overlay__header { - display: flex; - border-bottom: 1px inset; - } - - .overlay__heading { - margin: 0px; - padding: 6px 10px 0px; - flex: 1; - font-size: 130%; - font-weight: 400; - } - #overlay-content-node-wrapper { - flex: 1 1 auto; - padding: 10px; - } - - .overlay__heading > .overlay__close-button { - flex: none; - } - - .overlay__close-button { - min-width: 24px; - min-height: 24px; - border-width: 0; - padding: 0; - font-size: 24px; - } - #overlay-content-buttons { - display: flex; - flex-wrap: nowrap; - justify-content: flex-start; - align-items: stretch; - gap: 5px; - border-top: 1px solid silver; - margin-top: 0.5em; - padding: 5px; - } - /* Override style for legacy nextmatch action dialogs */ - ::slotted([slot="content"]) { - display: block !important; - position: relative !important; - inset: initial !important; - } - ::slotted([slot="buttons"]) { - flex: 1 0 auto; - } - ::slotted([align="right"]) { - margin-left: auto; - order: 1; - }`]; - } - - get properties() - { - return { - // Allow to force size, otherwise it sizes to contents - width: Number, - height: Number, - } - } - - get slots() - { - return { - ...super.slots, - buttons: () => - { - return this._buttonsTemplate(); - } - } - } - - constructor() - { - super(); - this.buttons = []; - } - - - - // Need to wait for Overlay - async getUpdateComplete() - { - let result = await super.getUpdateComplete(); - if(this._contentNode && this._contentNode instanceof LitElement) - { - await (this._contentNode).updateComplete; - } - return result; - } - - connectedCallback() - { - super.connectedCallback(); - // Need to wait for Overlay - this.updateComplete - .then(async() => - { - if(this._contentNode && this._contentNode instanceof LitElement) - { - // Re-do render to get proper images - this._contentNode.requestUpdate(); - - await this._contentNode.updateComplete; - } - }); - } - - egw() : IegwAppLocal - { - if(this._dialog) - { - return this._dialog.egw(); - } - else - { - return egw(); - } - } - - /** - * Block until after the paint - This is needed to deal with children not fully "done" before the OverlayController - * tries to do things with them - * - * @returns {Promise} - */ - async performUpdate() - { - await new Promise((resolve) => setTimeout(() => resolve())); - return super.performUpdate(); - } - - get _contentNode() : Et2DialogContent | et2_template - { - // @ts-ignore - return this.querySelector("[slot='content']"); - } - - /** @private */ - __dispatchCloseEvent() - { - this.dispatchEvent(new Event('close-overlay')); - } - - render() - { - // This style is just for this dialog - let styles = [Et2DialogOverlay._styles]; - - if(this.width && Number.isInteger(this.width)) - { - styles.push(css`.overlay {width: ${this.width}px}`); - } - if(this.height && Number.isInteger(this.height)) - { - styles.push(css`.overlay {height: ${this.height}px}`); - } - return html` - -
-
-

- -

- - ${this._closeButtonTemplate()} -
-
- -
-
- -
-
- `; - } - - _closeButtonTemplate() - { - if (this._dialog.noCloseButton) - { - return; - } - - return html` - `; - } - - _buttonsTemplate() - { - if(!this.buttons) - { - return; - } - - // Set button._parent here, otherwise button will have trouble finding our egw() - return html`${repeat(this.buttons, (button : DialogButton) => button.id, (button, index) => - { - return html` - - - ` - })}`; - } -} \ No newline at end of file diff --git a/api/js/etemplate/et2_widget_dialog.ts b/api/js/etemplate/et2_widget_dialog.ts index c234c97632..61c0aa1231 100644 --- a/api/js/etemplate/et2_widget_dialog.ts +++ b/api/js/etemplate/et2_widget_dialog.ts @@ -54,6 +54,13 @@ export class et2_dialog extends Et2Dialog constructor(parent?, attrs?) { super(parent?.egw() || egw); + if(attrs.hasOwnProperty("modal")) + { + // modal is an internal property of SlDialog + console.warn("modal is an internal property, use isModal instead"); + attrs.isModal = attrs.modal; + delete attrs.modal; + } if(attrs) { this.transformAttributes(attrs); @@ -95,7 +102,7 @@ export class et2_dialog extends Et2Dialog // move the overlay dialog into appendTo dom since we want it to be shown in that container if(this.appendTo) { - document.getElementsByClassName(this.appendTo.replace('.', ''))[0].appendChild(this._cachedOverlayContentNode); + document.getElementsByClassName(this.appendTo.replace('.', ''))[0].appendChild(this); } } @@ -104,7 +111,7 @@ export class et2_dialog extends Et2Dialog // revert the moved container back to its original position in order to be able to teardown the overlay properly if(this.appendTo) { - document.getElementsByClassName('global-overlays__overlay-container')[0].appendChild(this._cachedOverlayContentNode); + document.getElementsByClassName('global-overlays__overlay-container')[0].appendChild(this); } super.handleClose(ev); }