mirror of
https://github.com/EGroupware/egroupware.git
synced 2024-11-21 23:43:17 +01:00
Framework WIP:
- Avatar menu working - App list dropdown in header - Currently open apps in tabs in header
This commit is contained in:
parent
0bcb402b2e
commit
45d905378d
203
kdots/assets/styles/framework.less
Normal file
203
kdots/assets/styles/framework.less
Normal file
@ -0,0 +1,203 @@
|
||||
html, body {
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
overflow: clip;
|
||||
padding: 0px;
|
||||
margin: 0px;
|
||||
}
|
||||
|
||||
egw-framework#egw_fw_basecontainer {
|
||||
--icon-size: 32px;
|
||||
--sl-tooltip-arrow-size: 0;
|
||||
|
||||
/* Internals */
|
||||
|
||||
&::part(status-split) {
|
||||
--max: 150px;
|
||||
}
|
||||
|
||||
&::part(header) {
|
||||
background-color: var(--primary-background-color);
|
||||
color: var(--sl-color-neutral-0);
|
||||
gap: var(--sl-spacing-medium);
|
||||
|
||||
font-size: var(--icon-size);
|
||||
|
||||
}
|
||||
|
||||
/* Content slotted inside */
|
||||
|
||||
[slot="logo"] img {
|
||||
max-width: 220px;
|
||||
max-height: var(--icon-size);
|
||||
display: block;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
sl-icon-button[slot="header"], et2-image[slot="header"] {
|
||||
font-size: var(--icon-size);
|
||||
color: inherit
|
||||
}
|
||||
|
||||
|
||||
div#egw_fw_toggler {
|
||||
position: initial;
|
||||
display: none;
|
||||
}
|
||||
|
||||
#egw_fw_topmenu_info_items {
|
||||
position: relative;
|
||||
order: 99;
|
||||
margin-left: auto;
|
||||
display: flex;
|
||||
|
||||
.topmenu_info_item {
|
||||
min-width: 1em;
|
||||
min-height: 1em;
|
||||
}
|
||||
}
|
||||
|
||||
div#egw_fw_sidebar_r {
|
||||
position: initial;
|
||||
top: initial;
|
||||
}
|
||||
|
||||
.egw_fw_ui_sidemenu_entry_header {
|
||||
display: flex;
|
||||
gap: var(--sl-spacing-medium);
|
||||
padding-left: 1em;
|
||||
}
|
||||
}
|
||||
|
||||
egw-app {
|
||||
&::part(name) {
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
|
||||
/*** HEADER ***/
|
||||
#egw_fw_topmenu_info_items {
|
||||
display: flex !important;
|
||||
flex-direction: row-reverse;
|
||||
height: var(--icon-size);
|
||||
background-color: #fbfbfb;
|
||||
|
||||
& > * {
|
||||
border: none;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
et2-avatar {
|
||||
--size: var(--icon-size);
|
||||
}
|
||||
|
||||
.topmenu_info_item {
|
||||
height: var(--icon-size);
|
||||
width: var(--icon-size);
|
||||
border-left: 1px solid #bfc0bf;
|
||||
display: inline-block;
|
||||
padding-left: 0px;
|
||||
background-size: 20px;
|
||||
background-position: center center;
|
||||
background-repeat: no-repeat;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
background-color: var(--sl-input-background-color-hover);
|
||||
}
|
||||
}
|
||||
|
||||
#topmenu_info_timer {
|
||||
order: 1;
|
||||
position: relative;
|
||||
|
||||
#topmenu_timer {
|
||||
position: relative;
|
||||
top: 10px !important;
|
||||
display: block;
|
||||
height: 45px;
|
||||
width: 45px;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
&:before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-image: url(../../../timesheet/templates/default/images/navbar.svg);
|
||||
background-repeat: no-repeat;
|
||||
background-size: 32px;
|
||||
background-position: center center;
|
||||
filter: opacity(0.3);
|
||||
}
|
||||
}
|
||||
|
||||
ul a#topmenu_cats {
|
||||
background-image: url(../../../api/templates/default/images/topmenu_items/category.svg);
|
||||
}
|
||||
|
||||
ul a#topmenu_password {
|
||||
background-image: url(../../../api/templates/default/images/topmenu_items/password.svg);
|
||||
}
|
||||
|
||||
ul a#topmenu_search {
|
||||
background-image: url(../../../api/templates/default/images/topmenu_items/search.svg);
|
||||
}
|
||||
|
||||
ul a#topmenu_prefs {
|
||||
background-image: url(../../../api/templates/default/images/topmenu_items/setup.svg);
|
||||
}
|
||||
|
||||
ul a#topmenu_home {
|
||||
background-image: url(../../../api/templates/default/images/topmenu_items/home.svg);
|
||||
}
|
||||
|
||||
ul a#topmenu_acl {
|
||||
background-image: url(../../../api/templates/default/images/topmenu_items/access.svg);
|
||||
}
|
||||
|
||||
ul a#topmenu_useraccount {
|
||||
background-image: url(../../../api/templates/default/images/accounts.svg);
|
||||
background-repeat: no-repeat;
|
||||
background-size: 18px;
|
||||
background-position-x: -2px;
|
||||
}
|
||||
|
||||
ul a#topmenu_calls {
|
||||
background-image: url(../../../api/templates/default/images/phone.svg);
|
||||
background-repeat: no-repeat;
|
||||
background-position-x: -2px;
|
||||
}
|
||||
}
|
||||
|
||||
#topmenu_info_logout {
|
||||
background-image: url(../../../api/templates/default/images/logout.svg);
|
||||
|
||||
a {
|
||||
width: 45px;
|
||||
height: 45px;
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
|
||||
#topmenu_info_print_title {
|
||||
background-image: url(../../../api/templates/default/images/print.svg);
|
||||
|
||||
span {
|
||||
width: 45px;
|
||||
height: 45px;
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
|
||||
/*** END HEADER ***/
|
||||
|
||||
div#egw_fw_basecontainer {
|
||||
display: none;
|
||||
}
|
@ -44,13 +44,7 @@
|
||||
</script>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- Fake apps -->
|
||||
<sl-icon-button name="backpack" slot="header" label="Backpack application"></sl-icon-button>
|
||||
<sl-icon-button name="airplane" slot="header" label="Airplaine application"></sl-icon-button>
|
||||
<sl-icon-button name="mortarboard" slot="header" label="Mortarboard application"></sl-icon-button>
|
||||
<et2-image src="mail/navbar" slot="header"></et2-image>
|
||||
|
||||
<!-- status app is looking for this -->
|
||||
<div slot="aside" id="egw_fw_sidebar_r"></div>
|
||||
|
||||
<!-- Fake app -->
|
||||
|
@ -28,7 +28,7 @@ class kdots_framework extends Api\Framework\Ajax
|
||||
protected function _get_header(array $extra = array())
|
||||
{
|
||||
$data = parent::_get_header($extra);
|
||||
$data['applicationlist'] = 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');
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
@ -6,6 +6,8 @@ export default css`
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
position: relative;
|
||||
|
||||
--icon-size: 32px;
|
||||
}
|
||||
|
||||
.egw_fw__layout-default {
|
||||
@ -109,4 +111,35 @@ export default css`
|
||||
.egw_fw__header sl-icon-button {
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.egw_fw__app_list::part(panel) {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(5, 1fr);
|
||||
background-color: var(--sl-color-neutral-0);
|
||||
font-size: var(--icon-size);
|
||||
}
|
||||
|
||||
.egw_fw__open_applications et2-image {
|
||||
height: var(--icon-size);
|
||||
width: var(--icon-size);
|
||||
}
|
||||
|
||||
.egw_fw__open_applications sl-tab::part(base) {
|
||||
padding: 0px;
|
||||
font-size: var(--icon-size);
|
||||
}
|
||||
|
||||
.egw_fw__open_applications sl-tab::part(close-button) {
|
||||
visibility: hidden;
|
||||
margin-inline-start: var(--sl-spacing-2x-small);
|
||||
color: var(--sl-color-neutral-100);
|
||||
}
|
||||
|
||||
.egw_fw__open_applications sl-tab et2-image {
|
||||
padding: var(--sl-spacing-small) var(--sl-spacing-3x-small);
|
||||
}
|
||||
|
||||
.egw_fw__open_applications sl-tab:hover::part(close-button) {
|
||||
visibility: visible;
|
||||
}
|
||||
`
|
@ -3,10 +3,33 @@ 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
|
||||
@ -67,7 +90,7 @@ export class EgwFramework extends LitElement
|
||||
@property()
|
||||
layout = "default";
|
||||
|
||||
@property({type: Array})
|
||||
@property({type: Array, attribute: "application-list"})
|
||||
applicationList = [];
|
||||
|
||||
get egw()
|
||||
@ -79,9 +102,47 @@ export class EgwFramework extends LitElement
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 statusPosition = this.egw?.preference("statusPosition", this.egw?.app_name()) ?? "36";
|
||||
const iconSize = getComputedStyle(this).getPropertyValue("--icon-size");
|
||||
const statusPosition = this.egw?.preference("statusPosition", "common") ?? parseInt(iconSize) ?? "36";
|
||||
|
||||
const classes = {
|
||||
"egw_fw__base": true
|
||||
@ -95,24 +156,26 @@ export class EgwFramework extends LitElement
|
||||
</div>
|
||||
<header class="egw_fw__header" part="header">
|
||||
<slot name="logo"></slot>
|
||||
<sl-dropdown class="egw_fw__app_list">
|
||||
<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) => html`
|
||||
<et2-image src="${app.icon}" aria-label="${app.title}"></et2-image>`)}
|
||||
${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 45px 0px"
|
||||
snap-threshold="40"
|
||||
snap="150px ${iconSize} 0px"
|
||||
snap-threshold="${Math.min(40, parseInt(iconSize) - 5)}"
|
||||
aria-label="Side menu resize">
|
||||
|
||||
<main slot="start" part="main">
|
||||
<slot></slot>
|
||||
</main>
|
||||
|
Loading…
Reference in New Issue
Block a user