From c6c8de0a3b4eac800140c89d10eee8610ecea520 Mon Sep 17 00:00:00 2001 From: nathan Date: Wed, 8 May 2024 13:46:09 -0600 Subject: [PATCH] Framework WIP: - Apps loading --- kdots/head.tpl | 10 ++---- kdots/inc/class.kdots_framework.inc.php | 7 +++- kdots/js/EgwFramework.styles.ts | 8 +++++ kdots/js/EgwFramework.ts | 48 +++++++++++++++++++++---- kdots/js/EgwFrameworkApp.styles.ts | 14 ++++++++ kdots/js/EgwFrameworkApp.ts | 36 +++++++++++-------- 6 files changed, 92 insertions(+), 31 deletions(-) diff --git a/kdots/head.tpl b/kdots/head.tpl index c9e37ae1e9..17086968d6 100644 --- a/kdots/head.tpl +++ b/kdots/head.tpl @@ -48,14 +48,8 @@
- - -
Something inside the app - main
-
Left content
-
right content
-
-
-
+ + diff --git a/kdots/inc/class.kdots_framework.inc.php b/kdots/inc/class.kdots_framework.inc.php index 472007c2cb..fa7fa73d7e 100644 --- a/kdots/inc/class.kdots_framework.inc.php +++ b/kdots/inc/class.kdots_framework.inc.php @@ -29,7 +29,12 @@ class kdots_framework extends Api\Framework\Ajax { $data = parent::_get_header($extra); $data['application-list'] = htmlentities(json_encode($extra['navbar-apps'], JSON_HEX_QUOT | JSON_HEX_AMP), ENT_QUOTES, 'UTF-8'); - + $open_app = current(array_filter($extra['navbar-apps'], function ($app) + { + return $app['active'] ?? false; + })) ?? []; + $data['open_app_name'] = $open_app['name']; + $data['open_app_url'] = $open_app['url']; return $data; } diff --git a/kdots/js/EgwFramework.styles.ts b/kdots/js/EgwFramework.styles.ts index 2f7babde8f..67371a7892 100644 --- a/kdots/js/EgwFramework.styles.ts +++ b/kdots/js/EgwFramework.styles.ts @@ -131,4 +131,12 @@ export default css` .egw_fw__open_applications sl-tab:hover::part(close-button), .egw_fw__open_applications sl-tab[active]::part(close-button) { visibility: visible; } + + ::slotted(egw-app) { + display: none; + } + + ::slotted(egw-app[active]) { + display: flex; + } ` \ No newline at end of file diff --git a/kdots/js/EgwFramework.ts b/kdots/js/EgwFramework.ts index 8b956e7c2a..5833295572 100644 --- a/kdots/js/EgwFramework.ts +++ b/kdots/js/EgwFramework.ts @@ -1,4 +1,4 @@ -import {css, html, LitElement} from "lit"; +import {css, html, LitElement, nothing} from "lit"; import {customElement} from "lit/decorators/custom-element.js"; import {property} from "lit/decorators/property.js"; import {classMap} from "lit/directives/class-map.js"; @@ -6,7 +6,7 @@ import {repeat} from "lit/directives/repeat.js"; import "@shoelace-style/shoelace/dist/components/split-panel/split-panel.js"; import styles from "./EgwFramework.styles"; import {egw} from "../../api/js/jsapi/egw_global"; -import {SlTab, SlTabGroup} from "@shoelace-style/shoelace"; +import {SlDropdown, SlTab, SlTabGroup} from "@shoelace-style/shoelace"; import {EgwFrameworkApp} from "./EgwFrameworkApp"; /** @@ -93,9 +93,16 @@ export class EgwFramework extends LitElement @property() layout = "default"; + /** + * This is the list of all applications we know about + * + * @type {any[]} + */ @property({type: Array, attribute: "application-list"}) applicationList = []; + private get tabs() : SlTabGroup { return this.shadowRoot.querySelector("sl-tab-group");} + get egw() : typeof egw { return window.egw ?? { @@ -131,16 +138,30 @@ export class EgwFramework extends LitElement (menuaction ? '.' + menuaction[1] : ''); }; - public loadApp(appname) + public loadApp(appname, active = false) { const app = this.applicationList.find(a => a.name == appname); let appComponent = document.createElement("egw-app"); appComponent.id = appname; appComponent.name = appname; appComponent.url = app?.url; + this.append(appComponent); - app.opened = this.shadowRoot.querySelectorAll("sl-tab").length; + // App was not in the tab list + if(typeof app.opened == "undefined") + { + app.opened = this.shadowRoot.querySelectorAll("sl-tab").length; this.requestUpdate("applicationList"); + } + + // Wait until new tab is there to activate it + if(active) + { + this.updateComplete.then(() => + { + this.tabs.show(appname); + }) + } return appComponent; } @@ -214,17 +235,28 @@ export class EgwFramework extends LitElement */ protected _applicationListAppTemplate(app) { + if(app.status !== "1") + { + return nothing; + } + return html` + helptext="${app.title}" + @click=${() => + { + this.loadApp(app.name, true); + (this.shadowRoot.querySelector(".egw_fw__app_list")).hide(); + }} + > `; } protected _applicationTabTemplate(app) { return html` - + @@ -260,7 +292,9 @@ export class EgwFramework extends LitElement @sl-tab-show=${this.handleApplicationTabShow} @sl-close=${this.handleApplicationTabClose} > - ${repeat(this.applicationList.filter(app => app.opened).sort((a, b) => a.opened - b.opened), (app) => this._applicationTabTemplate(app))} + ${repeat(this.applicationList + .filter(app => typeof app.opened !== "undefined") + .sort((a, b) => a.opened - b.opened), (app) => this._applicationTabTemplate(app))} header header-right diff --git a/kdots/js/EgwFrameworkApp.styles.ts b/kdots/js/EgwFrameworkApp.styles.ts index 061c13d692..6b3385442f 100644 --- a/kdots/js/EgwFrameworkApp.styles.ts +++ b/kdots/js/EgwFrameworkApp.styles.ts @@ -122,6 +122,20 @@ export default css` overflow: hidden; } + .egw_fw_app { + --application-color: var(--primary-background-color); + } + + .egw_fw_app__loading { + text-align: center; + + sl-spinner { + --track-width: 1rem; + font-size: 10rem; + --indicator-color: var(--application-color, var(--primary-background-color, var(--sl-color-primary-600))); + } + } + @media (min-width: 600px) { .egw_fw_app__main { grid-template-columns: [start left] min-content [ main] 1fr [right] min-content [end]; diff --git a/kdots/js/EgwFrameworkApp.ts b/kdots/js/EgwFrameworkApp.ts index 0dbf6c4466..12fa7e72d2 100644 --- a/kdots/js/EgwFrameworkApp.ts +++ b/kdots/js/EgwFrameworkApp.ts @@ -1,8 +1,9 @@ -import {css, html, LitElement, nothing} from "lit"; +import {css, html, LitElement, nothing, render} from "lit"; import {customElement} from "lit/decorators/custom-element.js"; import {property} from "lit/decorators/property.js"; import {state} from "lit/decorators/state.js"; import {classMap} from "lit/directives/class-map.js"; +import {unsafeHTML} from "lit/directives/unsafe-html.js"; import styles from "./EgwFrameworkApp.styles"; import {SlSplitPanel} from "@shoelace-style/shoelace"; @@ -138,15 +139,11 @@ export class EgwFrameworkApp extends LitElement { this.rightPanelInfo.preferenceWidth = parseInt(width) ?? this.rightPanelInfo.defaultWidth; }); - - // Register the "data" plugin - this.egw.registerJSONPlugin(this.jsonDataHandler, this, 'data'); } disconnectedCallback() { super.disconnectedCallback(); - this.egw.unregisterJSONPlugin(this.jsonDataHandler, this, "data", false) } firstUpdated() @@ -186,16 +183,11 @@ export class EgwFrameworkApp extends LitElement return this.loadingPromise = this.egw.request( this.framework.getMenuaction('ajax_exec', targetUrl, this.name), [targetUrl] - ); - } - - protected jsonDataHandler(type, res, req) - { - if(req.context !== this) + ).then((data : string[]) => { - return; - } - debugger; + // Load request returns HTML. Shove it in. + render(html`${unsafeHTML(data.join(""))}`, this); + }); } public showLeft() @@ -275,6 +267,20 @@ export class EgwFrameworkApp extends LitElement } } + /** + * Displayed for the time between when the application is added and when the server responds with content + * + * @returns {TemplateResult<1>} + * @protected + */ + protected _loadingTemplate() + { + return html` +
+ +
`; + } + protected _asideTemplate(parentSlot, side, label?) { const asideClassMap = classMap({ @@ -367,7 +373,7 @@ export class EgwFrameworkApp extends LitElement
- main + ${this._loadingTemplate()}main