egroupware_official/kdots/js/EgwFramework.ts
nathan 0f4ee3790a Framework WIP:
Polish on app framework position & styles
2024-04-26 10:13:30 -06:00

194 lines
6.4 KiB
TypeScript

import {css, html, LitElement} 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";
import "@shoelace-style/shoelace/dist/components/split-panel/split-panel.js";
import styles from "./EgwFramework.styles";
import {repeat} from "lit/directives/repeat.js";
/**
* @summary Accessable, webComponent-based EGroupware framework
*
* @dependency sl-dropdown
* @dependency sl-icon-button
*
* @slot - Current application
* @slot banner - Very top, used for things like persistant, system wide messages. Normally hidden.
* @slot header - Top of page, contains logo, app icons.
* @slot header-right - Top right, contains user info / actions.
* @slot status - Home of the status app, it is limited in size and can be resized and hidden.
* @slot footer - Very bottom. Normally hidden.
* *
* @csspart base - Wraps it all.
* @csspart banner -
* @csspart header -
* @csspart open-applications - Tab group that has the currently open applications
* @csspart status-split - Status splitter
* @csspart main
* @csspart status
* @csspart footer
*
* @cssproperty [--icon-size=32] - Height of icons used in the framework
*/
@customElement('egw-framework')
//@ts-ignore
export class EgwFramework extends LitElement
{
static get styles()
{
return [
styles,
// TEMP STUFF
css`
:host .placeholder {
display: none;
}
:host(.placeholder) .placeholder {
width: 100%;
display: block;
font-size: 200%;
text-align: center;
background-color: var(--placeholder-background-color, silver);
}
.placeholder:after {
content: " (placeholder)";
}
.egw_fw__base {
--placeholder-background-color: #75bd20;
}
.egw_fw__status .placeholder {
writing-mode: vertical-rl;
text-orientation: mixed;
height: 100%;
}
:host(.placeholder) [class*="left"] .placeholder {
background-color: color-mix(in lch, var(--placeholder-background-color), rgba(.5, .5, 1, .5));
}
:host(.placeholder) [class*="right"] .placeholder {
background-color: color-mix(in lch, var(--placeholder-background-color), rgba(.5, 1, .5, .5));
}
:host(.placeholder) [class*="footer"] .placeholder {
background-color: color-mix(in lch, var(--placeholder-background-color), rgba(1, 1, 1, .05));
}
::slotted(div#egw_fw_sidebar_r) {
position: relative;
}
`
];
}
@property()
layout = "default";
@property({type: Array, attribute: "application-list"})
applicationList = [];
get egw()
{
return window.egw ?? {
app_name: () => "",
lang: (t) => t,
preference: (n, app) => ""
};
}
/**
* An application tab is chosen, show the app
*
* @param e
* @protected
*/
protected handleApplicationTabShow(e)
{
}
/**
* Renders one application into the 9-dots application menu
*
* @param app
* @returns {TemplateResult<1>}
* @protected
*/
protected _applicationListAppTemplate(app)
{
return html`
<sl-tooltip placement="bottom" role="menuitem" content="${app.title}">
<et2-button-icon src="${app.icon}" aria-label="${app.title}" role="menuitem" noSubmit
helptext="${app.title}"></et2-button-icon>
</sl-tooltip>`;
}
protected _applicationTabTemplate(app)
{
return html`
<sl-tab slot="nav" panel="${app.app}" closable aria-label="${app.title}">
<sl-tooltip placement="bottom" content="${app.title}" hoist>
<et2-image src="${app.icon}"></et2-image>
</sl-tooltip>
</sl-tab>`;
}
render()
{
const iconSize = getComputedStyle(this).getPropertyValue("--icon-size");
const statusPosition = this.egw?.preference("statusPosition", "common") ?? parseInt(iconSize) ?? "36";
const classes = {
"egw_fw__base": true
}
classes[`egw_fw__layout-${this.layout}`] = true;
return html`
<div class=${classMap(classes)} part="base">
<div class="egw_fw__banner" part="banner" role="banner">
<slot name="banner"><span class="placeholder">Banner</span></slot>
</div>
<header class="egw_fw__header" part="header">
<slot name="logo"></slot>
<sl-dropdown class="egw_fw__app_list" role="menu">
<sl-icon-button slot="trigger" name="grid-3x3-gap"
label="${this.egw.lang("Application list")}"
aria-description="${this.egw.lang("Activate for a list of applications")}"
></sl-icon-button>
${repeat(this.applicationList, (app) => this._applicationListAppTemplate(app))}
</sl-dropdown>
<sl-tab-group part="open-applications" class="egw_fw__open_applications" activation="manual"
role="tablist"
@sl-tab-show=${this.handleApplicationTabShow}>
${repeat(this.applicationList.filter(app => app.opened), (app) => this._applicationTabTemplate(app))}
</sl-tab-group>
<slot name="header"><span class="placeholder">header</span></slot>
<slot name="header-right"><span class="placeholder">header-right</span></slot>
</header>
<div class="egw_fw__divider">
<sl-split-panel part="status-split" position-in-pixels="${statusPosition}" primary="end"
snap="150px ${iconSize} 0px"
snap-threshold="${Math.min(40, parseInt(iconSize) - 5)}"
aria-label="Side menu resize">
<main slot="start" part="main" class="egw_fw__main">
<slot></slot>
</main>
<sl-icon slot="divider" name="grip-vertical"></sl-icon>
<aside slot="end" class="egw_fw__status" part="status">
<slot name="status"><span class="placeholder">status</span></slot>
</aside>
</sl-split-panel>
</div>
<footer class="egw_fw__footer" part="footer">
<slot name="footer"><span class="placeholder">footer</span></slot>
</footer>
</div>
`;
}
}