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 {Et2DialogOverlay} from "./Et2DialogOverlay";
import {Et2DialogContent} from "./Et2DialogContent";
import {et2_template} from "../et2_widget_template";
import {etemplate2} from "../etemplate2";
export interface DialogButton
{
@ -46,6 +48,16 @@ export class Et2Dialog extends Et2Widget(ScopedElementsMixin(SlotMixin(LionDialo
*/
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
* @constant
@ -73,10 +85,18 @@ export class Et2Dialog extends Et2Widget(ScopedElementsMixin(SlotMixin(LionDialo
return {
...super.properties(),
callback: Function,
// There's an issue with Et2DialogContent.style being undefined, so this has to stay false until it gets
// figured out
modal: Boolean,
title: String,
buttons: Number,
template: String,
width: Number,
height: Number,
// We just pass these on to Et2DialogContent
message: String,
dialog_type: Number,
@ -140,11 +160,16 @@ export class Et2Dialog extends Et2Widget(ScopedElementsMixin(SlotMixin(LionDialo
]
];
constructor()
constructor(parent_egw? : string | IegwAppLocal)
{
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._onClose = this._onClose.bind(this);
@ -154,6 +179,12 @@ export class Et2Dialog extends Et2Widget(ScopedElementsMixin(SlotMixin(LionDialo
connectedCallback()
{
super.connectedCallback();
// Wait for everything to complete, then auto-open
this.getUpdateComplete().then(() =>
{
window.setTimeout(this.open, 0);
});
}
// Need to wait for Overlay
@ -162,15 +193,18 @@ export class Et2Dialog extends Et2Widget(ScopedElementsMixin(SlotMixin(LionDialo
await super.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._overlayContentNode.addEventListener(
'close-overlay',
this._onClose,
);
this._overlayContentNode.addEventListener(
'click',
this._onClick
)
this._overlayContentNode.querySelectorAll("et2-button").forEach((button) => button.addEventListener("click", this._onClick));
}
_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;
// Handle anything bound via et2 onclick property
let et2_widget_result = super._handleClick(ev);
if(!et2_widget_result)
try
{
ev.preventDefault();
ev.stopPropagation();
return false;
let et2_widget_result = super._handleClick(ev);
if(!et2_widget_result)
{
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();
ev.stopPropagation();
return false;
let callback_result = this.callback ? this.callback(button_id, this.value) : true;
if(callback_result === false)
{
ev.preventDefault();
ev.stopPropagation();
return false;
}
}
catch(e)
{
console.log(e);
}
this.close();
}
@ -213,18 +260,97 @@ export class Et2Dialog extends Et2Widget(ScopedElementsMixin(SlotMixin(LionDialo
get value() : Object
{
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;
}
/**
* 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)
{
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()
{
return this._overlayTemplate();
@ -240,6 +366,8 @@ export class Et2Dialog extends Et2Widget(ScopedElementsMixin(SlotMixin(LionDialo
return html`
<div id="overlay-content-node-wrapper">
<et2-dialog-overlay-frame class="dialog__overlay-frame"
.width=${this.width}
.height=${this.height}
._dialog=${this}
.buttons=${this._getButtons()}>
<span slot="heading">${this.title}</span>
@ -267,16 +395,22 @@ export class Et2Dialog extends Et2Widget(ScopedElementsMixin(SlotMixin(LionDialo
_contentTemplate()
{
return html`
<et2-dialog-content slot="content" ?icon=${this.icon}
.dialog_type=${this.dialog_type}
.value=${this.value}
>
${this.message}
</et2-dialog-content>
`;
if(this.template)
{
return html`
<div slot="content"></div>`;
}
else
{
return html`
<et2-dialog-content slot="content" ?icon=${this.icon}
.dialog_type=${this.dialog_type}
.value=${this.value}
>
${this.message}
</et2-dialog-content>
`;
}
}
_getButtons()
@ -379,12 +513,15 @@ export class Et2Dialog extends Et2Widget(ScopedElementsMixin(SlotMixin(LionDialo
icon: _icon,
value: _value,
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
});
// Let other things run, then open
dialog.getUpdateComplete().then(() =>
{
window.setTimeout(dialog.open, 0);
});
document.body.appendChild(<LitElement><unknown>dialog);
return dialog;

View File

@ -1,5 +1,5 @@
import {css, CSSResultArray, html, LitElement} from "@lion/core";
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
@ -7,11 +7,8 @@ import {css, html, LitElement} from "@lion/core";
*/
export class Et2DialogContent extends Et2Widget(LitElement)
{
static get styles()
{
return [
...super.styles,
css`
static styles : CSSResultArray = [
css`
:host {
display: block;
min-width: 200px;
@ -25,8 +22,8 @@ export class Et2DialogContent extends Et2Widget(LitElement)
vertical-align: middle;
}
`
];
}
];
get properties()
{

View File

@ -1,12 +1,15 @@
import {css, html, LitElement, repeat, SlotMixin} from '@lion/core';
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.
*
* 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[];
@ -30,6 +33,10 @@ export class Et2DialogOverlay extends Et2Widget(SlotMixin(LitElement))
display: none;
}
.overlay {
display: flex;
flex-direction: column;
}
.overlay__header {
display: flex;
}
@ -39,6 +46,9 @@ export class Et2DialogOverlay extends Et2Widget(SlotMixin(LitElement))
padding: 6px 16px 8px;
flex: 1;
}
#overlay-content-node-wrapper {
flex: 1 1 auto;
}
.overlay__heading > .overlay__close-button {
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()
{
return {
@ -84,7 +103,6 @@ export class Et2DialogOverlay extends Et2Widget(SlotMixin(LitElement))
{
super();
this.buttons = [];
}
firstUpdated(_changedProperties)
@ -92,22 +110,39 @@ export class Et2DialogOverlay extends Et2Widget(SlotMixin(LitElement))
super.firstUpdated(_changedProperties);
// Tell content about its parent, but don't move it
//@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
async getUpdateComplete()
{
await super.getUpdateComplete();
await this._contentNode.getUpdateComplete();
let result = await super.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
{
if(this._dialog)
@ -132,8 +167,9 @@ export class Et2DialogOverlay extends Et2Widget(SlotMixin(LitElement))
return super.performUpdate();
}
get _contentNode()
get _contentNode() : Et2DialogContent | et2_template
{
// @ts-ignore
return this.querySelector("[slot='content']");
}
@ -145,8 +181,17 @@ export class Et2DialogOverlay extends Et2Widget(SlotMixin(LitElement))
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`
${(this.width || this.height) ? style : ""}
<div class="overlay">
<div class="overlay__header">
<h1 class="overlay__heading">