diff options
author | Robert Jaros <rjaros@finn.pl> | 2018-03-26 09:34:38 +0200 |
---|---|---|
committer | Robert Jaros <rjaros@finn.pl> | 2018-03-26 09:41:24 +0200 |
commit | 6287ce18301d58792e803bc7daa7068c164e704d (patch) | |
tree | cf90971cb887e8f16317f5b795bd5825b3bde29e | |
parent | 3b1eae9ed8b7e1d57f8f05968820c5eb4bbe8fe2 (diff) | |
download | kvision-6287ce18301d58792e803bc7daa7068c164e704d.tar.gz kvision-6287ce18301d58792e803bc7daa7068c164e704d.tar.bz2 kvision-6287ce18301d58792e803bc7daa7068c164e704d.zip |
Plain HTML form attributes (method, action, enctype, target, ...) support.
Form controls refactoring.
22 files changed, 290 insertions, 188 deletions
diff --git a/src/main/kotlin/pl/treksoft/kvision/dropdown/DropDown.kt b/src/main/kotlin/pl/treksoft/kvision/dropdown/DropDown.kt index 0a61a814..70b63c9f 100644 --- a/src/main/kotlin/pl/treksoft/kvision/dropdown/DropDown.kt +++ b/src/main/kotlin/pl/treksoft/kvision/dropdown/DropDown.kt @@ -30,6 +30,7 @@ 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 @@ -257,7 +258,7 @@ internal class DropDownButton( id: String, text: String, icon: String? = null, style: ButtonStyle = ButtonStyle.DEFAULT, disabled: Boolean = false, val forNavbar: Boolean = false, classes: Set<String> = setOf() ) : - Button(text, icon, style, disabled, classes) { + Button(text, icon, style, ButtonType.BUTTON, disabled, classes) { init { this.id = id diff --git a/src/main/kotlin/pl/treksoft/kvision/form/FormControl.kt b/src/main/kotlin/pl/treksoft/kvision/form/FormControl.kt index 41d964fd..091f4162 100644 --- a/src/main/kotlin/pl/treksoft/kvision/form/FormControl.kt +++ b/src/main/kotlin/pl/treksoft/kvision/form/FormControl.kt @@ -32,6 +32,21 @@ enum class InputSize(val className: String) { SMALL("input-sm") } +interface FormInput : Component { + /** + * Determines if the field is disabled. + */ + var disabled: Boolean + /** + * Input control field size. + */ + var size: InputSize? + /** + * The name attribute of the generated HTML input element. + */ + var name: String? +} + /** * Base interface of a form control. */ @@ -40,14 +55,30 @@ interface FormControl : Component { * Determines if the field is disabled. */ var disabled: Boolean + get() = input.disabled + set(value) { + input.disabled = value + } /** - * Input control size. + * Input control field size. */ var size: InputSize? + get() = input.size + set(value) { + input.size = value + } + /** + * The name attribute of the generated HTML input element. + */ + var name: String? + get() = input.name + set(value) { + input.name = value + } /** * The actual input component. */ - val input: Component + val input: FormInput /** * Form field label. */ diff --git a/src/main/kotlin/pl/treksoft/kvision/form/FormPanel.kt b/src/main/kotlin/pl/treksoft/kvision/form/FormPanel.kt index 1be29f31..2a274684 100644 --- a/src/main/kotlin/pl/treksoft/kvision/form/FormPanel.kt +++ b/src/main/kotlin/pl/treksoft/kvision/form/FormPanel.kt @@ -24,6 +24,7 @@ package pl.treksoft.kvision.form import com.github.snabbdom.VNode 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 @@ -42,20 +43,80 @@ enum class FormType(internal val formType: String) { } /** + * Form methods. + */ +enum class FormMethod(internal val method: String) { + GET("get"), + POST("post") +} + +/** + * Form encoding types. + */ +enum class FormEnctype(internal val enctype: String) { + URLENCODED("application/x-www-form-urlencoded"), + MULTIPART("multipart/form-data"), + PLAIN("text/plain") +} + +/** + * Form targets. + */ +enum class FormTarget(internal val target: String) { + BLANK("_blank"), + SELF("_self"), + PARENT("_parent"), + TOP("_top") +} + +/** * Bootstrap form component. * * @constructor * @param K model class type + * @param method HTTP method + * @param action the URL address to send data + * @param enctype form encoding type * @param type form layout * @param classes set of CSS class names * @param modelFactory function transforming a Map<String, Any?> to a data model of class K */ open class FormPanel<K>( + method: FormMethod? = null, action: String? = null, enctype: FormEnctype? = null, private val type: FormType? = null, classes: Set<String> = setOf(), modelFactory: (Map<String, Any?>) -> K ) : SimplePanel(classes) { /** + * HTTP method. + */ + var method by refreshOnUpdate(method) + /** + * The URL address to send data. + */ + var action by refreshOnUpdate(action) + /** + * The form encoding type. + */ + var enctype by refreshOnUpdate(enctype) + /** + * The form name. + */ + var name: String? by refreshOnUpdate() + /** + * The form target. + */ + var target: FormTarget? by refreshOnUpdate() + /** + * Determines if the form is not validated. + */ + var novalidate: Boolean? by refreshOnUpdate() + /** + * Determines if the form should have autocomplete. + */ + var autocomplete: Boolean? by refreshOnUpdate() + + /** * Function returning validation message. */ var validatorMessage @@ -111,6 +172,32 @@ open class FormPanel<K>( return cl } + override fun getSnAttrs(): List<StringPair> { + val sn = super.getSnAttrs().toMutableList() + method?.let { + sn.add("method" to it.method) + } + action?.let { + sn.add("action" to it) + } + enctype?.let { + sn.add("enctype" to it.enctype) + } + name?.let { + sn.add("name" to it) + } + target?.let { + sn.add("target" to it.target) + } + if (autocomplete == false) { + sn.add("autocomplete" to "off") + } + if (novalidate == true) { + sn.add("novalidate" to "novalidate") + } + return sn + } + protected fun <C : FormControl> addInternal( key: KProperty1<K, *>, control: C, required: Boolean = false, validatorMessage: ((C) -> String?)? = null, @@ -284,10 +371,11 @@ open class FormPanel<K>( * It takes the same parameters as the constructor of the built component. */ fun <K> Container.formPanel( + method: FormMethod? = null, action: String? = null, enctype: FormEnctype? = null, type: FormType? = null, classes: Set<String> = setOf(), modelFactory: (Map<String, Any?>) -> K ): FormPanel<K> { - val panel = FormPanel(type, classes, modelFactory) + val panel = FormPanel(method, action, enctype, type, classes, modelFactory) this.add(panel) return panel } 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 58033d3a..3afca46e 100644 --- a/src/main/kotlin/pl/treksoft/kvision/form/check/CheckBox.kt +++ b/src/main/kotlin/pl/treksoft/kvision/form/check/CheckBox.kt @@ -48,11 +48,12 @@ enum class CheckBoxStyle(internal val className: String) { * * @constructor * @param value selection state + * @param name the name of the input element * @param label label text bound to the input element * @param rich determines if [label] can contain HTML code */ open class CheckBox( - value: Boolean = false, label: String? = null, + value: Boolean = false, name: String? = null, label: String? = null, rich: Boolean = false ) : SimplePanel(setOf("checkbox")), BoolFormControl { @@ -76,19 +77,6 @@ open class CheckBox( input.startValue = value } /** - * The name attribute of the generated HTML input element. - */ - var name - get() = input.name - set(value) { - input.name = value - } - override var disabled - get() = input.disabled - set(value) { - input.disabled = value - } - /** * The label text bound to the input element. */ var label @@ -116,20 +104,15 @@ open class CheckBox( * Determines if the checkbox is rendered inline. */ var inline by refreshOnUpdate(false) - /** - * The size of the input. - */ - override var size - get() = input.size - set(value) { - input.size = value - } private val idc = "kv_form_checkbox_$counter" final override val input: CheckInput = CheckInput( CheckInputType.CHECKBOX, value, setOf("styled") - ).apply { id = idc } + ).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 } @@ -204,10 +187,10 @@ open class CheckBox( * It takes the same parameters as the constructor of the built component. */ fun Container.checkBox( - value: Boolean = false, label: String? = null, + value: Boolean = false, name: String? = null, label: String? = null, rich: Boolean = false, init: (CheckBox.() -> Unit)? = null ): CheckBox { - val checkBox = CheckBox(value, label, rich).apply { init?.invoke(this) } + val checkBox = CheckBox(value, name, label, rich).apply { init?.invoke(this) } this.add(checkBox) return checkBox } 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 0ed85e44..f79c1b48 100644 --- a/src/main/kotlin/pl/treksoft/kvision/form/check/CheckInput.kt +++ b/src/main/kotlin/pl/treksoft/kvision/form/check/CheckInput.kt @@ -27,6 +27,7 @@ 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.form.FormInput import pl.treksoft.kvision.form.InputSize /** @@ -48,7 +49,7 @@ enum class CheckInputType(internal val type: String) { open class CheckInput( type: CheckInputType = CheckInputType.CHECKBOX, value: Boolean = false, classes: Set<String> = setOf() -) : Widget(classes) { +) : Widget(classes), FormInput { init { this.setInternalEventListener<CheckInput> { @@ -81,11 +82,11 @@ open class CheckInput( /** * The name attribute of the generated HTML input element. */ - var name: String? by refreshOnUpdate() + override var name: String? by refreshOnUpdate() /** * Determines if the field is disabled. */ - var disabled by refreshOnUpdate(false) + override var disabled by refreshOnUpdate(false) /** * The additional String value used for the radio button group. */ @@ -93,7 +94,7 @@ open class CheckInput( /** * The size of the input. */ - var size: InputSize? by refreshOnUpdate() + override var size: InputSize? by refreshOnUpdate() override fun render(): VNode { return render("input") 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 6e26fa5b..181d9665 100644 --- a/src/main/kotlin/pl/treksoft/kvision/form/check/Radio.kt +++ b/src/main/kotlin/pl/treksoft/kvision/form/check/Radio.kt @@ -86,19 +86,6 @@ open class Radio( input.extraValue = value } /** - * The name attribute of the generated HTML input element. - */ - var name - get() = input.name - set(value) { - input.name = value - } - override var disabled - get() = input.disabled - set(value) { - input.disabled = value - } - /** * The label text bound to the input element. */ var label @@ -126,14 +113,6 @@ open class Radio( * Determines if the radio button is rendered inline. */ var inline by refreshOnUpdate(false) - /** - * The size of the input. - */ - override var size - get() = input.size - set(value) { - input.size = value - } private val idc = "kv_form_radio_$counter" final override val input: CheckInput = CheckInput(CheckInputType.RADIO, value).apply { 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 84e3d89f..8900437b 100644 --- a/src/main/kotlin/pl/treksoft/kvision/form/check/RadioGroup.kt +++ b/src/main/kotlin/pl/treksoft/kvision/form/check/RadioGroup.kt @@ -24,7 +24,6 @@ package pl.treksoft.kvision.form.check 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.form.FieldLabel import pl.treksoft.kvision.form.HelpBlock import pl.treksoft.kvision.form.InputSize @@ -41,12 +40,13 @@ import pl.treksoft.kvision.panel.SimplePanel * @constructor * @param options an optional list of options (label to value pairs) for the group * @param value selected option + * @param name the name attribute of the generated HTML input element * @param inline determines if the options are rendered inline * @param label label text of the options group * @param rich determines if [label] can contain HTML code */ open class RadioGroup( - options: List<StringPair>? = null, value: String? = null, inline: Boolean = false, + options: List<StringPair>? = null, value: String? = null, name: String? = null, inline: Boolean = false, label: String? = null, rich: Boolean = false ) : SimplePanel(setOf("form-group")), StringFormControl { @@ -87,16 +87,26 @@ open class RadioGroup( set(value) { flabel.rich = value } - override var size: InputSize? = null + override var name + get() = getNameFromChildren() + set(value) { + setNameToChildren(value) + } + override var size + get() = getSizeFromChildren() + set(value) { + setSizeToChildren(value) + } private val idc = "kv_form_radiogroup_" + Select.counter - final override val input = Widget() + final override val input = CheckInput() final override val flabel: FieldLabel = FieldLabel(idc, label, rich) final override val validationInfo: HelpBlock = HelpBlock().apply { visible = false } init { setChildrenFromOptions() setValueToChildren(value) + setNameToChildren(name) counter++ } @@ -129,16 +139,34 @@ open class RadioGroup( getChildren().filterIsInstance<Radio>().forEach { it.disabled = disabled } } + private fun getNameFromChildren(): String? { + return getChildren().filterIsInstance<Radio>().firstOrNull()?.name ?: this.idc + } + + private fun setNameToChildren(name: String?) { + val tname = name ?: this.idc + getChildren().filterIsInstance<Radio>().forEach { it.name = tname } + } + + private fun getSizeFromChildren(): InputSize? { + return getChildren().filterIsInstance<Radio>().firstOrNull()?.size + } + + private fun setSizeToChildren(size: InputSize?) { + getChildren().filterIsInstance<Radio>().forEach { it.size = size } + } + private fun setChildrenFromOptions() { + val currentName = this.name super.removeAll() this.addInternal(flabel) options?.let { - val tidc = this.idc + val tname = currentName ?: this.idc val tinline = this.inline val c = it.map { Radio(false, extraValue = it.first, label = it.second).apply { inline = tinline - name = tidc + name = tname eventTarget = this@RadioGroup setEventListener<Radio> { change = { @@ -169,10 +197,10 @@ open class RadioGroup( * It takes the same parameters as the constructor of the built component. */ fun Container.radioGroup( - options: List<StringPair>? = null, value: String? = null, inline: Boolean = false, + options: List<StringPair>? = null, value: String? = null, name: String? = null, inline: Boolean = false, label: String? = null, rich: Boolean = false, init: (RadioGroup.() -> Unit)? = null ): RadioGroup { - val radioGroup = RadioGroup(options, value, inline, label, rich).apply { init?.invoke(this) } + val radioGroup = RadioGroup(options, value, name, inline, label, rich).apply { init?.invoke(this) } this.add(radioGroup) return radioGroup } diff --git a/src/main/kotlin/pl/treksoft/kvision/form/select/Select.kt b/src/main/kotlin/pl/treksoft/kvision/form/select/Select.kt index 0c9cdb2e..f19081e1 100644 --- a/src/main/kotlin/pl/treksoft/kvision/form/select/Select.kt +++ b/src/main/kotlin/pl/treksoft/kvision/form/select/Select.kt @@ -41,6 +41,7 @@ import pl.treksoft.kvision.utils.SnOn * @constructor * @param options an optional list of options (label to value pairs) for the select control * @param value selected value + * @param name the name attribute of the generated HTML input element * @param multiple allows multiple value selection (multiple values are comma delimited) * @param ajaxOptions additional options for remote (AJAX) data source * @param label label text bound to the input element @@ -48,7 +49,7 @@ import pl.treksoft.kvision.utils.SnOn */ @Suppress("TooManyFunctions") open class Select( - options: List<StringPair>? = null, value: String? = null, + options: List<StringPair>? = null, value: String? = null, name: String? = null, multiple: Boolean = false, ajaxOptions: AjaxOptions? = null, label: String? = null, rich: Boolean = false ) : SimplePanel(setOf("form-group")), StringFormControl { @@ -70,14 +71,6 @@ open class Select( input.value = value } /** - * The name attribute of the generated HTML select element. - */ - var name - get() = input.name - set(value) { - input.name = value - } - /** * Determines if multiple value selection is allowed. */ var multiple @@ -149,11 +142,6 @@ open class Select( set(value) { input.emptyOption = value } - override var disabled - get() = input.disabled - set(value) { - input.disabled = value - } /** * Determines if the select is automatically focused. */ @@ -178,17 +166,15 @@ open class Select( set(value) { flabel.rich = value } - override var size - get() = input.size - set(value) { - input.size = value - } private val idc = "kv_form_select_$counter" final override val input: SelectInput = SelectInput( options, value, multiple, ajaxOptions, setOf("form-control") - ).apply { id = idc } + ).apply { + this.id = idc + this.name = name + } final override val flabel: FieldLabel = FieldLabel(idc, label, rich) final override val validationInfo: HelpBlock = HelpBlock().apply { visible = false } @@ -287,11 +273,11 @@ open class Select( * It takes the same parameters as the constructor of the built component. */ fun Container.select( - options: List<StringPair>? = null, value: String? = null, + options: List<StringPair>? = null, value: String? = null, name: String? = null, multiple: Boolean = false, ajaxOptions: AjaxOptions? = null, label: String? = null, rich: Boolean = false, init: (Select.() -> Unit)? = null ): Select { - val select = Select(options, value, multiple, ajaxOptions, label, rich).apply { init?.invoke(this) } + val select = Select(options, value, name, multiple, ajaxOptions, label, rich).apply { init?.invoke(this) } this.add(select) return select } diff --git a/src/main/kotlin/pl/treksoft/kvision/form/select/SelectInput.kt b/src/main/kotlin/pl/treksoft/kvision/form/select/SelectInput.kt index 87580139..97f3989c 100644 --- a/src/main/kotlin/pl/treksoft/kvision/form/select/SelectInput.kt +++ b/src/main/kotlin/pl/treksoft/kvision/form/select/SelectInput.kt @@ -28,6 +28,7 @@ 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.form.FormInput import pl.treksoft.kvision.form.InputSize import pl.treksoft.kvision.html.ButtonStyle import pl.treksoft.kvision.panel.SimplePanel @@ -60,7 +61,7 @@ open class SelectInput( options: List<StringPair>? = null, value: String? = null, multiple: Boolean = false, ajaxOptions: AjaxOptions? = null, classes: Set<String> = setOf() -) : SimplePanel(classes) { +) : SimplePanel(classes), FormInput { /** * A list of options (label to value pairs) for the select control. @@ -73,7 +74,7 @@ open class SelectInput( /** * The name attribute of the generated HTML select element. */ - var name: String? by refreshOnUpdate() + override var name: String? by refreshOnUpdate() /** * Determines if multiple value selection is allowed. */ @@ -118,7 +119,7 @@ open class SelectInput( /** * Determines if the field is disabled. */ - var disabled by refreshOnUpdate(false) + override var disabled by refreshOnUpdate(false) /** * Determines if the select is automatically focused. */ @@ -126,7 +127,7 @@ open class SelectInput( /** * The size of the input. */ - var size: InputSize? by refreshOnUpdate() + override var size: InputSize? by refreshOnUpdate() init { setChildrenFromOptions() diff --git a/src/main/kotlin/pl/treksoft/kvision/form/spinner/Spinner.kt b/src/main/kotlin/pl/treksoft/kvision/form/spinner/Spinner.kt index 9d72f36b..4fa68e47 100644 --- a/src/main/kotlin/pl/treksoft/kvision/form/spinner/Spinner.kt +++ b/src/main/kotlin/pl/treksoft/kvision/form/spinner/Spinner.kt @@ -35,6 +35,7 @@ import pl.treksoft.kvision.utils.SnOn * * @constructor * @param value spinner value + * @param name the name attribute of the generated HTML input element * @param min minimal value (default 0) * @param max maximal value (default 100) * @param step step value (default 1) @@ -45,7 +46,7 @@ import pl.treksoft.kvision.utils.SnOn * @param rich determines if [label] can contain HTML code */ open class Spinner( - value: Number? = null, min: Int = 0, max: Int = DEFAULT_MAX, step: Double = DEFAULT_STEP, + value: Number? = null, name: String? = 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 @@ -127,19 +128,6 @@ open class Spinner( input.placeholder = value } /** - * The name attribute of the generated HTML input element. - */ - var name - get() = input.name - set(value) { - input.name = value - } - override var disabled - get() = input.disabled - set(value) { - input.disabled = value - } - /** * Determines if the spinner is automatically focused. */ var autofocus @@ -171,15 +159,13 @@ open class Spinner( 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 } + .apply { + this.id = idc + this.name = name + } final override val flabel: FieldLabel = FieldLabel(idc, label, rich) final override val validationInfo: HelpBlock = HelpBlock().apply { visible = false } @@ -253,12 +239,19 @@ open class Spinner( * It takes the same parameters as the constructor of the built component. */ fun Container.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, init: (Spinner.() -> Unit)? = null + value: Number? = null, + name: String? = 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, + init: (Spinner.() -> Unit)? = null ): Spinner { - val spinner = Spinner(value, min, max, step, decimals, buttonsType, forceType, label, rich).apply { + val spinner = Spinner(value, name, min, max, step, decimals, buttonsType, forceType, label, rich).apply { init?.invoke( 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 index b9903ff8..e318db35 100644 --- a/src/main/kotlin/pl/treksoft/kvision/form/spinner/SpinnerInput.kt +++ b/src/main/kotlin/pl/treksoft/kvision/form/spinner/SpinnerInput.kt @@ -27,6 +27,7 @@ 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.form.FormInput import pl.treksoft.kvision.form.InputSize import pl.treksoft.kvision.utils.obj @@ -71,7 +72,7 @@ open class SpinnerInput( decimals: Int = 0, buttonsType: ButtonsType = ButtonsType.VERTICAL, forceType: ForceType = ForceType.NONE, classes: Set<String> = setOf() -) : Widget(classes + "form-control") { +) : Widget(classes + "form-control"), FormInput { init { this.addSurroundingCssClass("input-group") @@ -136,11 +137,11 @@ open class SpinnerInput( /** * The name attribute of the generated HTML input element. */ - var name: String? by refreshOnUpdate() + override var name: String? by refreshOnUpdate() /** * Determines if the field is disabled. */ - var disabled by refreshOnUpdate(false) + override var disabled by refreshOnUpdate(false) /** * Determines if the spinner is automatically focused. */ @@ -152,7 +153,7 @@ open class SpinnerInput( /** * The size of the input. */ - var size: InputSize? by refreshOnUpdate() + override var size: InputSize? by refreshOnUpdate() private var siblings: JQuery? = null 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 3c62d4a8..7c23615a 100644 --- a/src/main/kotlin/pl/treksoft/kvision/form/text/AbstractText.kt +++ b/src/main/kotlin/pl/treksoft/kvision/form/text/AbstractText.kt @@ -67,14 +67,6 @@ abstract class AbstractText(label: String? = null, rich: Boolean = false) : input.placeholder = value } /** - * The name attribute of the generated HTML input element. - */ - var name - get() = input.name - set(value) { - input.name = value - } - /** * Maximal length of the text input value. */ var maxlength @@ -82,11 +74,6 @@ abstract class AbstractText(label: String? = null, rich: Boolean = false) : set(value) { input.maxlength = value } - override var disabled - get() = input.disabled - set(value) { - input.disabled = value - } /** * Determines if the text input is automatically focused. */ @@ -119,11 +106,6 @@ abstract class AbstractText(label: String? = null, rich: Boolean = false) : set(value) { flabel.rich = value } - override var size - get() = input.size - set(value) { - input.size = value - } /** * @suppress 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 5f4a243d..52cc7792 100644 --- a/src/main/kotlin/pl/treksoft/kvision/form/text/AbstractTextInput.kt +++ b/src/main/kotlin/pl/treksoft/kvision/form/text/AbstractTextInput.kt @@ -25,6 +25,7 @@ import com.github.snabbdom.VNode import pl.treksoft.kvision.core.StringBoolPair import pl.treksoft.kvision.core.StringPair import pl.treksoft.kvision.core.Widget +import pl.treksoft.kvision.form.FormInput import pl.treksoft.kvision.form.InputSize /** @@ -37,7 +38,7 @@ import pl.treksoft.kvision.form.InputSize abstract class AbstractTextInput( value: String? = null, classes: Set<String> = setOf() -) : Widget(classes) { +) : Widget(classes), FormInput { init { this.setInternalEventListener<AbstractTextInput> { @@ -65,7 +66,7 @@ abstract class AbstractTextInput( /** * The name attribute of the generated HTML input element. */ - var name: String? by refreshOnUpdate() + override var name: String? by refreshOnUpdate() /** * Maximal length of the text input value. */ @@ -73,7 +74,7 @@ abstract class AbstractTextInput( /** * Determines if the field is disabled. */ - var disabled by refreshOnUpdate(false) + override var disabled by refreshOnUpdate(false) /** * Determines if the text input is automatically focused. */ @@ -85,7 +86,7 @@ abstract class AbstractTextInput( /** * The size of the input. */ - var size: InputSize? by refreshOnUpdate() + override var size: InputSize? by refreshOnUpdate() override fun getSnClass(): List<StringBoolPair> { val cl = super.getSnClass().toMutableList() diff --git a/src/main/kotlin/pl/treksoft/kvision/form/text/Password.kt b/src/main/kotlin/pl/treksoft/kvision/form/text/Password.kt index 995243c9..1d36516e 100644 --- a/src/main/kotlin/pl/treksoft/kvision/form/text/Password.kt +++ b/src/main/kotlin/pl/treksoft/kvision/form/text/Password.kt @@ -28,12 +28,13 @@ import pl.treksoft.kvision.core.Container * * @constructor * @param value text input value + * @param name the name attribute of the generated HTML input element * @param label label text bound to the input element * @param rich determines if [label] can contain HTML code */ -open class Password(value: String? = null, label: String? = null, rich: Boolean = false) : Text( +open class Password(value: String? = null, name: String? = null, label: String? = null, rich: Boolean = false) : Text( TextInputType.PASSWORD, - value, label, rich + value, name, label, rich ) { companion object { /** @@ -42,9 +43,13 @@ open class Password(value: String? = null, label: String? = null, rich: Boolean * It takes the same parameters as the constructor of the built component. */ fun Container.password( - value: String? = null, label: String? = null, rich: Boolean = false, init: (Password.() -> Unit)? = null + value: String? = null, + name: String? = null, + label: String? = null, + rich: Boolean = false, + init: (Password.() -> Unit)? = null ): Password { - val password = Password(value, label, rich).apply { init?.invoke(this) } + val password = Password(value, name, label, rich).apply { init?.invoke(this) } this.add(password) return password } diff --git a/src/main/kotlin/pl/treksoft/kvision/form/text/RichText.kt b/src/main/kotlin/pl/treksoft/kvision/form/text/RichText.kt index 953fec90..22126797 100644 --- a/src/main/kotlin/pl/treksoft/kvision/form/text/RichText.kt +++ b/src/main/kotlin/pl/treksoft/kvision/form/text/RichText.kt @@ -28,11 +28,12 @@ import pl.treksoft.kvision.core.Container * * @constructor * @param value text input value + * @param name the name attribute of the generated HTML input element * @param label label text bound to the input element * @param rich determines if [label] can contain HTML code */ open class RichText( - value: String? = null, + value: String? = null, name: String? = null, label: String? = null, rich: Boolean = false ) : AbstractText(label, rich) { @@ -45,7 +46,10 @@ open class RichText( input.height = value } - final override val input: RichTextInput = RichTextInput(value).apply { id = idc } + final override val input: RichTextInput = RichTextInput(value).apply { + this.id = idc + this.name = name + } init { @Suppress("LeakingThis") @@ -61,9 +65,13 @@ open class RichText( * It takes the same parameters as the constructor of the built component. */ fun Container.richText( - value: String? = null, label: String? = null, rich: Boolean = false, init: (RichText.() -> Unit)? = null + value: String? = null, + name: String? = null, + label: String? = null, + rich: Boolean = false, + init: (RichText.() -> Unit)? = null ): RichText { - val richText = RichText(value, label, rich).apply { init?.invoke(this) } + val richText = RichText(value, name, label, rich).apply { init?.invoke(this) } this.add(richText) return richText } 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 fd53adff..7e4127f5 100644 --- a/src/main/kotlin/pl/treksoft/kvision/form/text/Text.kt +++ b/src/main/kotlin/pl/treksoft/kvision/form/text/Text.kt @@ -29,11 +29,12 @@ import pl.treksoft.kvision.core.Container * @constructor * @param type text input type (default "text") * @param value text input value + * @param name the name attribute of the generated HTML input element * @param label label text bound to the input element * @param rich determines if [label] can contain HTML code */ open class Text( - type: TextInputType = TextInputType.TEXT, value: String? = null, + type: TextInputType = TextInputType.TEXT, value: String? = null, name: String? = null, label: String? = null, rich: Boolean = false ) : AbstractText(label, rich) { @@ -54,7 +55,10 @@ open class Text( input.autocomplete = value } - final override val input: TextInput = TextInput(type, value).apply { id = idc } + final override val input: TextInput = TextInput(type, value).apply { + this.id = idc + this.name = name + } init { @Suppress("LeakingThis") @@ -70,10 +74,10 @@ open class Text( * It takes the same parameters as the constructor of the built component. */ fun Container.text( - type: TextInputType = TextInputType.TEXT, value: String? = null, + type: TextInputType = TextInputType.TEXT, value: String? = null, name: String? = null, label: String? = null, rich: Boolean = false, init: (Text.() -> Unit)? = null ): Text { - val text = Text(type, value, label, rich).apply { init?.invoke(this) } + val text = Text(type, value, name, label, rich).apply { init?.invoke(this) } this.add(text) return text } 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 58f63028..d5058583 100644 --- a/src/main/kotlin/pl/treksoft/kvision/form/text/TextArea.kt +++ b/src/main/kotlin/pl/treksoft/kvision/form/text/TextArea.kt @@ -30,11 +30,12 @@ import pl.treksoft.kvision.core.Container * @param cols number of columns * @param rows number of rows * @param value text input value + * @param name the name attribute of the generated HTML input element * @param label label text bound to the input element * @param rich determines if [label] can contain HTML code */ open class TextArea( - cols: Int? = null, rows: Int? = null, value: String? = null, + cols: Int? = null, rows: Int? = null, value: String? = null, name: String? = null, label: String? = null, rich: Boolean = false ) : AbstractText(label, rich) { @@ -63,7 +64,10 @@ open class TextArea( input.wrapHard = value } - final override val input: TextAreaInput = TextAreaInput(cols, rows, value).apply { id = idc } + final override val input: TextAreaInput = TextAreaInput(cols, rows, value).apply { + this.id = idc + this.name = name + } init { @Suppress("LeakingThis") @@ -79,10 +83,10 @@ open class TextArea( * It takes the same parameters as the constructor of the built component. */ fun Container.textArea( - cols: Int? = null, rows: Int? = null, value: String? = null, + cols: Int? = null, rows: Int? = null, value: String? = null, name: String? = null, label: String? = null, rich: Boolean = false, init: (TextArea.() -> Unit)? = null ): TextArea { - val textArea = TextArea(cols, rows, value, label, rich).apply { init?.invoke(this) } + val textArea = TextArea(cols, rows, value, name, label, rich).apply { init?.invoke(this) } this.add(textArea) return textArea } diff --git a/src/main/kotlin/pl/treksoft/kvision/form/time/DateTime.kt b/src/main/kotlin/pl/treksoft/kvision/form/time/DateTime.kt index ecf930e2..3d32fd8c 100644 --- a/src/main/kotlin/pl/treksoft/kvision/form/time/DateTime.kt +++ b/src/main/kotlin/pl/treksoft/kvision/form/time/DateTime.kt @@ -36,12 +36,13 @@ import kotlin.js.Date * * @constructor * @param value date/time input value + * @param name the name attribute of the generated HTML input element * @param format date/time format (default YYYY-MM-DD HH:mm) * @param label label text bound to the input element * @param rich determines if [label] can contain HTML code */ open class DateTime( - value: Date? = null, format: String = "YYYY-MM-DD HH:mm", label: String? = null, + value: Date? = null, name: String? = null, format: String = "YYYY-MM-DD HH:mm", label: String? = null, rich: Boolean = false ) : SimplePanel(setOf("form-group")), DateFormControl { @@ -70,19 +71,6 @@ open class DateTime( input.placeholder = value } /** - * The name attribute of the generated HTML input element. - */ - var name - get() = input.name - set(value) { - input.name = value - } - override var disabled - get() = input.disabled - set(value) { - input.disabled = value - } - /** * Determines if the date/time input is automatically focused. */ var autofocus @@ -170,14 +158,12 @@ open class DateTime( set(value) { flabel.rich = value } - override var size - get() = input.size - set(value) { - input.size = value - } private val idc = "kv_form_time_$counter" - final override val input: DateTimeInput = DateTimeInput(value, format).apply { id = idc } + final override val input: DateTimeInput = DateTimeInput(value, format).apply { + this.id = idc + this.name = name + } final override val flabel: FieldLabel = FieldLabel(idc, label, rich) final override val validationInfo: HelpBlock = HelpBlock().apply { visible = false } @@ -249,10 +235,10 @@ open class DateTime( * It takes the same parameters as the constructor of the built component. */ fun Container.dateTime( - value: Date? = null, format: String = "YYYY-MM-DD HH:mm", label: String? = null, + value: Date? = null, name: String? = null, format: String = "YYYY-MM-DD HH:mm", label: String? = null, rich: Boolean = false, init: (DateTime.() -> Unit)? = null ): DateTime { - val dateTime = DateTime(value, format, label, rich).apply { init?.invoke(this) } + val dateTime = DateTime(value, name, format, label, rich).apply { init?.invoke(this) } this.add(dateTime) return dateTime } diff --git a/src/main/kotlin/pl/treksoft/kvision/form/time/DateTimeInput.kt b/src/main/kotlin/pl/treksoft/kvision/form/time/DateTimeInput.kt index d935487b..86e9c7ba 100644 --- a/src/main/kotlin/pl/treksoft/kvision/form/time/DateTimeInput.kt +++ b/src/main/kotlin/pl/treksoft/kvision/form/time/DateTimeInput.kt @@ -26,6 +26,7 @@ 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.form.FormInput import pl.treksoft.kvision.form.InputSize import pl.treksoft.kvision.utils.obj import pl.treksoft.kvision.utils.toDateF @@ -47,7 +48,7 @@ internal const val MAX_VIEW = 4 open class DateTimeInput( value: Date? = null, format: String = "YYYY-MM-DD HH:mm", classes: Set<String> = setOf() -) : Widget(classes + "form-control") { +) : Widget(classes + "form-control"), FormInput { init { this.setInternalEventListener<DateTimeInput> { @@ -72,11 +73,11 @@ open class DateTimeInput( /** * The name attribute of the generated HTML input element. */ - var name: String? by refreshOnUpdate() + override var name: String? by refreshOnUpdate() /** * Determines if the field is disabled. */ - var disabled by refreshOnUpdate(false) + override var disabled by refreshOnUpdate(false) /** * Determines if the text input is automatically focused. */ @@ -88,7 +89,7 @@ open class DateTimeInput( /** * The size of the input. */ - var size: InputSize? by refreshOnUpdate() + override var size: InputSize? by refreshOnUpdate() /** * Day of the week start. 0 (Sunday) to 6 (Saturday). */ diff --git a/src/main/kotlin/pl/treksoft/kvision/html/Button.kt b/src/main/kotlin/pl/treksoft/kvision/html/Button.kt index 84f7c309..99f4ca13 100644 --- a/src/main/kotlin/pl/treksoft/kvision/html/Button.kt +++ b/src/main/kotlin/pl/treksoft/kvision/html/Button.kt @@ -52,6 +52,15 @@ enum class ButtonSize(internal val className: String) { } /** + * Button types. + */ +enum class ButtonType(internal val buttonType: String) { + BUTTON("button"), + SUBMIT("submit"), + RESET("reset") +} + +/** * Button component. * * @constructor @@ -62,7 +71,7 @@ enum class ButtonSize(internal val className: String) { * @param classes a set of CSS class names */ open class Button( - text: String, icon: String? = null, style: ButtonStyle = ButtonStyle.DEFAULT, + text: String, icon: String? = null, style: ButtonStyle = ButtonStyle.DEFAULT, type: ButtonType = ButtonType.BUTTON, disabled: Boolean = false, classes: Set<String> = setOf() ) : Widget(classes) { @@ -79,6 +88,10 @@ open class Button( */ var style by refreshOnUpdate(style) /** + * Button types. + */ + var type by refreshOnUpdate(type) + /** * Determines if button is disabled. */ var disabled by refreshOnUpdate(disabled) @@ -117,7 +130,7 @@ open class Button( } override fun getSnAttrs(): List<StringPair> { - return super.getSnAttrs() + ("type" to "button") + return super.getSnAttrs() + ("type" to type.buttonType) } /** @@ -139,10 +152,15 @@ open class Button( * It takes the same parameters as the constructor of the built component. */ fun Container.button( - text: String, icon: String? = null, style: ButtonStyle = ButtonStyle.DEFAULT, - disabled: Boolean = false, classes: Set<String> = setOf(), init: (Button.() -> Unit)? = null + text: String, + icon: String? = null, + style: ButtonStyle = ButtonStyle.DEFAULT, + type: ButtonType = ButtonType.BUTTON, + disabled: Boolean = false, + classes: Set<String> = setOf(), + init: (Button.() -> Unit)? = null ): Button { - val button = Button(text, icon, style, disabled, classes).apply { init?.invoke(this) } + val button = Button(text, icon, style, type, disabled, classes).apply { init?.invoke(this) } this.add(button) return button } diff --git a/src/main/kotlin/pl/treksoft/kvision/html/Tag.kt b/src/main/kotlin/pl/treksoft/kvision/html/Tag.kt index 936d7b89..75536b88 100644 --- a/src/main/kotlin/pl/treksoft/kvision/html/Tag.kt +++ b/src/main/kotlin/pl/treksoft/kvision/html/Tag.kt @@ -76,7 +76,8 @@ enum class TAG(internal val tagName: String) { TR("tr"), TD("td"), - FORM("form") + FORM("form"), + INPUT("input") } /** diff --git a/src/test/kotlin/test/pl/treksoft/kvision/form/select/SelectSpec.kt b/src/test/kotlin/test/pl/treksoft/kvision/form/select/SelectSpec.kt index 19634125..eaccd551 100644 --- a/src/test/kotlin/test/pl/treksoft/kvision/form/select/SelectSpec.kt +++ b/src/test/kotlin/test/pl/treksoft/kvision/form/select/SelectSpec.kt @@ -35,7 +35,7 @@ class SelectSpec : DomSpec { fun render() { run { val root = Root("test", true) - val select = Select(listOf("test1" to "Test 1", "test2" to "Test 2"), "test1", true, null, "Label").apply { + val select = Select(listOf("test1" to "Test 1", "test2" to "Test 2"), "test1", null, true, null, "Label").apply { liveSearch = true placeholder = "Choose ..." selectWidthType = SelectWidthType.FIT |