diff --git a/kdots/assets/styles/framework.less b/kdots/assets/styles/framework.less
new file mode 100644
index 0000000000..9476b081b0
--- /dev/null
+++ b/kdots/assets/styles/framework.less
@@ -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;
+}
\ No newline at end of file
diff --git a/kdots/head.tpl b/kdots/head.tpl
index 565df81d30..09dc273c55 100644
--- a/kdots/head.tpl
+++ b/kdots/head.tpl
@@ -44,13 +44,7 @@
-
-
-
-
-
-
-
+
diff --git a/kdots/inc/class.kdots_framework.inc.php b/kdots/inc/class.kdots_framework.inc.php
index c37276236d..5fb0eff7e4 100644
--- a/kdots/inc/class.kdots_framework.inc.php
+++ b/kdots/inc/class.kdots_framework.inc.php
@@ -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;
}
diff --git a/kdots/js/EgwFramework.styles.ts b/kdots/js/EgwFramework.styles.ts
index bdaa01a017..a4e3d206b4 100644
--- a/kdots/js/EgwFramework.styles.ts
+++ b/kdots/js/EgwFramework.styles.ts
@@ -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;
+ }
`
\ No newline at end of file
diff --git a/kdots/js/EgwFramework.ts b/kdots/js/EgwFramework.ts
index 06e45e5779..e49d91d442 100644
--- a/kdots/js/EgwFramework.ts
+++ b/kdots/js/EgwFramework.ts
@@ -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`
+
+
+ `;
+ }
+
+ protected _applicationTabTemplate(app)
+ {
+ return html`
+
+
+
+
+ `;
+ }
+
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
-