aboutsummaryrefslogtreecommitdiff
path: root/src/main
diff options
context:
space:
mode:
authorRobert Jaros <rjaros@finn.pl>2019-10-07 09:58:14 +0200
committerRobert Jaros <rjaros@finn.pl>2019-10-07 09:58:14 +0200
commit04ac8542c218b7ce5199350f0880e8f7cb4252b6 (patch)
tree4f96d1c3bb8281289b96e2b11eecc404a3c98788 /src/main
parent6678eec9799681b09e5ac85de1a39596d56de22f (diff)
parent6b14906f0e35dc522bd1c1a44682d728315cf619 (diff)
downloadkvision-04ac8542c218b7ce5199350f0880e8f7cb4252b6.tar.gz
kvision-04ac8542c218b7ce5199350f0880e8f7cb4252b6.tar.bz2
kvision-04ac8542c218b7ce5199350f0880e8f7cb4252b6.zip
Merge branch 'bs4'
Diffstat (limited to 'src/main')
-rw-r--r--src/main/kotlin/pl/treksoft/kvision/KVManager.kt59
-rw-r--r--src/main/kotlin/pl/treksoft/kvision/core/Widget.kt66
-rw-r--r--src/main/kotlin/pl/treksoft/kvision/dropdown/ContextMenu.kt98
-rw-r--r--src/main/kotlin/pl/treksoft/kvision/dropdown/DropDown.kt333
-rw-r--r--src/main/kotlin/pl/treksoft/kvision/dropdown/Header.kt62
-rw-r--r--src/main/kotlin/pl/treksoft/kvision/dropdown/Separator.kt63
-rw-r--r--src/main/kotlin/pl/treksoft/kvision/form/FormControl.kt69
-rw-r--r--src/main/kotlin/pl/treksoft/kvision/form/FormPanel.kt38
-rw-r--r--src/main/kotlin/pl/treksoft/kvision/form/HelpText.kt (renamed from src/main/kotlin/pl/treksoft/kvision/form/HelpBlock.kt)8
-rw-r--r--src/main/kotlin/pl/treksoft/kvision/form/InvalidFeedback.kt (renamed from src/main/kotlin/pl/treksoft/kvision/modal/CloseIcon.kt)35
-rw-r--r--src/main/kotlin/pl/treksoft/kvision/form/check/CheckBox.kt47
-rw-r--r--src/main/kotlin/pl/treksoft/kvision/form/check/CheckInput.kt8
-rw-r--r--src/main/kotlin/pl/treksoft/kvision/form/check/Radio.kt54
-rw-r--r--src/main/kotlin/pl/treksoft/kvision/form/check/RadioGroup.kt69
-rw-r--r--src/main/kotlin/pl/treksoft/kvision/form/check/RadioGroupInput.kt14
-rw-r--r--src/main/kotlin/pl/treksoft/kvision/form/select/SimpleSelect.kt8
-rw-r--r--src/main/kotlin/pl/treksoft/kvision/form/select/SimpleSelectInput.kt8
-rw-r--r--src/main/kotlin/pl/treksoft/kvision/form/text/AbstractText.kt6
-rw-r--r--src/main/kotlin/pl/treksoft/kvision/form/text/AbstractTextInput.kt8
-rw-r--r--src/main/kotlin/pl/treksoft/kvision/form/text/Text.kt2
-rw-r--r--src/main/kotlin/pl/treksoft/kvision/form/text/TextArea.kt2
-rw-r--r--src/main/kotlin/pl/treksoft/kvision/html/Button.kt35
-rw-r--r--src/main/kotlin/pl/treksoft/kvision/html/Icon.kt8
-rw-r--r--src/main/kotlin/pl/treksoft/kvision/html/Image.kt6
-rw-r--r--src/main/kotlin/pl/treksoft/kvision/html/Label.kt51
-rw-r--r--src/main/kotlin/pl/treksoft/kvision/html/Link.kt33
-rw-r--r--src/main/kotlin/pl/treksoft/kvision/html/List.kt5
-rw-r--r--src/main/kotlin/pl/treksoft/kvision/html/Tag.kt6
-rw-r--r--src/main/kotlin/pl/treksoft/kvision/modal/Alert.kt121
-rw-r--r--src/main/kotlin/pl/treksoft/kvision/modal/Confirm.kt176
-rw-r--r--src/main/kotlin/pl/treksoft/kvision/modal/Modal.kt298
-rw-r--r--src/main/kotlin/pl/treksoft/kvision/navbar/Nav.kt73
-rw-r--r--src/main/kotlin/pl/treksoft/kvision/navbar/NavForm.kt74
-rw-r--r--src/main/kotlin/pl/treksoft/kvision/navbar/Navbar.kt195
-rw-r--r--src/main/kotlin/pl/treksoft/kvision/panel/ResponsiveGridPanel.kt185
-rw-r--r--src/main/kotlin/pl/treksoft/kvision/panel/Root.kt26
-rw-r--r--src/main/kotlin/pl/treksoft/kvision/panel/SimplePanel.kt4
-rw-r--r--src/main/kotlin/pl/treksoft/kvision/panel/TabPanel.kt270
-rw-r--r--src/main/kotlin/pl/treksoft/kvision/progress/ProgressBar.kt162
-rw-r--r--src/main/kotlin/pl/treksoft/kvision/progress/ProgressIndicator.kt125
-rw-r--r--src/main/kotlin/pl/treksoft/kvision/table/Cell.kt16
-rw-r--r--src/main/kotlin/pl/treksoft/kvision/table/HeaderCell.kt13
-rw-r--r--src/main/kotlin/pl/treksoft/kvision/table/Table.kt46
-rw-r--r--src/main/kotlin/pl/treksoft/kvision/toolbar/ButtonGroup.kt103
-rw-r--r--src/main/kotlin/pl/treksoft/kvision/toolbar/Toolbar.kt58
-rw-r--r--src/main/kotlin/pl/treksoft/kvision/window/MaximizeIcon.kt48
-rw-r--r--src/main/kotlin/pl/treksoft/kvision/window/MinimizeIcon.kt48
-rw-r--r--src/main/kotlin/pl/treksoft/kvision/window/Window.kt446
-rw-r--r--src/main/resources/css/style.css377
49 files changed, 806 insertions, 3259 deletions
diff --git a/src/main/kotlin/pl/treksoft/kvision/KVManager.kt b/src/main/kotlin/pl/treksoft/kvision/KVManager.kt
index d1a4a8be..0ba694eb 100644
--- a/src/main/kotlin/pl/treksoft/kvision/KVManager.kt
+++ b/src/main/kotlin/pl/treksoft/kvision/KVManager.kt
@@ -30,8 +30,6 @@ import com.github.snabbdom.eventListenersModule
import com.github.snabbdom.propsModule
import com.github.snabbdom.styleModule
import org.w3c.dom.HTMLElement
-import pl.treksoft.kvision.core.Component
-import pl.treksoft.kvision.utils.isIE11
import kotlin.browser.document
import kotlin.dom.clear
@@ -44,21 +42,26 @@ external fun require(name: String): dynamic
/**
* Internal singleton object which initializes and configures KVision framework.
*/
-@Suppress("EmptyCatchBlock", "TooGenericExceptionCaught", "LargeClass")
-internal object KVManager {
- private val kvisionBootstrap = try {
- require("kvision-bootstrap").pl.treksoft.kvision.KVManagerBootstrap
- } catch (e: Throwable) {
- }
- private val elementResizeEvent = try {
- require("element-resize-event")
- } catch (e: Throwable) {
- }
- private val resizable = try {
+@Suppress("EmptyCatchBlock", "TooGenericExceptionCaught")
+object KVManager {
+ init {
+ try {
+ require("kvision-bootstrap-css").pl.treksoft.kvision.KVManagerBootstrapCss
+ } catch (e: Throwable) {
+ }
+ try {
+ require("kvision-bootstrap").pl.treksoft.kvision.KVManagerBootstrap
+ } catch (e: Throwable) {
+ }
+ try {
+ require("kvision-fontawesome").pl.treksoft.kvision.KVManagerFontAwesome
+ } catch (e: Throwable) {
+ }
+ require("./css/style.css")
require("jquery-resizable-dom")
- } catch (e: Throwable) {
}
- internal val fecha = require("fecha")
+
+ internal val fecha = require("fecha").default
private val sdPatch = Snabbdom.init(
arrayOf(
classModule, attributesModule, propsModule, styleModule,
@@ -81,28 +84,12 @@ internal object KVManager {
return sdPatch(oldVNode, newVNode)
}
+ /**
+ * @suppress
+ * Internal function.
+ */
@Suppress("UnsafeCastFromDynamic")
- internal fun virtualize(html: String): VNode {
+ fun virtualize(html: String): VNode {
return sdVirtualize(html)
}
-
- @Suppress("UnsafeCastFromDynamic")
- internal fun setResizeEvent(component: Component, callback: () -> Unit) {
- if (!isIE11()) {
- component.getElement()?.let {
- elementResizeEvent(it, callback)
- }
- }
- }
-
- @Suppress("UnsafeCastFromDynamic")
- internal fun clearResizeEvent(component: Component) {
- if (!isIE11()) {
- if (component.getElement()?.asDynamic()?.__resizeTrigger__?.contentDocument != null) {
- component.getElement()?.let {
- elementResizeEvent.unbind(it)
- }
- }
- }
- }
}
diff --git a/src/main/kotlin/pl/treksoft/kvision/core/Widget.kt b/src/main/kotlin/pl/treksoft/kvision/core/Widget.kt
index e17d7ba9..24543b1f 100644
--- a/src/main/kotlin/pl/treksoft/kvision/core/Widget.kt
+++ b/src/main/kotlin/pl/treksoft/kvision/core/Widget.kt
@@ -27,11 +27,9 @@ import com.github.snabbdom.h
import org.w3c.dom.CustomEventInit
import org.w3c.dom.DragEvent
import org.w3c.dom.Node
-import org.w3c.dom.events.MouseEvent
import pl.treksoft.jquery.JQuery
import pl.treksoft.jquery.jQuery
import pl.treksoft.kvision.KVManager
-import pl.treksoft.kvision.dropdown.ContextMenu
import pl.treksoft.kvision.i18n.I18n
import pl.treksoft.kvision.i18n.I18n.trans
import pl.treksoft.kvision.panel.Root
@@ -83,6 +81,10 @@ open class Widget(classes: Set<String> = setOf()) : StyledComponent(), Component
*/
var role: String? by refreshOnUpdate()
/**
+ * A tabindex attribute of generated HTML element.
+ */
+ var tabindex: Int? by refreshOnUpdate()
+ /**
* Determines if the current widget is draggable.
*/
var draggable: Boolean? by refreshOnUpdate()
@@ -241,6 +243,9 @@ open class Widget(classes: Set<String> = setOf()) : StyledComponent(), Component
role?.let {
snattrs.add("role" to it)
}
+ tabindex?.let {
+ snattrs.add("tabindex" to it.toString())
+ }
if (draggable == true) {
snattrs.add("draggable" to "true")
}
@@ -439,7 +444,8 @@ open class Widget(classes: Set<String> = setOf()) : StyledComponent(), Component
*/
open fun enableTooltip(options: TooltipOptions = TooltipOptions()): Widget {
this.tooltipOptions = options
- getElementJQueryD()?.tooltip(options.copy(title = options.title?.let { translate(it) }).toJs())
+ val tooltipFun = getElementJQueryD()?.tooltip
+ if (tooltipFun != undefined) getElementJQueryD()?.tooltip(options.copy(title = options.title?.let { translate(it) }).toJs())
return this
}
@@ -449,7 +455,8 @@ open class Widget(classes: Set<String> = setOf()) : StyledComponent(), Component
*/
open fun showTooltip(): Widget {
if (this.tooltipOptions != null) {
- getElementJQueryD()?.tooltip("show")
+ val tooltipFun = getElementJQueryD()?.tooltip
+ if (tooltipFun != undefined) getElementJQueryD()?.tooltip("show")
}
return this
}
@@ -460,7 +467,8 @@ open class Widget(classes: Set<String> = setOf()) : StyledComponent(), Component
*/
open fun hideTooltip(): Widget {
if (this.tooltipOptions != null) {
- getElementJQueryD()?.tooltip("hide")
+ val tooltipFun = getElementJQueryD()?.tooltip
+ if (tooltipFun != undefined) getElementJQueryD()?.tooltip("hide")
}
return this
}
@@ -471,7 +479,8 @@ open class Widget(classes: Set<String> = setOf()) : StyledComponent(), Component
*/
open fun disableTooltip(): Widget {
this.tooltipOptions = null
- getElementJQueryD()?.tooltip("destroy")
+ val tooltipFun = getElementJQueryD()?.tooltip
+ if (tooltipFun != undefined) getElementJQueryD()?.tooltip("dispose")
return this
}
@@ -482,7 +491,8 @@ open class Widget(classes: Set<String> = setOf()) : StyledComponent(), Component
*/
open fun enablePopover(options: PopoverOptions = PopoverOptions()): Widget {
this.popoverOptions = options
- getElementJQueryD()?.popover(
+ val popoverFun = getElementJQueryD()?.popover
+ if (popoverFun != undefined) getElementJQueryD()?.popover(
options.copy(title = options.title?.let { translate(it) },
content = options.content?.let { translate(it) }).toJs()
)
@@ -495,7 +505,8 @@ open class Widget(classes: Set<String> = setOf()) : StyledComponent(), Component
*/
open fun showPopover(): Widget {
if (this.popoverOptions != null) {
- getElementJQueryD()?.popover("show")
+ val popoverFun = getElementJQueryD()?.popover
+ if (popoverFun != undefined) getElementJQueryD()?.popover("show")
}
return this
}
@@ -506,7 +517,8 @@ open class Widget(classes: Set<String> = setOf()) : StyledComponent(), Component
*/
open fun hidePopover(): Widget {
if (this.popoverOptions != null) {
- getElementJQueryD()?.popover("hide")
+ val popoverFun = getElementJQueryD()?.popover
+ if (popoverFun != undefined) getElementJQueryD()?.popover("hide")
}
return this
}
@@ -517,7 +529,8 @@ open class Widget(classes: Set<String> = setOf()) : StyledComponent(), Component
*/
open fun disablePopover(): Widget {
this.popoverOptions = null
- getElementJQueryD()?.popover("destroy")
+ val popoverFun = getElementJQueryD()?.popover
+ if (popoverFun != undefined) getElementJQueryD()?.popover("dispose")
return this
}
@@ -624,11 +637,13 @@ open class Widget(classes: Set<String> = setOf()) : StyledComponent(), Component
internal open fun afterInsertInternal(node: VNode) {
this.tooltipOptions?.let {
@Suppress("UnsafeCastFromDynamic")
- getElementJQueryD().tooltip(it.copy(title = it.title?.let { translate(it) }).toJs())
+ val tooltipFun = getElementJQueryD()?.tooltip
+ if (tooltipFun != undefined) getElementJQueryD()?.tooltip(it.copy(title = it.title?.let { translate(it) }).toJs())
}
this.popoverOptions?.let {
@Suppress("UnsafeCastFromDynamic")
- getElementJQueryD().popover(
+ val popoverFun = getElementJQueryD()?.popover
+ if (popoverFun != undefined) getElementJQueryD()?.popover(
it.copy(title = it.title?.let { translate(it) },
content = it.content?.let { translate(it) }).toJs()
)
@@ -647,10 +662,12 @@ open class Widget(classes: Set<String> = setOf()) : StyledComponent(), Component
@Suppress("UnsafeCastFromDynamic")
internal open fun afterDestroyInternal() {
this.tooltipOptions?.let {
- getElementJQueryD().tooltip("destroy")
+ val tooltipFun = getElementJQueryD()?.tooltip
+ if (tooltipFun != undefined) getElementJQueryD()?.tooltip("dispose")
}
this.popoverOptions?.let {
- getElementJQueryD().popover("destroy")
+ val popoverFun = getElementJQueryD()?.popover
+ if (popoverFun != undefined) getElementJQueryD()?.popover("dispose")
}
}
@@ -731,21 +748,6 @@ open class Widget(classes: Set<String> = setOf()) : StyledComponent(), Component
}
/**
- * Sets context menu for the current widget.
- * @param contextMenu a context menu
- * @return current widget
- */
- open fun setContextMenu(contextMenu: ContextMenu): Widget {
- setEventListener<Widget> {
- contextmenu = { e: MouseEvent ->
- e.preventDefault()
- contextMenu.positionMenu(e)
- }
- }
- return this
- }
-
- /**
* @suppress
* Internal function
*/
@@ -755,11 +757,7 @@ open class Widget(classes: Set<String> = setOf()) : StyledComponent(), Component
): Array<out Any> {
val translatedLabel = translate(label)
return if (icon != null) {
- if (icon.startsWith("fa-")) {
- arrayOf(KVManager.virtualize("<i class='fa $icon'></i>"), " $translatedLabel")
- } else {
- arrayOf(KVManager.virtualize("<span class='glyphicon glyphicon-$icon'></span>"), " $translatedLabel")
- }
+ arrayOf(KVManager.virtualize("<i class='$icon'></i>"), " $translatedLabel")
} else if (image != null) {
arrayOf(KVManager.virtualize("<img src='$image' alt='' />"), " $translatedLabel")
} else {
diff --git a/src/main/kotlin/pl/treksoft/kvision/dropdown/ContextMenu.kt b/src/main/kotlin/pl/treksoft/kvision/dropdown/ContextMenu.kt
deleted file mode 100644
index 5fac0494..00000000
--- a/src/main/kotlin/pl/treksoft/kvision/dropdown/ContextMenu.kt
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- * Copyright (c) 2017-present Robert Jaros
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in all
- * copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-package pl.treksoft.kvision.dropdown
-
-import org.w3c.dom.events.MouseEvent
-import pl.treksoft.kvision.core.Display
-import pl.treksoft.kvision.core.Widget
-import pl.treksoft.kvision.html.ListTag
-import pl.treksoft.kvision.html.ListType
-import pl.treksoft.kvision.panel.Root
-import pl.treksoft.kvision.utils.px
-
-/**
- * Context menu component.
- *
- * @constructor
- * @param element an element to bind
- * @param fixedPosition use fixed positioning
- * @param classes a set of CSS class names
- */
-open class ContextMenu(
- element: Widget? = null,
- protected val fixedPosition: Boolean = false,
- classes: Set<String> = setOf(), init: (ContextMenu.() -> Unit)? = null
-) : ListTag(ListType.UL, classes = classes + "dropdown-menu") {
-
- init {
- @Suppress("LeakingThis")
- hide()
- @Suppress("LeakingThis")
- display = Display.BLOCK
- val root = element?.getRoot() ?: Root.getLastRoot()
- if (root != null) {
- @Suppress("LeakingThis")
- root.addContextMenu(this)
- } else {
- println("At least one Root object is required to create a context menu!")
- }
- @Suppress("LeakingThis")
- init?.invoke(this)
- }
-
- /**
- * Positions and shows a context menu based on a mouse event.
- * @param mouseEvent mouse event
- * @return current context menu
- */
- open fun positionMenu(mouseEvent: MouseEvent): ContextMenu {
- if (fixedPosition) {
- this.top = DEFAULT_FIXED_POS_Y.px
- this.left = DEFAULT_FIXED_POS_X.px
- } else {
- this.top = mouseEvent.pageY.toInt().px
- this.left = mouseEvent.pageX.toInt().px
- }
- this.show()
- return this
- }
-
- companion object {
-
- const val DEFAULT_FIXED_POS_X = 5
- const val DEFAULT_FIXED_POS_Y = 5
-
- /**
- * DSL builder extension function.
- *
- * It takes the same parameters as the constructor of the built component.
- */
- fun Widget.contextMenu(
- fixedPosition: Boolean = false,
- classes: Set<String> = setOf(), init: (ContextMenu.() -> Unit)? = null
- ): ContextMenu {
- val contextMenu = ContextMenu(this, fixedPosition, classes).apply { init?.invoke(this) }
- this.setContextMenu(contextMenu)
- return contextMenu
- }
- }
-}
diff --git a/src/main/kotlin/pl/treksoft/kvision/dropdown/DropDown.kt b/src/main/kotlin/pl/treksoft/kvision/dropdown/DropDown.kt
deleted file mode 100644
index a521fe95..00000000
--- a/src/main/kotlin/pl/treksoft/kvision/dropdown/DropDown.kt
+++ /dev/null
@@ -1,333 +0,0 @@
-/*
- * Copyright (c) 2017-present Robert Jaros
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in all
- * copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-package pl.treksoft.kvision.dropdown
-
-import com.github.snabbdom.VNode
-import pl.treksoft.kvision.KVManager
-import pl.treksoft.kvision.core.Component
-import pl.treksoft.kvision.core.Container
-import pl.treksoft.kvision.core.CssSize
-import pl.treksoft.kvision.core.StringBoolPair
-import pl.treksoft.kvision.core.StringPair
-import pl.treksoft.kvision.html.Button
-import pl.treksoft.kvision.html.ButtonStyle
-import pl.treksoft.kvision.html.ButtonType
-import pl.treksoft.kvision.html.Link
-import pl.treksoft.kvision.html.ListTag
-import pl.treksoft.kvision.html.ListType
-import pl.treksoft.kvision.html.TAG
-import pl.treksoft.kvision.html.Tag
-import pl.treksoft.kvision.panel.SimplePanel
-import pl.treksoft.kvision.utils.obj
-
-/**
- * Useful options for use in DropDown's *elements* parameter.
- */
-enum class DD(val option: String) {
- HEADER("DD#HEADER"),
- DISABLED("DD#DISABLED"),
- SEPARATOR("DD#SEPARATOR")
-}
-
-/**
- * Bootstrap dropdown component.
- *
- * @constructor
- * @param text the label of the dropdown button
- * @param elements an optional list of link elements (special options from [DD] enum class can be used as values)
- * @param icon the icon of the dropdown button
- * @param style the style of the dropdown button
- * @param disabled determines if the component is disabled on start
- * @param forNavbar determines if the component will be used in a navbar
- * @param withCaret determines if the dropdown button renders caret
- * @param classes a set of CSS class names
- */
-@Suppress("TooManyFunctions")
-open class DropDown(
- text: String, elements: List<StringPair>? = null, icon: String? = null,
- style: ButtonStyle = ButtonStyle.DEFAULT, disabled: Boolean = false, val forNavbar: Boolean = false,
- withCaret: Boolean = true, classes: Set<String> = setOf()
-) : SimplePanel(classes) {
- /**
- * Label of the dropdown button.
- */
- var text
- get() = button.text
- set(value) {
- button.text = value
- }
- private var elements by refreshOnUpdate(elements) { setChildrenFromElements() }
- /**
- * The icon of the dropdown button.
- */
- var icon
- get() = button.icon
- set(value) {
- button.icon = value
- }
- /**
- * The style of the dropdown button.
- */
- var style
- get() = button.style
- set(value) {
- button.style = value
- }
- /**
- * The size of the dropdown button.
- */
- var size
- get() = button.size
- set(value) {
- button.size = value
- }
- /**
- * Determines if the dropdown button takes all the space horizontally.
- */
- var block
- get() = button.block
- set(value) {
- button.block = value
- }
- /**
- * Determines if the dropdown is disabled.
- */
- var disabled
- get() = button.disabled
- set(value) {
- button.disabled = value
- }
- /**
- * The image on the dropdown button.
- */
- var image
- get() = button.image
- set(value) {
- button.image = value
- }
- /**
- * Determines if the dropdown is showing upwards.
- */
- var dropup by refreshOnUpdate(false)
- /**
- * Width of the dropdown button.
- */
- override var width: CssSize?
- get() = super.width
- set(value) {
- super.width = value
- button.width = value
- }
-
- private val idc = "kv_dropdown_$counter"
- internal val button: DropDownButton = DropDownButton(
- idc, text, icon, style,
- disabled, forNavbar, withCaret, setOf("dropdown")
- )
-
- fun buttonId() = button.id
-
- internal val list: DropDownListTag = DropDownListTag(idc, setOf("dropdown-menu"))
-
- init {
- setChildrenFromElements()
- this.addInternal(button)
- this.addInternal(list)
- counter++
- }
-
- override fun render(): VNode {
- return if (forNavbar) {
- render("li", childrenVNodes())
- } else {
- render("div", childrenVNodes())
- }
- }
-
- override fun add(child: Component): SimplePanel {
- list.add(child)
- return this
- }
-
- override fun addAll(children: List<Component>): SimplePanel {
- list.addAll(children)
- return this
- }
-
- override fun remove(child: Component): SimplePanel {
- list.remove(child)
- return this
- }
-
- override fun removeAll(): SimplePanel {
- list.removeAll()
- return this
- }
-
- override fun getChildren(): List<Component> {
- return list.getChildren()
- }
-
- private fun setChildrenFromElements() {
- list.removeAll()
- elements?.let { elems ->
- val c = elems.map {
- when (it.second) {
- DD.HEADER.option -> Header(it.first)
- DD.SEPARATOR.option -> Separator()
- DD.DISABLED.option -> {
- val tag = Tag(TAG.LI, classes = setOf("disabled"))
- tag.add(Link(it.first))
- tag
- }
- else -> Link(it.first, it.second)
- }
- }
- list.addAll(c)
- }
- }
-
- @Suppress("UnsafeCastFromDynamic")
- override fun afterInsert(node: VNode) {
- this.getElementJQuery()?.on("show.bs.dropdown") { e, _ ->
- this.dispatchEvent("showBsDropdown", obj { detail = e })
- }
- this.getElementJQuery()?.on("shown.bs.dropdown") { e, _ ->
- this.dispatchEvent("shownBsDropdown", obj { detail = e })
- }
- this.getElementJQuery()?.on("hide.bs.dropdown") { e, _ ->
- this.dispatchEvent("hideBsDropdown", obj { detail = e })
- }
- this.getElementJQuery()?.on("hidden.bs.dropdown") { e, _ ->
- this.dispatchEvent("hiddenBsDropdown", obj { detail = e })
- }
- }
-
- override fun getSnClass(): List<StringBoolPair> {
- val cl = super.getSnClass().toMutableList()
- if (dropup)
- cl.add("dropup" to true)
- else
- cl.add("dropdown" to true)
- return cl
- }
-
- /**
- * Toggles dropdown visibility.
- */
- open fun toggle() {
- this.list.getElementJQueryD()?.dropdown("toggle")
- }
-
- companion object {
- internal var counter = 0
-
- /**
- * DSL builder extension function.
- *
- * It takes the same parameters as the constructor of the built component.
- */
- fun Container.dropDown(
- text: String, elements: List<StringPair>? = null, icon: String? = null,
- style: ButtonStyle = ButtonStyle.DEFAULT, disabled: Boolean = false, forNavbar: Boolean = false,
- withCaret: Boolean = true, classes: Set<String> = setOf(), init: (DropDown.() -> Unit)? = null
- ): DropDown {
- val dropDown =
- DropDown(
- text,
- elements,
- icon,
- style,
- disabled,
- forNavbar,
- withCaret,
- classes
- ).apply { init?.invoke(this) }
- this.add(dropDown)
- return dropDown
- }
- }
-}
-
-internal class DropDownButton(
- id: String,
- text: String,
- icon: String? = null,
- style: ButtonStyle = ButtonStyle.DEFAULT,
- disabled: Boolean = false,
- val forNavbar: Boolean = false,
- val withCaret: Boolean = true,
- classes: Set<String> = setOf()
-) :
- Button(text, icon, style, ButtonType.BUTTON, disabled, classes) {
-
- init {
- this.id = id
- this.role = "button"
- setInternalEventListener<DropDownButton> {
- click = { e ->
- if (parent?.parent is ContextMenu) e.asDynamic().dropDownCM = true
- }
- }
- }
-
- override fun render(): VNode {
- val text = createLabelWithIcon(text, icon, image)
- return if (forNavbar) {
- if (withCaret) {
- val textWithCarret = text.toMutableList()
- textWithCarret.add(" ")
- textWithCarret.add(KVManager.virtualize("<span class='caret'></span>"))
- render("a", textWithCarret.toTypedArray())
- } else {
- render("a", text)
- }
- } else {
- render("button", text)
- }
- }
-
- override fun getSnClass(): List<StringBoolPair> {
- return if (forNavbar) {
- listOf("dropdown" to true)
- } else {
- super.getSnClass()
- }
- }
-
- override fun getSnAttrs(): List<StringPair> {
- return super.getSnAttrs() + listOf(
- "data-toggle" to "dropdown", "aria-haspopup" to "true",
- "aria-expanded" to "false", "href" to "#"
- )
- }
-}
-
-internal class DropDownListTag(private val ariaId: String, classes: Set<String> = setOf()) : ListTag(
- ListType.UL, null,
- false, classes
-) {
-
- override fun getSnAttrs(): List<StringPair> {
- return super.getSnAttrs() + listOf("aria-labelledby" to ariaId)
- }
-}
diff --git a/src/main/kotlin/pl/treksoft/kvision/dropdown/Header.kt b/src/main/kotlin/pl/treksoft/kvision/dropdown/Header.kt
deleted file mode 100644
index 4cee7b7b..00000000
--- a/src/main/kotlin/pl/treksoft/kvision/dropdown/Header.kt
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Copyright (c) 2017-present Robert Jaros
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in all
- * copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-package pl.treksoft.kvision.dropdown
-
-import pl.treksoft.kvision.html.ListTag
-import pl.treksoft.kvision.html.TAG
-import pl.treksoft.kvision.html.Tag
-
-/**
- * Menu header component.
- *
- * @constructor
- * @param content header content text
- * @param classes a set of CSS class names
- */
-open class Header(content: String? = null, classes: Set<String> = setOf()) :
- Tag(TAG.LI, content, classes = classes + "dropdown-header") {
-
-
- companion object {
- /**
- * DSL builder extension function.
- *
- * It takes the same parameters as the constructor of the built component.
- */
- fun ListTag.header(content: String? = null, classes: Set<String> = setOf()): Header {
- val header = Header(content, classes)
- this.add(header)
- return header
- }
-
- /**
- * DSL builder extension function.
- *
- * It takes the same parameters as the constructor of the built component.
- */
- fun DropDown.header(content: String? = null, classes: Set<String> = setOf()): Header {
- val header = Header(content, classes)
- this.add(header)
- return header
- }
- }
-}
diff --git a/src/main/kotlin/pl/treksoft/kvision/dropdown/Separator.kt b/src/main/kotlin/pl/treksoft/kvision/dropdown/Separator.kt
deleted file mode 100644
index c682b0e9..00000000
--- a/src/main/kotlin/pl/treksoft/kvision/dropdown/Separator.kt
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright (c) 2017-present Robert Jaros
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in all
- * copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-package pl.treksoft.kvision.dropdown
-
-import pl.treksoft.kvision.html.ListTag
-import pl.treksoft.kvision.html.TAG
-import pl.treksoft.kvision.html.Tag
-
-/**
- * Menu separator component.
- *
- * @constructor
- * @param classes a set of CSS class names
- */
-open class Separator(classes: Set<String> = setOf()) : Tag(TAG.LI, classes = classes + "divider") {
-
- init {
- role = "separator"
- }
-
- companion object {
- /**
- * DSL builder extension function.
- *
- * It takes the same parameters as the constructor of the built component.
- */
- fun ListTag.separator(classes: Set<String> = setOf()): Separator {
- val separator = Separator(classes)
- this.add(separator)
- return separator
- }
-
- /**
- * DSL builder extension function.
- *
- * It takes the same parameters as the constructor of the built component.
- */
- fun DropDown.separator(classes: Set<String> = setOf()): Separator {
- val separator = Separator(classes)
- this.add(separator)
- return separator
- }
- }
-}
diff --git a/src/main/kotlin/pl/treksoft/kvision/form/FormControl.kt b/src/main/kotlin/pl/treksoft/kvision/form/FormControl.kt
index 3019fb6f..ece72189 100644
--- a/src/main/kotlin/pl/treksoft/kvision/form/FormControl.kt
+++ b/src/main/kotlin/pl/treksoft/kvision/form/FormControl.kt
@@ -30,8 +30,16 @@ import kotlin.js.Date
* Input controls sizes.
*/
enum class InputSize(val className: String) {
- LARGE("input-lg"),
- SMALL("input-sm")
+ LARGE("form-control-lg"),
+ SMALL("form-control-sm")
+}
+
+/**
+ * Input controls validation status.
+ */
+enum class ValidationStatus(val className: String) {
+ VALID("is-valid"),
+ INVALID("is-invalid")
}
interface FormInput : Component {
@@ -47,6 +55,10 @@ interface FormInput : Component {
* The name attribute of the generated HTML input element.
*/
var name: String?
+ /**
+ * Input control validation status.
+ */
+ var validationStatus: ValidationStatus?
/**
* Makes the input element focused.
@@ -78,6 +90,16 @@ interface FormControl : Component {
get() = input.size
set(value) {
input.size = value
+ if (value == InputSize.SMALL) {
+ flabel.addCssClass("col-form-label-sm")
+ } else {
+ flabel.removeCssClass("col-form-label-sm")
+ }
+ if (value == InputSize.LARGE) {
+ flabel.addCssClass("col-form-label-lg")
+ } else {
+ flabel.removeCssClass("col-form-label-lg")
+ }
}
/**
* The name attribute of the generated HTML input element.
@@ -88,6 +110,14 @@ interface FormControl : Component {
input.name = value
}
/**
+ * Input control validation status.
+ */
+ var validationStatus: ValidationStatus?
+ get() = input.validationStatus
+ set(value) {
+ input.validationStatus = value
+ }
+ /**
* The actual input component.
*/
val input: FormInput
@@ -96,9 +126,9 @@ interface FormControl : Component {
*/
val flabel: FieldLabel
/**
- * Validation info component.
+ * Invalid feedback component.
*/
- val validationInfo: HelpBlock
+ val invalidFeedback: InvalidFeedback
/**
* Returns the value of the control.
@@ -129,10 +159,11 @@ interface FormControl : Component {
* Validator error message.
*/
var validatorError: String?
- get() = validationInfo.content
+ get() = invalidFeedback.content
set(value) {
- validationInfo.content = value
- validationInfo.visible = value != null
+ invalidFeedback.content = value
+ invalidFeedback.visible = value != null
+ input.validationStatus = if (value != null) ValidationStatus.INVALID else null
refresh()
}
@@ -145,6 +176,30 @@ interface FormControl : Component {
* Makes the input element blur.
*/
fun blur()
+
+ /**
+ * Style form control element for vertical form panel.
+ */
+ fun styleForVerticalFormPanel() {
+ }
+
+ /**
+ * Style form control element for horizontal form panel.
+ */
+ fun styleForHorizontalFormPanel() {
+ addCssClass("row")
+ flabel.addCssClass("col-sm-2")
+ flabel.addCssClass("col-form-label")
+ input.addCssClass("col-sm-10")
+ invalidFeedback.addCssClass("offset-sm-2")
+ invalidFeedback.addCssClass("col-sm-10")
+ }
+
+ /**
+ * Style form control element for inline form panel.
+ */
+ fun styleForInlineFormPanel() {
+ }
}
/**
diff --git a/src/main/kotlin/pl/treksoft/kvision/form/FormPanel.kt b/src/main/kotlin/pl/treksoft/kvision/form/FormPanel.kt
index 21281556..e8fa0a42 100644
--- a/src/main/kotlin/pl/treksoft/kvision/form/FormPanel.kt
+++ b/src/main/kotlin/pl/treksoft/kvision/form/FormPanel.kt
@@ -28,10 +28,7 @@ import kotlinx.serialization.serializer
import pl.treksoft.kvision.core.Container
import pl.treksoft.kvision.core.StringBoolPair
import pl.treksoft.kvision.core.StringPair
-import pl.treksoft.kvision.form.check.CheckBox
-import pl.treksoft.kvision.form.check.Radio
-import pl.treksoft.kvision.html.TAG
-import pl.treksoft.kvision.html.Tag
+import pl.treksoft.kvision.html.Div
import pl.treksoft.kvision.panel.SimplePanel
import pl.treksoft.kvision.types.KFile
import kotlin.js.Date
@@ -82,13 +79,14 @@ enum class FormTarget(internal val target: String) {
* @param action the URL address to send data
* @param enctype form encoding type
* @param type form layout
+ * @param condensed determines if the form is condensed.
* @param classes set of CSS class names
* @param serializer a serializer for model type
*/
@Suppress("TooManyFunctions")
open class FormPanel<K : Any>(
method: FormMethod? = null, action: String? = null, enctype: FormEnctype? = null,
- private val type: FormType? = null, classes: Set<String> = setOf(),
+ private val type: FormType? = null, condensed: Boolean = false, classes: Set<String> = setOf(),
serializer: KSerializer<K>
) : SimplePanel(classes) {
@@ -120,6 +118,10 @@ open class FormPanel<K : Any>(
* Determines if the form should have autocomplete.
*/
var autocomplete: Boolean? by refreshOnUpdate()
+ /**
+ * Determines if the form is condensed.
+ */
+ var condensed by refreshOnUpdate(condensed)
/**
* Function returning validation message.
@@ -156,7 +158,7 @@ open class FormPanel<K : Any>(
* @suppress
* Internal property.
*/
- protected val validationAlert = Tag(TAG.H5, classes = setOf("alert", "alert-danger")).apply {
+ protected val validationAlert = Div(classes = setOf("alert", "alert-danger")).apply {
role = "alert"
visible = false
}
@@ -173,7 +175,9 @@ open class FormPanel<K : Any>(
val cl = super.getSnClass().toMutableList()
if (type != null) {
cl.add(type.formType to true)
+ if (type == FormType.HORIZONTAL) cl.add("container-fluid" to true)
}
+ if (condensed) cl.add("kv-form-condensed" to true)
return cl
}
@@ -208,16 +212,10 @@ open class FormPanel<K : Any>(
validatorMessage: ((C) -> String?)? = null,
validator: ((C) -> Boolean?)? = null
): FormPanel<K> {
- if (type == FormType.HORIZONTAL) {
- if (control is CheckBox || control is Radio) {
- control.addCssClass("col-sm-offset-2")
- control.addCssClass("col-sm-10")
- } else {
- control.flabel.addCssClass("col-sm-2")
- control.input.addSurroundingCssClass("col-sm-10")
- control.validationInfo.addCssClass("col-sm-offset-2")
- control.validationInfo.addCssClass("col-sm-10")
- }
+ when (type) {
+ FormType.INLINE -> control.styleForInlineFormPanel()
+ FormType.HORIZONTAL -> control.styleForHorizontalFormPanel()
+ else -> control.styleForVerticalFormPanel()
}
super.add(control)
form.addInternal(key, control, required, requiredMessage, validatorMessage, validator)
@@ -399,10 +397,10 @@ open class FormPanel<K : Any>(
*/
inline fun <reified K : Any> Container.formPanel(
method: FormMethod? = null, action: String? = null, enctype: FormEnctype? = null,
- type: FormType? = null, classes: Set<String> = setOf(),
+ type: FormType? = null, condensed: Boolean = false, classes: Set<String> = setOf(),
noinline init: (FormPanel<K>.() -> Unit)? = null
): FormPanel<K> {
- val formPanel = create<K>(method, action, enctype, type, classes)
+ val formPanel = create<K>(method, action, enctype, type, condensed, classes)
init?.invoke(formPanel)
this.add(formPanel)
return formPanel
@@ -411,10 +409,10 @@ open class FormPanel<K : Any>(
@UseExperimental(ImplicitReflectionSerializer::class)
inline fun <reified K : Any> create(
method: FormMethod? = null, action: String? = null, enctype: FormEnctype? = null,
- type: FormType? = null, classes: Set<String> = setOf(),
+ type: FormType? = null, condensed: Boolean = false, classes: Set<String> = setOf(),
noinline init: (FormPanel<K>.() -> Unit)? = null
): FormPanel<K> {
- val formPanel = FormPanel(method, action, enctype, type, classes, K::class.serializer())
+ val formPanel = FormPanel(method, action, enctype, type, condensed, classes, K::class.serializer())
init?.invoke(formPanel)
return formPanel
}
diff --git a/src/main/kotlin/pl/treksoft/kvision/form/HelpBlock.kt b/src/main/kotlin/pl/treksoft/kvision/form/HelpText.kt
index 9ec3d8ae..0362d03a 100644
--- a/src/main/kotlin/pl/treksoft/kvision/form/HelpBlock.kt
+++ b/src/main/kotlin/pl/treksoft/kvision/form/HelpText.kt
@@ -25,13 +25,13 @@ import pl.treksoft.kvision.html.TAG
import pl.treksoft.kvision.html.Tag
/**
- * Helper class for Bootstrap help block element.
+ * Helper class for Bootstrap help text element.
*
* @constructor
* @param content the text of the label
* @param rich determines if [content] can contain HTML code
*/
-open class HelpBlock(content: String? = null, rich: Boolean = false) : Tag(
- TAG.SPAN, content, rich,
- classes = setOf("help-block", "small")
+open class HelpText(content: String? = null, rich: Boolean = false) : Tag(
+ TAG.SMALL, content, rich,
+ classes = setOf("form-text", "text-muted")
)
diff --git a/src/main/kotlin/pl/treksoft/kvision/modal/CloseIcon.kt b/src/main/kotlin/pl/treksoft/kvision/form/InvalidFeedback.kt
index 5f0440a6..dad68239 100644
--- a/src/main/kotlin/pl/treksoft/kvision/modal/CloseIcon.kt
+++ b/src/main/kotlin/pl/treksoft/kvision/form/InvalidFeedback.kt
@@ -19,30 +19,19 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
-package pl.treksoft.kvision.modal
+package pl.treksoft.kvision.form
-import com.github.snabbdom.VNode
-import pl.treksoft.kvision.KVManager
-import pl.treksoft.kvision.core.StringBoolPair
-import pl.treksoft.kvision.core.StringPair
-import pl.treksoft.kvision.core.Widget
+import pl.treksoft.kvision.html.TAG
+import pl.treksoft.kvision.html.Tag
/**
- * Helper class for close icon component.
+ * Helper class for Bootstrap invalid feedback element.
+ *
+ * @constructor
+ * @param content the text of the label
+ * @param rich determines if [content] can contain HTML code
*/
-open class CloseIcon : Widget(setOf()) {
-
- override fun render(): VNode {
- return render("button", arrayOf(KVManager.virtualize("<span aria-hidden='true'>&times;</span>")))
- }
-
- override fun getSnClass(): List<StringBoolPair> {
- val cl = super.getSnClass().toMutableList()
- cl.add("close" to true)
- return cl
- }
-
- override fun getSnAttrs(): List<StringPair> {
- return super.getSnAttrs() + listOf("type" to "button", "aria-label" to "Close")
- }
-}
+open class InvalidFeedback(content: String? = null, rich: Boolean = false) : Tag(
+ TAG.DIV, content, rich,
+ classes = setOf("invalid-feedback")
+)
diff --git a/src/main/kotlin/pl/treksoft/kvision/form/check/CheckBox.kt b/src/main/kotlin/pl/treksoft/kvision/form/check/CheckBox.kt
index 730488eb..cb962850 100644
--- a/src/main/kotlin/pl/treksoft/kvision/form/check/CheckBox.kt
+++ b/src/main/kotlin/pl/treksoft/kvision/form/check/CheckBox.kt
@@ -27,7 +27,7 @@ import pl.treksoft.kvision.core.StringBoolPair
import pl.treksoft.kvision.core.Widget
import pl.treksoft.kvision.form.BoolFormControl
import pl.treksoft.kvision.form.FieldLabel
-import pl.treksoft.kvision.form.HelpBlock
+import pl.treksoft.kvision.form.InvalidFeedback
import pl.treksoft.kvision.panel.SimplePanel
import pl.treksoft.kvision.utils.SnOn
@@ -35,12 +35,11 @@ import pl.treksoft.kvision.utils.SnOn
* Checkbox style options.
*/
enum class CheckBoxStyle(internal val className: String) {
- DEFAULT("checkbox-default"),
- PRIMARY("checkbox-primary"),
- SUCCESS("checkbox-success"),
- INFO("checkbox-info"),
- WARNING("checkbox-warning"),
- DANGER("checkbox-danger"),
+ PRIMARY("abc-checkbox-primary"),
+ SUCCESS("abc-checkbox-success"),
+ INFO("abc-checkbox-info"),
+ WARNING("abc-checkbox-warning"),
+ DANGER("abc-checkbox-danger"),
}
/**
@@ -55,7 +54,7 @@ enum class CheckBoxStyle(internal val className: String) {
open class CheckBox(
value: Boolean = false, name: String? = null, label: String? = null,
rich: Boolean = false
-) : SimplePanel(setOf("checkbox")), BoolFormControl {
+) : SimplePanel(setOf("form-check", "abc-checkbox")), BoolFormControl {
/**
* The selection state of the checkbox.
@@ -106,22 +105,19 @@ open class CheckBox(
var inline by refreshOnUpdate(false)
private val idc = "kv_form_checkbox_$counter"
- final override val input: CheckBoxInput = CheckBoxInput(
- value,
- setOf("styled")
- ).apply {
+ final override val input: CheckBoxInput = CheckBoxInput(value, classes = setOf("form-check-input")).apply {
this.id = idc
this.name = name
}
- final override val flabel: FieldLabel = FieldLabel(idc, label, rich, classes = setOf())
- final override val validationInfo: HelpBlock = HelpBlock().apply { visible = false }
+ final override val flabel: FieldLabel = FieldLabel(idc, label, rich, classes = setOf("form-check-label"))
+ final override val invalidFeedback: InvalidFeedback = InvalidFeedback().apply { visible = false }
init {
@Suppress("LeakingThis")
input.eventTarget = this
this.addInternal(input)
this.addInternal(flabel)
- this.addInternal(validationInfo)
+ this.addInternal(invalidFeedback)
counter++
}
@@ -147,13 +143,13 @@ open class CheckBox(
cl.add(it.className to true)
}
if (circled) {
- cl.add("checkbox-circle" to true)
+ cl.add("abc-checkbox-circle" to true)
}
if (inline) {
- cl.add("checkbox-inline" to true)
+ cl.add("form-check-inline" to true)
}
if (validatorError != null) {
- cl.add("has-error" to true)
+ cl.add("text-danger" to true)
}
return cl
}
@@ -178,6 +174,21 @@ open class CheckBox(
input.blur()
}
+ override fun styleForHorizontalFormPanel() {
+ addCssClass("form-group")
+ addSurroundingCssClass("row")
+ addCssClass("offset-sm-2")
+ addCssClass("col-sm-10")
+ }
+
+ override fun styleForInlineFormPanel() {
+ addCssClass("form-group")
+ }
+
+ override fun styleForVerticalFormPanel() {
+ addCssClass("form-group")
+ }
+
companion object {
internal var counter = 0
diff --git a/src/main/kotlin/pl/treksoft/kvision/form/check/CheckInput.kt b/src/main/kotlin/pl/treksoft/kvision/form/check/CheckInput.kt
index a486b82e..f93fd436 100644
--- a/src/main/kotlin/pl/treksoft/kvision/form/check/CheckInput.kt
+++ b/src/main/kotlin/pl/treksoft/kvision/form/check/CheckInput.kt
@@ -28,6 +28,7 @@ import pl.treksoft.kvision.core.StringPair
import pl.treksoft.kvision.core.Widget
import pl.treksoft.kvision.form.FormInput
import pl.treksoft.kvision.form.InputSize
+import pl.treksoft.kvision.form.ValidationStatus
/**
* Type of the check input control (checkbox or radio).
@@ -94,6 +95,10 @@ abstract class CheckInput(
* The size of the input.
*/
override var size: InputSize? by refreshOnUpdate()
+ /**
+ * The validation status of the input.
+ */
+ override var validationStatus: ValidationStatus? by refreshOnUpdate()
override fun render(): VNode {
return render("input")
@@ -101,6 +106,9 @@ abstract class CheckInput(
override fun getSnClass(): List<StringBoolPair> {
val cl = super.getSnClass().toMutableList()
+ validationStatus?.let {
+ cl.add(it.className to true)
+ }
size?.let {
cl.add(it.className to true)
}
diff --git a/src/main/kotlin/pl/treksoft/kvision/form/check/Radio.kt b/src/main/kotlin/pl/treksoft/kvision/form/check/Radio.kt
index 29b3cb35..e9551196 100644
--- a/src/main/kotlin/pl/treksoft/kvision/form/check/Radio.kt
+++ b/src/main/kotlin/pl/treksoft/kvision/form/check/Radio.kt
@@ -27,7 +27,7 @@ import pl.treksoft.kvision.core.StringBoolPair
import pl.treksoft.kvision.core.Widget
import pl.treksoft.kvision.form.BoolFormControl
import pl.treksoft.kvision.form.FieldLabel
-import pl.treksoft.kvision.form.HelpBlock
+import pl.treksoft.kvision.form.InvalidFeedback
import pl.treksoft.kvision.panel.SimplePanel
import pl.treksoft.kvision.utils.SnOn
@@ -35,12 +35,11 @@ import pl.treksoft.kvision.utils.SnOn
* Radio style options.
*/
enum class RadioStyle(internal val className: String) {
- DEFAULT("radio-default"),
- PRIMARY("radio-primary"),
- SUCCESS("radio-success"),
- INFO("radio-info"),
- WARNING("radio-warning"),
- DANGER("radio-danger"),
+ PRIMARY("abc-radio-primary"),
+ SUCCESS("abc-radio-success"),
+ INFO("abc-radio-info"),
+ WARNING("abc-radio-warning"),
+ DANGER("abc-radio-danger"),
}
/**
@@ -56,7 +55,7 @@ enum class RadioStyle(internal val className: String) {
open class Radio(
value: Boolean = false, extraValue: String? = null, name: String? = null, label: String? = null,
rich: Boolean = false
-) : SimplePanel(), BoolFormControl {
+) : SimplePanel(classes = setOf("form-check")), BoolFormControl {
/**
* The selection state of the radio button.
@@ -115,20 +114,20 @@ open class Radio(
var inline by refreshOnUpdate(false)
private val idc = "kv_form_radio_$counter"
- final override val input: RadioInput = RadioInput(value).apply {
+ final override val input: RadioInput = RadioInput(value, classes = setOf("form-check-input")).apply {
this.id = idc
this.extraValue = extraValue
this.name = name
}
- final override val flabel: FieldLabel = FieldLabel(idc, label, rich, classes = setOf())
- final override val validationInfo: HelpBlock = HelpBlock().apply { visible = false }
+ final override val flabel: FieldLabel = FieldLabel(idc, label, rich, classes = setOf("form-check-label"))
+ final override val invalidFeedback: InvalidFeedback = InvalidFeedback().apply { visible = false }
init {
@Suppress("LeakingThis")
input.eventTarget = this.eventTarget ?: this
this.addInternal(input)
this.addInternal(flabel)
- this.addInternal(validationInfo)
+ this.addInternal(invalidFeedback)
counter++
}
@@ -151,25 +150,21 @@ open class Radio(
override fun getSnClass(): List<StringBoolPair> {
val cl = super.getSnClass().toMutableList()
if (!squared) {
- cl.add("radio" to true)
+ cl.add("abc-radio" to true)
style?.let {
cl.add(it.className to true)
}
- if (inline) {
- cl.add("radio-inline" to true)
- }
} else {
- cl.add("checkbox" to true)
- cl.add("kv-radio-checkbox" to true)
+ cl.add("abc-checkbox" to true)
style?.let {
cl.add(it.className.replace("radio", "checkbox") to true)
}
- if (inline) {
- cl.add("checkbox-inline" to true)
- }
+ }
+ if (inline) {
+ cl.add("form-check-inline" to true)
}
if (validatorError != null) {
- cl.add("has-error" to true)
+ cl.add("text-danger" to true)
}
return cl
}
@@ -194,6 +189,21 @@ open class Radio(
input.blur()
}
+ override fun styleForHorizontalFormPanel() {
+ addCssClass("form-group")
+ addSurroundingCssClass("row")
+ addCssClass("offset-sm-2")
+ addCssClass("col-sm-10")
+ }
+
+ override fun styleForInlineFormPanel() {
+ addCssClass("form-group")
+ }
+
+ override fun styleForVerticalFormPanel() {
+ addCssClass("form-group")
+ }
+
companion object {
internal var counter = 0
diff --git a/src/main/kotlin/pl/treksoft/kvision/form/check/RadioGroup.kt b/src/main/kotlin/pl/treksoft/kvision/form/check/RadioGroup.kt
index 6ac58bb7..14b426a3 100644
--- a/src/main/kotlin/pl/treksoft/kvision/form/check/RadioGroup.kt
+++ b/src/main/kotlin/pl/treksoft/kvision/form/check/RadioGroup.kt
@@ -25,9 +25,10 @@ import pl.treksoft.kvision.core.Container
import pl.treksoft.kvision.core.StringBoolPair
import pl.treksoft.kvision.core.StringPair
import pl.treksoft.kvision.form.FieldLabel
-import pl.treksoft.kvision.form.HelpBlock
import pl.treksoft.kvision.form.InputSize
+import pl.treksoft.kvision.form.InvalidFeedback
import pl.treksoft.kvision.form.StringFormControl
+import pl.treksoft.kvision.form.ValidationStatus
import pl.treksoft.kvision.panel.SimplePanel
/**
@@ -97,13 +98,33 @@ open class RadioGroup(
set(value) {
setSizeToChildren(value)
}
+ override var validationStatus
+ get() = getValidationStatusFromChildren()
+ set(value) {
+ setValidationStatusToChildren(value)
+ }
+ override var validatorError: String?
+ get() = super.validatorError
+ set(value) {
+ super.validatorError = value
+ if (value != null) {
+ container.addCssClass("is-invalid")
+ } else {
+ container.removeCssClass("is-invalid")
+ }
+ }
private val idc = "kv_form_radiogroup_$counter"
final override val input = RadioInput()
final override val flabel: FieldLabel = FieldLabel(idc, label, rich)
- final override val validationInfo: HelpBlock = HelpBlock().apply { visible = false }
+ final override val invalidFeedback: InvalidFeedback = InvalidFeedback().apply { visible = false }
+
+ internal val container = SimplePanel(setOf("kv-radiogroup-container"))
init {
+ this.addInternal(flabel)
+ this.addInternal(container)
+ this.addInternal(invalidFeedback)
setChildrenFromOptions()
setValueToChildren(value)
setNameToChildren(name)
@@ -113,7 +134,7 @@ open class RadioGroup(
override fun getSnClass(): List<StringBoolPair> {
val cl = super.getSnClass().toMutableList()
if (validatorError != null) {
- cl.add("has-error" to true)
+ cl.add("text-danger" to true)
}
if (inline) {
cl.add("kv-radiogroup-inline" to true)
@@ -124,7 +145,7 @@ open class RadioGroup(
}
private fun setValueToChildren(value: String?) {
- val radios = getChildren().filterIsInstance<Radio>()
+ val radios = container.getChildren().filterIsInstance<Radio>()
radios.forEach { it.value = false }
radios.find {
it.extraValue == value
@@ -132,34 +153,42 @@ open class RadioGroup(
}
private fun getDisabledFromChildren(): Boolean {
- return getChildren().filterIsInstance<Radio>().firstOrNull()?.disabled ?: false
+ return container.getChildren().filterIsInstance<Radio>().firstOrNull()?.disabled ?: false
}
private fun setDisabledToChildren(disabled: Boolean) {
- getChildren().filterIsInstance<Radio>().forEach { it.disabled = disabled }
+ container.getChildren().filterIsInstance<Radio>().forEach { it.disabled = disabled }
}
private fun getNameFromChildren(): String? {
- return getChildren().filterIsInstance<Radio>().firstOrNull()?.name ?: this.idc
+ return container.getChildren().filterIsInstance<Radio>().firstOrNull()?.name ?: this.idc
}
private fun setNameToChildren(name: String?) {
val tname = name ?: this.idc
- getChildren().filterIsInstance<Radio>().forEach { it.name = tname }
+ container.getChildren().filterIsInstance<Radio>().forEach { it.name = tname }
}
private fun getSizeFromChildren(): InputSize? {
- return getChildren().filterIsInstance<Radio>().firstOrNull()?.size
+ return container.getChildren().filterIsInstance<Radio>().firstOrNull()?.size
}
private fun setSizeToChildren(size: InputSize?) {
- getChildren().filterIsInstance<Radio>().forEach { it.size = size }
+ container.getChildren().filterIsInstance<Radio>().forEach { it.size = size }
+ super.size = size
+ }
+
+ private fun getValidationStatusFromChildren(): ValidationStatus? {
+ return container.getChildren().filterIsInstance<Radio>().firstOrNull()?.validationStatus
+ }
+
+ private fun setValidationStatusToChildren(validationStatus: ValidationStatus?) {
+ container.getChildren().filterIsInstance<Radio>().forEach { it.validationStatus = validationStatus }
}
private fun setChildrenFromOptions() {
val currentName = this.name
- super.removeAll()
- this.addInternal(flabel)
+ container.removeAll()
options?.let {
val tname = currentName ?: this.idc
val tinline = this.inline
@@ -175,17 +204,25 @@ open class RadioGroup(
}
}
}
- super.addAll(c)
+ container.addAll(c)
}
- this.addInternal(validationInfo)
}
override fun focus() {
- getChildren().filterIsInstance<Radio>().firstOrNull()?.focus()
+ container.getChildren().filterIsInstance<Radio>().firstOrNull()?.focus()
}
override fun blur() {
- getChildren().filterIsInstance<Radio>().firstOrNull()?.blur()
+ container.getChildren().filterIsInstance<Radio>().firstOrNull()?.blur()
+ }
+
+ override fun styleForHorizontalFormPanel() {
+ addCssClass("row")
+ flabel.addCssClass("col-sm-2")
+ flabel.addCssClass("col-form-label")
+ container.addCssClass("col-sm-10")
+ invalidFeedback.addCssClass("offset-sm-2")
+ invalidFeedback.addCssClass("col-sm-10")
}
companion object {
diff --git a/src/main/kotlin/pl/treksoft/kvision/form/check/RadioGroupInput.kt b/src/main/kotlin/pl/treksoft/kvision/form/check/RadioGroupInput.kt
index fca681f6..d4301709 100644
--- a/src/main/kotlin/pl/treksoft/kvision/form/check/RadioGroupInput.kt
+++ b/src/main/kotlin/pl/treksoft/kvision/form/check/RadioGroupInput.kt
@@ -26,6 +26,7 @@ import pl.treksoft.kvision.core.StringBoolPair
import pl.treksoft.kvision.core.StringPair
import pl.treksoft.kvision.form.FormInput
import pl.treksoft.kvision.form.InputSize
+import pl.treksoft.kvision.form.ValidationStatus
import pl.treksoft.kvision.panel.SimplePanel
/**
@@ -75,6 +76,11 @@ open class RadioGroupInput(
set(value) {
setSizeToChildren(value)
}
+ override var validationStatus
+ get() = getValidationStatusFromChildren()
+ set(value) {
+ setValidationStatusToChildren(value)
+ }
private val idc = "kv_form_radiogroup_$counter"
@@ -128,6 +134,14 @@ open class RadioGroupInput(
getChildren().filterIsInstance<Radio>().forEach { it.size = size }
}
+ private fun getValidationStatusFromChildren(): ValidationStatus? {
+ return getChildren().filterIsInstance<Radio>().firstOrNull()?.validationStatus
+ }
+
+ private fun setValidationStatusToChildren(validationStatus: ValidationStatus?) {
+ getChildren().filterIsInstance<Radio>().forEach { it.validationStatus = validationStatus }
+ }
+
private fun setChildrenFromOptions() {
val currentName = this.name
super.removeAll()
diff --git a/src/main/kotlin/pl/treksoft/kvision/form/select/SimpleSelect.kt b/src/main/kotlin/pl/treksoft/kvision/form/select/SimpleSelect.kt
index 4d278ad2..bef14bfa 100644
--- a/src/main/kotlin/pl/treksoft/kvision/form/select/SimpleSelect.kt
+++ b/src/main/kotlin/pl/treksoft/kvision/form/select/SimpleSelect.kt
@@ -27,7 +27,7 @@ import pl.treksoft.kvision.core.StringBoolPair
import pl.treksoft.kvision.core.StringPair
import pl.treksoft.kvision.core.Widget
import pl.treksoft.kvision.form.FieldLabel
-import pl.treksoft.kvision.form.HelpBlock
+import pl.treksoft.kvision.form.InvalidFeedback
import pl.treksoft.kvision.form.StringFormControl
import pl.treksoft.kvision.panel.SimplePanel
import pl.treksoft.kvision.utils.SnOn
@@ -117,21 +117,21 @@ open class SimpleSelect(
this.name = name
}
final override val flabel: FieldLabel = FieldLabel(idc, label, rich)
- final override val validationInfo: HelpBlock = HelpBlock().apply { visible = false }
+ final override val invalidFeedback: InvalidFeedback = InvalidFeedback().apply { visible = false }
init {
@Suppress("LeakingThis")
input.eventTarget = this
this.addInternal(flabel)
this.addInternal(input)
- this.addInternal(validationInfo)
+ this.addInternal(invalidFeedback)
counter++
}
override fun getSnClass(): List<StringBoolPair> {
val cl = super.getSnClass().toMutableList()
if (validatorError != null) {
- cl.add("has-error" to true)
+ cl.add("text-danger" to true)
}
return cl
}
diff --git a/src/main/kotlin/pl/treksoft/kvision/form/select/SimpleSelectInput.kt b/src/main/kotlin/pl/treksoft/kvision/form/select/SimpleSelectInput.kt
index b2ba1d43..31d32052 100644
--- a/src/main/kotlin/pl/treksoft/kvision/form/select/SimpleSelectInput.kt
+++ b/src/main/kotlin/pl/treksoft/kvision/form/select/SimpleSelectInput.kt
@@ -27,6 +27,7 @@ import pl.treksoft.kvision.core.StringBoolPair
import pl.treksoft.kvision.core.StringPair
import pl.treksoft.kvision.form.FormInput
import pl.treksoft.kvision.form.InputSize
+import pl.treksoft.kvision.form.ValidationStatus
import pl.treksoft.kvision.html.TAG
import pl.treksoft.kvision.html.Tag
import pl.treksoft.kvision.panel.SimplePanel
@@ -83,6 +84,10 @@ open class SimpleSelectInput(
* The size of the input.
*/
override var size: InputSize? by refreshOnUpdate()
+ /**
+ * The validation status of the input.
+ */
+ override var validationStatus: ValidationStatus? by refreshOnUpdate()
init {
setChildrenFromOptions()
@@ -129,6 +134,9 @@ open class SimpleSelectInput(
override fun getSnClass(): List<StringBoolPair> {
val cl = super.getSnClass().toMutableList()
+ validationStatus?.let {
+ cl.add(it.className to true)
+ }
size?.let {
cl.add(it.className to true)
}
diff --git a/src/main/kotlin/pl/treksoft/kvision/form/text/AbstractText.kt b/src/main/kotlin/pl/treksoft/kvision/form/text/AbstractText.kt
index 7c23615a..091278fc 100644
--- a/src/main/kotlin/pl/treksoft/kvision/form/text/AbstractText.kt
+++ b/src/main/kotlin/pl/treksoft/kvision/form/text/AbstractText.kt
@@ -24,7 +24,7 @@ package pl.treksoft.kvision.form.text
import pl.treksoft.kvision.core.StringBoolPair
import pl.treksoft.kvision.core.Widget
import pl.treksoft.kvision.form.FieldLabel
-import pl.treksoft.kvision.form.HelpBlock
+import pl.treksoft.kvision.form.InvalidFeedback
import pl.treksoft.kvision.form.StringFormControl
import pl.treksoft.kvision.panel.SimplePanel
import pl.treksoft.kvision.utils.SnOn
@@ -114,7 +114,7 @@ abstract class AbstractText(label: String? = null, rich: Boolean = false) :
protected val idc = "kv_form_text_$counter"
abstract override val input: AbstractTextInput
final override val flabel: FieldLabel = FieldLabel(idc, label, rich)
- final override val validationInfo: HelpBlock = HelpBlock().apply { visible = false }
+ final override val invalidFeedback: InvalidFeedback = InvalidFeedback().apply { visible = false }
init {
this.addInternal(flabel)
@@ -128,7 +128,7 @@ abstract class AbstractText(label: String? = null, rich: Boolean = false) :
override fun getSnClass(): List<StringBoolPair> {
val cl = super.getSnClass().toMutableList()
if (validatorError != null) {
- cl.add("has-error" to true)
+ cl.add("text-danger" to true)
}
return cl
}
diff --git a/src/main/kotlin/pl/treksoft/kvision/form/text/AbstractTextInput.kt b/src/main/kotlin/pl/treksoft/kvision/form/text/AbstractTextInput.kt
index e7ecba8a..192b33c8 100644
--- a/src/main/kotlin/pl/treksoft/kvision/form/text/AbstractTextInput.kt
+++ b/src/main/kotlin/pl/treksoft/kvision/form/text/AbstractTextInput.kt
@@ -27,6 +27,7 @@ import pl.treksoft.kvision.core.StringPair
import pl.treksoft.kvision.core.Widget
import pl.treksoft.kvision.form.FormInput
import pl.treksoft.kvision.form.InputSize
+import pl.treksoft.kvision.form.ValidationStatus
/**
* Base class for basic text components.
@@ -87,9 +88,16 @@ abstract class AbstractTextInput(
* The size of the input.
*/
override var size: InputSize? by refreshOnUpdate()
+ /**
+ * The validation status of the input.
+ */
+ override var validationStatus: ValidationStatus? by refreshOnUpdate()
override fun getSnClass(): List<StringBoolPair> {
val cl = super.getSnClass().toMutableList()
+ validationStatus?.let {
+ cl.add(it.className to true)
+ }
size?.let {
cl.add(it.className to true)
}
diff --git a/src/main/kotlin/pl/treksoft/kvision/form/text/Text.kt b/src/main/kotlin/pl/treksoft/kvision/form/text/Text.kt
index 7e4127f5..f493b06e 100644
--- a/src/main/kotlin/pl/treksoft/kvision/form/text/Text.kt
+++ b/src/main/kotlin/pl/treksoft/kvision/form/text/Text.kt
@@ -64,7 +64,7 @@ open class Text(
@Suppress("LeakingThis")
input.eventTarget = this
this.addInternal(input)
- this.addInternal(validationInfo)
+ this.addInternal(invalidFeedback)
}
companion object {
diff --git a/src/main/kotlin/pl/treksoft/kvision/form/text/TextArea.kt b/src/main/kotlin/pl/treksoft/kvision/form/text/TextArea.kt
index d5058583..ccf17892 100644
--- a/src/main/kotlin/pl/treksoft/kvision/form/text/TextArea.kt
+++ b/src/main/kotlin/pl/treksoft/kvision/form/text/TextArea.kt
@@ -73,7 +73,7 @@ open class TextArea(
@Suppress("LeakingThis")
input.eventTarget = this
this.addInternal(input)
- this.addInternal(validationInfo)
+ this.addInternal(invalidFeedback)
}
companion object {
diff --git a/src/main/kotlin/pl/treksoft/kvision/html/Button.kt b/src/main/kotlin/pl/treksoft/kvision/html/Button.kt
index 0b6b43e5..aaa0f735 100644
--- a/src/main/kotlin/pl/treksoft/kvision/html/Button.kt
+++ b/src/main/kotlin/pl/treksoft/kvision/html/Button.kt
@@ -33,13 +33,23 @@ import pl.treksoft.kvision.core.Widget
* Button styles.
*/
enum class ButtonStyle(val className: String) {
- DEFAULT("btn-default"),
PRIMARY("btn-primary"),
+ SECONDARY("btn-secondary"),
SUCCESS("btn-success"),
- INFO("btn-info"),
- WARNING("btn-warning"),
DANGER("btn-danger"),
- LINK("btn-link")
+ WARNING("btn-warning"),
+ INFO("btn-info"),
+ LIGHT("btn-light"),
+ DARK("btn-dark"),
+ LINK("btn-link"),
+ OUTLINEPRIMARY("btn-outline-primary"),
+ OUTLINESECONDARY("btn-outline-secondary"),
+ OUTLINESUCCESS("btn-outline-success"),
+ OUTLINEDANGER("btn-outline-danger"),
+ OUTLINEWARNING("btn-outline-warning"),
+ OUTLINEINFO("btn-outline-info"),
+ OUTLINELIGHT("btn-outline-light"),
+ OUTLINEDARK("btn-outline-dark")
}
/**
@@ -47,8 +57,7 @@ enum class ButtonStyle(val className: String) {
*/
enum class ButtonSize(internal val className: String) {
LARGE("btn-lg"),
- SMALL("btn-sm"),
- XSMALL("btn-xs")
+ SMALL("btn-sm")
}
/**
@@ -71,7 +80,7 @@ enum class ButtonType(internal val buttonType: String) {
* @param classes a set of CSS class names
*/
open class Button(
- text: String, icon: String? = null, style: ButtonStyle = ButtonStyle.DEFAULT, type: ButtonType = ButtonType.BUTTON,
+ text: String, icon: String? = null, style: ButtonStyle = ButtonStyle.PRIMARY, type: ButtonType = ButtonType.BUTTON,
disabled: Boolean = false, classes: Set<String> = setOf()
) : Widget(classes) {
@@ -123,14 +132,16 @@ open class Button(
if (block) {
cl.add("btn-block" to true)
}
- if (disabled) {
- cl.add("disabled" to true)
- }
return cl
}
override fun getSnAttrs(): List<StringPair> {
- return super.getSnAttrs() + ("type" to type.buttonType)
+ val snattrs = super.getSnAttrs().toMutableList()
+ snattrs.add("type" to type.buttonType)
+ if (disabled) {
+ snattrs.add("disabled" to "disabled")
+ }
+ return snattrs
}
/**
@@ -154,7 +165,7 @@ open class Button(
fun Container.button(
text: String,
icon: String? = null,
- style: ButtonStyle = ButtonStyle.DEFAULT,
+ style: ButtonStyle = ButtonStyle.PRIMARY,
type: ButtonType = ButtonType.BUTTON,
disabled: Boolean = false,
classes: Set<String> = setOf(),
diff --git a/src/main/kotlin/pl/treksoft/kvision/html/Icon.kt b/src/main/kotlin/pl/treksoft/kvision/html/Icon.kt
index 7e10c10f..a9fb03db 100644
--- a/src/main/kotlin/pl/treksoft/kvision/html/Icon.kt
+++ b/src/main/kotlin/pl/treksoft/kvision/html/Icon.kt
@@ -39,13 +39,7 @@ open class Icon(icon: String) : Tag(TAG.SPAN) {
override fun getSnClass(): List<StringBoolPair> {
val cl = super.getSnClass().toMutableList()
- if (icon.startsWith("fa-")) {
- cl.add("fa" to true)
- cl.add(icon to true)
- } else {
- cl.add("glyphicon" to true)
- cl.add("glyphicon-$icon" to true)
- }
+ icon.split(" ").forEach { cl.add(it to true) }
return cl
}
diff --git a/src/main/kotlin/pl/treksoft/kvision/html/Image.kt b/src/main/kotlin/pl/treksoft/kvision/html/Image.kt
index 4d373270..81873088 100644
--- a/src/main/kotlin/pl/treksoft/kvision/html/Image.kt
+++ b/src/main/kotlin/pl/treksoft/kvision/html/Image.kt
@@ -32,8 +32,8 @@ import pl.treksoft.kvision.core.Widget
* Image shapes.
*/
enum class ImageShape(internal val className: String) {
- ROUNDED("img-rounded"),
- CIRCLE("img-circle"),
+ ROUNDED("rounded"),
+ CIRCLE("rounded-circle"),
THUMBNAIL("img-thumbnail")
}
@@ -89,7 +89,7 @@ open class Image(
override fun getSnClass(): List<StringBoolPair> {
val cl = super.getSnClass().toMutableList()
if (responsive) {
- cl.add("img-responsive" to true)
+ cl.add("img-fluid" to true)
}
if (centered) {
cl.add("center-block" to true)
diff --git a/src/main/kotlin/pl/treksoft/kvision/html/Label.kt b/src/main/kotlin/pl/treksoft/kvision/html/Label.kt
deleted file mode 100644
index 827c90fd..00000000
--- a/src/main/kotlin/pl/treksoft/kvision/html/Label.kt
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright (c) 2017-present Robert Jaros
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in all
- * copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-package pl.treksoft.kvision.html
-
-import pl.treksoft.kvision.core.Container
-
-/**
- * Simple label component rendered as *span*.
- *
- * @constructor
- * @param content label text
- * @param rich determines if [content] can contain HTML code
- */
-@Deprecated("Use Span class instead.")
-open class Label(content: String? = null, rich: Boolean = false) : Span(content, rich) {
- companion object {
- /**
- * DSL builder extension function.
- *
- * It takes the same parameters as the constructor of the built component.
- */
- @Deprecated("User Span.Companion.span function instead.")
- @Suppress("DEPRECATION")
- fun Container.label(
- content: String? = null, rich: Boolean = false, init: (Label.() -> Unit)? = null
- ): Label {
- val label = Label(content, rich).apply { init?.invoke(this) }
- this.add(label)
- return label
- }
- }
-}
diff --git a/src/main/kotlin/pl/treksoft/kvision/html/Link.kt b/src/main/kotlin/pl/treksoft/kvision/html/Link.kt
index 5fc613e2..63104248 100644
--- a/src/main/kotlin/pl/treksoft/kvision/html/Link.kt
+++ b/src/main/kotlin/pl/treksoft/kvision/html/Link.kt
@@ -26,7 +26,6 @@ import org.w3c.dom.events.MouseEvent
import pl.treksoft.kvision.core.Container
import pl.treksoft.kvision.core.ResString
import pl.treksoft.kvision.core.StringPair
-import pl.treksoft.kvision.dropdown.DropDown
import pl.treksoft.kvision.panel.SimplePanel
/**
@@ -99,37 +98,5 @@ open class Link(
this.add(link)
return link
}
-
- /**
- * DSL builder extension function for a disabled link in a dropdown list.
- *
- * It takes the same parameters as the constructor of the built component.
- */
- fun ListTag.linkDisabled(
- label: String, icon: String? = null, image: ResString? = null,
- classes: Set<String> = setOf(), init: (Link.() -> Unit)? = null
- ): Link {
- val link = Link(label, null, icon, image, classes).apply { init?.invoke(this) }
- val tag = Tag(TAG.LI, classes = setOf("disabled"))
- tag.add(link)
- this.add(tag)
- return link
- }
-
- /**
- * DSL builder extension function for a disabled link in a dropdown list.
- *
- * It takes the same parameters as the constructor of the built component.
- */
- fun DropDown.linkDisabled(
- label: String, icon: String? = null, image: ResString? = null,
- classes: Set<String> = setOf(), init: (Link.() -> Unit)? = null
- ): Link {
- val link = Link(label, "javascript:void(0)", icon, image, classes).apply { init?.invoke(this) }
- val tag = Tag(TAG.LI, classes = setOf("disabled"))
- tag.add(link)
- this.add(tag)
- return link
- }
}
}
diff --git a/src/main/kotlin/pl/treksoft/kvision/html/List.kt b/src/main/kotlin/pl/treksoft/kvision/html/List.kt
index 5fb489da..cf3f8be6 100644
--- a/src/main/kotlin/pl/treksoft/kvision/html/List.kt
+++ b/src/main/kotlin/pl/treksoft/kvision/html/List.kt
@@ -27,7 +27,6 @@ import pl.treksoft.kvision.KVManager
import pl.treksoft.kvision.core.Component
import pl.treksoft.kvision.core.Container
import pl.treksoft.kvision.core.StringBoolPair
-import pl.treksoft.kvision.dropdown.DropDown
import pl.treksoft.kvision.panel.SimplePanel
/**
@@ -98,14 +97,14 @@ open class ListTag(
val childrenElements = children.filter { it.visible }
val res = when (type) {
ListType.UL, ListType.OL, ListType.UNSTYLED, ListType.INLINE -> childrenElements.map { v ->
- if (v is Tag && v.type == TAG.LI || v is DropDown && v.forNavbar) {
+ if (v is Tag && v.type == TAG.LI /*|| v is DropDown && v.forNavbar*/) {
v.renderVNode()
} else {
h("li", arrayOf(v.renderVNode()))
}
}
ListType.DL, ListType.DL_HORIZ -> childrenElements.mapIndexed { index, v ->
- if (v is Tag && v.type == TAG.LI || v is DropDown && v.forNavbar) {
+ if (v is Tag && v.type == TAG.LI /*|| v is DropDown && v.forNavbar*/) {
v.renderVNode()
} else {
h(if (index % 2 == 0) "dt" else "dd", arrayOf(v.renderVNode()))
diff --git a/src/main/kotlin/pl/treksoft/kvision/html/Tag.kt b/src/main/kotlin/pl/treksoft/kvision/html/Tag.kt
index 512ca8b8..a5e855ae 100644
--- a/src/main/kotlin/pl/treksoft/kvision/html/Tag.kt
+++ b/src/main/kotlin/pl/treksoft/kvision/html/Tag.kt
@@ -73,6 +73,12 @@ enum class TAG(internal val tagName: String) {
BR("br"),
CAPTION("caption"),
+ FIGURE("figure"),
+ FIGCAPTION("figcaption"),
+ PICTURE("figcaption"),
+ SOURCE("figcaption"),
+
+ TABLE("table"),
THEAD("thead"),
TH("th"),
TBODY("tbody"),
diff --git a/src/main/kotlin/pl/treksoft/kvision/modal/Alert.kt b/src/main/kotlin/pl/treksoft/kvision/modal/Alert.kt
deleted file mode 100644
index 5f5a1a80..00000000
--- a/src/main/kotlin/pl/treksoft/kvision/modal/Alert.kt
+++ /dev/null
@@ -1,121 +0,0 @@
-/*
- * Copyright (c) 2017-present Robert Jaros
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in all
- * copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-package pl.treksoft.kvision.modal
-
-import pl.treksoft.kvision.core.Widget
-import pl.treksoft.kvision.html.Align
-import pl.treksoft.kvision.html.ButtonStyle
-import pl.treksoft.kvision.html.Button
-import pl.treksoft.kvision.html.TAG
-import pl.treksoft.kvision.html.Tag
-import pl.treksoft.kvision.utils.ENTER_KEY
-
-/**
- * Alert window based on Bootstrap modal.
- *
- * @constructor
- * @param caption window title
- * @param text window content text.
- * @param rich determines if [text] can contain HTML code
- * @param align text align
- * @param size modal window size
- * @param animation determines if animations are used
- * @param callback a function called after closing window with OK button
- */
-open class Alert(
- caption: String? = null, text: String? = null, rich: Boolean = false,
- align: Align? = null, size: ModalSize? = null, animation: Boolean = true,
- private val callback: (() -> Unit)? = null
-) : Modal(caption, true, size, animation) {
-
- /**
- * Window content text.
- */
- var text
- get() = contentTag.content
- set(value) {
- contentTag.content = value
- }
- /**
- * Determines if [text] can contain HTML code.
- */
- var rich
- get() = contentTag.rich
- set(value) {
- contentTag.rich = value
- }
- /**
- * Text align.
- */
- var align
- get() = contentTag.align
- set(value) {
- contentTag.align = value
- }
-
- private val contentTag = Tag(TAG.DIV, text, rich, align)
-
- init {
- body.add(contentTag)
- val okButton = Button("OK", "ok", ButtonStyle.PRIMARY)
- okButton.setEventListener {
- click = {
- hide()
- }
- }
- this.addButton(okButton)
- this.setEventListener {
- keydown = { e ->
- if (e.keyCode == ENTER_KEY) {
- hide()
- }
- }
- }
- }
-
- override fun hide(): Widget {
- super.hide()
- this.callback?.invoke()
- return this
- }
-
- companion object {
- /**
- * Helper function for opening Alert window.
- * @param caption window title
- * @param text window content text.
- * @param rich determines if [text] can contain HTML code
- * @param align text align
- * @param size modal window size
- * @param animation determines if animations are used
- * @param callback a function called after closing window with OK button
- */
- @Suppress("LongParameterList")
- fun show(
- caption: String? = null, text: String? = null, rich: Boolean = false,
- align: Align? = null, size: ModalSize? = null, animation: Boolean = true,
- callback: (() -> Unit)? = null
- ) {
- Alert(caption, text, rich, align, size, animation, callback).show()
- }
- }
-}
diff --git a/src/main/kotlin/pl/treksoft/kvision/modal/Confirm.kt b/src/main/kotlin/pl/treksoft/kvision/modal/Confirm.kt
deleted file mode 100644
index e16ca87e..00000000
--- a/src/main/kotlin/pl/treksoft/kvision/modal/Confirm.kt
+++ /dev/null
@@ -1,176 +0,0 @@
-/*
- * Copyright (c) 2017-present Robert Jaros
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in all
- * copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-package pl.treksoft.kvision.modal
-
-import pl.treksoft.kvision.html.Align
-import pl.treksoft.kvision.html.Button
-import pl.treksoft.kvision.html.ButtonStyle
-import pl.treksoft.kvision.html.TAG
-import pl.treksoft.kvision.html.Tag
-
-/**
- * Confirm window based on Bootstrap modal.
- *
- * @constructor
- * @param caption window title
- * @param text window content text.
- * @param rich determines if [text] can contain HTML code
- * @param align text align
- * @param size modal window size
- * @param animation determines if animations are used
- * @param cancelVisible determines if Cancel button is visible
- * @param yesTitle yes button text
- * @param noTitle no button text
- * @param cancelTitle cancel button text
- * @param noCallback a function called after closing window with No button
- * @param yesCallback a function called after closing window with Yes button
- */
-open class Confirm(
- caption: String? = null, text: String? = null, rich: Boolean = false,
- align: Align? = null, size: ModalSize? = null, animation: Boolean = true,
- cancelVisible: Boolean = false, yesTitle: String = "Yes", noTitle: String = "No", cancelTitle: String = "Cancel",
- private val noCallback: (() -> Unit)? = null,
- private val yesCallback: (() -> Unit)? = null
-) : Modal(caption, false, size, animation, false) {
- /**
- * Window content text.
- */
- var text
- get() = contentTag.content
- set(value) {
- contentTag.content = value
- }
- /**
- * Determines if [text] can contain HTML code.
- */
- var rich
- get() = contentTag.rich
- set(value) {
- contentTag.rich = value
- }
- /**
- * Text align.
- */
- var align
- get() = contentTag.align
- set(value) {
- contentTag.align = value
- }
- /**
- * Determines if Cancel button is visible.
- */
- var cancelVisible by refreshOnUpdate(cancelVisible) { refreshCancelButton() }
-
- /**
- * Yes button text.
- */
- var yesTitle
- get() = yesButton.text
- set(value) {
- yesButton.text = value
- }
-
- /**
- * No button text.
- */
- var noTitle
- get() = noButton.text
- set(value) {
- noButton.text = value
- }
-
- /**
- * Cancel button text.
- */
- var cancelTitle
- get() = cancelButton.text
- set(value) {
- cancelButton.text = value
- }
-
- private val contentTag = Tag(TAG.DIV, text, rich, align)
- private val cancelButton = Button(cancelTitle, "remove")
- private val noButton = Button(noTitle, "ban-circle")
- private val yesButton = Button(yesTitle, "ok", ButtonStyle.PRIMARY)
-
- init {
- body.add(contentTag)
- cancelButton.setEventListener {
- click = {
- hide()
- }
- }
- this.addButton(cancelButton)
- noButton.setEventListener {
- click = {
- hide()
- noCallback?.invoke()
- }
- }
- this.addButton(noButton)
- yesButton.setEventListener {
- click = {
- hide()
- yesCallback?.invoke()
- }
- }
- this.addButton(yesButton)
- refreshCancelButton()
- }
-
- private fun refreshCancelButton() {
- if (cancelVisible) {
- cancelButton.show()
- closeIcon.show()
- } else {
- cancelButton.hide()
- closeIcon.hide()
- }
- }
-
- companion object {
- /**
- * Helper function for opening Confirm window.
- * @param caption window title
- * @param text window content text.
- * @param rich determines if [text] can contain HTML code
- * @param align text align
- * @param size modal window size
- * @param animation determines if animations are used
- * @param cancelVisible determines if Cancel button is visible
- * @param noCallback a function called after closing window with No button
- * @param yesCallback a function called after closing window with Yes button
- */
- @Suppress("LongParameterList")
- fun show(
- caption: String? = null, text: String? = null, rich: Boolean = false,
- align: Align? = null, size: ModalSize? = null, animation: Boolean = true,
- cancelVisible: Boolean = false, yesTitle: String = "Yes", noTitle: String = "No",
- cancelTitle: String = "Cancel", noCallback: (() -> Unit)? = null, yesCallback: (() -> Unit)? = null
- ) {
- Confirm(
- caption, text, rich, align, size, animation, cancelVisible, yesTitle, noTitle, cancelTitle,
- noCallback, yesCallback
- ).show()
- }
- }
-}
diff --git a/src/main/kotlin/pl/treksoft/kvision/modal/Modal.kt b/src/main/kotlin/pl/treksoft/kvision/modal/Modal.kt
deleted file mode 100644
index 7f120f73..00000000
--- a/src/main/kotlin/pl/treksoft/kvision/modal/Modal.kt
+++ /dev/null
@@ -1,298 +0,0 @@
-/*
- * Copyright (c) 2017-present Robert Jaros
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in all
- * copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-package pl.treksoft.kvision.modal
-
-import com.github.snabbdom.VNode
-import pl.treksoft.kvision.core.Component
-import pl.treksoft.kvision.core.Container
-import pl.treksoft.kvision.core.StringBoolPair
-import pl.treksoft.kvision.core.StringPair
-import pl.treksoft.kvision.core.Widget
-import pl.treksoft.kvision.html.Button
-import pl.treksoft.kvision.html.TAG
-import pl.treksoft.kvision.html.Tag
-import pl.treksoft.kvision.panel.Root
-import pl.treksoft.kvision.panel.SimplePanel
-import pl.treksoft.kvision.utils.obj
-
-/**
- * Modal window sizes.
- */
-enum class ModalSize(val className: String) {
- LARGE("modal-lg"),
- SMALL("modal-sm")
-}
-
-/**
- * Configurable modal window based on Bootstrap modal.
- *
- * @constructor
- * @param caption window title
- * @param closeButton determines if Close button is visible
- * @param size modal window size
- * @param animation determines if animations are used
- * @param escape determines if dialog can be closed with Esc key
- * @param classes a set of CSS class names
- * @param init an initializer extension function
- */
-@Suppress("TooManyFunctions")
-open class Modal(
- caption: String? = null, closeButton: Boolean = true,
- size: ModalSize? = null, animation: Boolean = true, private val escape: Boolean = true,
- classes: Set<String> = setOf(), init: (Modal.() -> Unit)? = null
-) : SimplePanel(classes) {
-
- override var parent: Container? = Root.getFirstRoot()
-
- /**
- * Window caption text.
- */
- var caption
- get() = captionTag.content
- set(value) {
- captionTag.content = value
- checkHeaderVisibility()
- }
- /**
- * Determines if Close button is visible.
- */
- var closeButton
- get() = closeIcon.visible
- set(value) {
- closeIcon.visible = value
- checkHeaderVisibility()
- }
- /**
- * Window size.
- */
- var size
- get() = dialog.size
- set(value) {
- dialog.size = value
- }
- /**
- * Determines if animations are used.
- */
- var animation by refreshOnUpdate(animation)
-
- private val dialog = ModalDialog(size)
- private val header = SimplePanel(setOf("modal-header"))
- /**
- * @suppress
- * Internal property.
- */
- protected val closeIcon = CloseIcon()
- private val captionTag = Tag(TAG.H4, caption, classes = setOf("modal-title"))
- /**
- * @suppress
- * Internal property.
- */
- protected val body = SimplePanel(setOf("modal-body"))
- private val footer = SimplePanel(setOf("modal-footer"))
-
- init {
- this.hide()
- this.role = "dialog"
- this.addInternal(dialog)
- val content = SimplePanel(setOf("modal-content"))
- dialog.role = "document"
- dialog.add(content)
- closeIcon.visible = closeButton
- closeIcon.setEventListener {
- click = {
- hide()
- }
- }
- header.add(closeIcon)
- header.add(captionTag)
- checkHeaderVisibility()
- content.add(header)
- content.add(body)
- content.add(footer)
- @Suppress("LeakingThis")
- modals.add(this)
- @Suppress("LeakingThis")
- init?.invoke(this)
- }
-
- private fun checkHeaderVisibility() {
- if (!closeButton && caption == null) {
- header.hide()
- } else {
- header.show()
- }
- }
-
- override fun add(child: Component): SimplePanel {
- body.add(child)
- return this
- }
-
- override fun addAll(children: List<Component>): SimplePanel {
- body.addAll(children)
- return this
- }
-
- override fun remove(child: Component): SimplePanel {
- body.remove(child)
- return this
- }
-
- override fun removeAll(): SimplePanel {
- body.removeAll()
- return this
- }
-
- override fun getChildren(): List<Component> {
- return body.getChildren()
- }
-
- /**
- * Adds given button to the bottom section of dialog window.
- * @param button a [Button] component
- * @return this modal
- */
- open fun addButton(button: Button): Modal {
- footer.add(button)
- return this
- }
-
- /**
- * Removes given button from the bottom section of dialog window.
- * @param button a [Button] component
- * @return this modal
- */
- open fun removeButton(button: Button): Modal {
- footer.remove(button)
- return this
- }
-
- /**
- * Removes all buttons from the bottom section of dialog window.
- * @return this modal
- */
- open fun removeAllButtons(): Modal {
- footer.removeAll()
- return this
- }
-
- override fun getSnAttrs(): List<StringPair> {
- val pr = super.getSnAttrs().toMutableList()
- pr.add("tabindex" to "-1")
- return pr
- }
-
- override fun getSnClass(): List<StringBoolPair> {
- val cl = super.getSnClass().toMutableList()
- cl.add("modal" to true)
- if (animation) {
- cl.add("fade" to true)
- }
- return cl
- }
-
- @Suppress("UnsafeCastFromDynamic")
- override fun afterInsert(node: VNode) {
- getElementJQueryD()?.modal(obj {
- keyboard = escape
- backdrop = if (escape) "true" else "static"
- })
- this.getElementJQuery()?.on("show.bs.modal") { e, _ ->
- this.dispatchEvent("showBsModal", obj { detail = e })
- }
- this.getElementJQuery()?.on("shown.bs.modal") { e, _ ->
- this.dispatchEvent("shownBsModal", obj { detail = e })
- }
- this.getElementJQuery()?.on("hide.bs.modal") { e, _ ->
- this.dispatchEvent("hideBsModal", obj { detail = e })
- }
- this.getElementJQuery()?.on("hidden.bs.modal") { e, _ ->
- this.visible = false
- hide()
- this.dispatchEvent("hiddenBsModal", obj { detail = e })
- }
- }
-
- override fun hide(): Widget {
- if (visible) hideInternal()
- return super.hide()
- }
-
- /**
- * Toggle modal window visibility.
- */
- open fun toggle() {
- if (visible)
- hide()
- else
- show()
- }
-
- @Suppress("UnsafeCastFromDynamic")
- private fun showInternal() {
- getElementJQueryD()?.modal("show")
- }
-
- @Suppress("UnsafeCastFromDynamic")
- private fun hideInternal() {
- getElementJQueryD()?.modal("hide")
- }
-
- override fun clearParent(): Widget {
- this.parent = null
- return this
- }
-
- override fun getRoot(): Root? {
- return this.parent?.getRoot()
- }
-
- override fun dispose() {
- modals.remove(this)
- }
-
- companion object {
- internal var modals = mutableListOf<Modal>()
- }
-}
-
-/**
- * Internal helper class for modal content.
- *
- * @constructor
- * @param size modal window size
- */
-internal class ModalDialog(size: ModalSize?) : SimplePanel(setOf("modal-dialog")) {
-
- /**
- * Modal window size.
- */
- var size by refreshOnUpdate(size)
-
- override fun getSnClass(): List<StringBoolPair> {
- val cl = super.getSnClass().toMutableList()
- size?.let {
- cl.add(it.className to true)
- }
- return cl
- }
-}
diff --git a/src/main/kotlin/pl/treksoft/kvision/navbar/Nav.kt b/src/main/kotlin/pl/treksoft/kvision/navbar/Nav.kt
deleted file mode 100644
index 3bbb6eed..00000000
--- a/src/main/kotlin/pl/treksoft/kvision/navbar/Nav.kt
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * Copyright (c) 2017-present Robert Jaros
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in all
- * copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-package pl.treksoft.kvision.navbar
-
-import pl.treksoft.kvision.core.StringBoolPair
-import pl.treksoft.kvision.html.TAG
-import pl.treksoft.kvision.html.Tag
-
-/**
- * The Bootstrap Nav container.
- *
- * @constructor
- * @param rightAlign determines if the nav is aligned to the right
- * @param classes a set of CSS class names
- * @param init an initializer extension function
- */
-open class Nav(rightAlign: Boolean = false, classes: Set<String> = setOf(), init: (Nav.() -> Unit)? = null) :
- Tag(TAG.UL, classes = classes) {
-
- /**
- * Determines if the nav is aligned to the right.
- */
- var rightAlign by refreshOnUpdate(rightAlign)
-
- init {
- @Suppress("LeakingThis")
- init?.invoke(this)
- }
-
- override fun getSnClass(): List<StringBoolPair> {
- val cl = super.getSnClass().toMutableList()
- cl.add("nav" to true)
- cl.add("navbar-nav" to true)
- if (rightAlign) {
- cl.add("navbar-right" to true)
- }
- return cl
- }
-
- companion object {
- /**
- * DSL builder extension function.
- *
- * It takes the same parameters as the constructor of the built component.
- */
- fun Navbar.nav(
- rightAlign: Boolean = false, classes: Set<String> = setOf(), init: (Nav.() -> Unit)? = null
- ): Nav {
- val nav = Nav(rightAlign, classes).apply { init?.invoke(this) }
- this.add(nav)
- return nav
- }
- }
-}
diff --git a/src/main/kotlin/pl/treksoft/kvision/navbar/NavForm.kt b/src/main/kotlin/pl/treksoft/kvision/navbar/NavForm.kt
deleted file mode 100644
index 725c298e..00000000
--- a/src/main/kotlin/pl/treksoft/kvision/navbar/NavForm.kt
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * Copyright (c) 2017-present Robert Jaros
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in all
- * copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-package pl.treksoft.kvision.navbar
-
-import pl.treksoft.kvision.core.StringBoolPair
-import pl.treksoft.kvision.html.TAG
-import pl.treksoft.kvision.html.Tag
-
-/**
- * The Bootstrap Nav form container.
- *
- * @constructor
- * @param rightAlign determines if the nav form is aligned to the right
- * @param classes a set of CSS class names
- * @param init an initializer extension function
- */
-open class NavForm(rightAlign: Boolean = false, classes: Set<String> = setOf(), init: (NavForm.() -> Unit)? = null) :
- Tag(TAG.FORM, classes = classes) {
-
- /**
- * Determines if the nav form is aligned to the right.
- */
- var rightAlign by refreshOnUpdate(rightAlign)
-
- init {
- @Suppress("LeakingThis")
- init?.invoke(this)
- }
-
- override fun getSnClass(): List<StringBoolPair> {
- val cl = super.getSnClass().toMutableList()
- cl.add("navbar-form" to true)
- if (rightAlign) {
- cl.add("navbar-right" to true)
- } else {
- cl.add("navbar-left" to true)
- }
- return cl
- }
-
- companion object {
- /**
- * DSL builder extension function.
- *
- * It takes the same parameters as the constructor of the built component.
- */
- fun Navbar.navForm(
- rightAlign: Boolean = false, classes: Set<String> = setOf(), init: (NavForm.() -> Unit)? = null
- ): NavForm {
- val navForm = NavForm(rightAlign, classes).apply { init?.invoke(this) }
- this.add(navForm)
- return navForm
- }
- }
-}
diff --git a/src/main/kotlin/pl/treksoft/kvision/navbar/Navbar.kt b/src/main/kotlin/pl/treksoft/kvision/navbar/Navbar.kt
deleted file mode 100644
index 2080bb8e..00000000
--- a/src/main/kotlin/pl/treksoft/kvision/navbar/Navbar.kt
+++ /dev/null
@@ -1,195 +0,0 @@
-/*
- * Copyright (c) 2017-present Robert Jaros
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in all
- * copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-package pl.treksoft.kvision.navbar
-
-import com.github.snabbdom.VNode
-import pl.treksoft.kvision.core.Component
-import pl.treksoft.kvision.core.Container
-import pl.treksoft.kvision.core.StringBoolPair
-import pl.treksoft.kvision.core.StringPair
-import pl.treksoft.kvision.html.Link
-import pl.treksoft.kvision.html.TAG
-import pl.treksoft.kvision.html.Tag.Companion.tag
-import pl.treksoft.kvision.panel.SimplePanel
-
-/**
- * Navbar types.
- */
-enum class NavbarType(internal val navbarType: String) {
- FIXEDTOP("navbar-fixed-top"),
- FIXEDBOTTOM("navbar-fixed-bottom"),
- STATICTOP("navbar-static-top")
-}
-
-/**
- * The Bootstrap Navbar container.
- *
- * @constructor
- * @param label the navbar label
- * @param type the navbar type
- * @param inverted determines if the navbar is inverted
- * @param classes a set of CSS class names
- * @param init an initializer extension function
- */
-open class Navbar(
- label: String? = null,
- type: NavbarType? = null,
- inverted: Boolean = false,
- classes: Set<String> = setOf(), init: (Navbar.() -> Unit)? = null
-) : SimplePanel(classes) {
-
- /**
- * The navbar header label.
- */
- var label
- get() = if (brandLink.visible) brandLink.label else null
- set(value) {
- if (value != null) {
- brandLink.label = value
- brandLink.show()
- } else {
- brandLink.hide()
- }
- }
-
- /**
- * The navbar type.
- */
- var type by refreshOnUpdate(type)
- /**
- * Determines if the navbar is inverted.
- */
- var inverted by refreshOnUpdate(inverted)
-
- private val idc = "kv_navbar_$counter"
-
- private val brandLink = Link(label ?: "", "#", classes = setOf("navbar-brand"))
- internal val container = SimplePanel(setOf("collapse", "navbar-collapse")) {
- id = idc
- }
-
- init {
- val c = SimplePanel(setOf("container-fluid")) {
- simplePanel(setOf("navbar-header")) {
- add(NavbarButton(idc))
- add(brandLink)
- }
- add(container)
- }
- addInternal(c)
- if (label == null) brandLink.hide()
- counter++
- @Suppress("LeakingThis")
- init?.invoke(this)
- }
-
- override fun render(): VNode {
- return render("nav", childrenVNodes())
- }
-
- override fun add(child: Component): Navbar {
- container.add(child)
- return this
- }
-
- override fun addAll(children: List<Component>): Navbar {
- container.addAll(children)
- return this
- }
-
- override fun remove(child: Component): Navbar {
- container.remove(child)
- return this
- }
-
- override fun removeAll(): Navbar {
- container.removeAll()
- return this
- }
-
- override fun getChildren(): List<Component> {
- return container.getChildren()
- }
-
- override fun getSnClass(): List<StringBoolPair> {
- val cl = super.getSnClass().toMutableList()
- cl.add("navbar" to true)
- type?.let {
- cl.add(it.navbarType to true)
- }
- if (inverted) {
- cl.add("navbar-inverse" to true)
- } else {
- cl.add("navbar-default" to true)
- }
- return cl
- }
-
- companion object {
- internal var counter = 0
-
- /**
- * DSL builder extension function.
- *
- * It takes the same parameters as the constructor of the built component.
- */
- fun Container.navbar(
- label: String? = null,
- type: NavbarType? = null,
- inverted: Boolean = false,
- classes: Set<String> = setOf(), init: (Navbar.() -> Unit)? = null
- ): Navbar {
- val navbar = Navbar(label, type, inverted, classes, init)
- this.add(navbar)
- return navbar
- }
- }
-}
-
-/**
- * @suppress
- * Internal component.
- * The Bootstrap Navbar header button.
- */
-internal class NavbarButton(private val idc: String, toggle: String = "Toggle navigation") :
- SimplePanel(setOf("navbar-toggle", "collapsed")) {
-
- init {
- tag(TAG.SPAN, toggle, classes = setOf("sr-only"))
- tag(TAG.SPAN, classes = setOf("icon-bar"))
- tag(TAG.SPAN, classes = setOf("icon-bar"))
- tag(TAG.SPAN, classes = setOf("icon-bar"))
- }
-
- override fun render(): VNode {
- return render("button", childrenVNodes())
- }
-
- override fun getSnAttrs(): List<StringPair> {
- return super.getSnAttrs() + listOf(
- "type" to "button",
- "data-toggle" to "collapse",
- "data-target" to "#$idc",
- "aria-expanded" to "false"
- )
- }
-}
diff --git a/src/main/kotlin/pl/treksoft/kvision/panel/ResponsiveGridPanel.kt b/src/main/kotlin/pl/treksoft/kvision/panel/ResponsiveGridPanel.kt
deleted file mode 100644
index 7a5b07d6..00000000
--- a/src/main/kotlin/pl/treksoft/kvision/panel/ResponsiveGridPanel.kt
+++ /dev/null
@@ -1,185 +0,0 @@
-/*
- * Copyright (c) 2017-present Robert Jaros
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in all
- * copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-package pl.treksoft.kvision.panel
-
-import pl.treksoft.kvision.core.Component
-import pl.treksoft.kvision.core.Container
-import pl.treksoft.kvision.core.WidgetWrapper
-import pl.treksoft.kvision.html.Align
-import pl.treksoft.kvision.html.TAG
-import pl.treksoft.kvision.html.Tag
-
-/**
- * Bootstrap grid sizes.
- */
-enum class GridSize(internal val size: String) {
- XS("xs"),
- SM("sm"),
- MD("md"),
- LG("lg")
-}
-
-internal const val MAX_COLUMNS = 12
-
-internal data class WidgetParam(val widget: Component, val size: Int, val offset: Int)
-
-/**
- * The container with support for Bootstrap responsive grid layout.
- *
- * @constructor
- * @param gridSize grid size
- * @param rows number of rows
- * @param cols number of columns
- * @param align text align of grid cells
- * @param classes a set of CSS class names
- * @param init an initializer extension function
- */
-open class ResponsiveGridPanel(
- private val gridSize: GridSize = GridSize.MD,
- private var rows: Int = 0, private var cols: Int = 0, align: Align? = null,
- classes: Set<String> = setOf(), init: (ResponsiveGridPanel.() -> Unit)? = null
-) : SimplePanel(classes + "container-fluid") {
-
- /**
- * Text align of grid cells.
- */
- var align by refreshOnUpdate(align) { refreshRowContainers() }
-
- internal val map = mutableMapOf<Int, MutableMap<Int, WidgetParam>>()
- private var auto: Boolean = true
-
- init {
- @Suppress("LeakingThis")
- init?.invoke(this)
- }
-
- /**
- * Adds child component to the grid.
- * @param child child component
- * @param col column number
- * @param row row number
- * @param size cell size (colspan)
- * @param offset cell offset
- * @return this container
- */
- open fun add(child: Component, col: Int, row: Int, size: Int = 0, offset: Int = 0): ResponsiveGridPanel {
- val cRow = maxOf(row, 0)
- val cCol = maxOf(col, 0)
- if (row > rows - 1) rows = cRow + 1
- if (col > cols - 1) cols = cCol + 1
- map.getOrPut(cRow) { mutableMapOf() }[cCol] = WidgetParam(child, size, offset)
- if (size > 0 || offset > 0) auto = false
- refreshRowContainers()
- return this
- }
-
- override fun add(child: Component): ResponsiveGridPanel {
- return this.add(child, this.cols, 0)
- }
-
- override fun addAll(children: List<Component>): ResponsiveGridPanel {
- children.forEach { this.add(it) }
- return this
- }
-
- @Suppress("NestedBlockDepth")
- override fun remove(child: Component): ResponsiveGridPanel {
- map.values.forEach { row ->
- row.filterValues { it.widget == child }
- .forEach { (i, _) -> row.remove(i) }
- }
- refreshRowContainers()
- return this
- }
-
- /**
- * Removes child component at given location (column, row).
- * @param col column number
- * @param row row number
- * @return this container
- */
- open fun removeAt(col: Int, row: Int): ResponsiveGridPanel {
- map[row]?.remove(col)
- refreshRowContainers()
- return this
- }
-
- @Suppress("ComplexMethod", "NestedBlockDepth")
- private fun refreshRowContainers() {
- singleRender {
- clearRowContainers()
- val num = MAX_COLUMNS / cols
- for (i in 0 until rows) {
- val rowContainer = SimplePanel(setOf("row"))
- val row = map[i]
- if (row != null) {
- (0 until cols).map { row[it] }.forEach { wp ->
- if (auto) {
- val widget = wp?.widget?.let {
- WidgetWrapper(it, setOf("col-" + gridSize.size + "-" + num))
- } ?: Tag(TAG.DIV, classes = setOf("col-" + gridSize.size + "-" + num))
- align?.let {
- widget.addCssClass(it.className)
- }
- rowContainer.add(widget)
- } else {
- if (wp != null) {
- val s = if (wp.size > 0) wp.size else num
- val widget = WidgetWrapper(wp.widget, setOf("col-" + gridSize.size + "-" + s))
- if (wp.offset > 0) {
- widget.addCssClass("col-" + gridSize.size + "-offset-" + wp.offset)
- }
- align?.let {
- widget.addCssClass(it.className)
- }
- rowContainer.add(widget)
- }
- }
- }
- }
- addInternal(rowContainer)
- }
- }
- }
-
- private fun clearRowContainers() {
- children.forEach { it.dispose() }
- removeAll()
- }
-
- companion object {
- /**
- * DSL builder extension function.
- *
- * It takes the same parameters as the constructor of the built component.
- */
- fun Container.responsiveGridPanel(
- gridSize: GridSize = GridSize.MD,
- rows: Int = 0, cols: Int = 0, align: Align? = null,
- classes: Set<String> = setOf(), init: (ResponsiveGridPanel.() -> Unit)? = null
- ): ResponsiveGridPanel {
- val responsiveGridPanel = ResponsiveGridPanel(gridSize, rows, cols, align, classes, init)
- this.add(responsiveGridPanel)
- return responsiveGridPanel
- }
- }
-}
diff --git a/src/main/kotlin/pl/treksoft/kvision/panel/Root.kt b/src/main/kotlin/pl/treksoft/kvision/panel/Root.kt
index 2d9dcc46..e0c70ac4 100644
--- a/src/main/kotlin/pl/treksoft/kvision/panel/Root.kt
+++ b/src/main/kotlin/pl/treksoft/kvision/panel/Root.kt
@@ -27,8 +27,7 @@ import org.w3c.dom.HTMLElement
import pl.treksoft.kvision.KVManager
import pl.treksoft.kvision.core.StringBoolPair
import pl.treksoft.kvision.core.Style
-import pl.treksoft.kvision.dropdown.ContextMenu
-import pl.treksoft.kvision.modal.Modal
+import pl.treksoft.kvision.core.Widget
import pl.treksoft.kvision.utils.snClasses
import pl.treksoft.kvision.utils.snOpt
@@ -52,7 +51,7 @@ class Root(
private val fixed: Boolean = false,
init: (Root.() -> Unit)? = null
) : SimplePanel() {
- private val contextMenus: MutableList<ContextMenu> = mutableListOf()
+ private val contextMenus: MutableList<Widget> = mutableListOf()
private var rootVnode: VNode = renderVNode()
internal var renderDisabled = false
@@ -71,7 +70,7 @@ class Root(
}
roots.add(this)
if (isFirstRoot) {
- Modal.modals.forEach { it.parent = this }
+ modals.forEach { it.parent = this }
}
@Suppress("LeakingThis")
init?.invoke(this)
@@ -87,7 +86,7 @@ class Root(
}
}
- internal fun addContextMenu(contextMenu: ContextMenu) {
+ fun addContextMenu(contextMenu: Widget) {
contextMenus.add(contextMenu)
contextMenu.parent = this
this.setInternalEventListener<Root> {
@@ -114,7 +113,7 @@ class Root(
private fun modalsVNodes(): Array<VNode> {
return if (isFirstRoot) {
- Modal.modals.filter { it.visible }.map { it.renderVNode() }.toTypedArray()
+ modals.filter { it.visible }.map { it.renderVNode() }.toTypedArray()
} else {
arrayOf()
}
@@ -150,12 +149,13 @@ class Root(
roots.remove(this)
if (isFirstRoot) {
Style.styles.clear()
- Modal.modals.clear()
+ modals.clear()
}
}
companion object {
internal var counter = 0
+ private val modals: MutableList<Widget> = mutableListOf()
/**
* @suppress internal function
@@ -167,18 +167,26 @@ class Root(
internal val roots: MutableList<Root> = mutableListOf()
- internal fun getFirstRoot(): Root? {
+ fun getFirstRoot(): Root? {
return if (roots.isNotEmpty())
roots[0]
else
null
}
- internal fun getLastRoot(): Root? {
+ fun getLastRoot(): Root? {
return if (roots.isNotEmpty())
roots[roots.size - 1]
else
null
}
+
+ fun addModal(modal: Widget) {
+ modals.add(modal)
+ }
+
+ fun removeModal(modal: Widget) {
+ modals.remove(modal)
+ }
}
}
diff --git a/src/main/kotlin/pl/treksoft/kvision/panel/SimplePanel.kt b/src/main/kotlin/pl/treksoft/kvision/panel/SimplePanel.kt
index 2f702fad..91eeda80 100644
--- a/src/main/kotlin/pl/treksoft/kvision/panel/SimplePanel.kt
+++ b/src/main/kotlin/pl/treksoft/kvision/panel/SimplePanel.kt
@@ -35,7 +35,7 @@ import pl.treksoft.kvision.core.Widget
*/
open class SimplePanel(classes: Set<String> = setOf(), init: (SimplePanel.() -> Unit)? = null) : Widget(classes),
Container {
- internal val children: MutableList<Component> = mutableListOf()
+ protected val children: MutableList<Component> = mutableListOf()
init {
@Suppress("LeakingThis")
@@ -94,7 +94,7 @@ open class SimplePanel(classes: Set<String> = setOf(), init: (SimplePanel.() ->
}
override fun getChildren(): List<Component> {
- return ArrayList(children)
+ return children
}
override fun dispose() {
diff --git a/src/main/kotlin/pl/treksoft/kvision/panel/TabPanel.kt b/src/main/kotlin/pl/treksoft/kvision/panel/TabPanel.kt
deleted file mode 100644
index f620f2b0..00000000
--- a/src/main/kotlin/pl/treksoft/kvision/panel/TabPanel.kt
+++ /dev/null
@@ -1,270 +0,0 @@
-/*
- * Copyright (c) 2017-present Robert Jaros
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in all
- * copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-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.Icon
-import pl.treksoft.kvision.html.Link.Companion.link
-import pl.treksoft.kvision.html.TAG
-import pl.treksoft.kvision.html.Tag
-import pl.treksoft.kvision.routing.routing
-import pl.treksoft.kvision.utils.obj
-import pl.treksoft.kvision.html.Icon.Companion.icon as cicon
-
-/**
- * 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 scrollableTabs determines if tabs are scrollable (default: false)
- * @param classes a set of CSS class names
- * @param init an initializer extension function
- */
-open class TabPanel(
- private val tabPosition: TabPosition = TabPosition.TOP,
- private val sideTabSize: SideTabSize = SideTabSize.SIZE_3,
- scrollableTabs: Boolean = false,
- classes: Set<String> = setOf(),
- init: (TabPanel.() -> Unit)? = null
-) : SimplePanel(classes) {
-
- /**
- * The index of active (visible) tab.
- */
- var activeIndex
- get() = content.activeIndex
- set(value) {
- if (content.activeIndex != value) {
- content.activeIndex = value
- nav.children.forEach {
- it.removeCssClass("active")
- }
- if (content.activeIndex in nav.children.indices) {
- nav.children[content.activeIndex].addCssClass("active")
- }
- }
- }
- private val navClasses = when (tabPosition) {
- TabPosition.TOP -> if (scrollableTabs) setOf("nav", "nav-tabs", "tabs-top") else 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)
-
- internal val childrenMap = mutableMapOf<Int, Component>()
-
- init {
- 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<String, String> {
- 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
- * @param panel child component
- * @param icon icon of the tab
- * @param image image of the tab
- * @param closable determines if this tab is closable
- * @param route JavaScript route to activate given child
- * @return current container
- */
- open fun addTab(
- title: String, panel: Component, icon: String? = null,
- image: ResString? = null, closable: Boolean = false, route: String? = null
- ): TabPanel {
- val currentIndex = counter++
- childrenMap[currentIndex] = panel
- val tag = Tag(TAG.LI) {
- role = "presentation"
- link(title, "#", icon, image) {
- if (closable) {
- cicon("remove") {
- addCssClass("kv-tab-close")
- setEventListener<Icon> {
- click = { e ->
- val actIndex = this@TabPanel.content.children.indexOf(childrenMap[currentIndex])
- e.asDynamic().data = actIndex
- @Suppress("UnsafeCastFromDynamic")
- if (this@TabPanel.dispatchEvent(
- "tabClosing",
- obj { detail = e; cancelable = true }) != false
- ) {
- this@TabPanel.removeTab(actIndex)
- this@TabPanel.dispatchEvent("tabClosed", obj { detail = e })
- }
- e.stopPropagation()
- }
- }
- }
- }
- }
- setEventListener {
- click = { e ->
- activeIndex = this@TabPanel.content.children.indexOf(childrenMap[currentIndex])
- e.preventDefault()
- if (route != null) {
- routing.navigate(route)
- }
- }
- }
- }
- nav.add(tag)
- if (nav.children.size == 1) {
- tag.addCssClass("active")
- activeIndex = 0
- }
- content.add(panel)
- if (route != null) {
- routing.on(route, { _ -> activeIndex = this@TabPanel.content.children.indexOf(childrenMap[currentIndex]) })
- .resolve()
- }
- return this
- }
-
- /**
- * Removes tab at given index.
- */
- open fun removeTab(index: Int): TabPanel {
- nav.remove(nav.children[index])
- childrenMap.filter { it.value == content.children[index] }.keys.firstOrNull()?.let {
- childrenMap.remove(it)
- }
- content.remove(content.children[index])
- activeIndex = content.activeIndex
- return this
- }
-
- override fun add(child: Component): TabPanel {
- return addTab("", child)
- }
-
- override fun addAll(children: List<Component>): TabPanel {
- children.forEach { add(it) }
- return this
- }
-
- override fun remove(child: Component): TabPanel {
- val index = content.children.indexOf(child)
- return removeTab(index)
- }
-
- /**
- * Returns child component by tab index.
- * @param index tab index
- */
- open fun getChildComponent(index: Int): Component? {
- return content.children[index]
- }
-
- /**
- * Returns tab header component by tab index.
- * @param index tab index
- */
- open fun getNavComponent(index: Int): Tag? {
- return nav.children[index] as? Tag
- }
-
- override fun removeAll(): TabPanel {
- content.removeAll()
- nav.removeAll()
- childrenMap.clear()
- refresh()
- return this
- }
-
- companion object {
- internal var counter = 0
- /**
- * DSL builder extension function.
- *
- * It takes the same parameters as the constructor of the built component.
- */
- fun Container.tabPanel(
- tabPosition: TabPosition = TabPosition.TOP,
- sideTabSize: SideTabSize = SideTabSize.SIZE_3,
- scrollableTabs: Boolean = false,
- classes: Set<String> = setOf(),
- init: (TabPanel.() -> Unit)? = null
- ): TabPanel {
- val tabPanel = TabPanel(tabPosition, sideTabSize, scrollableTabs, classes, init)
- this.add(tabPanel)
- return tabPanel
- }
- }
-}
diff --git a/src/main/kotlin/pl/treksoft/kvision/progress/ProgressBar.kt b/src/main/kotlin/pl/treksoft/kvision/progress/ProgressBar.kt
deleted file mode 100644
index 4d0f4b93..00000000
--- a/src/main/kotlin/pl/treksoft/kvision/progress/ProgressBar.kt
+++ /dev/null
@@ -1,162 +0,0 @@
-/*
- * Copyright (c) 2017-present Robert Jaros
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in all
- * copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-package pl.treksoft.kvision.progress
-
-import pl.treksoft.kvision.core.Container
-import pl.treksoft.kvision.html.Align
-import pl.treksoft.kvision.panel.SimplePanel
-
-/**
- * The Bootstrap progress bar.
- *
- * @constructor
- * @param progress the current progress
- * @param min the minimal progress
- * @param max the maximal progress
- * @param style the style of the progress bar
- * @param striped determines if the progress bar is striped
- * @param animated determines if the progress bar is animated
- * @param content element text
- * @param rich determines if content can contain HTML code
- * @param align content align
- * @param classes a set of CSS class names
- * @param init an initializer extension function
- */
-open class ProgressBar(
- progress: Int, min: Int = DEFAULT_MIN, max: Int = DEFAULT_MAX, style: ProgressBarStyle? = null,
- striped: Boolean = false, animated: Boolean = false, content: String? = null,
- rich: Boolean = false, align: Align? = null,
- classes: Set<String> = setOf(), init: (ProgressBar.() -> Unit)? = null
-) :
- SimplePanel(classes + "progress") {
-
- /**
- * The current progress.
- */
- var progress
- get() = indicator.progress
- set(value) {
- indicator.progress = value
- }
- /**
- * The minimal progress.
- */
- var min
- get() = indicator.min
- set(value) {
- indicator.min = value
- }
- /**
- * The maximal progress.
- */
- var max
- get() = indicator.max
- set(value) {
- indicator.max = value
- }
- /**
- * The style of the progress bar.
- */
- var style
- get() = indicator.style
- set(value) {
- indicator.style = value
- }
- /**
- * Determines if the progress bar is striped.
- */
- var striped
- get() = indicator.striped
- set(value) {
- indicator.striped = value
- }
- /**
- * Determines if the progress bar is animated.
- */
- var animated
- get() = indicator.animated
- set(value) {
- indicator.animated = value
- }
- /**
- * Text content of the progress bar.
- */
- var content
- get() = indicator.content
- set(value) {
- indicator.content = value
- }
- /**
- * Determines if [content] can contain HTML code.
- */
- var rich
- get() = indicator.rich
- set(value) {
- indicator.rich = value
- }
- /**
- * Text align of the progress bar.
- */
- var align
- get() = indicator.align
- set(value) {
- indicator.align = value
- }
-
- internal val indicator = ProgressIndicator(progress, min, max, style, striped, animated, content, rich, align)
-
- init {
- addInternal(indicator)
-
- @Suppress("LeakingThis")
- init?.invoke(this)
- }
-
- companion object {
- /**
- * DSL builder extension function.
- *
- * It takes the same parameters as the constructor of the built component.
- */
- fun Container.progressBar(
- progress: Int, min: Int = DEFAULT_MIN, max: Int = DEFAULT_MAX, style: ProgressBarStyle? = null,
- striped: Boolean = false, animated: Boolean = false,
- content: String? = null, rich: Boolean = false, align: Align? = null,
- classes: Set<String> = setOf(), init: (ProgressBar.() -> Unit)? = null
- ): ProgressBar {
- val progressBar = ProgressBar(
- progress,
- min,
- max,
- style,
- striped,
- animated,
- content,
- rich,
- align,
- classes
- ).apply { init?.invoke(this) }
- this.add(progressBar)
- return progressBar
- }
- }
-}
diff --git a/src/main/kotlin/pl/treksoft/kvision/progress/ProgressIndicator.kt b/src/main/kotlin/pl/treksoft/kvision/progress/ProgressIndicator.kt
deleted file mode 100644
index 256d15d7..00000000
--- a/src/main/kotlin/pl/treksoft/kvision/progress/ProgressIndicator.kt
+++ /dev/null
@@ -1,125 +0,0 @@
-/*
- * Copyright (c) 2017-present Robert Jaros
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in all
- * copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-package pl.treksoft.kvision.progress
-
-import pl.treksoft.kvision.core.StringBoolPair
-import pl.treksoft.kvision.core.StringPair
-import pl.treksoft.kvision.html.Align
-import pl.treksoft.kvision.html.Div
-import pl.treksoft.kvision.utils.perc
-
-/**
- * Progress bar styles.
- */
-enum class ProgressBarStyle(internal val className: String) {
- SUCCESS("progress-bar-success"),
- INFO("progress-bar-info"),
- WARNING("progress-bar-warning"),
- DANGER("progress-bar-danger")
-}
-
-internal const val DEFAULT_MIN = 0
-internal const val DEFAULT_MAX = 100
-
-
-/**
- * The Bootstrap progress bar indicator.
- *
- * @constructor
- * @param progress the current progress
- * @param min the minimal progress
- * @param max the maximal progress
- * @param style the style of the progress bar indicator
- * @param striped determines if the progress bar indicator is striped
- * @param animated determines if the progress bar indicator is animated
- * @param content element text
- * @param rich determines if [content] can contain HTML code
- * @param align content align
- * @param classes a set of CSS class names
- */
-internal class ProgressIndicator(
- progress: Int, min: Int = DEFAULT_MIN, max: Int = DEFAULT_MAX, style: ProgressBarStyle? = null,
- striped: Boolean = false, animated: Boolean = false,
- content: String? = null, rich: Boolean = false, align: Align? = null,
- classes: Set<String> = setOf()
-) :
- Div(content, rich, align, classes) {
-
- /**
- * The current progress.
- */
- var progress by refreshOnUpdate(progress) { refreshWidth() }
- /**
- * The minimal progress.
- */
- var min by refreshOnUpdate(min) { refreshWidth() }
- /**
- * The maximal progress.
- */
- var max by refreshOnUpdate(max) { refreshWidth() }
- /**
- * The style of the progress indicator.
- */
- var style by refreshOnUpdate(style)
- /**
- * Determines if the progress indicator is striped.
- */
- var striped by refreshOnUpdate(striped)
- /**
- * Determines if the progress indicator is animated.
- */
- var animated by refreshOnUpdate(animated)
-
- init {
- role = "progressbar"
- refreshWidth()
- }
-
- private fun refreshWidth() {
- val value = (if (max - min > 0) (progress - min) * DEFAULT_MAX.toFloat() / (max - min) else 0f).toInt()
- val percent = if (value < 0) 0 else if (value > DEFAULT_MAX) DEFAULT_MAX else value
- width = percent.perc
- }
-
- override fun getSnClass(): List<StringBoolPair> {
- val cl = super.getSnClass().toMutableList()
- cl.add("progress-bar" to true)
- style?.let {
- cl.add(it.className to true)
- }
- if (striped || animated) {
- cl.add("progress-bar-striped" to true)
- }
- if (animated) {
- cl.add("active" to true)
- }
- return cl
- }
-
- override fun getSnAttrs(): List<StringPair> {
- val sn = super.getSnAttrs().toMutableList()
- sn.add("aria-valuenow" to "$progress")
- sn.add("aria-valuemin" to "$min")
- sn.add("aria-valuemax" to "$max")
- return sn
- }
-}
diff --git a/src/main/kotlin/pl/treksoft/kvision/table/Cell.kt b/src/main/kotlin/pl/treksoft/kvision/table/Cell.kt
index 0a79c9f9..44646897 100644
--- a/src/main/kotlin/pl/treksoft/kvision/table/Cell.kt
+++ b/src/main/kotlin/pl/treksoft/kvision/table/Cell.kt
@@ -64,6 +64,22 @@ open class Cell(
this.add(cell)
return cell
}
+
+ /**
+ * DSL builder extension function.
+ *
+ * It takes the same parameters as the constructor of the built component.
+ */
+ fun Row.thcell(
+ content: String? = null,
+ rich: Boolean = false,
+ align: Align? = null,
+ classes: Set<String> = setOf(), init: (HeaderCell.() -> Unit)? = null
+ ): HeaderCell {
+ val headerCell = HeaderCell(content, rich, align, Scope.ROW, classes, init)
+ this.add(headerCell)
+ return headerCell
+ }
}
}
diff --git a/src/main/kotlin/pl/treksoft/kvision/table/HeaderCell.kt b/src/main/kotlin/pl/treksoft/kvision/table/HeaderCell.kt
index b70845d6..14527f0f 100644
--- a/src/main/kotlin/pl/treksoft/kvision/table/HeaderCell.kt
+++ b/src/main/kotlin/pl/treksoft/kvision/table/HeaderCell.kt
@@ -25,6 +25,11 @@ import pl.treksoft.kvision.html.Align
import pl.treksoft.kvision.html.TAG
import pl.treksoft.kvision.html.Tag
+enum class Scope(internal val scope: String) {
+ ROW("row"),
+ COL("col")
+}
+
/**
* HTML table header cell component.
*
@@ -39,11 +44,16 @@ open class HeaderCell(
content: String? = null,
rich: Boolean = false,
align: Align? = null,
+ scope: Scope? = null,
classes: Set<String> = setOf(),
init: (HeaderCell.() -> Unit)? = null
) : Tag(TAG.TH, content, rich, align, classes) {
init {
+ scope?.let {
+ @Suppress("LeakingThis")
+ setAttribute("scope", it.scope)
+ }
@Suppress("LeakingThis")
init?.invoke(this)
}
@@ -58,9 +68,10 @@ open class HeaderCell(
content: String? = null,
rich: Boolean = false,
align: Align? = null,
+ scope: Scope? = null,
classes: Set<String> = setOf(), init: (HeaderCell.() -> Unit)? = null
): HeaderCell {
- val cell = HeaderCell(content, rich, align, classes, init)
+ val cell = HeaderCell(content, rich, align, scope, classes, init)
this.add(cell)
return cell
}
diff --git a/src/main/kotlin/pl/treksoft/kvision/table/Table.kt b/src/main/kotlin/pl/treksoft/kvision/table/Table.kt
index 11ed52fc..6d7c9b6e 100644
--- a/src/main/kotlin/pl/treksoft/kvision/table/Table.kt
+++ b/src/main/kotlin/pl/treksoft/kvision/table/Table.kt
@@ -38,8 +38,29 @@ import pl.treksoft.kvision.utils.snOpt
enum class TableType(val type: String) {
STRIPED("table-striped"),
BORDERED("table-bordered"),
+ BORDERLESS("table-borderless"),
HOVER("table-hover"),
- CONDENSED("table-condensed")
+ SMALL("table-sm"),
+ DARK("table-dark")
+}
+
+/**
+ * HTML table responsive types.
+ */
+enum class ResponsiveType(val type: String) {
+ RESPONSIVE("table-responsive"),
+ RESPONSIVESM("table-responsive-sm"),
+ RESPONSIVEMD("table-responsive-md"),
+ RESPONSIVELG("table-responsive-lg"),
+ RESPONSIVEXL("table-responsive-xl")
+}
+
+/**
+ * HTML table header types.
+ */
+enum class TheadType(internal val type: String) {
+ DARK("thead-dark"),
+ LIGHT("thead-light")
}
/**
@@ -56,8 +77,8 @@ enum class TableType(val type: String) {
@Suppress("TooManyFunctions")
open class Table(
headerNames: List<String>? = null,
- types: Set<TableType> = setOf(), caption: String? = null, responsive: Boolean = false,
- classes: Set<String> = setOf(), init: (Table.() -> Unit)? = null
+ types: Set<TableType> = setOf(), caption: String? = null, responsiveType: ResponsiveType? = null,
+ theadType: TheadType? = null, classes: Set<String> = setOf(), init: (Table.() -> Unit)? = null
) : SimplePanel(classes + "table") {
/**
@@ -75,10 +96,13 @@ open class Table(
/**
* Determines if the table is responsive.
*/
- var responsive by refreshOnUpdate(responsive)
+ var responsiveType by refreshOnUpdate(responsiveType)
private val theadRow = Tag(TAG.TR)
- private val thead = Tag(TAG.THEAD).add(theadRow)
+ private val thead = Tag(TAG.THEAD).apply {
+ if (theadType != null) addCssClass(theadType.type)
+ add(theadRow)
+ }
private val tbody = Tag(TAG.TBODY)
init {
@@ -94,7 +118,7 @@ open class Table(
private fun refreshHeaders() {
theadRow.removeAll()
headerNames?.forEach {
- theadRow.add(HeaderCell(it))
+ theadRow.add(HeaderCell(it, scope = Scope.COL))
}
}
@@ -128,9 +152,9 @@ open class Table(
}
override fun render(): VNode {
- return if (responsive) {
+ return if (responsiveType != null) {
val opt = snOpt {
- `class` = snClasses(listOf("table-responsive" to true))
+ `class` = snClasses(listOf(responsiveType!!.type to true))
}
h("div", opt, arrayOf(render("table", childrenVNodes())))
} else {
@@ -185,11 +209,11 @@ open class Table(
*/
fun Container.table(
headerNames: List<String>? = null,
- types: Set<TableType> = setOf(), caption: String? = null, responsive: Boolean = false,
- classes: Set<String> = setOf(), init: (Table.() -> Unit)? = null
+ types: Set<TableType> = setOf(), caption: String? = null, responsiveType: ResponsiveType? = null,
+ theadType: TheadType? = null, classes: Set<String> = setOf(), init: (Table.() -> Unit)? = null
): Table {
val table =
- Table(headerNames, types, caption, responsive, classes, init)
+ Table(headerNames, types, caption, responsiveType, theadType, classes, init)
this.add(table)
return table
}
diff --git a/src/main/kotlin/pl/treksoft/kvision/toolbar/ButtonGroup.kt b/src/main/kotlin/pl/treksoft/kvision/toolbar/ButtonGroup.kt
deleted file mode 100644
index ae57fc90..00000000
--- a/src/main/kotlin/pl/treksoft/kvision/toolbar/ButtonGroup.kt
+++ /dev/null
@@ -1,103 +0,0 @@
-/*
- * Copyright (c) 2017-present Robert Jaros
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in all
- * copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-package pl.treksoft.kvision.toolbar
-
-import pl.treksoft.kvision.core.Container
-import pl.treksoft.kvision.core.StringBoolPair
-import pl.treksoft.kvision.panel.SimplePanel
-
-/**
- * Button group sizes.
- */
-enum class ButtonGroupSize(internal val className: String) {
- LARGE("btn-group-lg"),
- SMALL("btn-group-sm"),
- XSMALL("btn-group-xs")
-}
-
-/**
- * Button group styles.
- */
-enum class ButtonGroupStyle(internal val className: String) {
- VERTICAL("btn-group-vertical"),
- JUSTIFIED("btn-group-justified")
-}
-
-/**
- * The Bootstrap button group.
- *
- * @constructor
- * @param size button group size
- * @param style button group style
- * @param classes a set of CSS class names
- * @param init an initializer extension function
- */
-open class ButtonGroup(
- size: ButtonGroupSize? = null, style: ButtonGroupStyle? = null,
- classes: Set<String> = setOf(), init: (ButtonGroup.() -> Unit)? = null
-) : SimplePanel(classes) {
-
- /**
- * Button group size.
- */
- var size by refreshOnUpdate(size)
- /**
- * Button group style.
- */
- var style by refreshOnUpdate(style)
-
- init {
- role = "group"
- @Suppress("LeakingThis")
- init?.invoke(this)
- }
-
- override fun getSnClass(): List<StringBoolPair> {
- val cl = super.getSnClass().toMutableList()
- if (style != ButtonGroupStyle.VERTICAL) {
- cl.add("btn-group" to true)
- }
- style?.let {
- cl.add(it.className to true)
- }
- size?.let {
- cl.add(it.className to true)
- }
- return cl
- }
-
- companion object {
- /**
- * DSL builder extension function.
- *
- * It takes the same parameters as the constructor of the built component.
- */
- fun Container.buttonGroup(
- size: ButtonGroupSize? = null, style: ButtonGroupStyle? = null,
- classes: Set<String> = setOf(), init: (ButtonGroup.() -> Unit)? = null
- ): ButtonGroup {
- val group = ButtonGroup(size, style, classes).apply { init?.invoke(this) }
- this.add(group)
- return group
- }
- }
-}
diff --git a/src/main/kotlin/pl/treksoft/kvision/toolbar/Toolbar.kt b/src/main/kotlin/pl/treksoft/kvision/toolbar/Toolbar.kt
deleted file mode 100644
index f348f4cc..00000000
--- a/src/main/kotlin/pl/treksoft/kvision/toolbar/Toolbar.kt
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright (c) 2017-present Robert Jaros
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in all
- * copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-package pl.treksoft.kvision.toolbar
-
-import pl.treksoft.kvision.core.Container
-import pl.treksoft.kvision.panel.SimplePanel
-
-/**
- * The Bootstrap toolbar.
- *
- * @constructor
- * @param classes a set of CSS class names
- * @param init an initializer extension function
- */
-open class Toolbar(
- classes: Set<String> = setOf(), init: (Toolbar.() -> Unit)? = null
-) : SimplePanel(classes + "btn-toolbar") {
-
- init {
- role = "toolbar"
- @Suppress("LeakingThis")
- init?.invoke(this)
- }
-
- companion object {
- /**
- * DSL builder extension function.
- *
- * It takes the same parameters as the constructor of the built component.
- */
- fun Container.toolbar(
- classes: Set<String> = setOf(), init: (Toolbar.() -> Unit)? = null
- ): Toolbar {
- val toolbar = Toolbar(classes).apply { init?.invoke(this) }
- this.add(toolbar)
- return toolbar
- }
- }
-}
diff --git a/src/main/kotlin/pl/treksoft/kvision/window/MaximizeIcon.kt b/src/main/kotlin/pl/treksoft/kvision/window/MaximizeIcon.kt
deleted file mode 100644
index a3ceaf61..00000000
--- a/src/main/kotlin/pl/treksoft/kvision/window/MaximizeIcon.kt
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright (c) 2017-present Robert Jaros
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in all
- * copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-package pl.treksoft.kvision.window
-
-import com.github.snabbdom.VNode
-import pl.treksoft.kvision.KVManager
-import pl.treksoft.kvision.core.StringBoolPair
-import pl.treksoft.kvision.core.StringPair
-import pl.treksoft.kvision.core.Widget
-
-/**
- * Helper class for maximize icon component.
- */
-open class MaximizeIcon : Widget(setOf()) {
-
- override fun render(): VNode {
- return render("button", arrayOf(KVManager.virtualize("<span aria-hidden='true'>&#x1f5d6;</span>")))
- }
-
- override fun getSnClass(): List<StringBoolPair> {
- val cl = super.getSnClass().toMutableList()
- cl.add("close" to true)
- return cl
- }
-
- override fun getSnAttrs(): List<StringPair> {
- return super.getSnAttrs() + listOf("type" to "button", "aria-label" to "Maximize")
- }
-}
diff --git a/src/main/kotlin/pl/treksoft/kvision/window/MinimizeIcon.kt b/src/main/kotlin/pl/treksoft/kvision/window/MinimizeIcon.kt
deleted file mode 100644
index c8034d09..00000000
--- a/src/main/kotlin/pl/treksoft/kvision/window/MinimizeIcon.kt
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright (c) 2017-present Robert Jaros
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in all
- * copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-package pl.treksoft.kvision.window
-
-import com.github.snabbdom.VNode
-import pl.treksoft.kvision.KVManager
-import pl.treksoft.kvision.core.StringBoolPair
-import pl.treksoft.kvision.core.StringPair
-import pl.treksoft.kvision.core.Widget
-
-/**
- * Helper class for minimize icon component.
- */
-open class MinimizeIcon : Widget(setOf()) {
-
- override fun render(): VNode {
- return render("button", arrayOf(KVManager.virtualize("<span aria-hidden='true'>&#x1f5d5;</span>")))
- }
-
- override fun getSnClass(): List<StringBoolPair> {
- val cl = super.getSnClass().toMutableList()
- cl.add("close" to true)
- return cl
- }
-
- override fun getSnAttrs(): List<StringPair> {
- return super.getSnAttrs() + listOf("type" to "button", "aria-label" to "Minimize")
- }
-}
diff --git a/src/main/kotlin/pl/treksoft/kvision/window/Window.kt b/src/main/kotlin/pl/treksoft/kvision/window/Window.kt
deleted file mode 100644
index 3816de3a..00000000
--- a/src/main/kotlin/pl/treksoft/kvision/window/Window.kt
+++ /dev/null
@@ -1,446 +0,0 @@
-/*
- * Copyright (c) 2017-present Robert Jaros
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in all
- * copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-package pl.treksoft.kvision.window
-
-import com.github.snabbdom.VNode
-import org.w3c.dom.events.Event
-import org.w3c.dom.events.MouseEvent
-import pl.treksoft.kvision.KVManager
-import pl.treksoft.kvision.core.Component
-import pl.treksoft.kvision.core.Container
-import pl.treksoft.kvision.core.CssSize
-import pl.treksoft.kvision.core.Overflow
-import pl.treksoft.kvision.core.Position
-import pl.treksoft.kvision.core.Resize
-import pl.treksoft.kvision.core.UNIT
-import pl.treksoft.kvision.html.Icon
-import pl.treksoft.kvision.html.TAG
-import pl.treksoft.kvision.html.Tag
-import pl.treksoft.kvision.modal.CloseIcon
-import pl.treksoft.kvision.panel.SimplePanel
-import pl.treksoft.kvision.utils.obj
-import pl.treksoft.kvision.utils.px
-
-internal const val DEFAULT_Z_INDEX = 900
-internal const val WINDOW_HEADER_HEIGHT = 40
-internal const val WINDOW_CONTENT_MARGIN_BOTTOM = 11
-
-/**
- * Floating window container.
- *
- * @constructor
- * @param caption window title
- * @param contentWidth window content width
- * @param contentHeight window content height
- * @param isResizable determines if the window is resizable
- * @param isDraggable determines if the window is draggable
- * @param closeButton determines if Close button is visible
- * @param maximizeButton determines if Maximize button is visible
- * @param minimizeButton determines if Minimize button is visible
- * @param classes a set of CSS class names
- * @param init an initializer extension function
- */
-@Suppress("TooManyFunctions")
-open class Window(
- caption: String? = null,
- contentWidth: CssSize? = CssSize(0, UNIT.auto),
- contentHeight: CssSize? = CssSize(0, UNIT.auto),
- isResizable: Boolean = true,
- isDraggable: Boolean = true,
- closeButton: Boolean = false,
- maximizeButton: Boolean = false,
- minimizeButton: Boolean = false,
- icon: String? = null,
- classes: Set<String> = setOf(),
- init: (Window.() -> Unit)? = null
-) :
- SimplePanel(classes + setOf("modal-content", "kv-window")) {
-
- /**
- * Window caption text.
- */
- var caption
- get() = captionTag.content
- set(value) {
- captionTag.content = value
- checkHeaderVisibility()
- }
- /**
- * Window content width.
- */
- var contentWidth
- get() = width
- set(value) {
- width = value
- }
- /**
- * Window content height.
- */
- var contentHeight
- get() = content.height
- set(value) {
- content.height = value
- }
- /**
- * Window content height.
- */
- var contentOverflow
- get() = content.overflow
- set(value) {
- content.overflow = value
- }
- /**
- * Determines if the window is resizable.
- */
- var isResizable by refreshOnUpdate(isResizable) { checkIsResizable() }
- /**
- * Determines if the window is draggable.
- */
- var isDraggable by refreshOnUpdate(isDraggable) { checkIsDraggable(); checkHeaderVisibility() }
- /**
- * Determines if Close button is visible.
- */
- var closeButton
- get() = closeIcon.visible
- set(value) {
- closeIcon.visible = value
- checkHeaderVisibility()
- }
- /**
- * Determines if Maximize button is visible.
- */
- var maximizeButton
- get() = maximizeIcon.visible
- set(value) {
- maximizeIcon.visible = value
- checkHeaderVisibility()
- }
- /**
- * Determines if Maximize button is visible.
- */
- var minimizeButton
- get() = minimizeIcon.visible
- set(value) {
- minimizeIcon.visible = value
- checkHeaderVisibility()
- }
- /**
- * Window icon.
- */
- var icon
- get() = if (windowIcon.icon == "") null else windowIcon.icon
- set(value) {
- windowIcon.icon = value ?: ""
- windowIcon.visible = (value != null && value != "")
- }
-
- private val header = SimplePanel(setOf("modal-header"))
-
- /**
- * @suppress
- * Internal property.
- */
- protected val content = SimplePanel().apply {
- this.height = contentHeight
- this.overflow = Overflow.AUTO
- }
- private val closeIcon = CloseIcon()
- private val maximizeIcon = MaximizeIcon()
- private val minimizeIcon = MinimizeIcon()
- private val captionTag = Tag(TAG.H4, caption, classes = setOf("modal-title"))
- private val windowIcon = Icon(icon ?: "").apply {
- addCssClass("window-icon")
- visible = (icon != null && icon != "")
- }
-
- private var isResizeEvent = false
-
- init {
- id = "kv_window_$counter"
- @Suppress("LeakingThis")
- position = Position.ABSOLUTE
- @Suppress("LeakingThis")
- overflow = Overflow.HIDDEN
- @Suppress("LeakingThis")
- width = contentWidth
- @Suppress("LeakingThis")
- zIndex = ++zIndexCounter
- closeIcon.visible = closeButton
- closeIcon.setEventListener {
- click = { _ ->
- @Suppress("UnsafeCastFromDynamic")
- if (this@Window.dispatchEvent("closeWindow", obj {}) != false) {
- close()
- }
- }
- mousedown = { e ->
- e.stopPropagation()
- }
- }
- header.add(closeIcon)
- maximizeIcon.visible = maximizeButton
- maximizeIcon.setEventListener {
- click = { _ ->
- @Suppress("UnsafeCastFromDynamic")
- if (this@Window.dispatchEvent("maximizeWindow", obj {}) != false) {
- toggleMaximize()
- }
- }
- mousedown = { e ->
- e.stopPropagation()
- }
- }
- header.add(maximizeIcon)
- minimizeIcon.visible = minimizeButton
- minimizeIcon.setEventListener {
- click = { _ ->
- @Suppress("UnsafeCastFromDynamic")
- if (this@Window.dispatchEvent("minimizeWindow", obj {}) != false) {
- toggleMinimize()
- }
- }
- mousedown = { e ->
- e.stopPropagation()
- }
- }
- header.add(minimizeIcon)
- header.add(captionTag)
- captionTag.add(windowIcon)
- checkHeaderVisibility()
- addInternal(header)
- addInternal(content)
- checkIsDraggable()
- if (isResizable) {
- @Suppress("LeakingThis")
- resize = Resize.BOTH
- content.marginBottom = WINDOW_CONTENT_MARGIN_BOTTOM.px
- }
- @Suppress("LeakingThis")
- setEventListener<Window> {
- click = {
- toFront()
- focus()
- }
- }
- @Suppress("LeakingThis")
- init?.invoke(this)
- counter++
- }
-
- private fun checkHeaderVisibility() {
- @Suppress("ComplexCondition")
- if (!closeButton && !maximizeButton && !minimizeButton && caption == null && !isDraggable) {
- header.hide()
- } else {
- header.show()
- }
- }
-
- private fun checkIsDraggable() {
- var isDrag: Boolean
- if (isDraggable) {
- header.setEventListener<SimplePanel> {
- mousedown = { e ->
- if (e.button.toInt() == 0) {
- isDrag = true
- val dragStartX = this@Window.getElementJQuery()?.position()?.left?.toInt() ?: 0
- val dragStartY = this@Window.getElementJQuery()?.position()?.top?.toInt() ?: 0
- val dragMouseX = e.pageX
- val dragMouseY = e.pageY
- val moveCallback = { me: Event ->
- if (isDrag) {
- this@Window.left = (dragStartX + (me as MouseEvent).pageX - dragMouseX).toInt().px
- this@Window.top = (dragStartY + (me).pageY - dragMouseY).toInt().px
- }
- }
- kotlin.browser.window.addEventListener("mousemove", moveCallback)
- var upCallback: ((Event) -> Unit)? = null
- upCallback = {
- isDrag = false
- kotlin.browser.window.removeEventListener("mousemove", moveCallback)
- kotlin.browser.window.removeEventListener("mouseup", upCallback)
- }
- kotlin.browser.window.addEventListener("mouseup", upCallback)
- }
- }
- }
- } else {
- isDrag = false
- header.removeEventListeners()
- }
- }
-
- private fun checkIsResizable() {
- checkResizablEventHandler()
- if (isResizable) {
- resize = Resize.BOTH
- val intHeight = (getElementJQuery()?.height()?.toInt() ?: 0)
- content.height = (intHeight - WINDOW_HEADER_HEIGHT - WINDOW_CONTENT_MARGIN_BOTTOM).px
- content.marginBottom = WINDOW_CONTENT_MARGIN_BOTTOM.px
- } else {
- resize = Resize.NONE
- val intHeight = (getElementJQuery()?.height()?.toInt() ?: 0)
- content.height = (intHeight - WINDOW_HEADER_HEIGHT).px
- content.marginBottom = 0.px
- }
- }
-
- @Suppress("UnsafeCastFromDynamic")
- private fun checkResizablEventHandler() {
- if (isResizable) {
- if (!isResizeEvent) {
- isResizeEvent = true
- KVManager.setResizeEvent(this) {
- val eid = getElementJQuery()?.attr("id")
- if (isResizable && eid == id) {
- val outerWidth = (getElementJQuery()?.outerWidth()?.toInt() ?: 0)
- val outerHeight = (getElementJQuery()?.outerHeight()?.toInt() ?: 0)
- val intWidth = (getElementJQuery()?.width()?.toInt() ?: 0)
- val intHeight = (getElementJQuery()?.height()?.toInt() ?: 0)
- content.width = intWidth.px
- content.height = (intHeight - WINDOW_HEADER_HEIGHT - WINDOW_CONTENT_MARGIN_BOTTOM).px
- width = outerWidth.px
- height = outerHeight.px
- this.dispatchEvent("resizeWindow", obj {
- detail = obj {
- this.width = outerWidth
- this.height = outerHeight
- }
- })
- }
- }
- }
- } else if (isResizeEvent) {
- KVManager.clearResizeEvent(this)
- isResizeEvent = false
- }
- }
-
- override fun add(child: Component): SimplePanel {
- content.add(child)
- return this
- }
-
- override fun addAll(children: List<Component>): SimplePanel {
- content.addAll(children)
- return this
- }
-
- override fun remove(child: Component): SimplePanel {
- content.remove(child)
- return this
- }
-
- override fun removeAll(): SimplePanel {
- content.removeAll()
- return this
- }
-
- override fun getChildren(): List<Component> {
- return content.getChildren()
- }
-
- override fun afterCreate(node: VNode) {
- checkResizablEventHandler()
- }
-
- override fun afterDestroy() {
- if (isResizeEvent) {
- KVManager.clearResizeEvent(this)
- isResizeEvent = false
- }
- }
-
- /**
- * Moves the current window to the front.
- */
- open fun toFront() {
- if ((zIndex ?: 0) < zIndexCounter) zIndex = ++zIndexCounter
- }
-
- /**
- * Makes the current window focused.
- */
- open fun focus() {
- getElementJQuery()?.focus()
- }
-
- /**
- * Close the window.
- */
- open fun close() {
- hide()
- }
-
- /**
- * Maximize or restore the window size.
- */
- open fun toggleMaximize() {
- }
-
- /**
- * Minimize or restore the window size.
- */
- open fun toggleMinimize() {
- }
-
- companion object {
- internal var counter = 0
- internal var zIndexCounter = DEFAULT_Z_INDEX
-
- /**
- * DSL builder extension function.
- *
- * It takes the same parameters as the constructor of the built component.
- */
- fun Container.window(
- caption: String? = null,
- contentWidth: CssSize? = CssSize(0, UNIT.auto),
- contentHeight: CssSize? = CssSize(0, UNIT.auto),
- isResizable: Boolean = true,
- isDraggable: Boolean = true,
- closeButton: Boolean = false,
- maximizeButton: Boolean = false,
- minimizeButton: Boolean = false,
- icon: String? = null,
- classes: Set<String> = setOf(),
- init: (Window.() -> Unit)? = null
- ): Window {
- val window =
- Window(
- caption,
- contentWidth,
- contentHeight,
- isResizable,
- isDraggable,
- closeButton,
- maximizeButton,
- minimizeButton,
- icon,
- classes,
- init
- )
- this.add(window)
- return window
- }
- }
-}
diff --git a/src/main/resources/css/style.css b/src/main/resources/css/style.css
new file mode 100644
index 00000000..fccaeb2c
--- /dev/null
+++ b/src/main/resources/css/style.css
@@ -0,0 +1,377 @@
+.splitpanel-vertical {
+ display: flex;
+ flex-direction: row;
+ overflow: auto;
+}
+
+.splitpanel-vertical > *:first-child {
+ max-width: calc(100% - 9px);
+}
+
+.splitpanel-vertical > * {
+ flex: 0 0 auto;
+ overflow: auto;
+}
+
+.splitpanel-vertical > *:last-child {
+ flex: 1 1 auto;
+ overflow: auto;
+}
+
+.splitpanel-horizontal {
+ display: flex;
+ flex-direction: column;
+ overflow: auto;
+}
+
+.splitpanel-horizontal > *:first-child {
+ max-height: calc(100% - 9px);
+}
+
+.splitpanel-horizontal > * {
+ flex: 0 0 auto;
+ overflow: auto;
+}
+
+.splitpanel-horizontal > *:last-child {
+ flex: 1 1 auto;
+ overflow: auto;
+}
+
+.splitter-vertical {
+ flex: 0 0 auto;
+ width: 9px;
+ background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAhCAQAAABOpSL+AAAAIklEQVR4AWMwbb/PdR+JZDD9f1/oPhI5sgVGBSruc9xHIgGdSQqqQJGkRgAAAABJRU5ErkJggg==') center center no-repeat #cecece;
+ cursor: col-resize;
+}
+
+.splitter-horizontal {
+ flex: 0 0 auto;
+ height: 9px;
+ background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACEAAAAICAQAAADdTl4aAAAAIElEQVQoz2MwrTD9TxFsZ7jPcV+IIsjFQAUw6hFqegQA+xzRHT2p7pEAAAAASUVORK5CYII=') center center no-repeat #cecece;
+ cursor: row-resize;
+}
+
+.trix-control {
+ overflow-y: auto;
+}
+
+trix-toolbar .trix-button-group {
+ margin-bottom: 3px;
+}
+
+.tabulator-row .tabulator-cell.tabulator-editing input, .tabulator-row .tabulator-cell.tabulator-editing select {
+ border: 1px solid #ccc;
+ border-radius: 4px;
+}
+
+.tabulator-row .tabulator-cell.tabulator-editing input:focus, .tabulator-row .tabulator-cell.tabulator-editing select:focus {
+ border-color: #66afe9;
+ -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 8px rgba(102, 175, 233, 0.6);
+ box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 8px rgba(102, 175, 233, 0.6);
+}
+
+.tabulator-row .tabulator-cell.tabulator-editing {
+ border-right: 1px solid #1d68cd !important;
+ padding: 2px !important;
+}
+
+.input-group.date.is-invalid~.invalid-feedback {
+ display: block;
+}
+
+.input-group.date {
+ padding-left: 0px;
+ padding-right: 0px;
+}
+
+.bootstrap-select .dropdown-toggle.btn-default:focus {
+ outline: none !important;
+}
+
+.bootstrap-select .dropdown-toggle::after {
+ margin-left: -1em !important;
+}
+
+.form-inline .bootstrap-select .form-control {
+ min-width: 200px;
+}
+
+.kv-spinner-btn-none .input-group-btn-vertical {
+ display: none;
+}
+
+.kv-spinner-btn-none .form-control {
+ border-radius: 4px !important;
+}
+
+.kv-spinner > span {
+ display: inline-block;
+ width: 100%;
+}
+
+.input-group.kv-spinner {
+ padding-left: 0px;
+ padding-right: 0px;
+}
+
+.kv-spinner.is-invalid~.invalid-feedback {
+ display: block;
+}
+
+.kv-radiogroup-inline label.control-label {
+ vertical-align: top;
+ margin-right: .75rem;
+ margin-bottom: 0px;
+}
+.row.kv-radiogroup-inline label.control-label {
+ margin-right: 0px;
+}
+
+.row.kv-radiogroup-inline .kv-radiogroup-container, .row.kv-radiogroup .kv-radiogroup-container {
+ margin-left: -15px;
+}
+
+.kv-radiogroup-inline .kv-radiogroup-container {
+ display: inline-flex;
+}
+
+.kv-radiogroup-container.is-invalid~.invalid-feedback {
+ display: block;
+}
+
+.form-check {
+ padding-left: 0.5rem;
+}
+
+.form-check-input.form-control-sm, .form-check-input.form-control-lg {
+ height: inherit;
+}
+
+.form-check-inline {
+ margin-left: 3px;
+}
+
+.form-check-inline.form-check {
+ padding-left: 0px;
+}
+
+.form-horizontal.container-fluid {
+ width: inherit;
+}
+
+.form-inline .form-group {
+ margin-right: 6px;
+}
+
+.form-inline .form-group .control-label {
+ margin-right: 6px;
+}
+
+.form-inline .form-check.form-group {
+ margin-left: 6px;
+}
+
+.kv-form-condensed .form-group {
+ margin-bottom: 0.5rem;
+}
+
+.kv-window.modal-content {
+ -webkit-box-shadow: 0 5px 15px rgba(0,0,0,.5);
+ box-shadow: 0 5px 15px rgba(0,0,0,.5);
+ border-radius: 0px;
+}
+
+.kv-window .modal-header {
+ height: 40px;
+ padding: 5px 15px 5px 15px;
+ align-items: center;
+}
+
+.kv-window .modal-header button.close {
+ width: 24px;
+ height: 24px;
+ margin: 0px;
+ padding: 0px;
+}
+
+.kv-window .modal-header .modal-title {
+ white-space: nowrap;
+}
+
+.kv-window .modal-header .window-icon {
+ margin-right: 6px;
+}
+
+.kv-window .kv-window-icons-container {
+ display: flex;
+}
+
+.kv-preview-thumb .btn, .kv-zoom-actions .btn, .file-zoom-dialog .floating-buttons .btn {
+ padding: 5px 8px;
+}
+
+.file-drop-zone.clickable:hover {
+ border: 1px dashed #999;
+}
+
+.file-drop-zone.clickable:focus {
+ border: 1px solid #5acde2;
+}
+
+.nav.tabs-top {
+ flex-wrap: nowrap;
+}
+
+ul.tabs-top {
+ overflow-x: auto;
+ overflow-y: hidden;
+ display: flex;
+}
+
+ul.tabs-top > li {
+ float:none;
+ flex-shrink: 0;
+}
+
+.kv-tab-close {
+ margin-left: 10px;
+ color: #000;
+ text-shadow: 0 1px 0 #fff;
+ filter: alpha(opacity=20);
+ opacity: 0.2;
+}
+
+.kv-tab-close:hover, .kv-tab-close:focus {
+ cursor: pointer;
+ filter: alpha(opacity=50);
+ opacity: 0.5;
+}
+
+select.form-control, .tabulator-row .tabulator-cell.tabulator-editing select {
+ -webkit-appearance: none;
+ -moz-appearance: none;
+ appearance: none;
+ background: transparent none no-repeat;
+ background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABUAAAAKCAYAAABblxXYAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH4wUKFyIn4IjqJgAAAENJREFUKM/l0LERACEQQlGsiTa2px1aokGugNNAx8wfMy8AeLoBALYjaTqoKkga2+gKPgF/2Q7JkEx359oftu+C7/UBCUIcVQz0PvcAAAAASUVORK5CYII=');
+ background-position: right center;
+ cursor: pointer;
+}
+
+.abc-checkbox input[type="checkbox"]:checked+label::after,
+.abc-checkbox input[type="radio"]:checked+label::after {
+ font-family: "Font Awesome 5 Pro", "Font Awesome 5 Free";
+ content: "\f00c";
+ font-weight: 900;
+}
+
+.abc-checkbox label::before {
+ top: 0px;
+}
+.abc-checkbox label::after {
+ top: 0px;
+}
+
+.abc-radio label::before {
+ top: 0px;
+}
+
+.abc-radio label::after {
+ top: 3px;
+}
+
+.abc-checkbox.form-check-inline label::before {
+ top: 2px;
+}
+
+.abc-checkbox.form-check-inline label::after {
+ top: 2px;
+}
+
+.abc-radio.form-check-inline label::before {
+ top: 2px;
+}
+
+.abc-radio.form-check-inline label::after {
+ top: 5px;
+}
+
+.abc-checkbox label.col-form-label-lg::before {
+ top: 10px;
+}
+.abc-checkbox label.col-form-label-lg::after {
+ top: 10px;
+}
+
+.abc-radio label.col-form-label-lg::before {
+ top: 10px;
+}
+
+.abc-radio label.col-form-label-lg::after {
+ top: 13px;
+}
+
+.abc-checkbox.form-check-inline label.col-form-label-lg::before {
+ top: 15px;
+}
+
+.abc-checkbox.form-check-inline label.col-form-label-lg::after {
+ top: 15px;
+}
+
+.abc-radio.form-check-inline label.col-form-label-lg::before {
+ top: 15px;
+}
+
+.abc-radio.form-check-inline label.col-form-label-lg::after {
+ top: 18px;
+}
+
+/*!
+ * bootstrap-vertical-tabs - v1.2.2
+ * https://dbtek.github.io/bootstrap-vertical-tabs
+ * 2016-12-02
+ * Copyright (c) 2016 İsmail Demirbilek
+ * License: MIT
+ */
+.nav-tabs.tabs-left, .nav-tabs.tabs-right {
+ border-bottom: none;
+ padding-top: 2px;
+}
+.nav-tabs.tabs-left {
+ border-right: 1px solid #dee2e6;
+}
+.nav-tabs.tabs-right {
+ border-left: 1px solid #dee2e6;
+}
+.nav-tabs.tabs-left>li.nav-item, .nav-tabs.tabs-right>li.nav-item {
+ float: none;
+ margin-bottom: 2px;
+}
+.nav-tabs.tabs-left>li.nav-item {
+ margin-right: -1px;
+}
+.nav-tabs.tabs-right>li.nav-item {
+ margin-left: -1px;
+}
+.nav-tabs.tabs-left>li.nav-item>a.nav-link.active,
+.nav-tabs.tabs-left>li.nav-item>a.nav-link.active:hover,
+.nav-tabs.tabs-left>li.nav-item>a.nav-link.active:focus {
+ border-bottom-color: #dee2e6;
+ border-right-color: transparent;
+}
+.nav-tabs.tabs-right>li.nav-item>a.nav-link.active,
+.nav-tabs.tabs-right>li.nav-item>a.nav-link.active:hover,
+.nav-tabs.tabs-right>li.nav-item>a.nav-link.active:focus {
+ border-bottom: 1px solid #dee2e6;
+ border-left-color: transparent;
+}
+.nav-tabs.tabs-left>li.nav-item>a.nav-link {
+ border-radius: 4px 0 0 4px;
+ margin-right: 0;
+ display:block;
+}
+.nav-tabs.tabs-right>li.nav-item>a.nav-link {
+ border-radius: 0 4px 4px 0;
+ margin-right: 0;
+}