diff --git a/admin/js/app.ts b/admin/js/app.ts
index 99179ad161..a6c393dfc7 100644
--- a/admin/js/app.ts
+++ b/admin/js/app.ts
@@ -620,8 +620,8 @@ class AdminApp extends EgwApp
{
dialog_options['width'] = 550;
modifications.tabs = {
- add_tabs: true,
- tabs: [{
+ addTabs: true,
+ extraTabs: [{
label: egw.lang('Documentation'),
template: 'policy.admin_cmd',
prepend: false
diff --git a/api/js/etemplate/Et2Dialog/Et2Dialog.ts b/api/js/etemplate/Et2Dialog/Et2Dialog.ts
index b84b79a01f..41b25a6292 100644
--- a/api/js/etemplate/Et2Dialog/Et2Dialog.ts
+++ b/api/js/etemplate/Et2Dialog/Et2Dialog.ts
@@ -11,17 +11,17 @@
import {Et2Widget} from "../Et2Widget/Et2Widget";
import {et2_button} from "../et2_widget_button";
-import {LionDialog} from "@lion/dialog";
import {et2_widget} from "../et2_core_widget";
-import {html, LitElement, ScopedElementsMixin, SlotMixin} from "@lion/core";
-import {Et2DialogOverlay} from "./Et2DialogOverlay";
-import {Et2DialogContent} from "./Et2DialogContent";
+import {classMap, css, html, ifDefined, LitElement, render, repeat, SlotMixin, styleMap} from "@lion/core";
import {et2_template} from "../et2_widget_template";
import {etemplate2} from "../etemplate2";
-import {IegwAppLocal} from "../../jsapi/egw_global";
+import {egw, IegwAppLocal} from "../../jsapi/egw_global";
import interact from "@interactjs/interactjs";
import type {InteractEvent} from "@interactjs/core/InteractEvent";
import {Et2Button} from "../Et2Button/Et2Button";
+import shoelace from "../Styles/shoelace";
+import {SlDialog} from "@shoelace-style/shoelace";
+import {egwIsMobile} from "../../egw_action/egw_action_common";
export interface DialogButton
{
@@ -122,11 +122,9 @@ export interface DialogButton
* let result = await dialog.getComplete();
*```
*
- * Because of how LionDialog does its layout and rendering, it's easiest to separate the dialog popup from
- * the dialog content. This allows us to easily preserve the WebComponent styling. You should interact with the
- * Et2Dialog though, and ignore the Et2DialogOverlay & Et2DialogContent.
+ * Customize initial focus by setting the "autofocus" attribute on a control, otherwise first input will have focus
*/
-export class Et2Dialog extends Et2Widget(ScopedElementsMixin(SlotMixin(LionDialog)))
+export class Et2Dialog extends Et2Widget(SlotMixin(SlDialog))
{
/**
* Dialogs don't always get added to an etemplate, so we keep our own egw
@@ -184,6 +182,31 @@ export class Et2Dialog extends Et2Widget(ScopedElementsMixin(SlotMixin(LionDialo
public static readonly YES_BUTTON : number = 2;
public static readonly NO_BUTTON : number = 3;
+
+ static get styles()
+ {
+ return [
+ ...shoelace,
+ ...(super.styles || []),
+ css`
+ :host {
+ --header-spacing: var(--sl-spacing-medium);
+ --body-spacing: var(--sl-spacing-medium)
+ }
+ .dialog__title {
+ user-select: none;
+ }
+ .dialog__footer {
+ display: flex;
+ flex-wrap: nowrap;
+ justify-content: flex-start;
+ align-items: stretch;
+ gap: 5px;
+ }
+ `
+ ];
+ }
+
static get properties()
{
return {
@@ -245,21 +268,14 @@ export class Et2Dialog extends Et2Widget(ScopedElementsMixin(SlotMixin(LionDialo
get slots()
{
return {
- ...super.slots
+ ...super.slots,
+ '': () =>
+ {
+ return this._contentTemplate();
+ }
}
}
- // Still not sure what this does, but it's important.
- // Seems to be related to the constructor, and what's available during the "creation"
- static get scopedElements()
- {
- return {
- ...super.scopedElements,
- 'et2-dialog-overlay-frame': Et2DialogOverlay,
- 'et2-dialog-content': Et2DialogContent
- };
- }
-
/*
* List of properties that get translated
* Done separately to not interfere with properties - if we re-define label property,
@@ -323,14 +339,20 @@ export class Et2Dialog extends Et2Widget(ScopedElementsMixin(SlotMixin(LionDialo
this.destroyOnClose = true;
this.hideOnEscape = this.hideOnEscape === false ? false : true;
this.__value = {};
+ this.open = true;
- this._onOpen = this._onOpen.bind(this);
- this._onClose = this._onClose.bind(this);
+ this.handleOpen = this.handleOpen.bind(this);
+ this.handleClose = this.handleClose.bind(this);
this._onClick = this._onClick.bind(this);
this._onButtonClick = this._onButtonClick.bind(this);
this._onMoveResize = this._onMoveResize.bind(this);
+ this.handleKeyDown = this.handleKeyDown.bind(this);
this._adoptTemplateButtons = this._adoptTemplateButtons.bind(this);
+ // Don't leave it undefined, it's easier to deal with if it's just already resolved.
+ // It will be re-set if a template is loaded
+ this._template_promise = Promise.resolve(false);
+
// Create this here so we have something, otherwise the creator might continue with undefined while we
// wait for the dialog to complete & open
this._complete_promise = new Promise<[number, Object]>((resolve) =>
@@ -343,32 +365,82 @@ export class Et2Dialog extends Et2Widget(ScopedElementsMixin(SlotMixin(LionDialo
{
super.connectedCallback();
- // Wait for everything to complete, then auto-open
- this.getUpdateComplete().then(() =>
+ // Prevent close if they click the overlay when the dialog is modal
+ this.addEventListener('sl-request-close', event =>
{
- // _overlayCtrl is not available earlier
- this._overlayCtrl?.addEventListener("show", this._onOpen);
- this._overlayCtrl.addEventListener('hide', this._onClose);
+ if(this.modal && event.detail.source === 'overlay')
+ {
+ event.preventDefault();
+ }
+ })
+
+ this.addEventListener("sl-after-show", this.handleOpen);
+ this.addEventListener('sl-hide', this.handleClose);
- // Bind on the ancestor, not the buttons, so their click handler gets a chance to run
- this._overlayContentNode.addEventListener("click", this._onButtonClick);
- window.setTimeout(this.open, 0);
- });
}
disconnectedCallback()
{
super.disconnectedCallback();
- this._overlayCtrl.removeEventListener("hide", this._onClose);
- this._overlayCtrl.removeEventListener("show", this._onOpen);
- this._overlayContentNode.removeEventListener("click", this._onButtonClick);
+ this.removeEventListener("sl-hide", this.handleClose);
+ this.removeEventListener("sl-after-show", this.handleOpen);
+ }
+
+ addOpenListeners()
+ {
+ //super.addOpenListeners();
+
+ // Bind on the ancestor, not the buttons, so their click handler gets a chance to run
+ this.addEventListener("click", this._onButtonClick);
+ this.addEventListener("keydown", this.handleKeyDown);
+ }
+
+ removeOpenListeners()
+ {
+ //super.removeOpenListeners();
+ this.removeEventListener("click", this._onButtonClick);
+ this.removeEventListener("keydown", this.handleKeyDown);
+ }
+
+ handleKeyDown(event : KeyboardEvent)
+ {
+ // Parent handles escape, but is already bound
+ super.handleKeyDown(event);
+
+ // Trigger the "primary" or first button
+ if(this.open && event.key === 'Enter')
+ {
+ let button = this.querySelectorAll("[varient='primary']");
+ if(button.length == 0)
+ {
+ // Nothing explicitly marked, check for buttons in the footer
+ button = this.querySelectorAll("et2-button[slot='footer']");
+ }
+ if(button && button[0])
+ {
+ event.stopPropagation();
+ button[0].dispatchEvent(new CustomEvent('click', {bubbles: true}));
+ }
+ }
+ }
+
+ firstUpdated()
+ {
+ super.firstUpdated();
+
+ // If we start open, fire handler to get setup done
+ if(this.open)
+ {
+ this.handleOpenChange();
+ }
+
+ this.updateComplete.then(() => this._setDefaultAutofocus());
}
// Need to wait for Overlay
async getUpdateComplete()
{
await super.getUpdateComplete();
- await this._overlayContentNode.getUpdateComplete();
// Wait for template to finish loading
if(this._template_widget)
@@ -382,18 +454,28 @@ export class Et2Dialog extends Et2Widget(ScopedElementsMixin(SlotMixin(LionDialo
return this._complete_promise;
}
- _onOpen()
+ handleOpen()
{
+ this.addOpenListeners();
this._button_id = null;
this._complete_promise = this._complete_promise || new Promise<[number, Object]>((resolve) => this._completeResolver);
- this._setupMoveResize(this._overlayContentNode);
// Now consumers can listen for "open" event, though getUpdateComplete().then(...) also works
this.dispatchEvent(new Event('open'));
+
+ Promise.all([this._template_promise, this.updateComplete])
+ .then(() => this._setupMoveResize());
}
- _onClose(ev : PointerEvent)
+ handleClose(ev : PointerEvent)
{
+ // Avoid closing if a selectbox is closed
+ if(ev.target !== this)
+ {
+ return;
+ }
+
+ this.removeOpenListeners();
this._completeResolver([this._button_id, this.value]);
this._button_id = null;
this._complete_promise = undefined;
@@ -406,7 +488,6 @@ export class Et2Dialog extends Et2Widget(ScopedElementsMixin(SlotMixin(LionDialo
{
this._template_widget.clear();
}
- this._overlayCtrl.teardown();
this.remove();
}
}
@@ -468,7 +549,7 @@ export class Et2Dialog extends Et2Widget(ScopedElementsMixin(SlotMixin(LionDialo
{
console.log(e);
}
- this.close();
+ this.hide();
}
/**
@@ -518,15 +599,6 @@ export class Et2Dialog extends Et2Widget(ScopedElementsMixin(SlotMixin(LionDialo
return value;
}
- /**
- * Fire the close-overlay event to use all registered listeners
- * @deprecated
- */
- destroy()
- {
- this._overlayContentNode.dispatchEvent(new Event('close-overlay'));
- }
-
/**
* @deprecated
* @returns {Object}
@@ -555,6 +627,12 @@ export class Et2Dialog extends Et2Widget(ScopedElementsMixin(SlotMixin(LionDialo
return this._template_widget || null;
}
+ get title() : string { return this.label }
+
+ set title(new_title : string)
+ {
+ this.label = new_title;
+ }
updated(changedProperties)
{
@@ -563,6 +641,11 @@ export class Et2Dialog extends Et2Widget(ScopedElementsMixin(SlotMixin(LionDialo
{
this._loadTemplate();
}
+ if(changedProperties.has("buttons"))
+ {
+ render(this._buttonsTemplate(), this);
+ this.requestUpdate();
+ }
}
_loadTemplate()
@@ -571,13 +654,14 @@ export class Et2Dialog extends Et2Widget(ScopedElementsMixin(SlotMixin(LionDialo
{
this._template_widget.clear();
}
+ this._contentNode.replaceChildren();
// Etemplate wants a content
if(typeof this.__value.content === "undefined")
{
this.__value.content = {};
}
- this._template_widget = new etemplate2(this._overlayContentNode._contentNode);
+ this._template_widget = new etemplate2(this._contentNode);
// Fire an event so consumers can do their thing - etemplate will fire its own load event when its done
if(!this.dispatchEvent(new CustomEvent("before-load", {
@@ -631,83 +715,103 @@ export class Et2Dialog extends Et2Widget(ScopedElementsMixin(SlotMixin(LionDialo
this._template_widget.DOMContainer.setAttribute('id', this.__template.replace(/^(.*\/)?([^/]+?)(\.xet)?(\?.*)?$/, '$2').replace(/\./g, '-'));
// Look for buttons after load
- this._overlayContentNode._contentNode.addEventListener("load", this._adoptTemplateButtons);
- }
+ this._contentNode.addEventListener("load", this._adoptTemplateButtons);
- render()
- {
- return this._overlayTemplate();
- }
+ // Default autofocus to first input if autofocus is not set
+ this._contentNode.addEventListener("load", this._setDefaultAutofocus);
- /**
- * Don't allow any children here, pass them on to the content node
- *
- * @param {HTMLElement} node
- * @returns {any}
- */
- appendChild(node : HTMLElement)
- {
- return this._overlayContentNode.appendChild(node);
- }
-
- /**
- * Defining this overlay as a templates from OverlayMixin
- * this is our source to give as .contentNode to OverlayController.
- * @protected
- */
- protected _overlayTemplate()
- {
- return html`
-
-
- ${this.title}
- ${this._contentTemplate()}
-
-
-
- `;
- }
-
- /**
- * @override Configures OverlayMixin
- */
- get _overlayContentNode()
- {
- if(this._cachedOverlayContentNode)
- {
- return this._cachedOverlayContentNode;
- }
- this._cachedOverlayContentNode = /** @type {HTMLElement} */ (
- /** @type {ShadowRoot} */ (this.shadowRoot).querySelector('.dialog__overlay-frame')
- );
- return this._cachedOverlayContentNode;
+ // Need to update to pick up changes
+ this.requestUpdate();
}
_contentTemplate()
{
- if(this.__template)
+ /**
+ * Classes for dialog type options
+ */
+ const _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"
+ ];
+ let icon = this.icon || this.egw().image(_dialogTypes[this.dialogType] || "") || "";
+ let type = _dialogTypes[this.dialogType];
+ let classes = {
+ dialog_content: true,
+ "dialog--has_message": this.message,
+ "dialog--has_template": this.__template
+ };
+ if(type)
{
- return html`
- `;
+ classes[type] = true;
}
- else
+
+ // Add in styles set via property
+ let styles = {};
+ if(this.width)
{
- return html`
-
- ${this.message}
-
- `;
+ styles["--width"] = this.width;
}
+ if(this.height)
+ {
+ styles.height = "--height: " + this.height;
+ }
+
+ return html`
+
+ ${this.__template ? "" :
+ html`
![](${icon}/)
+
${this.message}`
+ }
+
+
`;
+
}
- _getButtons()
+ _buttonsTemplate()
+ {
+ if(!this.buttons)
+ {
+ return;
+ }
+
+ let buttons = this._getButtons();
+ let hasDefault = false;
+ buttons.forEach((button) =>
+ {
+ if(button.default)
+ {
+ hasDefault = true;
+ }
+ })
+
+ // Set button._parent here, otherwise button will have trouble finding our egw()
+ return html`${repeat(buttons, (button : DialogButton) => button.id, (button, index) =>
+ {
+ let isDefault = hasDefault && button.default || !hasDefault && index == 0;
+ return html`
+
+
+ `
+ })}`;
+ }
+
+ _getButtons() : DialogButton[]
{
if(Number.isInteger(this.buttons))
{
@@ -733,7 +837,7 @@ export class Et2Dialog extends Et2Widget(ScopedElementsMixin(SlotMixin(LionDialo
_adoptTemplateButtons()
{
// Check for something with buttons slot set
- let search_in = this._template_widget?.DOMContainer || this._overlayContentNode._contentNode;
+ let search_in = (this._template_widget?.DOMContainer || this._contentNode);
let template_buttons = [
...search_in.querySelectorAll('[slot="buttons"]'),
// Look for a dialog footer, which will contain several buttons and possible other widgets
@@ -745,8 +849,8 @@ export class Et2Dialog extends Et2Widget(ScopedElementsMixin(SlotMixin(LionDialo
{
template_buttons.forEach((button) =>
{
- button.setAttribute("slot", "buttons");
- this._overlayContentNode.appendChild(button);
+ button.setAttribute("slot", "footer");
+ this.appendChild(button);
})
}
// do NOT submit dialog, if it has no etemplate_exec_id, it only gives and error on server-side
@@ -761,36 +865,56 @@ export class Et2Dialog extends Et2Widget(ScopedElementsMixin(SlotMixin(LionDialo
}
/**
- * @override Configures OverlayMixin
- * @desc overrides default configuration options for this component
- * @returns {Object}
+ * Set autofocus on first input element if nothing has autofocus
*/
- _defineOverlayConfig()
+ _setDefaultAutofocus()
{
- let not_modal = {
- hasBackdrop: false,
- preventsScroll: false,
- trapsKeyboardFocus: false,
+ const autofocused = this.querySelector("[autofocus]");
+ if(autofocused)
+ {
+ return;
}
- return {
- ...super._defineOverlayConfig(),
- hidesOnEscape: this.hideOnEscape,
- ...(this.modal ? {} : not_modal)
+ if(this.template)
+ {
+ this.template.focusOnFirstInput();
}
+ else
+ {
+ // Not a template, but maybe something?
+ const $input = jQuery('input:visible,et2-textbox:visible,et2-select-email:visible', this)
+ // Date fields open the calendar popup on focus
+ .not('.et2_date')
+ .filter(function()
+ {
+ // Skip inputs that are out of tab ordering
+ const $this = jQuery(this);
+ return !$this.attr('tabindex') || parseInt($this.attr('tabIndex')) >= 0;
+ }).first();
-
+ // mobile device, focus only if the field is empty (usually means new entry)
+ // should focus always for non-mobile one
+ if(egwIsMobile() && $input.val() == "" || !egwIsMobile())
+ {
+ $input.focus();
+ }
+ }
}
- _setupMoveResize(element)
+ get _contentNode() : HTMLElement
+ {
+ return this.querySelector('.dialog_content');
+ }
+
+ _setupMoveResize()
{
// Quick calculation of min size - dialog is made up of header, content & buttons
let minHeight = 0;
- for(let e of element.shadowRoot.querySelector('.overlay').children)
+ for(let e of this.panel.children)
{
minHeight += e.getBoundingClientRect().height + parseFloat(getComputedStyle(e).marginTop) + parseFloat(getComputedStyle(e).marginBottom)
}
- interact(element)
+ interact(this.panel)
.resizable({
edges: {bottom: true, right: true},
listeners: {
@@ -810,7 +934,8 @@ export class Et2Dialog extends Et2Widget(ScopedElementsMixin(SlotMixin(LionDialo
})
.draggable({
- allowFrom: ".overlay__heading",
+ allowFrom: ".dialog__header",
+ ignoreFrom: ".dialog__close",
listeners: {
move: this._onMoveResize
},
diff --git a/api/js/etemplate/et2_extension_nextmatch_actions.js b/api/js/etemplate/et2_extension_nextmatch_actions.js
index 5a4f3986fc..ab7cc3cb36 100644
--- a/api/js/etemplate/et2_extension_nextmatch_actions.js
+++ b/api/js/etemplate/et2_extension_nextmatch_actions.js
@@ -445,47 +445,50 @@ export function nm_open_popup(_action, _selected)
var popup = document.body.querySelector("et2-dialog[id*='" + _action.id + "_popup']") || document.body.querySelector("#" + (uid || "") + "_" + _action.id + "_popup") || document.body.querySelector("[id*='" + _action.id + "_popup']");
if (popup && popup instanceof Et2Dialog)
{
- popup.open();
+ popup.show();
}
else if (popup)
{
-
-
let dialog = new Et2Dialog();
- dialog.destroy_on_close = false;
+ dialog.destroyOnClose = false;
dialog.id = popup.id;
- popup.setAttribute("id", "_" + popup.id);
+ popup.removeAttribute("id");
+
+ // Set title
+ let title = popup.querySelector(".promptheader")
+ if (title)
+ {
+ title.slot = "label"
+ dialog.appendChild(title);
+ }
+ popup.slot = "";
+
dialog.addEventListener("close", () =>
{
window.nm_popup_action = null;
- })
- dialog.getUpdateComplete().then(() =>
- {
- let title = popup.querySelector(".promptheader")
- if (title)
- {
- title.slot = "heading"
- dialog.appendChild(title);
- }
- popup.slot = "content";
-
- // Move buttons
- popup.querySelectorAll('et2-button').forEach((button) =>
- {
- button.slot = "buttons";
- let button_click = button.onclick;
- button.onclick = (e) =>
- {
- window.nm_popup_action = button_click ? action : null;
- window.nm_popup_ids = selected;
- dialog.close();
-
- return button_click?.apply(button, e.currentTarget);
- };
- dialog.appendChild(button);
- })
- dialog.appendChild(popup);
});
+ // Move buttons
+ popup.querySelectorAll('et2-button').forEach((button, index) =>
+ {
+ button.slot = "footer";
+ if (index == 0)
+ {
+ button.variant = "primary";
+ button.outline = true;
+ }
+ let button_click = button.onclick;
+ button.onclick = (e) =>
+ {
+ window.nm_popup_action = button_click ? action : null;
+ window.nm_popup_ids = selected;
+ dialog.hide();
+
+ return button_click?.apply(button, e.currentTarget);
+ };
+ dialog.appendChild(button);
+ })
+ dialog.appendChild(popup);
+ dialog.requestUpdate();
document.body.appendChild(dialog);
diff --git a/api/js/etemplate/et2_widget_dialog.ts b/api/js/etemplate/et2_widget_dialog.ts
index 7f2d1a0596..c234c97632 100644
--- a/api/js/etemplate/et2_widget_dialog.ts
+++ b/api/js/etemplate/et2_widget_dialog.ts
@@ -88,25 +88,25 @@ export class et2_dialog extends Et2Dialog
return super._getButtons();
}
- _onOpen()
+ handleOpen()
{
- super._onOpen();
+ super.handleOpen();
// move the overlay dialog into appendTo dom since we want it to be shown in that container
- if (this.appendTo)
+ if(this.appendTo)
{
- document.getElementsByClassName(this.appendTo.replace('.',''))[0].appendChild(this._cachedOverlayContentNode);
+ document.getElementsByClassName(this.appendTo.replace('.', ''))[0].appendChild(this._cachedOverlayContentNode);
}
}
- _onClose(ev: PointerEvent)
+ handleClose(ev : PointerEvent)
{
// revert the moved container back to its original position in order to be able to teardown the overlay properly
- if (this.appendTo)
+ if(this.appendTo)
{
document.getElementsByClassName('global-overlays__overlay-container')[0].appendChild(this._cachedOverlayContentNode);
}
- super._onClose(ev);
+ super.handleClose(ev);
}
/**
diff --git a/api/js/jsapi/egw_timer.js b/api/js/jsapi/egw_timer.js
index 24f281b862..9dff3b182f 100644
--- a/api/js/jsapi/egw_timer.js
+++ b/api/js/jsapi/egw_timer.js
@@ -186,7 +186,7 @@ egw.extend('timer', egw.MODULE_GLOBAL, function()
if (!dialog) return;
// disable not matching / available menu-items
- dialog._overlayContentNode.querySelectorAll('et2-button').forEach(button =>
+ dialog.querySelectorAll('et2-button').forEach(button =>
{
if (button.id.substring(0, 7) === 'overall')
{
@@ -261,10 +261,16 @@ egw.extend('timer', egw.MODULE_GLOBAL, function()
// if dialog is open, it shows both timers
if (dialog)
{
- const specific_timer = dialog._overlayContentNode.querySelector('div#_specific_timer');
- const overall_timer = dialog._overlayContentNode.querySelector('div#_overall_timer');
- if (specific_timer) updateTimer(specific_timer, specific);
- if (overall_timer) updateTimer(overall_timer, overall);
+ const specific_timer = dialog.querySelector('div#_specific_timer');
+ const overall_timer = dialog.querySelector('div#_overall_timer');
+ if (specific_timer)
+ {
+ updateTimer(specific_timer, specific);
+ }
+ if (overall_timer)
+ {
+ updateTimer(overall_timer, overall);
+ }
}
}
@@ -356,8 +362,8 @@ egw.extend('timer', egw.MODULE_GLOBAL, function()
// if dialog is shown, update its timer(s) too
if (dialog)
{
- const specific_timer = dialog._overlayContentNode.querySelector('div#_specific_timer');
- const overall_timer = dialog?._overlayContentNode.querySelector('div#_overall_timer');
+ const specific_timer = dialog.querySelector('div#_specific_timer');
+ const overall_timer = dialog?.querySelector('div#_overall_timer');
if (specific_timer && _timer === specific)
{
updateTimer(specific_timer, specific)
@@ -489,8 +495,9 @@ egw.extend('timer', egw.MODULE_GLOBAL, function()
};
// disable not matching / available menu-items
- dialog._overlayContentNode.querySelectorAll('et2-date-time-today').forEach(_widget => {
- const [,timer, action] = _widget.id.match(/times\[([^\]]+)\]\[([^\]]+)\]/);
+ dialog.querySelectorAll('et2-date-time-today').forEach(_widget =>
+ {
+ const [, timer, action] = _widget.id.match(/times\[([^\]]+)\]\[([^\]]+)\]/);
_widget.value = times[timer][action];
});
}
diff --git a/api/templates/default/etemplate2.css b/api/templates/default/etemplate2.css
index 09ad0e141d..ed69397a75 100644
--- a/api/templates/default/etemplate2.css
+++ b/api/templates/default/etemplate2.css
@@ -776,35 +776,18 @@ button.et2_timestamper:hover {
/**
* Dialog widget
- * It uses jQueryUI, so this is just our little bits - icon on left
*/
-.ui-dialog-content .dialog_icon {
+et2-dialog .dialog_icon {
vertical-align: middle;
width: 2em;
+ margin: 0.5em;
}
-.ui-dialog-content {
+et2-dialog .dialog--has_message {
vertical-align: middle;
+ display: flex;
}
-.ui-dialog-content > div {
- white-space: pre-wrap;
- display: inline-block;
- vertical-align: middle;
-}
-
-/* These change button alignment, but it seems the standard is right-aligned for
-action buttons, left aligned for "extra" controls
-.ui-dialog .ui-dialog-buttonpane {
- text-align: left;
-}
-.ui-dialog .ui-dialog-buttonpane .ui-dialog-buttonset {
- float: none;
-}
-.ui-dialog .ui-dialog-buttonpane button {
- float: none;
-}
-*/
/**
* Custom field list
*/