mirror of
https://github.com/EGroupware/egroupware.git
synced 2024-11-25 01:13:25 +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>
|
</script>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- status app is looking for this -->
|
||||||
<!-- 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>
|
|
||||||
|
|
||||||
<div slot="aside" id="egw_fw_sidebar_r"></div>
|
<div slot="aside" id="egw_fw_sidebar_r"></div>
|
||||||
|
|
||||||
<!-- Fake app -->
|
<!-- Fake app -->
|
||||||
|
@ -28,7 +28,7 @@ class kdots_framework extends Api\Framework\Ajax
|
|||||||
protected function _get_header(array $extra = array())
|
protected function _get_header(array $extra = array())
|
||||||
{
|
{
|
||||||
$data = parent::_get_header($extra);
|
$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;
|
return $data;
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,8 @@ export default css`
|
|||||||
width: 100vw;
|
width: 100vw;
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
|
--icon-size: 32px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.egw_fw__layout-default {
|
.egw_fw__layout-default {
|
||||||
@ -109,4 +111,35 @@ export default css`
|
|||||||
.egw_fw__header sl-icon-button {
|
.egw_fw__header sl-icon-button {
|
||||||
color: inherit;
|
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 {property} from "lit/decorators/property.js";
|
||||||
import {classMap} from "lit/directives/class-map.js";
|
import {classMap} from "lit/directives/class-map.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 {repeat} from "lit/directives/repeat.js";
|
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')
|
@customElement('egw-framework')
|
||||||
//@ts-ignore
|
//@ts-ignore
|
||||||
export class EgwFramework extends LitElement
|
export class EgwFramework extends LitElement
|
||||||
@ -67,7 +90,7 @@ export class EgwFramework extends LitElement
|
|||||||
@property()
|
@property()
|
||||||
layout = "default";
|
layout = "default";
|
||||||
|
|
||||||
@property({type: Array})
|
@property({type: Array, attribute: "application-list"})
|
||||||
applicationList = [];
|
applicationList = [];
|
||||||
|
|
||||||
get egw()
|
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()
|
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 = {
|
const classes = {
|
||||||
"egw_fw__base": true
|
"egw_fw__base": true
|
||||||
@ -95,24 +156,26 @@ export class EgwFramework extends LitElement
|
|||||||
</div>
|
</div>
|
||||||
<header class="egw_fw__header" part="header">
|
<header class="egw_fw__header" part="header">
|
||||||
<slot name="logo"></slot>
|
<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"
|
<sl-icon-button slot="trigger" name="grid-3x3-gap"
|
||||||
label="${this.egw.lang("Application list")}"
|
label="${this.egw.lang("Application list")}"
|
||||||
aria-description="${this.egw.lang("Activate for a list of applications")}"
|
aria-description="${this.egw.lang("Activate for a list of applications")}"
|
||||||
></sl-icon-button>
|
></sl-icon-button>
|
||||||
${repeat(this.applicationList, (app) => html`
|
${repeat(this.applicationList, (app) => this._applicationListAppTemplate(app))}
|
||||||
<et2-image src="${app.icon}" aria-label="${app.title}"></et2-image>`)}
|
|
||||||
</sl-dropdown>
|
</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"><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>
|
||||||
</header>
|
</header>
|
||||||
<div class="egw_fw__divider">
|
<div class="egw_fw__divider">
|
||||||
<sl-split-panel part="status-split" position-in-pixels="${statusPosition}" primary="end"
|
<sl-split-panel part="status-split" position-in-pixels="${statusPosition}" primary="end"
|
||||||
snap="150px 45px 0px"
|
snap="150px ${iconSize} 0px"
|
||||||
snap-threshold="40"
|
snap-threshold="${Math.min(40, parseInt(iconSize) - 5)}"
|
||||||
aria-label="Side menu resize">
|
aria-label="Side menu resize">
|
||||||
|
|
||||||
<main slot="start" part="main">
|
<main slot="start" part="main">
|
||||||
<slot></slot>
|
<slot></slot>
|
||||||
</main>
|
</main>
|
||||||
|
Loading…
Reference in New Issue
Block a user