From fbcffecabef2bc6eab5eb504844efe08b1df8924 Mon Sep 17 00:00:00 2001
From: "michael.danielczok" <michael@pcsg.de>
Date: Sun, 14 Nov 2021 22:14:48 +0100
Subject: [PATCH] feat: new control NavTabsVertical.

---
 bin/Controls/NavTabs.js                   |  11 +--
 bin/Controls/NavTabsVerticalSettings.css  | 113 ++++++++++++++++++++++
 bin/Controls/NavTabsVerticalSettings.html |  22 +++++
 bin/Controls/NavTabsVerticalSettings.js   | 107 ++++++++++++++++++++
 bricks.xml                                |  54 +++++++++++
 locale.xml                                |  74 ++++++++++++++
 src/QUI/Menu/NavTabs.html                 |   8 +-
 src/QUI/Menu/NavTabsVertical.css          | 111 +++++++++++++++++++++
 src/QUI/Menu/NavTabsVertical.html         |  78 +++++++++++++++
 src/QUI/Menu/NavTabsVertical.php          |  68 +++++++++++++
 10 files changed, 636 insertions(+), 10 deletions(-)
 create mode 100644 bin/Controls/NavTabsVerticalSettings.css
 create mode 100644 bin/Controls/NavTabsVerticalSettings.html
 create mode 100644 bin/Controls/NavTabsVerticalSettings.js
 create mode 100644 src/QUI/Menu/NavTabsVertical.css
 create mode 100644 src/QUI/Menu/NavTabsVertical.html
 create mode 100644 src/QUI/Menu/NavTabsVertical.php

diff --git a/bin/Controls/NavTabs.js b/bin/Controls/NavTabs.js
index 16a857c..14cabf0 100644
--- a/bin/Controls/NavTabs.js
+++ b/bin/Controls/NavTabs.js
@@ -46,9 +46,9 @@ define('package/quiqqer/menu/bin/Controls/NavTabs', [
             var Elm  = this.getElm(),
                 self = this;
 
-            this.navTabs             = Elm.getElements('.quiqqer-menu-navTabs-tabs-item');
-            this.navContents         = Elm.getElements('.quiqqer-menu-navTabs-content-item');
-            this.NavContentContainer = Elm.getElement('.quiqqer-menu-navTabs-content');
+            this.navTabs             = Elm.getElements('.quiqqer-tab-nav-item');
+            this.navContents         = Elm.getElements('.quiqqer-tab-content-item');
+            this.NavContentContainer = Elm.getElement('.quiqqer-tab-content');
 
             if (!this.navTabs || !this.navContents) {
                 return;
@@ -63,8 +63,8 @@ define('package/quiqqer/menu/bin/Controls/NavTabs', [
                 }
             }
 
-            this.ActiveNavTab  = Elm.getElement('.quiqqer-menu-navTabs-tabs-item.active');
-            this.ActiveContent = Elm.getElement('.quiqqer-menu-navTabs-content-item.active');
+            this.ActiveNavTab  = Elm.getElement('.quiqqer-tab-nav-item.active');
+            this.ActiveContent = Elm.getElement('.quiqqer-tab-content-item.active');
 
             this.navTabs.addEvent('click', function (event) {
                 event.stop();
@@ -131,7 +131,6 @@ define('package/quiqqer/menu/bin/Controls/NavTabs', [
                 ])
             }).then(function () {
                 self.clicked = false;
-                console.log(TabContent)
                 self.NavContentContainer.setStyle('height', null);
             })
         },
diff --git a/bin/Controls/NavTabsVerticalSettings.css b/bin/Controls/NavTabsVerticalSettings.css
new file mode 100644
index 0000000..a06d3a4
--- /dev/null
+++ b/bin/Controls/NavTabsVerticalSettings.css
@@ -0,0 +1,113 @@
+/*.field-container-item {
+    flex-shrink: 0;
+}*/
+
+.qui-controls-formlist-navTabsVerticalSettings {
+    border: 1px solid #dedede;
+    border-left: none;
+    display: flex;
+    flex-direction: column;
+    flex-grow: 1;
+    padding: 15px;
+}
+
+.qui-controls-formlist-navTabsVerticalSettings .qui-controls-formlist-buttons {
+    order: 2;
+}
+
+.qui-controls-formlist-navTabsVerticalSettings .qui-controls-formlist-container {
+    display: flex;
+    flex-wrap: wrap;
+    margin-right: -15px;
+    order: 1;
+}
+
+.qui-controls-formlist-navTabsVerticalSettings .qui-controls-formlist-container:after {
+    content: '';
+    flex-basis: 450px;
+    flex-grow: 1;
+    height: 0;
+    margin-right: 20px;
+    order: 2;
+    position: relative;
+    visibility: hidden;
+}
+
+.qui-controls-formlist-navTabsVerticalSettings .qui-controls-formlist-entry {
+    background: #e8edff;
+    border-bottom: none;
+    flex-basis: 450px;
+    flex-grow: 1;
+    margin-bottom: 20px;
+    margin-right: 20px;
+    padding: 20px;
+    position: relative;
+}
+
+.qui-controls-formlist-navTabsVerticalSettings .qui-controls-formlist-entry-delete {
+    padding: 0 !important;
+    position: absolute;
+    right: 0;
+    top: 0;
+}
+
+.qui-controls-formlist-accordion .qui-controls-formlist-entry-delete .qui-button {
+    background: none;
+    border: none;
+    border-radius: 0;
+    box-shadow: none;
+    color: #333;
+}
+
+.qui-controls-formlist-navTabsVerticalSettings .qui-controls-formlist-entry-delete .qui-button:hover {
+    background: #a6abbd;
+}
+
+
+.qui-controls-formlist-navTabsVerticalSettings .qui-controls-formlist-entry-data {
+    padding: 0;
+    width: 100%;
+}
+
+.quiqqer-menu-navTabsVerticalSettings-entry label {
+    margin-bottom: 10px;
+    width: 100% !important;
+}
+
+.quiqqer-menu-navTabsVerticalSettings-entry label:last-child {
+    margin-bottom: 0;
+}
+
+.quiqqer-menu-navTabsVerticalSettings-entry span.entry-title {
+    display: block;
+    margin-bottom: 6px;
+}
+
+.quiqqer-menu-navTabsVerticalSettings-entry input,
+.quiqqer-menu-navTabsVerticalSettings-entry textarea {
+    max-width: 100%;
+    width: 100%;
+}
+
+.quiqqer-menu-navTabsVerticalSettings-entry .field-container-field {
+    border: none;
+    padding: 0;
+}
+
+.quiqqer-menu-navTabsVerticalSettings-entry .control-editor-preview {
+    background: #fff;
+    height: 120px !important;
+}
+
+.quiqqer-menu-navTabsVerticalSettings-entry .qui-controls-project-site-input {
+    display: flex;
+}
+
+.quiqqer-menu-navTabsVerticalSettings-entry .qui-controls-colorpicker {
+    width: 100%;
+    display: flex;
+}
+
+.quiqqer-menu-navTabsVerticalSettings-entry .qui-controls-colorpicker-colorContainer {
+    flex-grow: 1;
+}
\ No newline at end of file
diff --git a/bin/Controls/NavTabsVerticalSettings.html b/bin/Controls/NavTabsVerticalSettings.html
new file mode 100644
index 0000000..79b6f59
--- /dev/null
+++ b/bin/Controls/NavTabsVerticalSettings.html
@@ -0,0 +1,22 @@
+<div class="quiqqer-menu-navTabsVerticalSettings-entry" style="display: none;">
+    <label class="entry-title">
+        <span class="entry-title">{{titleIcon}}</span>
+        <input class="media-image hide-select-image-button" data-qui-options-cssclasses="1"
+               name="titleIcon" />
+    </label>
+    <label>
+        <span class="entry-title">{{title}}</span>
+        <input type="text"
+               name="title" />
+    </label>
+    <label class="entry-title">
+        <span class="entry-title">{{image}}</span>
+        <input class="media-image" data-qui-options-selectable_types="image"
+               name="image"/>
+    </label>
+    <label>
+        <span class="entry-title">{{content}}</span>
+        <input class="field-container-field field-description" data-qui="controls/editors/Input"
+               name="content" />
+    </label>
+</div>
\ No newline at end of file
diff --git a/bin/Controls/NavTabsVerticalSettings.js b/bin/Controls/NavTabsVerticalSettings.js
new file mode 100644
index 0000000..809e878
--- /dev/null
+++ b/bin/Controls/NavTabsVerticalSettings.js
@@ -0,0 +1,107 @@
+/**
+ *
+ * @module package/quiqqer/menu/bin/Controls/NavTabsVerticalSettings
+ *
+ * @require qui/controls/elements/FormList
+ * @require css!package/quiqqer/menu/bin/Controls/NavTabsVerticalSettings.css
+ */
+define('package/quiqqer/menu/bin/Controls/NavTabsVerticalSettings', [
+
+    'qui/controls/elements/FormList',
+    'utils/Controls',
+    'Locale',
+    'Mustache',
+
+    'text!package/quiqqer/menu/bin/Controls/NavTabsVerticalSettings.html',
+    'css!package/quiqqer/menu/bin/Controls/NavTabsVerticalSettings.css'
+
+], function (QUIFormList, QUIControls, QUILocale, Mustache, template) {
+    "use strict";
+
+    var lg = 'quiqqer/menu';
+
+    return new Class({
+
+        Extends: QUIFormList,
+        Type   : 'package/quiqqer/menu/bin/Controls/NavTabsVerticalSettings',
+
+        Binds: [
+            '$onParsed'
+        ],
+
+        initialize: function (options) {
+            this.parent(options);
+
+            this.$Project = null;
+
+            this.addEvents({
+                onParsed: this.$onParsed
+            });
+
+            this.getElm().addClass('qui-controls-formlist-navTabsVerticalSettings');
+
+            this.setAttributes({
+                buttonText: QUILocale.get(lg, 'control.navTabsVertical.entries.addButton'),
+                entry     : Mustache.render(template, {
+                    'title'         : QUILocale.get(lg, 'control.navTabsVertical.entries.entryTitle'),
+                    'titleIcon'     : QUILocale.get(lg, 'control.navTabsVertical.entries.entryTitleIcon'),
+                    'titleIconColor': QUILocale.get(lg, 'control.navTabsVertical.entries.entryTitleIconColor'),
+                    'image'         : QUILocale.get(lg, 'control.navTabsVertical.entries.entryImage'),
+                    'content'       : QUILocale.get(lg, 'control.navTabsVertical.entries.entryContent')
+                })
+            });
+        },
+
+        /**
+         * set the project to the control
+         *
+         * @param Project
+         */
+        setProject: function (Project) {
+            this.$Project = Project;
+            this.$onParsed(false, this.getElm());
+        },
+
+        /**
+         * Parses QUI controls when a new entry is created
+         *
+         * Fired after (inherited) FormList has parsed the content
+         *
+         * @param event
+         * @param Node - The element that was previously parsed by (inherited) FormList
+         */
+        $onParsed: function (event, Node) {
+            if (!this.$Project) {
+                return;
+            }
+
+            this.$executeParsing(Node);
+        },
+
+        /**
+         * Parse the editor
+         *
+         * @param Node
+         * @returns {Promise}
+         */
+        $executeParsing: function (Node) {
+            var self = this;
+
+            return QUIControls.parse(Node).then(function () {
+                // Element is fully parsed so we can finally show it
+                Node.getElements('.quiqqer-menu-navTabsVerticalSettings-entry').show();
+                self.getElm().addClass('qui-controls-formlist-navTabsVerticalSettings');
+
+                var inputEditors = Node.getElements('[data-qui="controls/editors/Input"]').map(function (InnerNode) {
+                    return QUI.Controls.getById(InnerNode.get('data-quiid'));
+                });
+
+                for (var i = 0, len = inputEditors.length; i < len; i++) {
+                    if (inputEditors[i]) {
+                        inputEditors[i].setProject(self.$Project);
+                    }
+                }
+            });
+        }
+    });
+});
diff --git a/bricks.xml b/bricks.xml
index dadba8c..dd2b973 100644
--- a/bricks.xml
+++ b/bricks.xml
@@ -88,6 +88,60 @@
                 </setting>
             </settings>
         </brick>
+        
+        <!-- nav tabs vertical -->
+        <brick control="\QUI\Menu\NavTabsVertical">
+            <title>
+                <locale group="quiqqer/menu"
+                        var="control.navTabsVertical.title"/>
+            </title>
+            <description>
+                <locale group="quiqqer/menu"
+                        var="control.verticalTabSwitcher.description"/>
+            </description>
+
+            <settings>
+                <setting name="imagePos" type="select">
+                    <locale group="quiqqer/menu"
+                            var="control.navTabsVertical.imagePos"/>
+
+                    <option value="top">
+                        <locale group="quiqqer/menu"
+                                var="control.navTabsVertical.imagePos.top"/>
+                    </option>
+                    <option value="bottom">
+                        <locale group="quiqqer/menu"
+                                var="control.navTabsVertical.imagePos.bottom"/>
+                    </option>
+                </setting>
+
+                <!--<setting name="imageMaxWidth" type="number" min="0" max="3000">
+                    <locale group="quiqqer/menu"
+                            var="control.navTabsVertical.imageMaxWidth"/>
+                </setting>
+
+                <setting name="imageMaxHeight" type="number" min="0" max="3000">
+                    <locale group="quiqqer/menu"
+                            var="control.navTabsVertical.imageMaxHeight"/>
+                </setting>-->
+
+                <setting name="navTitle" type="text">
+                    <locale group="quiqqer/menu"
+                            var="control.navTabsVertical.navTitle"/>
+                </setting>
+
+                <setting name="navContent" type="text" data-qui="controls/editors/Input">
+                    <locale group="quiqqer/menu"
+                            var="control.navTabsVertical.navContent"/>
+                </setting>
+
+                <setting name="entries"
+                         data-qui="package/quiqqer/menu/bin/Controls/NavTabsVerticalSettings">
+                    <locale group="quiqqer/menu"
+                            var="control.navTabsVertical.entries"/>
+                </setting>
+            </settings>
+        </brick>
 
     </bricks>
 </quiqqer>
\ No newline at end of file
diff --git a/locale.xml b/locale.xml
index 1ec087e..e1ff402 100644
--- a/locale.xml
+++ b/locale.xml
@@ -116,6 +116,14 @@
         </locale>
 
         <!-- one page nav -->
+        <locale name="menu.control.OnePageNav.title">
+            <de><![CDATA[Menu: One Page Menü (für Landingpage)]]></de>
+            <en><![CDATA[Menu: One Page Navigation (for landingpage)]]></en>
+        </locale>
+        <locale name="menu.control.OnePageNav.description">
+            <de><![CDATA[Menu für eine One-Pager. Die Menüeinträge scrollen zu den entsprechenden Abschnitten auf der Startseite.]]></de>
+            <en><![CDATA[Navigation for one page. The menu items scroll to the corresponding sections on the home page.]]></en>
+        </locale>
         <locale name="menu.control.OnePageNav.addLink">
             <de><![CDATA[Link hinzufügen]]></de>
             <en><![CDATA[Add link]]></en>
@@ -151,5 +159,71 @@
             E.g. <i>.css-class</i>, <i>#elementId</i>]]>
             </en>
         </locale>
+        
+        <!-- nav tabs vertical -->
+        <locale name="control.navTabsVertical.title">
+            <de><![CDATA[Menu: Navigation Tabs (vertical)]]></de>
+            <en><![CDATA[Menu: Navigation Tabs (vertical)]]></en>
+        </locale>
+        <locale name="control.navTabsVertical.description">
+            <de><![CDATA[Verticale Menüeinträge. Der Inhalt ist neben dem Menü angezeigt.]]></de>
+            <en><![CDATA[Vertical menu items. The content is next to the menu items.]]></en>
+        </locale>
+        <locale name="control.navTabsVertical.imagePos">
+            <de><![CDATA[Bild-Position]]></de>
+            <en><![CDATA[Image position]]></en>
+        </locale>
+        <locale name="control.navTabsVertical.imagePos.top">
+            <de><![CDATA[Vor dem Inhalt]]></de>
+            <en><![CDATA[Before the content]]></en>
+        </locale>
+        <locale name="control.navTabsVertical.imagePos.bottom">
+            <de><![CDATA[Nach dem Inhalt]]></de>
+            <en><![CDATA[After the content]]></en>
+        </locale>
+        <locale name="control.navTabsVertical.imageMaxWidth">
+            <de><![CDATA[Maximale Breite des Bildes]]></de>
+            <en><![CDATA[Image max width]]></en>
+        </locale>
+        <locale name="control.navTabsVertical.imageMaxHeight">
+            <de><![CDATA[Maximale Höhe des Bildes]]></de>
+            <en><![CDATA[Image max height]]></en>
+        </locale>
+        <locale name="control.navTabsVertical.navTitle">
+            <de><![CDATA[Title vor den Tabs]]></de>
+            <en><![CDATA[Title before tabs]]></en>
+        </locale>
+        <locale name="control.navTabsVertical.navContent">
+            <de><![CDATA[Inhalt vor den Tabs]]></de>
+            <en><![CDATA[Content before tabs]]></en>
+        </locale>
+        <locale name="control.navTabsVertical.entries">
+            <de><![CDATA[Tabs]]></de>
+            <en><![CDATA[Tabs]]></en>
+        </locale>
+        <locale name="control.navTabsVertical.entries.addButton">
+            <de><![CDATA[Eintrag hinzufügen]]></de>
+            <en><![CDATA[Add entry]]></en>
+        </locale>
+        <locale name="control.navTabsVertical.entries.entryTitle">
+            <de><![CDATA[Titel]]></de>
+            <en><![CDATA[Title]]></en>
+        </locale>
+        <locale name="control.navTabsVertical.entries.entryTitleIcon">
+            <de><![CDATA[Icon (vor dem Titel)]]></de>
+            <en><![CDATA[Icon (before the title)]]></en>
+        </locale>
+        <locale name="control.navTabsVertical.entries.entryTitleIconColor">
+            <de><![CDATA[Icon-Farbe]]></de>
+            <en><![CDATA[Icon color]]></en>
+        </locale>
+        <locale name="control.navTabsVertical.entries.entryContent">
+            <de><![CDATA[Inhalt]]></de>
+            <en><![CDATA[Content]]></en>
+        </locale>
+        <locale name="control.navTabsVertical.entries.entryImage">
+            <de><![CDATA[Bild]]></de>
+            <en><![CDATA[Image]]></en>
+        </locale>
     </groups>
 </locales>
\ No newline at end of file
diff --git a/src/QUI/Menu/NavTabs.html b/src/QUI/Menu/NavTabs.html
index 050bde3..650c140 100644
--- a/src/QUI/Menu/NavTabs.html
+++ b/src/QUI/Menu/NavTabs.html
@@ -1,13 +1,13 @@
-<ul class="quiqqer-menu-navTabs-tabs" role="tablist">
+<ul class="quiqqer-menu-navTabs-tabs quiqqer-tab-nav" role="tablist">
     {foreach from=$entries item=entry key=key}
-        <li class="quiqqer-menu-navTabs-tabs-item {if $key == $active - 1}active{/if}">
+        <li class="quiqqer-menu-navTabs-tabs-item quiqqer-tab-nav-item {if $key == $active - 1}active{/if}">
             <a href="#{$entry.title|escape:'url'}">{$entry.title}</a>
         </li>
     {/foreach}
 </ul>
-<div class="quiqqer-menu-navTabs-content">
+<div class="quiqqer-menu-navTabs-content quiqqer-tab-content">
     {foreach from=$entries item=entry key=key}
-        <div class="quiqqer-menu-navTabs-content-item {if $key == $active - 1}active{/if}"
+        <div class="quiqqer-menu-navTabs-content-item quiqqer-tab-content-item {if $key == $active - 1}active{/if}"
              {if $key !== $active - 1}style="display: none;"{/if} id="{$entry.title|escape:'url'}">
             {$entry.content}
         </div>
diff --git a/src/QUI/Menu/NavTabsVertical.css b/src/QUI/Menu/NavTabsVertical.css
new file mode 100644
index 0000000..b61addd
--- /dev/null
+++ b/src/QUI/Menu/NavTabsVertical.css
@@ -0,0 +1,111 @@
+.quiqqer-menu-navTabsVertical-container {
+    display: flex;
+    gap: 4rem;
+}
+
+.quiqqer-menu-navTabsVertical-tabs {
+    width: 35%;
+}
+
+.quiqqer-menu-navTabsVertical-content {
+    width: 65%;
+}
+
+.quiqqer-menu-navTabsVertical .quiqqer-menu-navTabsVertical-tabs-body {
+    margin-bottom: 2rem;
+}
+
+.quiqqer-menu-navTabsVertical-container .quiqqer-menu-navTabsVertical-tabs-nav,
+.quiqqer-menu-navTabsVertical-container .quiqqer-tab-content {
+    list-style: none;
+    margin-left: 0;
+    padding-left: 0;
+}
+
+.quiqqer-menu-navTabsVertical-container .quiqqer-tab-nav-item,
+.quiqqer-menu-navTabsVertical-container .quiqqer-tab-content-item {
+    margin-left: 0;
+    padding-left: 0;
+}
+
+.quiqqer-menu-navTabsVertical-container .quiqqer-tab-nav-item a {
+    display: block;
+    margin-bottom: 0.5rem;
+    padding: 1rem;
+}
+
+.quiqqer-menu-navTabsVertical-container .quiqqer-tab-nav-item:not(.active) a {
+    color: inherit;
+}
+
+.quiqqer-menu-navTabsVertical-container .quiqqer-tab-nav-item.active a {
+    box-shadow: 0 15px 30px 0 rgba(0, 0, 0, 0.11), 0 5px 15px 0 rgba(0, 0, 0, 0.08);
+}
+
+.quiqqer-menu-navTabsVertical-container .quiqqer-tab-nav-item .fa,
+.quiqqer-menu-navTabsVertical-container .quiqqer-tab-nav-item img {
+    margin-right: 1rem;
+}
+
+.quiqqer-menu-navTabsVertical-container .quiqqer-tab-nav-item img {
+    width: 2rem;
+    height: auto;
+    vertical-align: middle;
+}
+
+.quiqqer-menu-navTabsVertical-content-image {
+    text-align: center;
+    margin-bottom: 1rem;
+}
+
+@media screen and (max-width: 767px) {
+    .quiqqer-menu-navTabsVertical-container {
+        flex-wrap: wrap;
+        gap: 0;
+    }
+
+    .quiqqer-menu-navTabsVertical-tabs,
+    .quiqqer-menu-navTabsVertical-content {
+        width: 100%;
+    }
+
+    .quiqqer-menu-navTabsVertical .quiqqer-menu-navTabsVertical-tabs {
+        margin-bottom: 0;
+    }
+
+    .quiqqer-menu-navTabsVertical-tabs-nav-container {
+        position: relative;
+    }
+
+    .quiqqer-menu-navTabsVertical-tabs-nav-container:before,
+    .quiqqer-menu-navTabsVertical-tabs-nav-container:after {
+        content: '';
+        height: 100%;
+        position: absolute;
+        top: 0;
+        width: 40px;
+    }
+
+    .quiqqer-menu-navTabsVertical-tabs-nav-container:before {
+        background: linear-gradient(90deg, #fff, transparent);
+        left: 0;
+    }
+
+    .quiqqer-menu-navTabsVertical-tabs-nav-container:after {
+        background: linear-gradient(90deg, transparent, #fff);
+        right: 0;
+    }
+
+    .quiqqer-menu-navTabsVertical-container .quiqqer-menu-navTabsVertical-tabs-nav {
+        display: flex;
+        overflow: auto;
+        padding: 2rem 1rem;
+        white-space: nowrap;
+    }
+
+    .quiqqer-menu-navTabsVertical-container .quiqqer-tab-nav-item {
+        margin-left: 0.5rem;
+        margin-right: 0.5rem;
+        flex-shrink: 0;
+    }
+}
\ No newline at end of file
diff --git a/src/QUI/Menu/NavTabsVertical.html b/src/QUI/Menu/NavTabsVertical.html
new file mode 100644
index 0000000..49ab9a5
--- /dev/null
+++ b/src/QUI/Menu/NavTabsVertical.html
@@ -0,0 +1,78 @@
+{if $this->getAttribute('showTitle') && $this->getAttribute('frontendTitle')}
+<header class="control-header">
+    <h1>{$this->getAttribute('frontendTitle')}</h1>
+</header>
+{/if}
+
+{if $this->getAttribute('content') != ""}
+<div class="control-body">
+    {$this->getAttribute('content')}
+</div>
+{/if}
+
+<div class="control-template quiqqer-menu-navTabsVertical-container">
+
+    <div class="quiqqer-menu-navTabsVertical-tabs">
+        {if $navTitle || $navContent}
+            <div class="quiqqer-menu-navTabsVertical-tabs-body">
+                {if $navTitle}
+                    <header class="quiqqer-menu-navTabsVertical-body-header">
+                        <h4>{$navTitle}</h4>
+                    </header>
+                {/if}
+
+                {if $navContent}
+                    {$navContent}
+                {/if}
+            </div>
+        {/if}
+
+        <div class="quiqqer-menu-navTabsVertical-tabs-nav-container">
+            <ul class="quiqqer-menu-navTabsVertical-tabs-nav quiqqer-tab-nav">
+                {foreach from=$entries item=entry key=key}
+                <li class="quiqqer-tab-nav-item {if $key == 0}active{/if}" data-entry-nav-pos="{$key}">
+                    <a href="#{$entry.title|escape:'url'}">
+                        {if isset($entry.titleIcon) && $entry.titleIcon}
+                            {image src=$entry.titleIcon width="80" height="80"}
+                        {/if}
+
+                        <span class="verticalTabSwitcher-nav-entry-label">{$entry.title}</span>
+                    </a>
+                </li>
+                {assign var=modKey value=$key+1}
+                {/foreach}
+            </ul>
+        </div>
+    </div>
+
+
+    <div class="quiqqer-menu-navTabsVertical-content">
+        <ul class="quiqqer-tab-content">
+            {foreach from=$entries item=entry key=key}
+
+            <li class="quiqqer-tab-content-item {if $key == 0}active{/if}" {if $key !== 0}style="display: none;"{/if}
+                    data-entry-content-pos="{$key}" id="{$entry.title|escape:'url'}">
+
+                {if isset($entry.image) && $entry.image && $imagePos == 'top'}
+                <div class="quiqqer-menu-navTabsVertical-content-image
+                            quiqqer-menu-navTabsVertical-content-image__top">
+                    {image src=$entry.image width="600" height="600"}
+                </div>
+                {/if}
+
+                {if isset($entry.content) && $entry.content}
+                    {$entry.content}
+                {/if}
+
+                {if isset($entry.image) && $entry.image && $imagePos == 'bottom'}
+                <div class="quiqqer-menu-navTabsVertical-content-image
+                            quiqqer-menu-navTabsVertical-content-image__bottom">
+                    {image src=$entry.image width="600" height="600"}
+                </div>
+                {/if}
+            </li>
+            {assign var=modKey value=$key+1}
+            {/foreach}
+        </ul>
+    </div>
+</div>
\ No newline at end of file
diff --git a/src/QUI/Menu/NavTabsVertical.php b/src/QUI/Menu/NavTabsVertical.php
new file mode 100644
index 0000000..ca87c49
--- /dev/null
+++ b/src/QUI/Menu/NavTabsVertical.php
@@ -0,0 +1,68 @@
+<?php
+
+/**
+ * This file contains QUI\Menu\NavTabsVertical
+ */
+
+namespace QUI\Menu;
+
+use QUI;
+
+/**
+ * Class NavTabs
+ *
+ * @author  Michael Danielczok
+ * @package QUI\Menu
+ */
+class NavTabsVertical extends QUI\Control
+{
+    /**
+     * constructor
+     *
+     * @param array $attributes
+     */
+    public function __construct($attributes = [])
+    {
+        // default options
+        $this->setAttributes([
+            'class'          => 'quiqqer-menu-navTabsVertical',
+            'qui-class'      => 'package/quiqqer/menu/bin/Controls/NavTabs',
+            'navTitle'       => false,
+            'navContent'     => false,
+            'imagePos'       => 'top',
+            'imageMaxWidth'  => false,
+            'imageMaxHeight' => false,
+            'entries'        => [],
+            'template'       => 'default'
+        ]);
+
+        parent::__construct($attributes);
+
+        $this->addCSSFile(dirname(__FILE__).'/NavTabsVertical.css');
+    }
+
+    /**
+     * (non-PHPdoc)
+     *
+     * @see \QUI\Control::create()
+     */
+    public function getBody()
+    {
+        $Engine  = QUI::getTemplateManager()->getEngine();
+        $entries = $this->getAttribute('entries');
+
+        if (is_string($entries)) {
+            $entries = json_decode($entries, true);
+        }
+
+        $Engine->assign([
+            'this'       => $this,
+            'entries'    => $entries,
+            'navTitle'   => $this->getAttribute('navTitle'),
+            'navContent' => $this->getAttribute('navContent'),
+            'imagePos'   => $this->getAttribute('imagePos')
+        ]);
+
+        return $Engine->fetch(dirname(__FILE__).'/NavTabsVertical.html');
+    }
+}
-- 
GitLab