diff options
author | Robert Jaros <rjaros@finn.pl> | 2017-10-28 23:45:26 +0200 |
---|---|---|
committer | Robert Jaros <rjaros@finn.pl> | 2017-10-28 23:45:26 +0200 |
commit | 06f297d68887c7934e66d2c757abc8bf619df66a (patch) | |
tree | a828eec09f0bdc99b0f3fd45972b8cead37fbdec /src/main/kotlin/pl/treksoft | |
parent | 6b13b8909a302b0f0f2155b81b83cd5ab4d7a046 (diff) | |
download | kvision-06f297d68887c7934e66d2c757abc8bf619df66a.tar.gz kvision-06f297d68887c7934e66d2c757abc8bf619df66a.tar.bz2 kvision-06f297d68887c7934e66d2c757abc8bf619df66a.zip |
Databinding components
Event handlers refactoring
Diffstat (limited to 'src/main/kotlin/pl/treksoft')
25 files changed, 446 insertions, 189 deletions
diff --git a/src/main/kotlin/pl/treksoft/kvision/Showcase.kt b/src/main/kotlin/pl/treksoft/kvision/Showcase.kt index f1406505..282ea510 100644 --- a/src/main/kotlin/pl/treksoft/kvision/Showcase.kt +++ b/src/main/kotlin/pl/treksoft/kvision/Showcase.kt @@ -1,10 +1,27 @@ package pl.treksoft.kvision +import com.lightningkite.kotlin.observable.list.observableListOf import pl.treksoft.kvision.basic.Label -import pl.treksoft.kvision.core.* +import pl.treksoft.kvision.core.BGATTACH +import pl.treksoft.kvision.core.BGREPEAT +import pl.treksoft.kvision.core.BGSIZE +import pl.treksoft.kvision.core.BORDERSTYLE +import pl.treksoft.kvision.core.Background +import pl.treksoft.kvision.core.Border +import pl.treksoft.kvision.core.COLOR +import pl.treksoft.kvision.core.Img +import pl.treksoft.kvision.core.Root +import pl.treksoft.kvision.data.DataComponent +import pl.treksoft.kvision.data.DataContainer import pl.treksoft.kvision.dropdown.DD.* import pl.treksoft.kvision.dropdown.DropDown -import pl.treksoft.kvision.form.* +import pl.treksoft.kvision.form.CheckBox +import pl.treksoft.kvision.form.INPUTSIZE +import pl.treksoft.kvision.form.TEXTINPUTTYPE +import pl.treksoft.kvision.form.Text +import pl.treksoft.kvision.form.TextArea +import pl.treksoft.kvision.form.TextAreaInput +import pl.treksoft.kvision.form.TextInput import pl.treksoft.kvision.html.* import pl.treksoft.kvision.html.TAG.DIV import pl.treksoft.kvision.html.TAG.H1 @@ -21,7 +38,51 @@ class Showcase : ApplicationBase() { override fun start(state: Map<String, Any>) { val root = Root("showcase") - val container = Container(setOf("abc", "def")) + class Model(p: Boolean, t: String) : DataComponent() { + var p: Boolean by obs(p) + var t: String by obs(t) + } + + val model = observableListOf(Model(true, "Pierwszy"), Model(false, "Drugi"), Model(false, "Trzeci")) + val datac = DataContainer(model, { element, index -> + CheckBox(value = element.p, + label = if (element.p) "<b>" + (index + 1) + " " + element.t + "</b>" else element.t, + rich = true).setEventListener<CheckBox>({ + click = { + element.p = self.value + } + }) + }) + root.add(datac) + + val mbutton = Button("Pokaż wartości").setEventListener<Button> { + click = { + println(model.collection) + } + dblclick = { + model.add(Model(true, "XXX")) + } + } + root.add(mbutton) + val mbutton2 = Button("Zaznacz").setEventListener<Button> { + click = { + model.forEach { it.p = true } + } + dblclick = { + model.forEach { it.p = false } + } + } + root.add(mbutton2) + val textField = TextInput(placeholder = "Wprowadź hasło ...", value = "abc") + val mbutton3 = Button("Ukryj/Pokaż").setEventListener<Button> { + click = { + if (datac.visible) datac.hide() else datac.show() + if (textField.visible) textField.hide() else textField.show() + } + } + root.add(mbutton3) + + val container = SimplePanel(setOf("abc", "def")) val h1 = Tag(H1, "To jest <i>test pisania</i> tekstu", false, null, classes = setOf("test", "test2")) container.add(h1) val label = Label("KVLabel1") @@ -36,7 +97,6 @@ class Showcase : ApplicationBase() { link.add(Tag(TAG.P, "Cośtam")) root.add(link) - val textField = TextInput(placeholder = "Wprowadź hasło ...", value = "abc") root.add(textField) textField.setEventListener<TextInput> { input = { e -> @@ -54,7 +114,7 @@ class Showcase : ApplicationBase() { textField2.size = INPUTSIZE.LARGE root.add(textField2) - val checkbox = CheckBox(true, label = "Kliknij aby <b>przetestować</b>", rich = true, circled = true, +/* val checkbox = CheckBox(true, label = "Kliknij aby <b>przetestować</b>", rich = true, circled = true, style = CHECKBOXSTYLE.DANGER) root.add(checkbox) checkbox.setEventListener<CheckBox> { @@ -62,9 +122,9 @@ class Showcase : ApplicationBase() { println("click" + self.value) } change = { e -> println("change" + self.value) } - } + }*/ - val radio = Radio(true, name = "radios", label = "Opcja 1", inline = true, +/* val radio = Radio(true, name = "radios", label = "Opcja 1", inline = true, style = RADIOSTYLE.DANGER, extraValue = "o1") val radio2 = Radio(false, name = "radios", label = "Opcja 2", rich = true, inline = true, style = RADIOSTYLE.WARNING, extraValue = "o2") @@ -78,7 +138,7 @@ class Showcase : ApplicationBase() { println("rclick" + self.value) } change = { e -> println("rchange" + self.value) } - } + }*/ val text = Text(placeholder = "Pole formularza", maxlength = 5, label = "To jest pole") root.add(text) @@ -258,9 +318,10 @@ class Showcase : ApplicationBase() { val button = Button("To jest przycisk FA", "fa-flag", BUTTONSTYLE.DANGER) button.setEventListener<Button> { click = { _ -> + println(model.collection) println(self.text) println(textField.value) - println(checkbox.value) +// println(checkbox.value) textField2.disabled = false grid4.colorHex = 0xff0000 dd3.text = "Zmiana" @@ -340,7 +401,7 @@ class Showcase : ApplicationBase() { props = snProps("href" to "/foo", "target" to "_blank") }, "I\'ll take you places!") )) - val v = patch(container, vnode) + val v = patch(child, vnode) val vnode2 = virtualize("<a href='/top' target='_top'>Test2</a>") patch(v, vnode2)*/ } diff --git a/src/main/kotlin/pl/treksoft/kvision/core/Container.kt b/src/main/kotlin/pl/treksoft/kvision/core/Container.kt index df854637..4621a050 100644 --- a/src/main/kotlin/pl/treksoft/kvision/core/Container.kt +++ b/src/main/kotlin/pl/treksoft/kvision/core/Container.kt @@ -2,56 +2,13 @@ package pl.treksoft.kvision.core import com.github.snabbdom.VNode -open class Container(classes: Set<String> = setOf()) : Widget(classes) { - internal val children: MutableList<Widget> = mutableListOf() - - override fun render(): VNode { - return kvh("div", childrenVNodes()) - } - - protected open fun childrenVNodes(): Array<VNode> { - return children.filter { it.visible }.map { it.renderVNode() }.toTypedArray() - } - - protected fun addInternal(child: Widget): Container { - children.add(child) - child.parent = this - refresh() - return this - } - - open fun add(child: Widget): Container { - return addInternal(child) - } - - open fun addAll(children: List<Widget>): Container { - this.children.addAll(children) - children.map { it.parent = this } - refresh() - return this - } - - open fun remove(child: Widget): Container { - if (children.remove(child)) { - child.clearParent() - refresh() - } - return this - } - - open fun removeAll(): Container { - children.map { it.clearParent() } - children.clear() - refresh() - return this - } - - open fun getChildren(): List<Widget> { - return ArrayList(children) - } - - override fun dispose() { - children.forEach { it.dispose() } - removeAll() - } +interface Container { + var parent: Widget? + var visible: Boolean + fun renderVNode(): VNode + fun add(child: Widget): Container + fun addAll(children: List<Widget>): Container + fun remove(child: Widget): Container + fun removeAll(): Container + fun getChildren(): List<Widget> } diff --git a/src/main/kotlin/pl/treksoft/kvision/core/Root.kt b/src/main/kotlin/pl/treksoft/kvision/core/Root.kt index 0c8a80f6..9d968874 100644 --- a/src/main/kotlin/pl/treksoft/kvision/core/Root.kt +++ b/src/main/kotlin/pl/treksoft/kvision/core/Root.kt @@ -2,12 +2,15 @@ package pl.treksoft.kvision.core import com.github.snabbdom.VNode import pl.treksoft.kvision.modal.Modal +import pl.treksoft.kvision.panel.SimplePanel import pl.treksoft.kvision.snabbdom.StringBoolPair -class Root(id: String, private val fixed: Boolean = false) : Container() { +class Root(id: String, private val fixed: Boolean = false) : SimplePanel() { private val modals: MutableList<Modal> = mutableListOf() private var rootVnode: VNode = renderVNode() + internal var renderDisabled = false + init { rootVnode = KVManager.patch(id, this.renderVNode()) this.id = id @@ -34,7 +37,9 @@ class Root(id: String, private val fixed: Boolean = false) : Container() { } internal fun reRender(): Root { - rootVnode = KVManager.patch(rootVnode, renderVNode()) + if (!renderDisabled) { + rootVnode = KVManager.patch(rootVnode, renderVNode()) + } return this } diff --git a/src/main/kotlin/pl/treksoft/kvision/core/Widget.kt b/src/main/kotlin/pl/treksoft/kvision/core/Widget.kt index d56e7614..6ce1eade 100644 --- a/src/main/kotlin/pl/treksoft/kvision/core/Widget.kt +++ b/src/main/kotlin/pl/treksoft/kvision/core/Widget.kt @@ -17,11 +17,12 @@ import pl.treksoft.kvision.snabbdom.snClasses import pl.treksoft.kvision.snabbdom.snOpt import pl.treksoft.kvision.snabbdom.snStyle -@Suppress("TooManyFunctions") +@Suppress("TooManyFunctions", "LargeClass") open class Widget(classes: Set<String> = setOf()) : StyledComponent() { - val classes = classes.toMutableSet() - val listeners = mutableListOf<SnOn<Widget>.() -> Unit>() + internal val classes = classes.toMutableSet() + internal val internalListeners = mutableListOf<SnOn<Widget>.() -> Unit>() + internal val listeners = mutableListOf<SnOn<Widget>.() -> Unit>() var parent: Widget? = null internal set @@ -55,7 +56,15 @@ open class Widget(classes: Set<String> = setOf()) : StyledComponent() { private var snOnCache: com.github.snabbdom.On? = null private var snHooksCache: com.github.snabbdom.Hooks? = null - internal open fun renderVNode(): VNode { + protected fun <T> singleRender(block: () -> T): T { + getRoot()?.renderDisabled = true + val t = block() + getRoot()?.renderDisabled = false + getRoot()?.reRender() + return t + } + + open fun renderVNode(): VNode { return render() } @@ -132,9 +141,47 @@ open class Widget(classes: Set<String> = setOf()) : StyledComponent() { } protected open fun getSnOn(): com.github.snabbdom.On? { - return if (listeners.size > 0) { + return if (internalListeners.size > 0 || listeners.size > 0) { + val internalHandlers = on(this) + internalListeners.forEach { l -> (internalHandlers::apply)(l) } val handlers = on(this) listeners.forEach { l -> (handlers::apply)(l) } + if (internalHandlers.click != null) { + if (handlers.click == null) { + handlers.click = internalHandlers.click + } else { + val intc = internalHandlers.click + val c = handlers.click + handlers.click = { e -> + intc?.invoke(e) + c?.invoke(e) + } + } + } + if (internalHandlers.change != null) { + if (handlers.change == null) { + handlers.change = internalHandlers.change + } else { + val intc = internalHandlers.change + val c = handlers.change + handlers.change = { e -> + intc?.invoke(e) + c?.invoke(e) + } + } + } + if (internalHandlers.input != null) { + if (handlers.input == null) { + handlers.input = internalHandlers.input + } else { + val intc = internalHandlers.input + val c = handlers.input + handlers.input = { e -> + intc?.invoke(e) + c?.invoke(e) + } + } + } handlers } else { null @@ -144,13 +191,21 @@ open class Widget(classes: Set<String> = setOf()) : StyledComponent() { protected open fun getSnHooks(): com.github.snabbdom.Hooks? { val hooks = hooks() hooks.apply { + create = { _, v -> + vnode = v + afterCreate(v) + } insert = { v -> vnode = v afterInsert(v) } postpatch = { ov, v -> vnode = v - if (ov.elm !== v.elm) afterInsert(v) + if (ov.elm !== v.elm) { + afterInsert(v) + } else { + afterPostpatch(v) + } } destroy = { _ -> vnode = null @@ -160,6 +215,12 @@ open class Widget(classes: Set<String> = setOf()) : StyledComponent() { return hooks } + protected fun setInternalEventListener(block: SnOn<Widget>.() -> Unit): Widget { + internalListeners.add(block) + refresh() + return this + } + @Suppress("UNCHECKED_CAST") open fun <T : Widget> setEventListener(block: SnOn<T>.() -> Unit): Widget { listeners.add(block as SnOn<Widget>.() -> Unit) @@ -228,9 +289,15 @@ open class Widget(classes: Set<String> = setOf()) : StyledComponent() { return this } + protected open fun afterCreate(node: VNode) { + } + protected open fun afterInsert(node: VNode) { } + protected open fun afterPostpatch(node: VNode) { + } + protected open fun afterDestroy() { } diff --git a/src/main/kotlin/pl/treksoft/kvision/data/DataComponent.kt b/src/main/kotlin/pl/treksoft/kvision/data/DataComponent.kt new file mode 100644 index 00000000..605e1e2e --- /dev/null +++ b/src/main/kotlin/pl/treksoft/kvision/data/DataComponent.kt @@ -0,0 +1,15 @@ +package pl.treksoft.kvision.data + +import kotlin.properties.ObservableProperty +import kotlin.properties.ReadWriteProperty +import kotlin.reflect.KProperty + +open class DataComponent { + var container: DataUpdatable? = null + + fun <T> obs(initialValue: T): ReadWriteProperty<Any?, T> = object : ObservableProperty<T>(initialValue) { + override fun afterChange(property: KProperty<*>, oldValue: T, newValue: T) { + container?.update() + } + } +} diff --git a/src/main/kotlin/pl/treksoft/kvision/data/DataContainer.kt b/src/main/kotlin/pl/treksoft/kvision/data/DataContainer.kt new file mode 100644 index 00000000..3b6d3aa8 --- /dev/null +++ b/src/main/kotlin/pl/treksoft/kvision/data/DataContainer.kt @@ -0,0 +1,60 @@ +package pl.treksoft.kvision.data + +import com.github.snabbdom.VNode +import com.lightningkite.kotlin.observable.list.ObservableList +import pl.treksoft.kvision.core.Container +import pl.treksoft.kvision.core.Widget +import pl.treksoft.kvision.panel.VPanel + +class DataContainer<M : DataComponent, C : Widget>(val model: ObservableList<M>, + private val binding: (M, Int) -> C, + private val child: Container = VPanel()) : + Widget(setOf()), Container, DataUpdatable { + + override var visible + get() = child.visible + set(value) { + child.visible = value + } + + init { + child.parent = this + model.onUpdate += { _ -> + update() + } + update() + } + + override fun add(child: Widget): Container { + return this.child.add(child) + } + + override fun addAll(children: List<Widget>): Container { + return this.child.addAll(children) + } + + override fun remove(child: Widget): Container { + return this.child.remove(child) + } + + override fun removeAll(): Container { + return this.child.removeAll() + } + + override fun getChildren(): List<Widget> { + return this.child.getChildren() + } + + override fun renderVNode(): VNode { + return this.child.renderVNode() + } + + override fun update() { + model.forEach { it.container = this } + singleRender { + child.removeAll() + child.addAll(model.mapIndexed { index, m -> binding(m, index) }) + } + } + +} diff --git a/src/main/kotlin/pl/treksoft/kvision/data/DataUpdatable.kt b/src/main/kotlin/pl/treksoft/kvision/data/DataUpdatable.kt new file mode 100644 index 00000000..5f9b7563 --- /dev/null +++ b/src/main/kotlin/pl/treksoft/kvision/data/DataUpdatable.kt @@ -0,0 +1,5 @@ +package pl.treksoft.kvision.data + +interface DataUpdatable { + fun update() +} diff --git a/src/main/kotlin/pl/treksoft/kvision/dropdown/DropDown.kt b/src/main/kotlin/pl/treksoft/kvision/dropdown/DropDown.kt index 92f06591..1ba20211 100644 --- a/src/main/kotlin/pl/treksoft/kvision/dropdown/DropDown.kt +++ b/src/main/kotlin/pl/treksoft/kvision/dropdown/DropDown.kt @@ -1,7 +1,7 @@ package pl.treksoft.kvision.dropdown import com.github.snabbdom.VNode -import pl.treksoft.kvision.core.Container +import pl.treksoft.kvision.panel.SimplePanel import pl.treksoft.kvision.core.ResString import pl.treksoft.kvision.core.Widget import pl.treksoft.kvision.html.BUTTONSTYLE @@ -23,7 +23,7 @@ enum class DD(val type: String) { open class DropDown(text: String, elements: List<StringPair>? = null, icon: String? = null, style: BUTTONSTYLE = BUTTONSTYLE.DEFAULT, disabled: Boolean = false, image: ResString? = null, - dropup: Boolean = false, classes: Set<String> = setOf()) : Container(classes) { + dropup: Boolean = false, classes: Set<String> = setOf()) : SimplePanel(classes) { var text get() = button.text set(value) { @@ -93,12 +93,12 @@ open class DropDown(text: String, elements: List<StringPair>? = null, icon: Stri var counter = 0 } - override fun add(child: Widget): Container { + override fun add(child: Widget): SimplePanel { list.add(child) return this } - override fun addAll(children: List<Widget>): Container { + override fun addAll(children: List<Widget>): SimplePanel { list.addAll(children) return this } diff --git a/src/main/kotlin/pl/treksoft/kvision/form/AbstractText.kt b/src/main/kotlin/pl/treksoft/kvision/form/AbstractText.kt index 98619dbc..dfa4233b 100644 --- a/src/main/kotlin/pl/treksoft/kvision/form/AbstractText.kt +++ b/src/main/kotlin/pl/treksoft/kvision/form/AbstractText.kt @@ -1,11 +1,11 @@ package pl.treksoft.kvision.form -import pl.treksoft.kvision.core.Container +import pl.treksoft.kvision.panel.SimplePanel import pl.treksoft.kvision.core.Widget import pl.treksoft.kvision.snabbdom.SnOn abstract class AbstractText(label: String? = null, rich: Boolean = false) : - Container(setOf("form-group")), StringFormField { + SimplePanel(setOf("form-group")), StringFormField { override var value get() = input.value diff --git a/src/main/kotlin/pl/treksoft/kvision/form/AbstractTextInput.kt b/src/main/kotlin/pl/treksoft/kvision/form/AbstractTextInput.kt index 017e7fef..82846012 100644 --- a/src/main/kotlin/pl/treksoft/kvision/form/AbstractTextInput.kt +++ b/src/main/kotlin/pl/treksoft/kvision/form/AbstractTextInput.kt @@ -1,16 +1,25 @@ package pl.treksoft.kvision.form -import com.github.snabbdom.VNode import pl.treksoft.kvision.core.Widget import pl.treksoft.kvision.snabbdom.StringBoolPair import pl.treksoft.kvision.snabbdom.StringPair abstract class AbstractTextInput(placeholder: String? = null, - override var value: String? = null, name: String? = null, maxlength: Int? = null, - disabled: Boolean = false, id: String? = null, - classes: Set<String> = setOf()) : Widget(classes + "form-control"), StringFormField { + override var value: String? = null, name: String? = null, maxlength: Int? = null, + disabled: Boolean = false, id: String? = null, + classes: Set<String> = setOf()) : Widget(classes + "form-control"), StringFormField { init { this.id = id + this.setInternalEventListener { + input = { + val v = getElementJQuery()?.`val`() as String? + if (v != null && v.isNotEmpty()) { + value = v + } else { + value = null + } + } + } } @Suppress("LeakingThis") @@ -90,15 +99,4 @@ abstract class AbstractTextInput(placeholder: String? = null, } return sn } - - override fun afterInsert(node: VNode) { - this.getElementJQuery()?.on("input", { _, _ -> - val v = getElementJQuery()?.`val`() as String? - if (v != null && v.isNotEmpty()) { - value = v - } else { - value = null - } - }) - } } diff --git a/src/main/kotlin/pl/treksoft/kvision/form/CheckBox.kt b/src/main/kotlin/pl/treksoft/kvision/form/CheckBox.kt index 325c1982..65941495 100644 --- a/src/main/kotlin/pl/treksoft/kvision/form/CheckBox.kt +++ b/src/main/kotlin/pl/treksoft/kvision/form/CheckBox.kt @@ -1,6 +1,6 @@ package pl.treksoft.kvision.form -import pl.treksoft.kvision.core.Container +import pl.treksoft.kvision.panel.SimplePanel import pl.treksoft.kvision.core.Widget import pl.treksoft.kvision.snabbdom.SnOn import pl.treksoft.kvision.snabbdom.StringBoolPair @@ -16,7 +16,7 @@ enum class CHECKBOXSTYLE(val className: String) { open class CheckBox(value: Boolean = false, name: String? = null, style: CHECKBOXSTYLE? = null, circled: Boolean = false, inline: Boolean = false, disabled: Boolean = false, - label: String? = null, rich: Boolean = false) : Container(setOf("checkbox")), BoolFormField { + label: String? = null, rich: Boolean = false) : SimplePanel(setOf("checkbox")), BoolFormField { override var value get() = input.value diff --git a/src/main/kotlin/pl/treksoft/kvision/form/CheckInput.kt b/src/main/kotlin/pl/treksoft/kvision/form/CheckInput.kt index 131793b8..07008ddd 100644 --- a/src/main/kotlin/pl/treksoft/kvision/form/CheckInput.kt +++ b/src/main/kotlin/pl/treksoft/kvision/form/CheckInput.kt @@ -17,6 +17,16 @@ open class CheckInput(type: CHECKINPUTTYPE = CHECKINPUTTYPE.CHECKBOX, override v init { this.id = id + this.setInternalEventListener { + click = { + val v = getElementJQuery()?.prop("checked") as Boolean? + value = (v == true) + } + change = { + val v = getElementJQuery()?.prop("checked") as Boolean? + value = (v == true) + } + } } @Suppress("LeakingThis") @@ -83,15 +93,17 @@ open class CheckInput(type: CHECKINPUTTYPE = CHECKINPUTTYPE.CHECKBOX, override v } override fun afterInsert(node: VNode) { - this.getElementJQuery()?.on("change", { _, _ -> - val v = getElementJQuery()?.prop("checked") as Boolean? - value = (v == true) - true - }) - this.getElementJQuery()?.on("click", { _, _ -> - val v = getElementJQuery()?.prop("checked") as Boolean? - value = (v == true) - true - }) + refreshCheckedState() + } + + override fun afterPostpatch(node: VNode) { + refreshCheckedState() + } + + private fun refreshCheckedState() { + val v = getElementJQuery()?.prop("checked") as Boolean? + if (this.value != v) { + getElementJQuery()?.prop("checked", this.value) + } } } diff --git a/src/main/kotlin/pl/treksoft/kvision/form/Radio.kt b/src/main/kotlin/pl/treksoft/kvision/form/Radio.kt index eebbe65a..66c2d75c 100644 --- a/src/main/kotlin/pl/treksoft/kvision/form/Radio.kt +++ b/src/main/kotlin/pl/treksoft/kvision/form/Radio.kt @@ -1,6 +1,6 @@ package pl.treksoft.kvision.form -import pl.treksoft.kvision.core.Container +import pl.treksoft.kvision.panel.SimplePanel import pl.treksoft.kvision.core.Widget import pl.treksoft.kvision.snabbdom.SnOn import pl.treksoft.kvision.snabbdom.StringBoolPair @@ -16,7 +16,7 @@ enum class RADIOSTYLE(val className: String) { open class Radio(value: Boolean = false, extraValue: String? = null, name: String? = null, style: RADIOSTYLE? = null, squared: Boolean = false, inline: Boolean = false, disabled: Boolean = false, - label: String? = null, rich: Boolean = false) : Container(), BoolFormField { + label: String? = null, rich: Boolean = false) : SimplePanel(), BoolFormField { override var value get() = input.value diff --git a/src/main/kotlin/pl/treksoft/kvision/html/Link.kt b/src/main/kotlin/pl/treksoft/kvision/html/Link.kt index dc93d0e7..f103323b 100644 --- a/src/main/kotlin/pl/treksoft/kvision/html/Link.kt +++ b/src/main/kotlin/pl/treksoft/kvision/html/Link.kt @@ -1,12 +1,12 @@ package pl.treksoft.kvision.html import com.github.snabbdom.VNode -import pl.treksoft.kvision.core.Container +import pl.treksoft.kvision.panel.SimplePanel import pl.treksoft.kvision.core.ResString import pl.treksoft.kvision.snabbdom.StringPair open class Link(label: String, url: String, icon: String? = null, image: ResString? = null, - classes: Set<String> = setOf()) : Container(classes) { + classes: Set<String> = setOf()) : SimplePanel(classes) { private var label = label set(value) { field = value diff --git a/src/main/kotlin/pl/treksoft/kvision/html/List.kt b/src/main/kotlin/pl/treksoft/kvision/html/List.kt index 9bac3f41..40ebc50b 100644 --- a/src/main/kotlin/pl/treksoft/kvision/html/List.kt +++ b/src/main/kotlin/pl/treksoft/kvision/html/List.kt @@ -2,7 +2,7 @@ package pl.treksoft.kvision.html import com.github.snabbdom.VNode import com.github.snabbdom.h -import pl.treksoft.kvision.core.Container +import pl.treksoft.kvision.panel.SimplePanel import pl.treksoft.kvision.core.KVManager import pl.treksoft.kvision.snabbdom.StringBoolPair @@ -16,7 +16,7 @@ enum class LIST(val tagName: String) { } open class ListTag(type: LIST, elements: List<String>? = null, rich: Boolean = false, - classes: Set<String> = setOf()) : Container(classes) { + classes: Set<String> = setOf()) : SimplePanel(classes) { var type = type set(value) { field = value diff --git a/src/main/kotlin/pl/treksoft/kvision/html/Tag.kt b/src/main/kotlin/pl/treksoft/kvision/html/Tag.kt index 70725d97..71856627 100644 --- a/src/main/kotlin/pl/treksoft/kvision/html/Tag.kt +++ b/src/main/kotlin/pl/treksoft/kvision/html/Tag.kt @@ -1,7 +1,7 @@ package pl.treksoft.kvision.html import com.github.snabbdom.VNode -import pl.treksoft.kvision.core.Container +import pl.treksoft.kvision.panel.SimplePanel import pl.treksoft.kvision.core.KVManager import pl.treksoft.kvision.snabbdom.StringBoolPair @@ -50,7 +50,7 @@ enum class ALIGN(val className: String) { } open class Tag(type: TAG, text: String? = null, rich: Boolean = false, align: ALIGN? = null, - classes: Set<String> = setOf()) : Container(classes) { + classes: Set<String> = setOf()) : SimplePanel(classes) { var type = type set(value) { field = value diff --git a/src/main/kotlin/pl/treksoft/kvision/modal/Modal.kt b/src/main/kotlin/pl/treksoft/kvision/modal/Modal.kt index 94796326..a4e14d5c 100644 --- a/src/main/kotlin/pl/treksoft/kvision/modal/Modal.kt +++ b/src/main/kotlin/pl/treksoft/kvision/modal/Modal.kt @@ -1,7 +1,7 @@ package pl.treksoft.kvision.modal import com.github.snabbdom.VNode -import pl.treksoft.kvision.core.Container +import pl.treksoft.kvision.panel.SimplePanel import pl.treksoft.kvision.core.Root import pl.treksoft.kvision.core.Widget import pl.treksoft.kvision.helpers.CloseIcon @@ -20,7 +20,7 @@ enum class MODALSIZE(val className: String) { @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()) : Container(classes) { + classes: Set<String> = setOf()) : SimplePanel(classes) { private var caption get() = captionTag.text set(value) { @@ -45,17 +45,17 @@ open class Modal(caption: String? = null, closeButton: Boolean = true, } private val dialog = ModalDialog(size) - private val header = Container(setOf("modal-header")) + private val header = SimplePanel(setOf("modal-header")) protected val closeIcon = CloseIcon() private val captionTag = Tag(TAG.H4, caption, classes = setOf("modal-title")) - protected val body = Container(setOf("modal-body")) - private val footer = Container(setOf("modal-footer")) + 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 = Container(setOf("modal-content")) + val content = SimplePanel(setOf("modal-content")) dialog.role = "document" dialog.add(content) closeIcon.visible = closeButton @@ -87,12 +87,12 @@ open class Modal(caption: String? = null, closeButton: Boolean = true, } } - override fun add(child: Widget): Container { + override fun add(child: Widget): SimplePanel { body.add(child) return this } - override fun addAll(children: List<Widget>): Container { + override fun addAll(children: List<Widget>): SimplePanel { body.addAll(children) return this } @@ -171,7 +171,7 @@ open class Modal(caption: String? = null, closeButton: Boolean = true, } } -open class ModalDialog(size: MODALSIZE?) : Container(setOf("modal-dialog")) { +open class ModalDialog(size: MODALSIZE?) : SimplePanel(setOf("modal-dialog")) { var size = size set(value) { field = value diff --git a/src/main/kotlin/pl/treksoft/kvision/panel/DockPanel.kt b/src/main/kotlin/pl/treksoft/kvision/panel/DockPanel.kt index 6d86727c..6e037c28 100644 --- a/src/main/kotlin/pl/treksoft/kvision/panel/DockPanel.kt +++ b/src/main/kotlin/pl/treksoft/kvision/panel/DockPanel.kt @@ -1,6 +1,5 @@ package pl.treksoft.kvision.panel -import pl.treksoft.kvision.core.Container import pl.treksoft.kvision.core.Widget enum class SIDE { @@ -11,7 +10,7 @@ enum class SIDE { DOWN } -open class DockPanel(classes: Set<String> = setOf()) : Container(classes = classes) { +open class DockPanel(classes: Set<String> = setOf()) : SimplePanel(classes = classes) { protected var left: Widget? = null protected var center: Widget? = null protected var right: Widget? = null @@ -59,16 +58,16 @@ open class DockPanel(classes: Set<String> = setOf()) : Container(classes = class return this } - override fun add(child: Widget): Container { + override fun add(child: Widget): DockPanel { return this.add(child, SIDE.CENTER) } - override fun addAll(children: List<Widget>): Container { + override fun addAll(children: List<Widget>): DockPanel { children.forEach { this.add(it) } return this } - override fun remove(child: Widget): Container { + override fun remove(child: Widget): DockPanel { if (child == left) removeAt(SIDE.LEFT) if (child == center) removeAt(SIDE.CENTER) if (child == right) removeAt(SIDE.RIGHT) @@ -77,7 +76,7 @@ open class DockPanel(classes: Set<String> = setOf()) : Container(classes = class return this } - open fun removeAt(position: SIDE): Container { + open fun removeAt(position: SIDE): DockPanel { when (position) { SIDE.UP -> { up?.let { mainContainer.remove(it) } @@ -103,7 +102,7 @@ open class DockPanel(classes: Set<String> = setOf()) : Container(classes = class return this } - override fun removeAll(): Container { + override fun removeAll(): DockPanel { removeAt(SIDE.LEFT) removeAt(SIDE.CENTER) removeAt(SIDE.RIGHT) diff --git a/src/main/kotlin/pl/treksoft/kvision/panel/FlexPanel.kt b/src/main/kotlin/pl/treksoft/kvision/panel/FlexPanel.kt index 10483a8d..6b6e11fb 100644 --- a/src/main/kotlin/pl/treksoft/kvision/panel/FlexPanel.kt +++ b/src/main/kotlin/pl/treksoft/kvision/panel/FlexPanel.kt @@ -1,6 +1,5 @@ package pl.treksoft.kvision.panel -import pl.treksoft.kvision.core.Container import pl.treksoft.kvision.core.Widget import pl.treksoft.kvision.core.WidgetWrapper import pl.treksoft.kvision.snabbdom.StringPair @@ -46,7 +45,7 @@ enum class FLEXALIGNCONTENT(val alignContent: String) { open class FlexPanel(direction: FLEXDIR? = null, wrap: FLEXWRAP? = null, justify: FLEXJUSTIFY? = null, alignItems: FLEXALIGNITEMS? = null, alignContent: FLEXALIGNCONTENT? = null, - classes: Set<String> = setOf()) : Container(classes) { + classes: Set<String> = setOf()) : SimplePanel(classes) { var direction = direction set(value) { field = value @@ -75,20 +74,21 @@ open class FlexPanel(direction: FLEXDIR? = null, wrap: FLEXWRAP? = null, justify @Suppress("LongParameterList") fun add(child: Widget, order: Int? = null, grow: Int? = null, shrink: Int? = null, - basis: Int? = null, alignSelf: FLEXALIGNITEMS? = null, classes: Set<String> = setOf()): Container { - return addInternal(FlexWrapper(child, order, grow, shrink, basis, alignSelf, classes)) + basis: Int? = null, alignSelf: FLEXALIGNITEMS? = null, classes: Set<String> = setOf()): FlexPanel { + addInternal(FlexWrapper(child, order, grow, shrink, basis, alignSelf, classes)) + return this } - override fun add(child: Widget): Container { + override fun add(child: Widget): FlexPanel { return add(child, null) } - override fun addAll(children: List<Widget>): Container { + override fun addAll(children: List<Widget>): FlexPanel { children.forEach { add(it, null) } return this } - override fun remove(child: Widget): Container { + override fun remove(child: Widget): FlexPanel { children.find { (it as FlexWrapper).delegate == child }?.let { super.remove(it) it.dispose() @@ -96,7 +96,7 @@ open class FlexPanel(direction: FLEXDIR? = null, wrap: FLEXWRAP? = null, justify return this } - override fun removeAll(): Container { + override fun removeAll(): FlexPanel { children.map { it.clearParent() it.dispose() diff --git a/src/main/kotlin/pl/treksoft/kvision/panel/GridPanel.kt b/src/main/kotlin/pl/treksoft/kvision/panel/GridPanel.kt index 44ba509f..cac5c79b 100644 --- a/src/main/kotlin/pl/treksoft/kvision/panel/GridPanel.kt +++ b/src/main/kotlin/pl/treksoft/kvision/panel/GridPanel.kt @@ -1,6 +1,5 @@ package pl.treksoft.kvision.panel -import pl.treksoft.kvision.core.Container import pl.treksoft.kvision.core.Widget import pl.treksoft.kvision.core.WidgetWrapper import pl.treksoft.kvision.snabbdom.StringPair @@ -50,7 +49,7 @@ open class GridPanel(autoColumns: String? = null, autoRows: String? = null, auto templateColumns: String? = null, templateRows: String? = null, templateAreas: List<String>? = null, columnGap: Int? = null, rowGap: Int? = null, justifyItems: GRIDJUSTIFY? = null, alignItems: GRIDALIGN? = null, justifyContent: GRIDJUSTIFYCONTENT? = null, - alignContent: GRIDALIGNCONTENT? = null, classes: Set<String> = setOf()) : Container(classes) { + alignContent: GRIDALIGNCONTENT? = null, classes: Set<String> = setOf()) : SimplePanel(classes) { var autoColumns = autoColumns set(value) { field = value @@ -115,21 +114,21 @@ open class GridPanel(autoColumns: String? = null, autoRows: String? = null, auto @Suppress("LongParameterList") fun add(child: Widget, columnStart: Int? = null, rowStart: Int? = null, columnEnd: String? = null, rowEnd: String? = null, area: String? = null, justifySelf: GRIDJUSTIFY? = null, - alignSelf: GRIDALIGN? = null, classes: Set<String> = setOf()): Container { - return addInternal(GridWrapper(child, columnStart, rowStart, columnEnd, rowEnd, area, justifySelf, - alignSelf, classes)) + alignSelf: GRIDALIGN? = null, classes: Set<String> = setOf()): GridPanel { + addInternal(GridWrapper(child, columnStart, rowStart, columnEnd, rowEnd, area, justifySelf, alignSelf, classes)) + return this } - override fun add(child: Widget): Container { + override fun add(child: Widget): GridPanel { return add(child, null, null) } - override fun addAll(children: List<Widget>): Container { + override fun addAll(children: List<Widget>): GridPanel { children.forEach { add(it, null, null) } return this } - override fun remove(child: Widget): Container { + override fun remove(child: Widget): GridPanel { children.find { (it as GridWrapper).delegate == child }?.let { super.remove(it) it.dispose() @@ -137,7 +136,7 @@ open class GridPanel(autoColumns: String? = null, autoRows: String? = null, auto return this } - override fun removeAll(): Container { + override fun removeAll(): GridPanel { children.map { it.clearParent() it.dispose() diff --git a/src/main/kotlin/pl/treksoft/kvision/panel/ResponsiveGridPanel.kt b/src/main/kotlin/pl/treksoft/kvision/panel/ResponsiveGridPanel.kt index 17b3803f..3587ea8e 100644 --- a/src/main/kotlin/pl/treksoft/kvision/panel/ResponsiveGridPanel.kt +++ b/src/main/kotlin/pl/treksoft/kvision/panel/ResponsiveGridPanel.kt @@ -1,6 +1,5 @@ package pl.treksoft.kvision.panel -import pl.treksoft.kvision.core.Container import pl.treksoft.kvision.core.Widget import pl.treksoft.kvision.core.WidgetWrapper import pl.treksoft.kvision.html.ALIGN @@ -20,7 +19,7 @@ internal data class WidgetParam(val widget: Widget, val size: Int, val offset: I 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()) : Container(classes) { + classes: Set<String> = setOf()) : SimplePanel(classes) { protected var align = align set(value) { field = value @@ -30,7 +29,7 @@ open class ResponsiveGridPanel(private val gridsize: GRIDSIZE = GRIDSIZE.MD, internal val map = mutableMapOf<Int, MutableMap<Int, WidgetParam>>() private var auto: Boolean = true - open fun add(child: Widget, row: Int, col: Int, size: Int = 0, offset: Int = 0): Container { + open fun add(child: Widget, row: Int, col: Int, size: Int = 0, offset: Int = 0): ResponsiveGridPanel { val cRow = if (row < 0) 0 else row val cCol = if (col < 0) 0 else col if (row > rows - 1) rows = cRow + 1 @@ -41,17 +40,17 @@ open class ResponsiveGridPanel(private val gridsize: GRIDSIZE = GRIDSIZE.MD, return this } - override fun add(child: Widget): Container { + override fun add(child: Widget): ResponsiveGridPanel { return this.add(child, 0, this.cols) } - override fun addAll(children: List<Widget>): Container { + override fun addAll(children: List<Widget>): ResponsiveGridPanel { children.forEach { this.add(it) } return this } @Suppress("NestedBlockDepth") - override fun remove(child: Widget): Container { + override fun remove(child: Widget): ResponsiveGridPanel { for (i in 0 until rows) { val row = map[i] if (row != null) { @@ -67,7 +66,7 @@ open class ResponsiveGridPanel(private val gridsize: GRIDSIZE = GRIDSIZE.MD, return this } - open fun removeAt(row: Int, col: Int): Container { + open fun removeAt(row: Int, col: Int): ResponsiveGridPanel { map[row]?.remove(col) refreshRowContainers() return this @@ -75,38 +74,40 @@ open class ResponsiveGridPanel(private val gridsize: GRIDSIZE = GRIDSIZE.MD, @Suppress("ComplexMethod", "NestedBlockDepth") protected open fun refreshRowContainers() { - clearRowContainers() - val num = MAX_COLUMNS / cols - for (i in 0 until rows) { - val rowContainer = Container(setOf("row")) - val row = map[i] - if (row != null) { - for (j in 0 until cols) { - val wp = row[j] - 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) - } + 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) { + for (j in 0 until cols) { + val wp = row[j] + 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) } - addInternal(rowContainer) } } diff --git a/src/main/kotlin/pl/treksoft/kvision/panel/SimplePanel.kt b/src/main/kotlin/pl/treksoft/kvision/panel/SimplePanel.kt new file mode 100644 index 00000000..e9cf503b --- /dev/null +++ b/src/main/kotlin/pl/treksoft/kvision/panel/SimplePanel.kt @@ -0,0 +1,59 @@ +package pl.treksoft.kvision.panel + +import com.github.snabbdom.VNode +import pl.treksoft.kvision.core.Container +import pl.treksoft.kvision.core.Widget + +open class SimplePanel(classes: Set<String> = setOf()) : Widget(classes), Container { + internal val children: MutableList<Widget> = mutableListOf() + + override fun render(): VNode { + return kvh("div", childrenVNodes()) + } + + protected open fun childrenVNodes(): Array<VNode> { + return children.filter { it.visible }.map { it.renderVNode() }.toTypedArray() + } + + protected fun addInternal(child: Widget): SimplePanel { + children.add(child) + child.parent = this + refresh() + return this + } + + override fun add(child: Widget): SimplePanel { + return addInternal(child) + } + + override fun addAll(children: List<Widget>): SimplePanel { + this.children.addAll(children) + children.map { it.parent = this } + refresh() + return this + } + + override fun remove(child: Widget): SimplePanel { + if (children.remove(child)) { + child.clearParent() + refresh() + } + return this + } + + override fun removeAll(): SimplePanel { + children.map { it.clearParent() } + children.clear() + refresh() + return this + } + + override fun getChildren(): List<Widget> { + return ArrayList(children) + } + + override fun dispose() { + children.forEach { it.dispose() } + removeAll() + } +} diff --git a/src/main/kotlin/pl/treksoft/kvision/panel/SplitPanel.kt b/src/main/kotlin/pl/treksoft/kvision/panel/SplitPanel.kt index 03740222..02811639 100644 --- a/src/main/kotlin/pl/treksoft/kvision/panel/SplitPanel.kt +++ b/src/main/kotlin/pl/treksoft/kvision/panel/SplitPanel.kt @@ -3,7 +3,6 @@ package pl.treksoft.kvision.panel import com.github.snabbdom.VNode import pl.treksoft.jquery.JQuery import pl.treksoft.jquery.JQueryEventObject -import pl.treksoft.kvision.core.Container import pl.treksoft.kvision.core.UNIT import pl.treksoft.kvision.html.TAG import pl.treksoft.kvision.html.Tag @@ -15,7 +14,7 @@ enum class DIRECTION(val dir: String) { } open class SplitPanel(private val direction: DIRECTION = DIRECTION.VERTICAL, - classes: Set<String> = setOf()) : Container(classes + ("splitpanel-" + direction.dir)) { + classes: Set<String> = setOf()) : SimplePanel(classes + ("splitpanel-" + direction.dir)) { @Suppress("LeakingThis") internal val splitter = Splitter(this, direction) diff --git a/src/main/kotlin/pl/treksoft/kvision/panel/StackPanel.kt b/src/main/kotlin/pl/treksoft/kvision/panel/StackPanel.kt index c5162c0a..e8975dce 100644 --- a/src/main/kotlin/pl/treksoft/kvision/panel/StackPanel.kt +++ b/src/main/kotlin/pl/treksoft/kvision/panel/StackPanel.kt @@ -1,11 +1,10 @@ package pl.treksoft.kvision.panel import com.github.snabbdom.VNode -import pl.treksoft.kvision.core.Container import pl.treksoft.kvision.core.Widget open class StackPanel(private val activateLast: Boolean = true, - classes: Set<String> = setOf()) : Container(classes) { + classes: Set<String> = setOf()) : SimplePanel(classes) { var activeIndex = -1 set(value) { field = value @@ -20,27 +19,27 @@ open class StackPanel(private val activateLast: Boolean = true, } } - override fun add(child: Widget): Container { + override fun add(child: Widget): StackPanel { super.add(child) if (activateLast) activeIndex = children.size - 1 else if (activeIndex == -1) activeIndex = 0 return this } - override fun addAll(children: List<Widget>): Container { + override fun addAll(children: List<Widget>): StackPanel { super.addAll(children) if (activateLast) activeIndex = this.children.size - 1 else if (activeIndex == -1) activeIndex = 0 return this } - override fun remove(child: Widget): Container { + override fun remove(child: Widget): StackPanel { super.remove(child) if (activeIndex > children.size - 1) activeIndex = children.size - 1 return this } - override fun removeAll(): Container { + override fun removeAll(): StackPanel { super.removeAll() if (activeIndex > children.size - 1) activeIndex = children.size - 1 return this diff --git a/src/main/kotlin/pl/treksoft/kvision/panel/TabPanel.kt b/src/main/kotlin/pl/treksoft/kvision/panel/TabPanel.kt index 817ecaa0..4190dbae 100644 --- a/src/main/kotlin/pl/treksoft/kvision/panel/TabPanel.kt +++ b/src/main/kotlin/pl/treksoft/kvision/panel/TabPanel.kt @@ -1,15 +1,12 @@ package pl.treksoft.kvision.panel -import pl.treksoft.kvision.core.Container import pl.treksoft.kvision.core.ResString import pl.treksoft.kvision.core.Widget import pl.treksoft.kvision.html.Link import pl.treksoft.kvision.html.TAG import pl.treksoft.kvision.html.Tag -open class TabPanel : Container(setOf()) { - private var nav = Tag(TAG.UL, classes = setOf("nav", "nav-tabs")) - private var content = StackPanel(false) +open class TabPanel : SimplePanel(setOf()) { var activeIndex get() = content.activeIndex set(value) { @@ -20,9 +17,12 @@ open class TabPanel : Container(setOf()) { } } + private var nav = Tag(TAG.UL, classes = setOf("nav", "nav-tabs")) + private var content = StackPanel(false) + init { - this.add(nav) - this.add(content) + this.addInternal(nav) + this.addInternal(content) } open fun addTab(title: String, panel: Widget, icon: String? = null, @@ -52,4 +52,25 @@ open class TabPanel : Container(setOf()) { activeIndex = content.activeIndex return this } + + override fun add(child: Widget): TabPanel { + return addTab("", child) + } + + override fun addAll(children: List<Widget>): TabPanel { + children.forEach { add(it) } + return this + } + + override fun remove(child: Widget): TabPanel { + val index = content.children.indexOf(child) + return removeTab(index) + } + + override fun removeAll(): TabPanel { + content.removeAll() + nav.removeAll() + refresh() + return this + } } |