mirror of
https://github.com/EGroupware/egroupware.git
synced 2024-12-29 01:59:39 +01:00
Framework WIP:
- Apps loading
This commit is contained in:
parent
c1db022fb8
commit
c6c8de0a3b
@ -48,14 +48,8 @@
|
|||||||
<!-- status app is looking for this -->
|
<!-- status app is looking for this -->
|
||||||
<div slot="aside" id="egw_fw_sidebar_r"></div>
|
<div slot="aside" id="egw_fw_sidebar_r"></div>
|
||||||
|
|
||||||
<!-- Fake app -->
|
<!-- Currently open app -->
|
||||||
<egw-app name="fake app" class="placeholder">
|
<egw-app name="{open_app_name}" url="{open_app_url}" active></egw-app>
|
||||||
<div style="border: 1px dotted">Something inside the app - main</div>
|
|
||||||
<div slot="left">Left content</div>
|
|
||||||
<div slot="right">right content</div>
|
|
||||||
<div slot="right-header">
|
|
||||||
</div>
|
|
||||||
</egw-app>
|
|
||||||
</egw-framework>
|
</egw-framework>
|
||||||
|
|
||||||
|
|
||||||
|
@ -29,7 +29,12 @@ class kdots_framework extends Api\Framework\Ajax
|
|||||||
{
|
{
|
||||||
$data = parent::_get_header($extra);
|
$data = parent::_get_header($extra);
|
||||||
$data['application-list'] = htmlentities(json_encode($extra['navbar-apps'], JSON_HEX_QUOT | JSON_HEX_AMP), ENT_QUOTES, 'UTF-8');
|
$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;
|
return $data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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) {
|
.egw_fw__open_applications sl-tab:hover::part(close-button), .egw_fw__open_applications sl-tab[active]::part(close-button) {
|
||||||
visibility: visible;
|
visibility: visible;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
::slotted(egw-app) {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
::slotted(egw-app[active]) {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
`
|
`
|
@ -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 {customElement} from "lit/decorators/custom-element.js";
|
||||||
import {property} from "lit/decorators/property.js";
|
import {property} from "lit/decorators/property.js";
|
||||||
import {classMap} from "lit/directives/class-map.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 "@shoelace-style/shoelace/dist/components/split-panel/split-panel.js";
|
||||||
import styles from "./EgwFramework.styles";
|
import styles from "./EgwFramework.styles";
|
||||||
import {egw} from "../../api/js/jsapi/egw_global";
|
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";
|
import {EgwFrameworkApp} from "./EgwFrameworkApp";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -93,9 +93,16 @@ export class EgwFramework extends LitElement
|
|||||||
@property()
|
@property()
|
||||||
layout = "default";
|
layout = "default";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is the list of all applications we know about
|
||||||
|
*
|
||||||
|
* @type {any[]}
|
||||||
|
*/
|
||||||
@property({type: Array, attribute: "application-list"})
|
@property({type: Array, attribute: "application-list"})
|
||||||
applicationList = [];
|
applicationList = [];
|
||||||
|
|
||||||
|
private get tabs() : SlTabGroup { return this.shadowRoot.querySelector("sl-tab-group");}
|
||||||
|
|
||||||
get egw() : typeof egw
|
get egw() : typeof egw
|
||||||
{
|
{
|
||||||
return window.egw ?? <typeof egw>{
|
return window.egw ?? <typeof egw>{
|
||||||
@ -131,16 +138,30 @@ export class EgwFramework extends LitElement
|
|||||||
(menuaction ? '.' + menuaction[1] : '');
|
(menuaction ? '.' + menuaction[1] : '');
|
||||||
};
|
};
|
||||||
|
|
||||||
public loadApp(appname)
|
public loadApp(appname, active = false)
|
||||||
{
|
{
|
||||||
const app = this.applicationList.find(a => a.name == appname);
|
const app = this.applicationList.find(a => a.name == appname);
|
||||||
let appComponent = <EgwFrameworkApp>document.createElement("egw-app");
|
let appComponent = <EgwFrameworkApp>document.createElement("egw-app");
|
||||||
appComponent.id = appname;
|
appComponent.id = appname;
|
||||||
appComponent.name = appname;
|
appComponent.name = appname;
|
||||||
appComponent.url = app?.url;
|
appComponent.url = app?.url;
|
||||||
|
|
||||||
this.append(appComponent);
|
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");
|
this.requestUpdate("applicationList");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait until new tab is there to activate it
|
||||||
|
if(active)
|
||||||
|
{
|
||||||
|
this.updateComplete.then(() =>
|
||||||
|
{
|
||||||
|
this.tabs.show(appname);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
return appComponent;
|
return appComponent;
|
||||||
}
|
}
|
||||||
@ -214,17 +235,28 @@ export class EgwFramework extends LitElement
|
|||||||
*/
|
*/
|
||||||
protected _applicationListAppTemplate(app)
|
protected _applicationListAppTemplate(app)
|
||||||
{
|
{
|
||||||
|
if(app.status !== "1")
|
||||||
|
{
|
||||||
|
return nothing;
|
||||||
|
}
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<sl-tooltip placement="bottom" role="menuitem" content="${app.title}">
|
<sl-tooltip placement="bottom" role="menuitem" content="${app.title}">
|
||||||
<et2-button-icon src="${app.icon}" aria-label="${app.title}" role="menuitem" noSubmit
|
<et2-button-icon src="${app.icon}" aria-label="${app.title}" role="menuitem" noSubmit
|
||||||
helptext="${app.title}"></et2-button-icon>
|
helptext="${app.title}"
|
||||||
|
@click=${() =>
|
||||||
|
{
|
||||||
|
this.loadApp(app.name, true);
|
||||||
|
(<SlDropdown>this.shadowRoot.querySelector(".egw_fw__app_list")).hide();
|
||||||
|
}}
|
||||||
|
></et2-button-icon>
|
||||||
</sl-tooltip>`;
|
</sl-tooltip>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected _applicationTabTemplate(app)
|
protected _applicationTabTemplate(app)
|
||||||
{
|
{
|
||||||
return html`
|
return html`
|
||||||
<sl-tab slot="nav" panel="${app.name}" closable aria-label="${app.title}">
|
<sl-tab slot="nav" panel="${app.name}" closable aria-label="${app.title}" ?active=${app.active}>
|
||||||
<sl-tooltip placement="bottom" content="${app.title}" hoist>
|
<sl-tooltip placement="bottom" content="${app.title}" hoist>
|
||||||
<et2-image src="${app.icon}"></et2-image>
|
<et2-image src="${app.icon}"></et2-image>
|
||||||
</sl-tooltip>
|
</sl-tooltip>
|
||||||
@ -260,7 +292,9 @@ export class EgwFramework extends LitElement
|
|||||||
@sl-tab-show=${this.handleApplicationTabShow}
|
@sl-tab-show=${this.handleApplicationTabShow}
|
||||||
@sl-close=${this.handleApplicationTabClose}
|
@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))}
|
||||||
</sl-tab-group>
|
</sl-tab-group>
|
||||||
<slot name="header"><span class="placeholder">header</span></slot>
|
<slot name="header"><span class="placeholder">header</span></slot>
|
||||||
<slot name="header-right"><span class="placeholder">header-right</span></slot>
|
<slot name="header-right"><span class="placeholder">header-right</span></slot>
|
||||||
|
@ -122,6 +122,20 @@ export default css`
|
|||||||
overflow: hidden;
|
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) {
|
@media (min-width: 600px) {
|
||||||
.egw_fw_app__main {
|
.egw_fw_app__main {
|
||||||
grid-template-columns: [start left] min-content [ main] 1fr [right] min-content [end];
|
grid-template-columns: [start left] min-content [ main] 1fr [right] min-content [end];
|
||||||
|
@ -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 {customElement} from "lit/decorators/custom-element.js";
|
||||||
import {property} from "lit/decorators/property.js";
|
import {property} from "lit/decorators/property.js";
|
||||||
import {state} from "lit/decorators/state.js";
|
import {state} from "lit/decorators/state.js";
|
||||||
import {classMap} from "lit/directives/class-map.js";
|
import {classMap} from "lit/directives/class-map.js";
|
||||||
|
import {unsafeHTML} from "lit/directives/unsafe-html.js";
|
||||||
|
|
||||||
import styles from "./EgwFrameworkApp.styles";
|
import styles from "./EgwFrameworkApp.styles";
|
||||||
import {SlSplitPanel} from "@shoelace-style/shoelace";
|
import {SlSplitPanel} from "@shoelace-style/shoelace";
|
||||||
@ -138,15 +139,11 @@ export class EgwFrameworkApp extends LitElement
|
|||||||
{
|
{
|
||||||
this.rightPanelInfo.preferenceWidth = parseInt(width) ?? this.rightPanelInfo.defaultWidth;
|
this.rightPanelInfo.preferenceWidth = parseInt(width) ?? this.rightPanelInfo.defaultWidth;
|
||||||
});
|
});
|
||||||
|
|
||||||
// Register the "data" plugin
|
|
||||||
this.egw.registerJSONPlugin(this.jsonDataHandler, this, 'data');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
disconnectedCallback()
|
disconnectedCallback()
|
||||||
{
|
{
|
||||||
super.disconnectedCallback();
|
super.disconnectedCallback();
|
||||||
this.egw.unregisterJSONPlugin(this.jsonDataHandler, this, "data", false)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
firstUpdated()
|
firstUpdated()
|
||||||
@ -186,16 +183,11 @@ export class EgwFrameworkApp extends LitElement
|
|||||||
return this.loadingPromise = this.egw.request(
|
return this.loadingPromise = this.egw.request(
|
||||||
this.framework.getMenuaction('ajax_exec', targetUrl, this.name),
|
this.framework.getMenuaction('ajax_exec', targetUrl, this.name),
|
||||||
[targetUrl]
|
[targetUrl]
|
||||||
);
|
).then((data : string[]) =>
|
||||||
}
|
|
||||||
|
|
||||||
protected jsonDataHandler(type, res, req)
|
|
||||||
{
|
|
||||||
if(req.context !== this)
|
|
||||||
{
|
{
|
||||||
return;
|
// Load request returns HTML. Shove it in.
|
||||||
}
|
render(html`${unsafeHTML(data.join(""))}`, this);
|
||||||
debugger;
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public showLeft()
|
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`
|
||||||
|
<div class="egw_fw_app__loading">
|
||||||
|
<sl-spinner></sl-spinner>
|
||||||
|
</div>`;
|
||||||
|
}
|
||||||
|
|
||||||
protected _asideTemplate(parentSlot, side, label?)
|
protected _asideTemplate(parentSlot, side, label?)
|
||||||
{
|
{
|
||||||
const asideClassMap = classMap({
|
const asideClassMap = classMap({
|
||||||
@ -367,7 +373,7 @@ export class EgwFrameworkApp extends LitElement
|
|||||||
</header>
|
</header>
|
||||||
<div slot="start" class="egw_fw_app__main_content content" part="content"
|
<div slot="start" class="egw_fw_app__main_content content" part="content"
|
||||||
aria-label="${this.name}" tabindex="0">
|
aria-label="${this.name}" tabindex="0">
|
||||||
<slot><span class="placeholder">main</span></slot>
|
<slot>${this._loadingTemplate()}<span class="placeholder">main</span></slot>
|
||||||
</div>
|
</div>
|
||||||
<footer slot="start" class="egw_fw_app__footer footer" part="footer">
|
<footer slot="start" class="egw_fw_app__footer footer" part="footer">
|
||||||
<slot name="footer"><span class="placeholder">main-footer</span></slot>
|
<slot name="footer"><span class="placeholder">main-footer</span></slot>
|
||||||
|
Loading…
Reference in New Issue
Block a user