aboutsummaryrefslogtreecommitdiff
path: root/src/main/kotlin/pl/treksoft
diff options
context:
space:
mode:
authorRobert Jaros <rjaros@finn.pl>2018-01-16 00:13:54 +0100
committerRobert Jaros <rjaros@finn.pl>2018-01-16 00:13:54 +0100
commitbcc5292dd95e5824da41a19bfbf64ebc25d79102 (patch)
tree5a97a4da432d2db97ea2fa6755c8b0e42394690f /src/main/kotlin/pl/treksoft
parenta107c5b7fe1cf0429436045e336b08d16b387367 (diff)
downloadkvision-bcc5292dd95e5824da41a19bfbf64ebc25d79102.tar.gz
kvision-bcc5292dd95e5824da41a19bfbf64ebc25d79102.tar.bz2
kvision-bcc5292dd95e5824da41a19bfbf64ebc25d79102.zip
Spinner form controls
Unit tests
Diffstat (limited to 'src/main/kotlin/pl/treksoft')
-rw-r--r--src/main/kotlin/pl/treksoft/kvision/Showcase.kt29
-rw-r--r--src/main/kotlin/pl/treksoft/kvision/core/KVManager.kt2
-rw-r--r--src/main/kotlin/pl/treksoft/kvision/core/Widget.kt21
-rw-r--r--src/main/kotlin/pl/treksoft/kvision/form/spinner/Spinner.kt148
-rw-r--r--src/main/kotlin/pl/treksoft/kvision/form/spinner/SpinnerInput.kt242
-rw-r--r--src/main/kotlin/pl/treksoft/kvision/form/text/AbstractTextInput.kt5
-rw-r--r--src/main/kotlin/pl/treksoft/kvision/snabbdom/Types.kt2
7 files changed, 447 insertions, 2 deletions
diff --git a/src/main/kotlin/pl/treksoft/kvision/Showcase.kt b/src/main/kotlin/pl/treksoft/kvision/Showcase.kt
index 82651b71..a27fae2d 100644
--- a/src/main/kotlin/pl/treksoft/kvision/Showcase.kt
+++ b/src/main/kotlin/pl/treksoft/kvision/Showcase.kt
@@ -20,6 +20,9 @@ import pl.treksoft.kvision.form.select.Select
import pl.treksoft.kvision.form.select.SelectInput
import pl.treksoft.kvision.form.select.SelectOptGroup
import pl.treksoft.kvision.form.select.SelectOption
+import pl.treksoft.kvision.form.spinner.FORCETYPE
+import pl.treksoft.kvision.form.spinner.Spinner
+import pl.treksoft.kvision.form.spinner.SpinnerInput
import pl.treksoft.kvision.form.string
import pl.treksoft.kvision.form.text.Password
import pl.treksoft.kvision.form.text.RichText
@@ -414,6 +417,7 @@ class Showcase : ApplicationBase() {
val checkbox: Boolean by map
val radio: Boolean by map
val select: String? by map
+ val spinner: Double? by map
}
val formPanel = FormPanel() {
@@ -436,6 +440,7 @@ class Showcase : ApplicationBase() {
// selectWidthType = SELECTWIDTHTYPE.FIT
emptyOption = true
}, required = true)
+ add("spinner", Spinner(label = "Spinner"), required = true)
validator = {
var result = it["text"] == it["textarea"]
@@ -448,14 +453,38 @@ class Showcase : ApplicationBase() {
validatorMessage = { "Pole Tekst i Obszar muszą być takie same!" }
}
root.add(formPanel)
+ val spinner = SpinnerInput(15.05, min = -100000, max = 100000, decimals = 4, forceType = FORCETYPE.ROUND, step = 0.0001).apply {
+ size = INPUTSIZE.LARGE
+ }
+ val ttt = TextInput(value = "abc").apply {
+ size = INPUTSIZE.LARGE
+ }
+ spinner.setEventListener<SpinnerInput> {
+ onMinBsSpinner = { e ->
+ console.log(e)
+ }
+ onMaxBsSpinner = { e ->
+ console.log(e)
+ }
+ }
val formButton = Button("Pokaż dane").setEventListener<Button> {
click = {
console.log(formPanel.validate())
console.log(formPanel.getData().map.toString())
// formPanel.setData(Formularz(mapOf("zazn" to false, "select" to "a")))
+ spinner.toggleVisible()
+ spinner.max = spinner.max.plus(2)
+ ttt.toggleVisible()
}
}
formPanel.add(formButton)
+ spinner.setEventListener<SpinnerInput> {
+ change = {
+ console.log(spinner.value)
+ }
+ }
+ root.add(spinner)
+ root.add(ttt)
val dd = DropDown("Dropdown", listOf("abc" to "#!/x", "def" to "#!/y"), "flag")
root.add(dd)
diff --git a/src/main/kotlin/pl/treksoft/kvision/core/KVManager.kt b/src/main/kotlin/pl/treksoft/kvision/core/KVManager.kt
index 65a7fe6e..0c010553 100644
--- a/src/main/kotlin/pl/treksoft/kvision/core/KVManager.kt
+++ b/src/main/kotlin/pl/treksoft/kvision/core/KVManager.kt
@@ -31,6 +31,8 @@ object KVManager {
private val trix = require("trix")
private val bootstrapDateTimePickerCss = require("bootstrap-datetime-picker/css/bootstrap-datetimepicker.min.css")
private val bootstrapDateTimePicker = require("bootstrap-datetime-picker/js/bootstrap-datetimepicker.min.js")
+ private val bootstrapTouchspinCss = require("bootstrap-touchspin/dist/jquery.bootstrap-touchspin.min.css")
+ private val bootstrapTouchspin = require("bootstrap-touchspin/dist/jquery.bootstrap-touchspin.min.js")
internal val fecha = require("fecha")
private val sdPatch = Snabbdom.init(arrayOf(classModule, attributesModule, propsModule, styleModule,
diff --git a/src/main/kotlin/pl/treksoft/kvision/core/Widget.kt b/src/main/kotlin/pl/treksoft/kvision/core/Widget.kt
index fec62ec7..8a7dfd15 100644
--- a/src/main/kotlin/pl/treksoft/kvision/core/Widget.kt
+++ b/src/main/kotlin/pl/treksoft/kvision/core/Widget.kt
@@ -48,6 +48,11 @@ open class Widget(classes: Set<String> = setOf()) : StyledComponent() {
field = value
refresh()
}
+ var surroundingSpan: Boolean = false
+ set(value) {
+ field = value
+ refresh()
+ }
internal var eventTarget: Widget? = null
private var vnode: VNode? = null
@@ -67,12 +72,20 @@ open class Widget(classes: Set<String> = setOf()) : StyledComponent() {
override fun renderVNode(): VNode {
return if (surroundingClasses.isEmpty()) {
- render()
+ if (surroundingSpan) {
+ h("span", arrayOf(render()))
+ } else {
+ render()
+ }
} else {
val opt = snOpt {
`class` = snClasses(surroundingClasses.map { c -> c to true })
}
- h("div", opt, arrayOf(render()))
+ if (surroundingSpan) {
+ h("div", opt, arrayOf(h("span", arrayOf(render()))))
+ } else {
+ h("div", opt, arrayOf(render()))
+ }
}
}
@@ -265,6 +278,10 @@ open class Widget(classes: Set<String> = setOf()) : StyledComponent() {
return this
}
+ open fun toggleVisible(): Widget {
+ return if (visible) hide() else show()
+ }
+
override fun addCssClass(css: String): Widget {
this.classes.add(css)
refresh()
diff --git a/src/main/kotlin/pl/treksoft/kvision/form/spinner/Spinner.kt b/src/main/kotlin/pl/treksoft/kvision/form/spinner/Spinner.kt
new file mode 100644
index 00000000..c6084fa8
--- /dev/null
+++ b/src/main/kotlin/pl/treksoft/kvision/form/spinner/Spinner.kt
@@ -0,0 +1,148 @@
+package pl.treksoft.kvision.form.spinner
+
+import pl.treksoft.kvision.core.Widget
+import pl.treksoft.kvision.form.FieldLabel
+import pl.treksoft.kvision.form.HelpBlock
+import pl.treksoft.kvision.form.NumberFormControl
+import pl.treksoft.kvision.panel.SimplePanel
+import pl.treksoft.kvision.snabbdom.SnOn
+import pl.treksoft.kvision.snabbdom.StringBoolPair
+
+open class Spinner(value: Number? = null, min: Int = 0, max: Int = DEFAULT_MAX, step: Double = DEFAULT_STEP,
+ decimals: Int = 0, buttonsType: BUTTONSTYPE = BUTTONSTYPE.VERTICAL,
+ forceType: FORCETYPE = FORCETYPE.NONE, label: String? = null,
+ rich: Boolean = false) : SimplePanel(setOf("form-group")), NumberFormControl {
+
+ override var value
+ get() = input.value
+ set(value) {
+ input.value = value
+ }
+ var min
+ get() = input.min
+ set(value) {
+ input.min = value
+ }
+ var max
+ get() = input.max
+ set(value) {
+ input.max = value
+ }
+ var step
+ get() = input.step
+ set(value) {
+ input.step = value
+ }
+ var decimals
+ get() = input.decimals
+ set(value) {
+ input.decimals = value
+ }
+ var buttonsType
+ get() = input.buttonsType
+ set(value) {
+ input.buttonsType = value
+ }
+ var forceType
+ get() = input.forceType
+ set(value) {
+ input.forceType = value
+ }
+ var placeholder
+ get() = input.placeholder
+ set(value) {
+ input.placeholder = value
+ }
+ var name
+ get() = input.name
+ set(value) {
+ input.name = value
+ }
+ override var disabled
+ get() = input.disabled
+ set(value) {
+ input.disabled = value
+ }
+ var autofocus
+ get() = input.autofocus
+ set(value) {
+ input.autofocus = value
+ }
+ var readonly
+ get() = input.readonly
+ set(value) {
+ input.readonly = value
+ }
+ var label
+ get() = flabel.text
+ set(value) {
+ flabel.text = value
+ }
+ var rich
+ get() = flabel.rich
+ set(value) {
+ flabel.rich = value
+ }
+ override var size
+ get() = input.size
+ set(value) {
+ input.size = value
+ }
+
+ protected val idc = "kv_form_spinner_" + counter
+ final override val input: SpinnerInput = SpinnerInput(value, min, max, step, decimals, buttonsType, forceType)
+ .apply { id = idc }
+ final override val flabel: FieldLabel = FieldLabel(idc, label, rich)
+ final override val validationInfo: HelpBlock = HelpBlock().apply { visible = false }
+
+ init {
+ @Suppress("LeakingThis")
+ input.eventTarget = this
+ this.addInternal(flabel)
+ this.addInternal(input)
+ this.addInternal(validationInfo)
+ counter++
+ }
+
+ companion object {
+ var counter = 0
+ }
+
+ override fun getSnClass(): List<StringBoolPair> {
+ val cl = super.getSnClass().toMutableList()
+ if (validatorError != null) {
+ cl.add("has-error" to true)
+ }
+ return cl
+ }
+
+ @Suppress("UNCHECKED_CAST")
+ override fun <T : Widget> setEventListener(block: SnOn<T>.() -> Unit): Widget {
+ input.setEventListener(block)
+ return this
+ }
+
+ override fun setEventListener(block: SnOn<Widget>.() -> Unit): Widget {
+ input.setEventListener(block)
+ return this
+ }
+
+ override fun removeEventListeners(): Widget {
+ input.removeEventListeners()
+ return this
+ }
+
+ override fun getValueAsString(): String? {
+ return input.getValueAsString()
+ }
+
+ open fun spinUp(): Spinner {
+ input.spinUp()
+ return this
+ }
+
+ open fun spinDown(): Spinner {
+ input.spinDown()
+ return this
+ }
+}
diff --git a/src/main/kotlin/pl/treksoft/kvision/form/spinner/SpinnerInput.kt b/src/main/kotlin/pl/treksoft/kvision/form/spinner/SpinnerInput.kt
new file mode 100644
index 00000000..b7d0f959
--- /dev/null
+++ b/src/main/kotlin/pl/treksoft/kvision/form/spinner/SpinnerInput.kt
@@ -0,0 +1,242 @@
+package pl.treksoft.kvision.form.spinner
+
+import com.github.snabbdom.VNode
+import pl.treksoft.jquery.JQuery
+import pl.treksoft.kvision.core.Widget
+import pl.treksoft.kvision.form.INPUTSIZE
+import pl.treksoft.kvision.snabbdom.StringBoolPair
+import pl.treksoft.kvision.snabbdom.StringPair
+import pl.treksoft.kvision.snabbdom.obj
+
+enum class BUTTONSTYPE {
+ NONE,
+ HORIZONTAL,
+ VERTICAL
+}
+
+enum class FORCETYPE(val value: String) {
+ NONE("none"),
+ ROUND("round"),
+ FLOOR("floor"),
+ CEIL("cail")
+}
+
+const val DEFAULT_STEP = 1.0
+const val DEFAULT_MAX = 100
+
+@Suppress("TooManyFunctions")
+open class SpinnerInput(value: Number? = null, min: Int = 0, max: Int = DEFAULT_MAX, step: Double = DEFAULT_STEP,
+ decimals: Int = 0, buttonsType: BUTTONSTYPE = BUTTONSTYPE.VERTICAL,
+ forceType: FORCETYPE = FORCETYPE.NONE,
+ classes: Set<String> = setOf()) : Widget(classes + "form-control") {
+
+ init {
+ this.addSurroundingCssClass("input-group")
+ if (buttonsType == BUTTONSTYPE.NONE) {
+ this.addSurroundingCssClass("kv-spinner-btn-none")
+ } else {
+ this.removeSurroundingCssClass("kv-spinner-btn-none")
+ }
+ if (buttonsType == BUTTONSTYPE.VERTICAL) {
+ this.addSurroundingCssClass("kv-spinner-btn-vertical")
+ } else {
+ this.removeSurroundingCssClass("kv-spinner-btn-vertical")
+ }
+ this.surroundingSpan = true
+ this.refreshSpinner()
+ this.setInternalEventListener<SpinnerInput> {
+ change = {
+ self.changeValue()
+ }
+ }
+ }
+
+ var value: Number? = value
+ set(value) {
+ field = value
+ refreshState()
+ }
+ var startValue: Number? = value
+ set(value) {
+ field = value
+ this.value = value
+ refresh()
+ }
+ var min: Int = min
+ set(value) {
+ field = value
+ refreshSpinner()
+ }
+ var max: Int = max
+ set(value) {
+ field = value
+ refreshSpinner()
+ }
+ var step: Double = step
+ set(value) {
+ field = value
+ refreshSpinner()
+ }
+ var decimals: Int = decimals
+ set(value) {
+ field = value
+ refreshSpinner()
+ }
+ var buttonsType: BUTTONSTYPE = buttonsType
+ set(value) {
+ field = value
+ refreshSpinner()
+ }
+ var forceType: FORCETYPE = forceType
+ set(value) {
+ field = value
+ refreshSpinner()
+ }
+ var placeholder: String? = null
+ set(value) {
+ field = value
+ refresh()
+ }
+ var name: String? = null
+ set(value) {
+ field = value
+ refresh()
+ }
+ var disabled: Boolean = false
+ set(value) {
+ field = value
+ refresh()
+ }
+ var autofocus: Boolean? = null
+ set(value) {
+ field = value
+ refresh()
+ }
+ var readonly: Boolean? = null
+ set(value) {
+ field = value
+ refresh()
+ }
+ var size: INPUTSIZE? = null
+ set(value) {
+ field = value
+ refresh()
+ }
+
+ private var siblings: JQuery? = null
+
+ override fun render(): VNode {
+ return kvh("input")
+ }
+
+ override fun getSnClass(): List<StringBoolPair> {
+ val cl = super.getSnClass().toMutableList()
+ size?.let {
+ cl.add(it.className to true)
+ }
+ return cl
+ }
+
+ override fun getSnAttrs(): List<StringPair> {
+ val sn = super.getSnAttrs().toMutableList()
+ sn.add("type" to "text")
+ startValue?.let {
+ sn.add("value" to it.toString())
+ }
+ placeholder?.let {
+ sn.add("placeholder" to it)
+ }
+ name?.let {
+ sn.add("name" to it)
+ }
+ autofocus?.let {
+ if (it) {
+ sn.add("autofocus" to "autofocus")
+ }
+ }
+ readonly?.let {
+ if (it) {
+ sn.add("readonly" to "readonly")
+ }
+ }
+ if (disabled) {
+ sn.add("disabled" to "true")
+ value?.let {
+ sn.add("value" to it.toString())
+ }
+ }
+ return sn
+ }
+
+ protected open fun changeValue() {
+ val v = getElementJQuery()?.`val`() as String?
+ if (v != null && v.isNotEmpty()) {
+ this.value = v.toDoubleOrNull()
+ } else {
+ this.value = null
+ }
+ }
+
+ @Suppress("UnsafeCastFromDynamic")
+ override fun afterInsert(node: VNode) {
+ getElementJQueryD()?.TouchSpin(getSettingsObj())
+ siblings = getElementJQuery()?.parent(".bootstrap-touchspin")?.children("span")
+ size?.let {
+ siblings?.find("button")?.addClass(it.className)
+ }
+ this.getElementJQuery()?.on("change", { e, _ ->
+ if (e.asDynamic().isTrigger != null) {
+ val event = org.w3c.dom.events.Event("change")
+ this.getElement()?.dispatchEvent(event)
+ }
+ })
+ this.getElementJQuery()?.on("touchspin.on.min", { e, _ ->
+ this.dispatchEvent("onMinBsSpinner", obj { detail = e })
+ })
+ this.getElementJQuery()?.on("touchspin.on.max", { e, _ ->
+ this.dispatchEvent("onMaxBsSpinner", obj { detail = e })
+ })
+ refreshState()
+ }
+
+ override fun afterDestroy() {
+ siblings?.remove()
+ siblings = null
+ }
+
+ fun getValueAsString(): String? {
+ return value?.toString()
+ }
+
+ fun spinUp(): SpinnerInput {
+ getElementJQueryD()?.trigger("touchspin.uponce")
+ return this
+ }
+
+ fun spinDown(): SpinnerInput {
+ getElementJQueryD()?.trigger("touchspin.downonce")
+ return this
+ }
+
+ private fun refreshState() {
+ value?.let {
+ getElementJQuery()?.`val`(it.toString())
+ } ?: getElementJQueryD()?.`val`(null)
+ }
+
+ private fun refreshSpinner() {
+ getElementJQueryD()?.trigger("touchspin.updatesettings", getSettingsObj())
+ }
+
+ private fun getSettingsObj(): dynamic {
+ val verticalbuttons = buttonsType == BUTTONSTYPE.VERTICAL || buttonsType == BUTTONSTYPE.NONE
+ return obj {
+ this.min = min
+ this.max = max
+ this.step = step
+ this.decimals = decimals
+ this.verticalbuttons = verticalbuttons
+ this.forcestepdivisibility = forceType.value
+ }
+ }
+}
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 0303f5bd..88614774 100644
--- a/src/main/kotlin/pl/treksoft/kvision/form/text/AbstractTextInput.kt
+++ b/src/main/kotlin/pl/treksoft/kvision/form/text/AbstractTextInput.kt
@@ -1,5 +1,6 @@
package pl.treksoft.kvision.form.text
+import com.github.snabbdom.VNode
import pl.treksoft.kvision.core.Widget
import pl.treksoft.kvision.form.INPUTSIZE
import pl.treksoft.kvision.snabbdom.StringBoolPair
@@ -98,6 +99,10 @@ abstract class AbstractTextInput(value: String? = null,
return sn
}
+ override fun afterInsert(node: VNode) {
+ refreshState()
+ }
+
protected open fun refreshState() {
value?.let {
getElementJQuery()?.`val`(it)
diff --git a/src/main/kotlin/pl/treksoft/kvision/snabbdom/Types.kt b/src/main/kotlin/pl/treksoft/kvision/snabbdom/Types.kt
index 1606e83a..62f8c239 100644
--- a/src/main/kotlin/pl/treksoft/kvision/snabbdom/Types.kt
+++ b/src/main/kotlin/pl/treksoft/kvision/snabbdom/Types.kt
@@ -52,6 +52,8 @@ interface BtOn : On {
var changeDate: ((KvEvent) -> kotlin.Unit)?
var showBsDateTime: ((KvEvent) -> kotlin.Unit)?
var hideBsDateTime: ((KvEvent) -> kotlin.Unit)?
+ var onMinBsSpinner: ((KvEvent) -> kotlin.Unit)?
+ var onMaxBsSpinner: ((KvEvent) -> kotlin.Unit)?
}
interface SnOn<T> : BtOn {