Get templates working in Et2Dialog

This commit is contained in:
nathan 2022-03-16 13:53:50 -06:00
parent 689e7e6ee5
commit 5fdc594d00
3 changed files with 231 additions and 52 deletions

View File

@ -17,6 +17,8 @@ import {et2_createWidget, et2_widget} from "../et2_core_widget";
import {html, LitElement, ScopedElementsMixin, SlotMixin} from "@lion/core"; import {html, LitElement, ScopedElementsMixin, SlotMixin} from "@lion/core";
import {Et2DialogOverlay} from "./Et2DialogOverlay"; import {Et2DialogOverlay} from "./Et2DialogOverlay";
import {Et2DialogContent} from "./Et2DialogContent"; import {Et2DialogContent} from "./Et2DialogContent";
import {et2_template} from "../et2_widget_template";
import {etemplate2} from "../etemplate2";
export interface DialogButton export interface DialogButton
{ {
@ -46,6 +48,16 @@ export class Et2Dialog extends Et2Widget(ScopedElementsMixin(SlotMixin(LionDialo
*/ */
protected __egw : IegwAppLocal protected __egw : IegwAppLocal
/**
* As long as the template is a legacy widget, we want to hold on to the widget
* When it becomes a WebComponent, we can just include it in render()
*
* @type {et2_template | null}
* @protected
*/
protected __template_widget : etemplate2 | null;
protected __template_promise : Promise<boolean>;
/** /**
* Types * Types
* @constant * @constant
@ -73,10 +85,18 @@ export class Et2Dialog extends Et2Widget(ScopedElementsMixin(SlotMixin(LionDialo
return { return {
...super.properties(), ...super.properties(),
callback: Function, callback: Function,
// There's an issue with Et2DialogContent.style being undefined, so this has to stay false until it gets
// figured out
modal: Boolean, modal: Boolean,
title: String, title: String,
buttons: Number, buttons: Number,
template: String,
width: Number,
height: Number,
// We just pass these on to Et2DialogContent // We just pass these on to Et2DialogContent
message: String, message: String,
dialog_type: Number, dialog_type: Number,
@ -140,11 +160,16 @@ export class Et2Dialog extends Et2Widget(ScopedElementsMixin(SlotMixin(LionDialo
] ]
]; ];
constructor() constructor(parent_egw? : string | IegwAppLocal)
{ {
super(); super();
this.modal = true; if(parent_egw)
{
this._setApiInstance(parent_egw);
}
// Needs to not be modal until the style thing is figured out
this.modal = false;
this.__value = {}; this.__value = {};
this._onClose = this._onClose.bind(this); this._onClose = this._onClose.bind(this);
@ -154,6 +179,12 @@ export class Et2Dialog extends Et2Widget(ScopedElementsMixin(SlotMixin(LionDialo
connectedCallback() connectedCallback()
{ {
super.connectedCallback(); super.connectedCallback();
// Wait for everything to complete, then auto-open
this.getUpdateComplete().then(() =>
{
window.setTimeout(this.open, 0);
});
} }
// Need to wait for Overlay // Need to wait for Overlay
@ -162,15 +193,18 @@ export class Et2Dialog extends Et2Widget(ScopedElementsMixin(SlotMixin(LionDialo
await super.getUpdateComplete(); await super.getUpdateComplete();
await this._overlayContentNode.getUpdateComplete(); await this._overlayContentNode.getUpdateComplete();
// Wait for template to finish loading
if(this.__template_widget)
{
await this.__template_promise;
}
// This calls _onClose() when the dialog is closed // This calls _onClose() when the dialog is closed
this._overlayContentNode.addEventListener( this._overlayContentNode.addEventListener(
'close-overlay', 'close-overlay',
this._onClose, this._onClose,
); );
this._overlayContentNode.addEventListener( this._overlayContentNode.querySelectorAll("et2-button").forEach((button) => button.addEventListener("click", this._onClick));
'click',
this._onClick
)
} }
_onClose(ev : PointerEvent) _onClose(ev : PointerEvent)
@ -185,23 +219,36 @@ export class Et2Dialog extends Et2Widget(ScopedElementsMixin(SlotMixin(LionDialo
const button_id = parseInt(ev.target?.getAttribute("button_id")) || ev.target?.getAttribute("id") || null; const button_id = parseInt(ev.target?.getAttribute("button_id")) || ev.target?.getAttribute("id") || null;
// Handle anything bound via et2 onclick property // Handle anything bound via et2 onclick property
let et2_widget_result = super._handleClick(ev); try
if(!et2_widget_result)
{ {
ev.preventDefault(); let et2_widget_result = super._handleClick(ev);
ev.stopPropagation(); if(!et2_widget_result)
return false; {
ev.preventDefault();
ev.stopPropagation();
return false;
}
}
catch(e)
{
console.log(e);
} }
// Callback expects (button_id, value)
let callback_result = this.callback ? this.callback(button_id, this.value) : true;
if(callback_result === false) // Callback expects (button_id, value)
try
{ {
ev.preventDefault(); let callback_result = this.callback ? this.callback(button_id, this.value) : true;
ev.stopPropagation(); if(callback_result === false)
return false; {
ev.preventDefault();
ev.stopPropagation();
return false;
}
}
catch(e)
{
console.log(e);
} }
this.close(); this.close();
} }
@ -213,18 +260,97 @@ export class Et2Dialog extends Et2Widget(ScopedElementsMixin(SlotMixin(LionDialo
get value() : Object get value() : Object
{ {
let value = this.__value; let value = this.__value;
if(this.template) if(this.__template_widget)
{ {
value = this.template.getValues(this.template.widgetContainer); value = this.__template_widget.getValues(this.__template_widget.widgetContainer);
} }
return value; return value;
} }
/**
* Fire the close-overlay event to use all registered listeners
* @deprecated
*/
destroy()
{
this._overlayContentNode.dispatchEvent(new Event('close-overlay'));
}
/**
* @deprecated
* @returns {Object}
*/
get_value() : Object
{
console.warn("Deprecated get_value() called");
return this.value;
}
set value(new_value) set value(new_value)
{ {
this.__value = new_value; this.__value = new_value;
} }
set template(new_template_name)
{
let old_template = this.template;
this.__template = new_template_name;
this.requestUpdate("template", old_template);
}
get template()
{
return this.__template;
}
updated(changedProperties)
{
super.updated(changedProperties);
if(changedProperties.has("template"))
{
this._loadTemplate();
}
}
_loadTemplate()
{
if(this.__template_widget)
{
this.__template_widget.clear();
}
this.__template_widget = new etemplate2(this._overlayContentNode._contentNode);
if(this.template.indexOf('.xet') > 0)
{
// File name provided, fetch from server
this.__template_promise = this.__template_widget.load("", this.template, this.__value || {content: {}},);
}
else
{
// Just template name, it better be loaded already
this.__template_promise = this.__template_widget.load(this.template, '', this.__value || {},
// true: do NOT call et2_ready, as it would overwrite this.et2 in app.js
undefined, undefined, true);
}
// Don't let dialog closing destroy the parent session
if(this.__template_widget.etemplate_exec_id && this.__template_widget.app)
{
for(let et of etemplate2.getByApplication(this.__template_widget.app))
{
if(et !== this.__template_widget && et.etemplate_exec_id === this.__template_widget.etemplate_exec_id)
{
// Found another template using that exec_id, don't destroy when dialog closes.
this.__template_widget.unbind_unload();
break;
}
}
}
// set template-name as id, to allow to style dialogs
//this.div.children().attr('id', new_template_name.replace(/^(.*\/)?([^/]+)(\.xet)?$/, '$2').replace(/\./g, '-'));
}
render() render()
{ {
return this._overlayTemplate(); return this._overlayTemplate();
@ -240,6 +366,8 @@ export class Et2Dialog extends Et2Widget(ScopedElementsMixin(SlotMixin(LionDialo
return html` return html`
<div id="overlay-content-node-wrapper"> <div id="overlay-content-node-wrapper">
<et2-dialog-overlay-frame class="dialog__overlay-frame" <et2-dialog-overlay-frame class="dialog__overlay-frame"
.width=${this.width}
.height=${this.height}
._dialog=${this} ._dialog=${this}
.buttons=${this._getButtons()}> .buttons=${this._getButtons()}>
<span slot="heading">${this.title}</span> <span slot="heading">${this.title}</span>
@ -267,16 +395,22 @@ export class Et2Dialog extends Et2Widget(ScopedElementsMixin(SlotMixin(LionDialo
_contentTemplate() _contentTemplate()
{ {
if(this.template)
return html` {
<et2-dialog-content slot="content" ?icon=${this.icon} return html`
.dialog_type=${this.dialog_type} <div slot="content"></div>`;
.value=${this.value} }
> else
${this.message} {
</et2-dialog-content> return html`
`; <et2-dialog-content slot="content" ?icon=${this.icon}
.dialog_type=${this.dialog_type}
.value=${this.value}
>
${this.message}
</et2-dialog-content>
`;
}
} }
_getButtons() _getButtons()
@ -379,12 +513,15 @@ export class Et2Dialog extends Et2Widget(ScopedElementsMixin(SlotMixin(LionDialo
icon: _icon, icon: _icon,
value: _value, value: _value,
width: 'auto', width: 'auto',
/*
TODO: There's something going on with our Et2Widgets that they don't get a proper .style property
This lack of .style causes problems when we go modal. Non-modal works.
*/
modal: false modal: false
}); });
// Let other things run, then open // Let other things run, then open
dialog.getUpdateComplete().then(() => dialog.getUpdateComplete().then(() =>
{ {
window.setTimeout(dialog.open, 0);
}); });
document.body.appendChild(<LitElement><unknown>dialog); document.body.appendChild(<LitElement><unknown>dialog);
return dialog; return dialog;

View File

@ -1,5 +1,5 @@
import {css, CSSResultArray, html, LitElement} from "@lion/core";
import {Et2Widget} from "../Et2Widget/Et2Widget"; import {Et2Widget} from "../Et2Widget/Et2Widget";
import {css, html, LitElement} from "@lion/core";
/** /**
* Widget for the actual content of a dialog, used when we're not doing a template * Widget for the actual content of a dialog, used when we're not doing a template
@ -7,11 +7,8 @@ import {css, html, LitElement} from "@lion/core";
*/ */
export class Et2DialogContent extends Et2Widget(LitElement) export class Et2DialogContent extends Et2Widget(LitElement)
{ {
static get styles() static styles : CSSResultArray = [
{ css`
return [
...super.styles,
css`
:host { :host {
display: block; display: block;
min-width: 200px; min-width: 200px;
@ -25,8 +22,8 @@ export class Et2DialogContent extends Et2Widget(LitElement)
vertical-align: middle; vertical-align: middle;
} }
` `
]; ];
}
get properties() get properties()
{ {

View File

@ -1,12 +1,15 @@
import {css, html, LitElement, repeat, SlotMixin} from '@lion/core'; import {css, html, LitElement, repeat, SlotMixin} from '@lion/core';
import {DialogButton, Et2Dialog} from "./Et2Dialog"; import {DialogButton, Et2Dialog} from "./Et2Dialog";
import {Et2Widget} from "../Et2Widget/Et2Widget"; import {et2_template} from "../et2_widget_template";
import {Et2DialogContent} from "./Et2DialogContent";
/** /**
* This handles the visible portion of the dialog, including the title & close button. * 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 Et2Widget(SlotMixin(LitElement)) export class Et2DialogOverlay extends SlotMixin(LitElement)
{ {
protected buttons : DialogButton[]; protected buttons : DialogButton[];
@ -30,6 +33,10 @@ export class Et2DialogOverlay extends Et2Widget(SlotMixin(LitElement))
display: none; display: none;
} }
.overlay {
display: flex;
flex-direction: column;
}
.overlay__header { .overlay__header {
display: flex; display: flex;
} }
@ -39,6 +46,9 @@ export class Et2DialogOverlay extends Et2Widget(SlotMixin(LitElement))
padding: 6px 16px 8px; padding: 6px 16px 8px;
flex: 1; flex: 1;
} }
#overlay-content-node-wrapper {
flex: 1 1 auto;
}
.overlay__heading > .overlay__close-button { .overlay__heading > .overlay__close-button {
flex: none; flex: none;
@ -69,6 +79,15 @@ export class Et2DialogOverlay extends Et2Widget(SlotMixin(LitElement))
]; ];
} }
get properties()
{
return {
// Allow to force size, otherwise it sizes to contents
width: Number,
height: Number,
}
}
get slots() get slots()
{ {
return { return {
@ -84,7 +103,6 @@ export class Et2DialogOverlay extends Et2Widget(SlotMixin(LitElement))
{ {
super(); super();
this.buttons = []; this.buttons = [];
} }
firstUpdated(_changedProperties) firstUpdated(_changedProperties)
@ -92,22 +110,39 @@ export class Et2DialogOverlay extends Et2Widget(SlotMixin(LitElement))
super.firstUpdated(_changedProperties); super.firstUpdated(_changedProperties);
// Tell content about its parent, but don't move it // Tell content about its parent, but don't move it
//@ts-ignore //@ts-ignore
(<Et2Widget><unknown>this.querySelector("[slot='content']"))._parent = this._dialog; //(<Et2Widget><unknown>this.querySelector("[slot='content']"))._parent = this._dialog;
} }
// Need to wait for Overlay // Need to wait for Overlay
async getUpdateComplete() async getUpdateComplete()
{ {
await super.getUpdateComplete(); let result = await super.getUpdateComplete();
await this._contentNode.getUpdateComplete(); if(this._contentNode && this._contentNode.getUpdateComplete)
{
await this._contentNode.getUpdateComplete();
}
return result;
}
connectedCallback()
{
super.connectedCallback();
// Need to wait for Overlay
this.updateComplete
.then(async() =>
{
if(this._contentNode && this._contentNode.getUpdateComplete)
{
// Re-do render to get proper images
this._contentNode.requestUpdate();
await this._contentNode.getUpdateComplete();
}
});
} }
/**
* Dialog might not be part of an etemplate, use dialog's egw
*
* @returns {IegwAppLocal}
*/
egw() : IegwAppLocal egw() : IegwAppLocal
{ {
if(this._dialog) if(this._dialog)
@ -132,8 +167,9 @@ export class Et2DialogOverlay extends Et2Widget(SlotMixin(LitElement))
return super.performUpdate(); return super.performUpdate();
} }
get _contentNode() get _contentNode() : Et2DialogContent | et2_template
{ {
// @ts-ignore
return this.querySelector("[slot='content']"); return this.querySelector("[slot='content']");
} }
@ -145,8 +181,17 @@ export class Et2DialogOverlay extends Et2Widget(SlotMixin(LitElement))
render() render()
{ {
// eslint-disable-line class-methods-use-this // This style is just for this dialog
let style = html`
<style>
.overlay {
${this.width ? "width: " + this.width + "px" : ""};
${this.height ? "height: " + this.height + "px" : ""};
}
</style>`;
return html` return html`
${(this.width || this.height) ? style : ""}
<div class="overlay"> <div class="overlay">
<div class="overlay__header"> <div class="overlay__header">
<h1 class="overlay__heading"> <h1 class="overlay__heading">