From 6370bb46a7e47f308ee24f1a32ab8d8cf309dedf Mon Sep 17 00:00:00 2001 From: Robert Jaros Date: Mon, 12 Nov 2018 23:46:52 +0100 Subject: Support for vertical tabs. --- kvision-modules/kvision-bootstrap/build.gradle | 1 + .../pl/treksoft/kvision/KVManagerBootstrap.kt | 4 ++ .../kotlin/pl/treksoft/kvision/panel/TabPanel.kt | 80 +++++++++++++++++++--- src/main/resources/css/style.css | 5 ++ 4 files changed, 82 insertions(+), 8 deletions(-) diff --git a/kvision-modules/kvision-bootstrap/build.gradle b/kvision-modules/kvision-bootstrap/build.gradle index 9dc6953c..e78941ee 100644 --- a/kvision-modules/kvision-bootstrap/build.gradle +++ b/kvision-modules/kvision-bootstrap/build.gradle @@ -8,6 +8,7 @@ kotlinFrontend { dependency("font-awesome", "4.7.0") dependency("font-awesome-webpack", "github:jarecsni/font-awesome-webpack") dependency("awesome-bootstrap-checkbox", "0.3.7") + dependency("bootstrap-vertical-tabs", "1.2.2") } } diff --git a/kvision-modules/kvision-bootstrap/src/main/kotlin/pl/treksoft/kvision/KVManagerBootstrap.kt b/kvision-modules/kvision-bootstrap/src/main/kotlin/pl/treksoft/kvision/KVManagerBootstrap.kt index 552f8d42..105fa507 100644 --- a/kvision-modules/kvision-bootstrap/src/main/kotlin/pl/treksoft/kvision/KVManagerBootstrap.kt +++ b/kvision-modules/kvision-bootstrap/src/main/kotlin/pl/treksoft/kvision/KVManagerBootstrap.kt @@ -50,4 +50,8 @@ internal object KVManagerBootstrap { require("awesome-bootstrap-checkbox") } catch (e: Throwable) { } + private val bootstrapVerticalTabsCss = try { + require("bootstrap-vertical-tabs") + } catch (e: Throwable) { + } } diff --git a/src/main/kotlin/pl/treksoft/kvision/panel/TabPanel.kt b/src/main/kotlin/pl/treksoft/kvision/panel/TabPanel.kt index 1ac69b7b..b98ea1ad 100644 --- a/src/main/kotlin/pl/treksoft/kvision/panel/TabPanel.kt +++ b/src/main/kotlin/pl/treksoft/kvision/panel/TabPanel.kt @@ -24,21 +24,50 @@ package pl.treksoft.kvision.panel import pl.treksoft.kvision.core.Component import pl.treksoft.kvision.core.Container import pl.treksoft.kvision.core.ResString +import pl.treksoft.kvision.core.WidgetWrapper import pl.treksoft.kvision.html.Link import pl.treksoft.kvision.html.TAG import pl.treksoft.kvision.html.Tag import pl.treksoft.kvision.routing.routing +/** + * Tab position. + */ +enum class TabPosition { + TOP, + LEFT, + RIGHT +} + +/** + * Left or right tab size. + */ +enum class SideTabSize { + SIZE_1, + SIZE_2, + SIZE_3, + SIZE_4, + SIZE_5, + SIZE_6 +} + /** * The container rendering it's children as tabs. * * It supports activating children by a JavaScript route. * * @constructor + * @param tabPosition tab position + * @param sideTabSize side tab size * @param classes a set of CSS class names * @param init an initializer extension function */ -open class TabPanel(classes: Set = setOf(), init: (TabPanel.() -> Unit)? = null) : SimplePanel(classes) { +open class TabPanel( + private val tabPosition: TabPosition = TabPosition.TOP, + private val sideTabSize: SideTabSize = SideTabSize.SIZE_3, + classes: Set = setOf(), + init: (TabPanel.() -> Unit)? = null +) : SimplePanel(classes) { /** * The index of active (visible) tab. @@ -56,18 +85,48 @@ open class TabPanel(classes: Set = setOf(), init: (TabPanel.() -> Unit)? } } } - - private var nav = Tag(TAG.UL, classes = setOf("nav", "nav-tabs")) + private val navClasses = when (tabPosition) { + TabPosition.TOP -> setOf("nav", "nav-tabs") + TabPosition.LEFT -> setOf("nav", "nav-tabs", "tabs-left") + TabPosition.RIGHT -> setOf("nav", "nav-tabs", "tabs-right") + } + private var nav = Tag(TAG.UL, classes = navClasses) private var content = StackPanel(false) init { - this.addInternal(nav) - this.addInternal(content) - + when (tabPosition) { + TabPosition.TOP -> { + this.addInternal(nav) + this.addInternal(content) + } + TabPosition.LEFT -> { + this.addCssClass("clearfix") + val sizes = calculateSideClasses() + this.addInternal(WidgetWrapper(nav, setOf(sizes.first, "col-nopadding"))) + this.addInternal(WidgetWrapper(content, setOf(sizes.second, "col-nopadding"))) + } + TabPosition.RIGHT -> { + this.addCssClass("clearfix") + val sizes = calculateSideClasses() + this.addInternal(WidgetWrapper(content, setOf(sizes.second, "col-nopadding"))) + this.addInternal(WidgetWrapper(nav, setOf(sizes.first, "col-nopadding"))) + } + } @Suppress("LeakingThis") init?.invoke(this) } + private fun calculateSideClasses(): Pair { + return when (sideTabSize) { + SideTabSize.SIZE_1 -> Pair("col-xs-1", "col-xs-11") + SideTabSize.SIZE_2 -> Pair("col-xs-2", "col-xs-10") + SideTabSize.SIZE_3 -> Pair("col-xs-3", "col-xs-9") + SideTabSize.SIZE_4 -> Pair("col-xs-4", "col-xs-8") + SideTabSize.SIZE_5 -> Pair("col-xs-5", "col-xs-7") + SideTabSize.SIZE_6 -> Pair("col-xs-6", "col-xs-6") + } + } + /** * Adds new tab and optionally bounds it's activation to a given route. * @param title title of the tab @@ -143,8 +202,13 @@ open class TabPanel(classes: Set = setOf(), init: (TabPanel.() -> Unit)? * * It takes the same parameters as the constructor of the built component. */ - fun Container.tabPanel(classes: Set = setOf(), init: (TabPanel.() -> Unit)? = null): TabPanel { - val tabPanel = TabPanel(classes, init) + fun Container.tabPanel( + tabPosition: TabPosition = TabPosition.TOP, + sideTabSize: SideTabSize = SideTabSize.SIZE_3, + classes: Set = setOf(), + init: (TabPanel.() -> Unit)? = null + ): TabPanel { + val tabPanel = TabPanel(tabPosition, sideTabSize, classes, init) this.add(tabPanel) return tabPanel } diff --git a/src/main/resources/css/style.css b/src/main/resources/css/style.css index d6242ddd..691d252c 100644 --- a/src/main/resources/css/style.css +++ b/src/main/resources/css/style.css @@ -135,3 +135,8 @@ trix-toolbar .trix-button-group { ul.dropdown-menu li a { cursor: pointer; } + +.col-nopadding { + padding-left: 0; + padding-right: 0; +} -- cgit