aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/main/kotlin/pl/treksoft/kvision/core/StyledComponent.kt179
-rw-r--r--src/main/kotlin/pl/treksoft/kvision/core/Widget.kt26
-rw-r--r--src/main/kotlin/pl/treksoft/kvision/dropdown/DropDown.kt12
-rw-r--r--src/main/kotlin/pl/treksoft/kvision/form/check/CheckBox.kt18
-rw-r--r--src/main/kotlin/pl/treksoft/kvision/form/check/CheckInput.kt43
-rw-r--r--src/main/kotlin/pl/treksoft/kvision/form/check/Radio.kt18
-rw-r--r--src/main/kotlin/pl/treksoft/kvision/form/check/RadioGroup.kt18
-rw-r--r--src/main/kotlin/pl/treksoft/kvision/form/select/SelectInput.kt94
-rw-r--r--src/main/kotlin/pl/treksoft/kvision/form/select/SelectOptGroup.kt24
-rw-r--r--src/main/kotlin/pl/treksoft/kvision/form/select/SelectOption.kt36
-rw-r--r--src/main/kotlin/pl/treksoft/kvision/form/spinner/SpinnerInput.kt85
-rw-r--r--src/main/kotlin/pl/treksoft/kvision/form/text/AbstractTextInput.kt55
-rw-r--r--src/main/kotlin/pl/treksoft/kvision/form/text/TextAreaInput.kt18
-rw-r--r--src/main/kotlin/pl/treksoft/kvision/form/text/TextInput.kt12
-rw-r--r--src/main/kotlin/pl/treksoft/kvision/form/time/DateTimeInput.kt90
-rw-r--r--src/main/kotlin/pl/treksoft/kvision/html/Button.kt42
-rw-r--r--src/main/kotlin/pl/treksoft/kvision/html/Image.kt30
-rw-r--r--src/main/kotlin/pl/treksoft/kvision/html/Link.kt24
-rw-r--r--src/main/kotlin/pl/treksoft/kvision/html/List.kt18
-rw-r--r--src/main/kotlin/pl/treksoft/kvision/html/Tag.kt24
-rw-r--r--src/main/kotlin/pl/treksoft/kvision/modal/Confirm.kt6
-rw-r--r--src/main/kotlin/pl/treksoft/kvision/modal/Modal.kt12
-rw-r--r--src/main/kotlin/pl/treksoft/kvision/panel/FlexPanel.kt38
-rw-r--r--src/main/kotlin/pl/treksoft/kvision/panel/GridPanel.kt72
-rw-r--r--src/main/kotlin/pl/treksoft/kvision/panel/ResponsiveGridPanel.kt6
-rw-r--r--src/main/kotlin/pl/treksoft/kvision/panel/StackPanel.kt6
-rw-r--r--src/test/kotlin/test/pl/treksoft/kvision/html/ImageSpec.kt1
27 files changed, 202 insertions, 805 deletions
diff --git a/src/main/kotlin/pl/treksoft/kvision/core/StyledComponent.kt b/src/main/kotlin/pl/treksoft/kvision/core/StyledComponent.kt
index f0ceb587..d130ec0b 100644
--- a/src/main/kotlin/pl/treksoft/kvision/core/StyledComponent.kt
+++ b/src/main/kotlin/pl/treksoft/kvision/core/StyledComponent.kt
@@ -22,187 +22,102 @@
package pl.treksoft.kvision.core
import pl.treksoft.kvision.utils.asString
+import kotlin.reflect.KProperty
/**
* Base class for components supporting CSS styling.
*/
abstract class StyledComponent : Component {
+ private val propertyValues: MutableMap<String, Any?> = mutableMapOf()
+
/**
* Width of the current component.
*/
- open var width: CssSize? = null
- set(value) {
- field = value
- refresh()
- }
+ open var width: CssSize? by refreshOnUpdate()
/**
* Minimal width of the current component.
*/
- var minWidth: CssSize? = null
- set(value) {
- field = value
- refresh()
- }
+ var minWidth: CssSize? by refreshOnUpdate()
/**
* Maximal width of the current component.
*/
- var maxWidth: CssSize? = null
- set(value) {
- field = value
- refresh()
- }
+ var maxWidth: CssSize? by refreshOnUpdate()
/**
* Height of the current component.
*/
- var height: CssSize? = null
- set(value) {
- field = value
- refresh()
- }
+ var height: CssSize? by refreshOnUpdate()
/**
* Minimal height of the current component.
*/
- var minHeight: CssSize? = null
- set(value) {
- field = value
- refresh()
- }
+ var minHeight: CssSize? by refreshOnUpdate()
/**
* Maximal height of the current component.
*/
- var maxHeight: CssSize? = null
- set(value) {
- field = value
- refresh()
- }
+ var maxHeight: CssSize? by refreshOnUpdate()
/**
* Border of the current component.
*/
- var border: Border? = null
- set(value) {
- field = value
- refresh()
- }
+ var border: Border? by refreshOnUpdate()
/**
* Top border of the current component.
*/
- var borderTop: Border? = null
- set(value) {
- field = value
- refresh()
- }
+ var borderTop: Border? by refreshOnUpdate()
/**
* Right border of the current component.
*/
- var borderRight: Border? = null
- set(value) {
- field = value
- refresh()
- }
+ var borderRight: Border? by refreshOnUpdate()
/**
* Bottom border of the current component.
*/
- var borderBottom: Border? = null
- set(value) {
- field = value
- refresh()
- }
+ var borderBottom: Border? by refreshOnUpdate()
/**
* Left border of the current component.
*/
- var borderLeft: Border? = null
- set(value) {
- field = value
- refresh()
- }
+ var borderLeft: Border? by refreshOnUpdate()
/**
* Margin of the current component.
*/
- var margin: CssSize? = null
- set(value) {
- field = value
- refresh()
- }
+ var margin: CssSize? by refreshOnUpdate()
/**
* Top margin of the current component.
*/
- var marginTop: CssSize? = null
- set(value) {
- field = value
- refresh()
- }
+ var marginTop: CssSize? by refreshOnUpdate()
/**
* Right margin of the current component.
*/
- var marginRight: CssSize? = null
- set(value) {
- field = value
- refresh()
- }
+ var marginRight: CssSize? by refreshOnUpdate()
/**
* Bottom margin of the current component.
*/
- var marginBottom: CssSize? = null
- set(value) {
- field = value
- refresh()
- }
+ var marginBottom: CssSize? by refreshOnUpdate()
/**
* Left margin of the current component.
*/
- var marginLeft: CssSize? = null
- set(value) {
- field = value
- refresh()
- }
+ var marginLeft: CssSize? by refreshOnUpdate()
/**
* Padding of the current component.
*/
- var padding: CssSize? = null
- set(value) {
- field = value
- refresh()
- }
+ var padding: CssSize? by refreshOnUpdate()
/**
* Top padding of the current component.
*/
- var paddingTop: CssSize? = null
- set(value) {
- field = value
- refresh()
- }
+ var paddingTop: CssSize? by refreshOnUpdate()
/**
* Right padding of the current component.
*/
- var paddingRight: CssSize? = null
- set(value) {
- field = value
- refresh()
- }
+ var paddingRight: CssSize? by refreshOnUpdate()
/**
* Bottom padding of the current component.
*/
- var paddingBottom: CssSize? = null
- set(value) {
- field = value
- refresh()
- }
+ var paddingBottom: CssSize? by refreshOnUpdate()
/**
* Left padding of the current component.
*/
- var paddingLeft: CssSize? = null
- set(value) {
- field = value
- refresh()
- }
+ var paddingLeft: CssSize? by refreshOnUpdate()
/**
* Text color for the current component.
*/
- var color: Color? = null
- set(value) {
- field = value
- refresh()
- }
+ var color: Color? by refreshOnUpdate()
/**
* Text color for the current component given in hex format (write only).
*
@@ -234,19 +149,11 @@ abstract class StyledComponent : Component {
/**
* Opacity of the current component.
*/
- var opacity: Double? = null
- set(value) {
- field = value
- refresh()
- }
+ var opacity: Double? by refreshOnUpdate()
/**
* Background of the current component.
*/
- var background: Background? = null
- set(value) {
- field = value
- refresh()
- }
+ var background: Background? by refreshOnUpdate()
private var snStyleCache: List<StringPair>? = null
@@ -351,4 +258,36 @@ abstract class StyledComponent : Component {
}
return snstyle
}
+
+ internal fun <T> refreshOnUpdate(refreshFunction: ((T) -> Unit) = { this.refresh() }) =
+ RefreshDelegateProvider<T>(null, refreshFunction)
+
+ internal fun <T> refreshOnUpdate(initialValue: T, refreshFunction: ((T) -> Unit) = { this.refresh() }) =
+ RefreshDelegateProvider(initialValue, refreshFunction)
+
+ internal inner class RefreshDelegateProvider<T>(
+ private val initialValue: T?, private val refreshFunction: (T) -> Unit
+ ) {
+ operator fun provideDelegate(thisRef: Any?, prop: KProperty<*>): RefreshDelegate<T> {
+ if (initialValue != null) propertyValues[prop.name] = initialValue
+ return RefreshDelegate(refreshFunction)
+ }
+ }
+
+ internal inner class RefreshDelegate<T>(private val refreshFunction: ((T) -> Unit)) {
+ @Suppress("UNCHECKED_CAST")
+ operator fun getValue(thisRef: StyledComponent, property: KProperty<*>): T {
+ val value = propertyValues[property.name]
+ return if (value != null) {
+ value as T
+ } else {
+ null as T
+ }
+ }
+
+ operator fun setValue(thisRef: StyledComponent, property: KProperty<*>, value: T) {
+ propertyValues[property.name] = value
+ refreshFunction(value)
+ }
+ }
}
diff --git a/src/main/kotlin/pl/treksoft/kvision/core/Widget.kt b/src/main/kotlin/pl/treksoft/kvision/core/Widget.kt
index f50b1679..a746ffa4 100644
--- a/src/main/kotlin/pl/treksoft/kvision/core/Widget.kt
+++ b/src/main/kotlin/pl/treksoft/kvision/core/Widget.kt
@@ -65,32 +65,18 @@ open class Widget(classes: Set<String> = setOf()) : StyledComponent() {
/**
* A title attribute of generated HTML element.
*/
- var title: String? = null
- set(value) {
- field = value
- refresh()
- }
+ var title: String? by refreshOnUpdate()
/**
* An ID attribute of generated HTML element.
*/
- var id: String? = null
- set(value) {
- field = value
- refresh()
- }
+ var id: String? by refreshOnUpdate()
/**
* A role attribute of generated HTML element.
*/
- var role: String? = null
- set(value) {
- field = value
- refresh()
- }
- internal var surroundingSpan: Boolean = false
- set(value) {
- field = value
- refresh()
- }
+ var role: String? by refreshOnUpdate()
+
+ internal var surroundingSpan by refreshOnUpdate(false)
+
internal var eventTarget: Widget? = null
private var vnode: VNode? = null
diff --git a/src/main/kotlin/pl/treksoft/kvision/dropdown/DropDown.kt b/src/main/kotlin/pl/treksoft/kvision/dropdown/DropDown.kt
index 0f653d30..1370f25a 100644
--- a/src/main/kotlin/pl/treksoft/kvision/dropdown/DropDown.kt
+++ b/src/main/kotlin/pl/treksoft/kvision/dropdown/DropDown.kt
@@ -70,11 +70,7 @@ open class DropDown(
set(value) {
button.text = value
}
- private var elements = elements
- set(value) {
- field = value
- setChildrenFromElements()
- }
+ private var elements by refreshOnUpdate(elements, { setChildrenFromElements() })
/**
* The icon of the dropdown button.
*/
@@ -126,11 +122,7 @@ open class DropDown(
/**
* Determines if the dropdown is showing upwards.
*/
- var dropup = false
- set(value) {
- field = value
- refresh()
- }
+ var dropup by refreshOnUpdate(false)
/**
* Width of the dropdown button.
*/
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 7a8b5905..e76a1c7a 100644
--- a/src/main/kotlin/pl/treksoft/kvision/form/check/CheckBox.kt
+++ b/src/main/kotlin/pl/treksoft/kvision/form/check/CheckBox.kt
@@ -107,27 +107,15 @@ open class CheckBox(
/**
* The style (one of Bootstrap standard colors) of the input.
*/
- var style: CHECKBOXSTYLE? = null
- set(value) {
- field = value
- refresh()
- }
+ var style: CHECKBOXSTYLE? by refreshOnUpdate()
/**
* Determines if the checkbox is rendered as a circle.
*/
- var circled: Boolean = false
- set(value) {
- field = value
- refresh()
- }
+ var circled by refreshOnUpdate(false)
/**
* Determines if the checkbox is rendered inline.
*/
- var inline: Boolean = false
- set(value) {
- field = value
- refresh()
- }
+ var inline by refreshOnUpdate(false)
/**
* The size of the input.
*/
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 46e5395c..7c67c957 100644
--- a/src/main/kotlin/pl/treksoft/kvision/form/check/CheckInput.kt
+++ b/src/main/kotlin/pl/treksoft/kvision/form/check/CheckInput.kt
@@ -66,63 +66,34 @@ open class CheckInput(
/**
* The selection state of the input.
*/
- var value: Boolean = value
- set(value) {
- field = value
- refreshState()
- }
+ var value by refreshOnUpdate(value, { refreshState() })
/**
* The value attribute of the generated HTML input element.
*
* This value is placed directly in generated HTML code, while the [value] property is dynamically
* bound to the input selection state.
*/
- var startValue: Boolean = value
- set(value) {
- field = value
- this.value = value
- refresh()
- }
+ var startValue by refreshOnUpdate(value, { this.value = it; refresh() })
/**
* The type of the generated HTML input element.
*/
- var type: CHECKINPUTTYPE = type
- set(value) {
- field = value
- refresh()
- }
+ var type by refreshOnUpdate(type)
/**
* The name attribute of the generated HTML input element.
*/
- var name: String? = null
- set(value) {
- field = value
- refresh()
- }
+ var name: String? by refreshOnUpdate()
/**
* Determines if the field is disabled.
*/
- var disabled: Boolean = false
- set(value) {
- field = value
- refresh()
- }
+ var disabled by refreshOnUpdate(false)
/**
* The additional String value used for the radio button group.
*/
- var extraValue: String? = null
- set(value) {
- field = value
- refresh()
- }
+ var extraValue: String? by refreshOnUpdate()
/**
* The size of the input.
*/
- var size: INPUTSIZE? = null
- set(value) {
- field = value
- refresh()
- }
+ 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 ef5d6eb9..0cf85fc5 100644
--- a/src/main/kotlin/pl/treksoft/kvision/form/check/Radio.kt
+++ b/src/main/kotlin/pl/treksoft/kvision/form/check/Radio.kt
@@ -117,27 +117,15 @@ open class Radio(
/**
* The style (one of Bootstrap standard colors) of the input.
*/
- var style: RADIOSTYLE? = null
- set(value) {
- field = value
- refresh()
- }
+ var style: RADIOSTYLE? by refreshOnUpdate()
/**
* Determines if the radio button is rendered as a square.
*/
- var squared: Boolean = false
- set(value) {
- field = value
- refresh()
- }
+ var squared by refreshOnUpdate(false)
/**
* Determines if the radio button is rendered inline.
*/
- var inline: Boolean = false
- set(value) {
- field = value
- refresh()
- }
+ var inline by refreshOnUpdate(false)
/**
* The size of the input.
*/
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 aa29de34..e5b04a47 100644
--- a/src/main/kotlin/pl/treksoft/kvision/form/check/RadioGroup.kt
+++ b/src/main/kotlin/pl/treksoft/kvision/form/check/RadioGroup.kt
@@ -54,29 +54,17 @@ open class RadioGroup(
/**
* A list of options (label to value pairs) for the group.
*/
- var options = options
- set(value) {
- field = value
- setChildrenFromOptions()
- }
+ var options by refreshOnUpdate(options, { setChildrenFromOptions() })
/**
* A value of the selected option.
*/
- override var value = value
- set(value) {
- field = value
- setValueToChildren(value)
- }
+ override var value by refreshOnUpdate(value, { setValueToChildren(it) })
/**
* Determines if the options are rendered inline.
*/
- var inline: Boolean = inline
- set(value) {
- field = value
- refresh()
- }
+ var inline by refreshOnUpdate(inline)
override var disabled
get() = getDisabledFromChildren()
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 f90401ce..b0b7f4b3 100644
--- a/src/main/kotlin/pl/treksoft/kvision/form/select/SelectInput.kt
+++ b/src/main/kotlin/pl/treksoft/kvision/form/select/SelectInput.kt
@@ -65,124 +65,68 @@ open class SelectInput(
/**
* A list of options (label to value pairs) for the select control.
*/
- internal var options = options
- set(value) {
- field = value
- setChildrenFromOptions()
- }
+ internal var options by refreshOnUpdate(options, { setChildrenFromOptions() })
/**
* A value of the selected option.
*/
- var value: String? = value
- set(value) {
- field = value
- refreshState()
- }
+ var value by refreshOnUpdate(value, { refreshState() })
/**
* The name attribute of the generated HTML select element.
*/
- var name: String? = null
- set(value) {
- field = value
- refresh()
- }
+ var name: String? by refreshOnUpdate()
/**
* Determines if multiple value selection is allowed.
*/
- var multiple: Boolean = multiple
- set(value) {
- field = value
- refresh()
- }
+ var multiple by refreshOnUpdate(multiple)
/**
* Additional options for remote (AJAX) data source.
*/
- var ajaxOptions: AjaxOptions? = ajaxOptions
- set(value) {
- field = value
- if (value != null) liveSearch = true
- refresh()
+ var ajaxOptions by refreshOnUpdate(ajaxOptions, {
+ if (it != null) {
+ liveSearch = true
}
+ refresh()
+ })
/**
* Maximal number of selected options.
*/
- var maxOptions: Int? = null
- set(value) {
- field = value
- refresh()
- }
+ var maxOptions: Int? by refreshOnUpdate()
/**
* Determines if live search is available.
*/
- var liveSearch: Boolean = false
- set(value) {
- field = value
- refresh()
- }
+ var liveSearch by refreshOnUpdate(false)
/**
* The placeholder for the select control.
*/
- var placeholder: String? = null
- set(value) {
- field = value
- refresh()
- }
+ var placeholder: String? by refreshOnUpdate()
/**
* The style of the select control.
*/
- var style: BUTTONSTYLE? = null
- set(value) {
- field = value
- refresh()
- }
+ var style: BUTTONSTYLE? by refreshOnUpdate()
/**
* The width of the select control.
*/
- var selectWidth: CssSize? = null
- set(value) {
- field = value
- refresh()
- }
+ var selectWidth: CssSize? by refreshOnUpdate()
/**
* The width type of the select control.
*/
- var selectWidthType: SELECTWIDTHTYPE? = null
- set(value) {
- field = value
- refresh()
- }
+ var selectWidthType: SELECTWIDTHTYPE? by refreshOnUpdate()
/**
* Determines if an empty option is automatically generated.
*/
- var emptyOption: Boolean = false
- set(value) {
- field = value
- setChildrenFromOptions()
- }
+ var emptyOption by refreshOnUpdate(false, { setChildrenFromOptions() })
/**
* Determines if the field is disabled.
*/
- var disabled: Boolean = false
- set(value) {
- field = value
- refresh()
- }
+ var disabled by refreshOnUpdate(false)
/**
* Determines if the select is automatically focused.
*/
- var autofocus: Boolean? = null
- set(value) {
- field = value
- refresh()
- }
+ var autofocus: Boolean? by refreshOnUpdate()
/**
* The size of the input.
*/
- var size: INPUTSIZE? = null
- set(value) {
- field = value
- refresh()
- }
+ var size: INPUTSIZE? by refreshOnUpdate()
init {
setChildrenFromOptions()
diff --git a/src/main/kotlin/pl/treksoft/kvision/form/select/SelectOptGroup.kt b/src/main/kotlin/pl/treksoft/kvision/form/select/SelectOptGroup.kt
index 6819961f..95d8f178 100644
--- a/src/main/kotlin/pl/treksoft/kvision/form/select/SelectOptGroup.kt
+++ b/src/main/kotlin/pl/treksoft/kvision/form/select/SelectOptGroup.kt
@@ -45,35 +45,19 @@ open class SelectOptGroup(
/**
* A label for the group.
*/
- var label: String = label
- set(value) {
- field = value
- refresh()
- }
+ var label by refreshOnUpdate(label)
/**
* A list of options (label to value pairs) for the group.
*/
- var options = options
- set(value) {
- field = value
- setChildrenFromOptions()
- }
+ var options by refreshOnUpdate(options, { setChildrenFromOptions() })
/**
* Maximal number of selected options in the group.
*/
- var maxOptions: Int? = maxOptions
- set(value) {
- field = value
- refresh()
- }
+ var maxOptions by refreshOnUpdate(maxOptions)
/**
* Determines if the group is disabled.
*/
- var disabled: Boolean = disabled
- set(value) {
- field = value
- refresh()
- }
+ var disabled by refreshOnUpdate(disabled)
init {
setChildrenFromOptions()
diff --git a/src/main/kotlin/pl/treksoft/kvision/form/select/SelectOption.kt b/src/main/kotlin/pl/treksoft/kvision/form/select/SelectOption.kt
index e7a49120..141eff28 100644
--- a/src/main/kotlin/pl/treksoft/kvision/form/select/SelectOption.kt
+++ b/src/main/kotlin/pl/treksoft/kvision/form/select/SelectOption.kt
@@ -46,51 +46,27 @@ open class SelectOption(
/**
* The value of the option.
*/
- var value: String? = value
- set(value) {
- field = value
- refresh()
- }
+ var value by refreshOnUpdate(value)
/**
* The label of the option.
*/
- var label: String? = label
- set(value) {
- field = value
- refresh()
- }
+ var label by refreshOnUpdate(label)
/**
* The subtext after the label of the option.
*/
- var subtext: String? = subtext
- set(value) {
- field = value
- refresh()
- }
+ var subtext by refreshOnUpdate(subtext)
/**
* The icon before the label of the option.
*/
- var icon: String? = icon
- set(value) {
- field = value
- refresh()
- }
+ var icon by refreshOnUpdate(icon)
/**
* Determines if the option should be rendered as divider.
*/
- var divider: Boolean = divider
- set(value) {
- field = value
- refresh()
- }
+ var divider by refreshOnUpdate(divider)
/**
* Determines if the option should be disabled.
*/
- var disabled: Boolean = disabled
- set(value) {
- field = value
- refresh()
- }
+ var disabled by refreshOnUpdate(disabled)
override fun render(): VNode {
return if (!divider) {
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 a3f72b75..a5565d2a 100644
--- a/src/main/kotlin/pl/treksoft/kvision/form/spinner/SpinnerInput.kt
+++ b/src/main/kotlin/pl/treksoft/kvision/form/spinner/SpinnerInput.kt
@@ -97,119 +97,62 @@ open class SpinnerInput(
/**
* Spinner value.
*/
- var value: Number? = value
- set(value) {
- field = value
- refreshState()
- }
+ var value by refreshOnUpdate(value, { refreshState() })
/**
* The value attribute of the generated HTML input element.
*
* This value is placed directly in generated HTML code, while the [value] property is dynamically
* bound to the spinner input value.
*/
- var startValue: Number? = value
- set(value) {
- field = value
- this.value = value
- refresh()
- }
+ var startValue by refreshOnUpdate(value, { this.value = it; refresh() })
/**
* Minimal value.
*/
- var min: Int = min
- set(value) {
- field = value
- refreshSpinner()
- }
+ var min by refreshOnUpdate(min, { refreshSpinner() })
/**
* Maximal value.
*/
- var max: Int = max
- set(value) {
- field = value
- refreshSpinner()
- }
+ var max by refreshOnUpdate(max, { refreshSpinner() })
/**
* Step value.
*/
- var step: Double = step
- set(value) {
- field = value
- refreshSpinner()
- }
+ var step by refreshOnUpdate(step, { refreshSpinner() })
/**
* Number of decimal digits value.
*/
- var decimals: Int = decimals
- set(value) {
- field = value
- refreshSpinner()
- }
+ var decimals by refreshOnUpdate(decimals, { refreshSpinner() })
/**
* Spinner buttons type.
*/
- var buttonsType: BUTTONSTYPE = buttonsType
- set(value) {
- field = value
- refreshSpinner()
- }
+ var buttonsType by refreshOnUpdate(buttonsType, { refreshSpinner() })
/**
* Spinner force rounding type.
*/
- var forceType: FORCETYPE = forceType
- set(value) {
- field = value
- refreshSpinner()
- }
+ var forceType by refreshOnUpdate(forceType, { refreshSpinner() })
/**
* The placeholder for the spinner input.
*/
- var placeholder: String? = null
- set(value) {
- field = value
- refresh()
- }
+ var placeholder: String? by refreshOnUpdate()
/**
* The name attribute of the generated HTML input element.
*/
- var name: String? = null
- set(value) {
- field = value
- refresh()
- }
+ var name: String? by refreshOnUpdate()
/**
* Determines if the field is disabled.
*/
- var disabled: Boolean = false
- set(value) {
- field = value
- refresh()
- }
+ var disabled by refreshOnUpdate(false)
/**
* Determines if the spinner is automatically focused.
*/
- var autofocus: Boolean? = null
- set(value) {
- field = value
- refresh()
- }
+ var autofocus: Boolean? by refreshOnUpdate()
/**
* Determines if the spinner is read-only.
*/
- var readonly: Boolean? = null
- set(value) {
- field = value
- refresh()
- }
+ var readonly: Boolean? by refreshOnUpdate()
/**
* The size of the input.
*/
- var size: INPUTSIZE? = null
- set(value) {
- field = value
- refresh()
- }
+ var size: INPUTSIZE? by refreshOnUpdate()
private var siblings: JQuery? = null
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 17a6689b..8467dab8 100644
--- a/src/main/kotlin/pl/treksoft/kvision/form/text/AbstractTextInput.kt
+++ b/src/main/kotlin/pl/treksoft/kvision/form/text/AbstractTextInput.kt
@@ -50,79 +50,42 @@ abstract class AbstractTextInput(
/**
* Text input value.
*/
- var value: String? = value
- set(value) {
- field = value
- refreshState()
- }
+ var value by refreshOnUpdate(value, { refreshState() })
/**
* The value attribute of the generated HTML input element.
*
* This value is placed directly in generated HTML code, while the [value] property is dynamically
* bound to the text input value.
*/
- var startValue: String? = value
- set(value) {
- field = value
- this.value = value
- refresh()
- }
+ var startValue by refreshOnUpdate(value, { this.value = it; refresh() })
/**
* The placeholder for the text input.
*/
- var placeholder: String? = null
- set(value) {
- field = value
- refresh()
- }
+ var placeholder: String? by refreshOnUpdate()
/**
* The name attribute of the generated HTML input element.
*/
- var name: String? = null
- set(value) {
- field = value
- refresh()
- }
+ var name: String? by refreshOnUpdate()
/**
* Maximal length of the text input value.
*/
- var maxlength: Int? = null
- set(value) {
- field = value
- refresh()
- }
+ var maxlength: Int? by refreshOnUpdate()
/**
* Determines if the field is disabled.
*/
- var disabled: Boolean = false
- set(value) {
- field = value
- refresh()
- }
+ var disabled by refreshOnUpdate(false)
/**
* Determines if the text input is automatically focused.
*/
- var autofocus: Boolean? = null
- set(value) {
- field = value
- refresh()
- }
+ var autofocus: Boolean? by refreshOnUpdate()
/**
* Determines if the text input is read-only.
*/
- var readonly: Boolean? = null
- set(value) {
- field = value
- refresh()
- }
+ var readonly: Boolean? by refreshOnUpdate()
/**
* The size of the input.
*/
- var size: INPUTSIZE? = null
- set(value) {
- field = value
- refresh()
- }
+ 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/TextAreaInput.kt b/src/main/kotlin/pl/treksoft/kvision/form/text/TextAreaInput.kt
index 8ee150e1..9fc89544 100644
--- a/src/main/kotlin/pl/treksoft/kvision/form/text/TextAreaInput.kt
+++ b/src/main/kotlin/pl/treksoft/kvision/form/text/TextAreaInput.kt
@@ -40,27 +40,15 @@ open class TextAreaInput(cols: Int? = null, rows: Int? = null, value: String? =
/**
* Number of columns.
*/
- var cols: Int? = cols
- set(value) {
- field = value
- refresh()
- }
+ var cols by refreshOnUpdate(cols)
/**
* Number of rows.
*/
- var rows: Int? = rows
- set(value) {
- field = value
- refresh()
- }
+ var rows by refreshOnUpdate(rows)
/**
* Determines if hard wrapping is enabled for the textarea element.
*/
- var wrapHard: Boolean = false
- set(value) {
- field = value
- refresh()
- }
+ var wrapHard by refreshOnUpdate(false)
override fun render(): VNode {
return startValue?.let {
diff --git a/src/main/kotlin/pl/treksoft/kvision/form/text/TextInput.kt b/src/main/kotlin/pl/treksoft/kvision/form/text/TextInput.kt
index a2bfcd0b..998bfb2c 100644
--- a/src/main/kotlin/pl/treksoft/kvision/form/text/TextInput.kt
+++ b/src/main/kotlin/pl/treksoft/kvision/form/text/TextInput.kt
@@ -52,19 +52,11 @@ open class TextInput(type: TEXTINPUTTYPE = TEXTINPUTTYPE.TEXT, value: String? =
/**
* Text input type.
*/
- var type: TEXTINPUTTYPE = type
- set(value) {
- field = value
- refresh()
- }
+ var type by refreshOnUpdate(type)
/**
* Determines if autocomplete is enabled for the input element.
*/
- var autocomplete: Boolean? = null
- set(value) {
- field = value
- refresh()
- }
+ var autocomplete: Boolean? by refreshOnUpdate()
override fun render(): VNode {
return render("input")
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 c198ec77..3eeec789 100644
--- a/src/main/kotlin/pl/treksoft/kvision/form/time/DateTimeInput.kt
+++ b/src/main/kotlin/pl/treksoft/kvision/form/time/DateTimeInput.kt
@@ -60,123 +60,63 @@ open class DateTimeInput(
/**
* Date/time input value.
*/
- var value: Date? = value
- set(value) {
- field = value
- refreshState()
- }
+ var value by refreshOnUpdate(value, { refreshState() })
/**
* Date/time format.
*/
- var format: String = format
- set(value) {
- field = value
- refreshDatePicker()
- }
+ var format by refreshOnUpdate(format, { refreshDatePicker() })
/**
* The placeholder for the date/time input.
*/
- var placeholder: String? = null
- set(value) {
- field = value
- refresh()
- }
+ var placeholder: String? by refreshOnUpdate()
/**
* The name attribute of the generated HTML input element.
*/
- var name: String? = null
- set(value) {
- field = value
- refresh()
- }
+ var name: String? by refreshOnUpdate()
/**
* Determines if the field is disabled.
*/
- var disabled: Boolean = false
- set(value) {
- field = value
- refresh()
- }
+ var disabled by refreshOnUpdate(false)
/**
* Determines if the text input is automatically focused.
*/
- var autofocus: Boolean? = null
- set(value) {
- field = value
- refresh()
- }
+ var autofocus: Boolean? by refreshOnUpdate()
/**
* Determines if the date/time input is read-only.
*/
- var readonly: Boolean? = null
- set(value) {
- field = value
- refresh()
- }
+ var readonly: Boolean? by refreshOnUpdate()
/**
* The size of the input.
*/
- var size: INPUTSIZE? = null
- set(value) {
- field = value
- refresh()
- }
+ var size: INPUTSIZE? by refreshOnUpdate()
/**
* Day of the week start. 0 (Sunday) to 6 (Saturday).
*/
- var weekStart: Int = 0
- set(value) {
- field = value
- refreshDatePicker()
- }
+ var weekStart by refreshOnUpdate(0, { refreshDatePicker() })
/**
* Days of the week that should be disabled. Multiple values should be comma separated.
*/
- var daysOfWeekDisabled: Array<Int> = arrayOf()
- set(value) {
- field = value
- refreshDatePicker()
- }
+ var daysOfWeekDisabled by refreshOnUpdate(arrayOf<Int>(), { refreshDatePicker() })
/**
* Determines if *Clear* button should be visible.
*/
- var clearBtn: Boolean = true
- set(value) {
- field = value
- refreshDatePicker()
- }
+ var clearBtn by refreshOnUpdate(true, { refreshDatePicker() })
/**
* Determines if *Today* button should be visible.
*/
- var todayBtn: Boolean = false
- set(value) {
- field = value
- refreshDatePicker()
- }
+ var todayBtn by refreshOnUpdate(false, { refreshDatePicker() })
/**
* Determines if the current day should be highlighted.
*/
- var todayHighlight: Boolean = false
- set(value) {
- field = value
- refreshDatePicker()
- }
+ var todayHighlight by refreshOnUpdate(false, { refreshDatePicker() })
/**
* The increment used to build the hour view.
*/
- var minuteStep: Int = DEFAULT_MINUTE_STEP
- set(value) {
- field = value
- refreshDatePicker()
- }
+ var minuteStep by refreshOnUpdate(DEFAULT_MINUTE_STEP, { refreshDatePicker() })
/**
* Determines if meridian views are visible in day and hour views.
*/
- var showMeridian: Boolean = false
- set(value) {
- field = value
- refreshDatePicker()
- }
+ var showMeridian by refreshOnUpdate(false, { refreshDatePicker() })
override fun render(): VNode {
return render("input")
diff --git a/src/main/kotlin/pl/treksoft/kvision/html/Button.kt b/src/main/kotlin/pl/treksoft/kvision/html/Button.kt
index 79155395..e6f53737 100644
--- a/src/main/kotlin/pl/treksoft/kvision/html/Button.kt
+++ b/src/main/kotlin/pl/treksoft/kvision/html/Button.kt
@@ -69,59 +69,31 @@ open class Button(
/**
* Button label.
*/
- var text = text
- set(value) {
- field = value
- refresh()
- }
+ var text by refreshOnUpdate(text)
/**
* Button icon.
*/
- var icon = icon
- set(value) {
- field = value
- refresh()
- }
+ var icon by refreshOnUpdate(icon)
/**
* Button style.
*/
- var style = style
- set(value) {
- field = value
- refresh()
- }
+ var style by refreshOnUpdate(style)
/**
* Determines if button is disabled.
*/
- var disabled = disabled
- set(value) {
- field = value
- refresh()
- }
+ var disabled by refreshOnUpdate(disabled)
/**
* Button image.
*/
- var image: ResString? = null
- set(value) {
- field = value
- refresh()
- }
+ var image: ResString? by refreshOnUpdate()
/**
* Button size.
*/
- var size: BUTTONSIZE? = null
- set(value) {
- field = value
- refresh()
- }
+ var size: BUTTONSIZE? by refreshOnUpdate()
/**
* Determines if the button takes all the space horizontally.
*/
- var block = false
- set(value) {
- field = value
- refresh()
- }
+ var block by refreshOnUpdate(false)
override fun render(): VNode {
val t = createLabelWithIcon(text, icon, image)
diff --git a/src/main/kotlin/pl/treksoft/kvision/html/Image.kt b/src/main/kotlin/pl/treksoft/kvision/html/Image.kt
index bd469afe..ac7829e8 100644
--- a/src/main/kotlin/pl/treksoft/kvision/html/Image.kt
+++ b/src/main/kotlin/pl/treksoft/kvision/html/Image.kt
@@ -55,43 +55,23 @@ open class Image(
/**
* URL of the image.
*/
- var src = src
- set(value) {
- field = value
- refresh()
- }
+ var src by refreshOnUpdate(src)
/**
* The alternative text of the image.
*/
- var alt = alt
- set(value) {
- field = value
- refresh()
- }
+ var alt by refreshOnUpdate(alt)
/**
* Determines if the image is rendered as responsive.
*/
- var responsive = responsive
- set(value) {
- field = value
- refresh()
- }
+ var responsive by refreshOnUpdate(responsive)
/**
* The shape of the image.
*/
- var shape = shape
- set(value) {
- field = value
- refresh()
- }
+ var shape by refreshOnUpdate(shape)
/**
* Determines if the image is rendered as centered.
*/
- var centered = centered
- set(value) {
- field = value
- refresh()
- }
+ var centered by refreshOnUpdate(centered)
override fun render(): VNode {
return render("img")
diff --git a/src/main/kotlin/pl/treksoft/kvision/html/Link.kt b/src/main/kotlin/pl/treksoft/kvision/html/Link.kt
index e075d685..94c7c594 100644
--- a/src/main/kotlin/pl/treksoft/kvision/html/Link.kt
+++ b/src/main/kotlin/pl/treksoft/kvision/html/Link.kt
@@ -44,35 +44,19 @@ open class Link(
/**
* Link label.
*/
- var label = label
- set(value) {
- field = value
- refresh()
- }
+ var label by refreshOnUpdate(label)
/**
* Link URL address.
*/
- var url = url
- set(value) {
- field = value
- refresh()
- }
+ var url by refreshOnUpdate(url)
/**
* Link icon.
*/
- var icon = icon
- set(value) {
- field = value
- refresh()
- }
+ var icon by refreshOnUpdate(icon)
/**
* Link image.
*/
- var image = image
- set(value) {
- field = value
- refresh()
- }
+ var image by refreshOnUpdate(image)
override fun render(): VNode {
val t = createLabelWithIcon(label, icon, image)
diff --git a/src/main/kotlin/pl/treksoft/kvision/html/List.kt b/src/main/kotlin/pl/treksoft/kvision/html/List.kt
index 2dadd546..185d2eea 100644
--- a/src/main/kotlin/pl/treksoft/kvision/html/List.kt
+++ b/src/main/kotlin/pl/treksoft/kvision/html/List.kt
@@ -61,27 +61,15 @@ open class ListTag(
/**
* List type.
*/
- var type = type
- set(value) {
- field = value
- refresh()
- }
+ var type by refreshOnUpdate(type)
/**
* List of elements.
*/
- var elements = elements
- set(value) {
- field = value
- refresh()
- }
+ var elements by refreshOnUpdate(elements)
/**
* Determines if [elements] can contain HTML code.
*/
- var rich = rich
- set(value) {
- field = value
- refresh()
- }
+ var rich by refreshOnUpdate(rich)
init {
@Suppress("LeakingThis")
diff --git a/src/main/kotlin/pl/treksoft/kvision/html/Tag.kt b/src/main/kotlin/pl/treksoft/kvision/html/Tag.kt
index 62a647bc..a3b7f231 100644
--- a/src/main/kotlin/pl/treksoft/kvision/html/Tag.kt
+++ b/src/main/kotlin/pl/treksoft/kvision/html/Tag.kt
@@ -98,35 +98,19 @@ open class Tag(
/**
* Tag type.
*/
- var type = type
- set(value) {
- field = value
- refresh()
- }
+ var type by refreshOnUpdate(type)
/**
* Text content of the tag.
*/
- var text = text
- set(value) {
- field = value
- refresh()
- }
+ var text by refreshOnUpdate(text)
/**
* Determines if [text] can contain HTML code.
*/
- var rich = rich
- set(value) {
- field = value
- refresh()
- }
+ var rich by refreshOnUpdate(rich)
/**
* Text align.
*/
- var align = align
- set(value) {
- field = value
- refresh()
- }
+ var align by refreshOnUpdate(align)
init {
@Suppress("LeakingThis")
diff --git a/src/main/kotlin/pl/treksoft/kvision/modal/Confirm.kt b/src/main/kotlin/pl/treksoft/kvision/modal/Confirm.kt
index 33512a7e..a3bb8d77 100644
--- a/src/main/kotlin/pl/treksoft/kvision/modal/Confirm.kt
+++ b/src/main/kotlin/pl/treksoft/kvision/modal/Confirm.kt
@@ -75,11 +75,7 @@ open class Confirm(
/**
* Determines if Cancel button is visible.
*/
- var cancelVisible = cancelVisible
- set(value) {
- field = value
- refreshCancelButton()
- }
+ var cancelVisible by refreshOnUpdate(cancelVisible, { refreshCancelButton() })
private val content = Tag(TAG.DIV, text, rich, align)
private val cancelButton = Button("Cancel", "remove")
diff --git a/src/main/kotlin/pl/treksoft/kvision/modal/Modal.kt b/src/main/kotlin/pl/treksoft/kvision/modal/Modal.kt
index f913fac1..d9cd0e8e 100644
--- a/src/main/kotlin/pl/treksoft/kvision/modal/Modal.kt
+++ b/src/main/kotlin/pl/treksoft/kvision/modal/Modal.kt
@@ -89,11 +89,7 @@ open class Modal(
/**
* Determines if animations are used.
*/
- var animation = animation
- set(value) {
- field = value
- refresh()
- }
+ var animation by refreshOnUpdate(animation)
private val dialog = ModalDialog(size)
private val header = SimplePanel(setOf("modal-header"))
@@ -260,11 +256,7 @@ internal class ModalDialog(size: MODALSIZE?) : SimplePanel(setOf("modal-dialog")
/**
* Modal window size.
*/
- var size = size
- set(value) {
- field = value
- refresh()
- }
+ var size by refreshOnUpdate(size)
override fun getSnClass(): List<StringBoolPair> {
val cl = super.getSnClass().toMutableList()
diff --git a/src/main/kotlin/pl/treksoft/kvision/panel/FlexPanel.kt b/src/main/kotlin/pl/treksoft/kvision/panel/FlexPanel.kt
index e4e5dd47..3bb42d53 100644
--- a/src/main/kotlin/pl/treksoft/kvision/panel/FlexPanel.kt
+++ b/src/main/kotlin/pl/treksoft/kvision/panel/FlexPanel.kt
@@ -104,53 +104,27 @@ open class FlexPanel(
/**
* CSS flexbox direction.
*/
- var direction = direction
- set(value) {
- field = value
- refreshSpacing()
- refresh()
- }
+ var direction by refreshOnUpdate(direction, { refreshSpacing(); refresh() })
/**
* CSS flexbox wrap mode.
*/
- var wrap = wrap
- set(value) {
- field = value
- refresh()
- }
+ var wrap by refreshOnUpdate(wrap)
/**
* CSS flexbox content justification.
*/
- var justify = justify
- set(value) {
- field = value
- refresh()
- }
+ var justify by refreshOnUpdate(justify)
/**
* CSS flexbox items alignment.
*/
- var alignItems = alignItems
- set(value) {
- field = value
- refresh()
- }
+ var alignItems by refreshOnUpdate(alignItems)
/**
* CSS flexbox content alignment.
*/
- var alignContent = alignContent
- set(value) {
- field = value
- refresh()
- }
+ var alignContent by refreshOnUpdate(alignContent)
/**
* The spacing between columns/rows.
*/
- var spacing = spacing
- set(value) {
- field = value
- refreshSpacing()
- refresh()
- }
+ var spacing by refreshOnUpdate(spacing, { refreshSpacing(); refresh() })
init {
@Suppress("LeakingThis")
diff --git a/src/main/kotlin/pl/treksoft/kvision/panel/GridPanel.kt b/src/main/kotlin/pl/treksoft/kvision/panel/GridPanel.kt
index 178c1e7f..10e956be 100644
--- a/src/main/kotlin/pl/treksoft/kvision/panel/GridPanel.kt
+++ b/src/main/kotlin/pl/treksoft/kvision/panel/GridPanel.kt
@@ -112,99 +112,51 @@ open class GridPanel(
/**
* CSS grid auto columns.
*/
- var autoColumns = autoColumns
- set(value) {
- field = value
- refresh()
- }
+ var autoColumns by refreshOnUpdate(autoColumns)
/**
* CSS grid auto rows.
*/
- var autoRows = autoRows
- set(value) {
- field = value
- refresh()
- }
+ var autoRows by refreshOnUpdate(autoRows)
/**
* CSS grid auto flow.
*/
- var autoFlow = autoFlow
- set(value) {
- field = value
- refresh()
- }
+ var autoFlow by refreshOnUpdate(autoFlow)
/**
* CSS grid columns template.
*/
- var templateColumns = templateColumns
- set(value) {
- field = value
- refresh()
- }
+ var templateColumns by refreshOnUpdate(templateColumns)
/**
* CSS grid rows template.
*/
- var templateRows = templateRows
- set(value) {
- field = value
- refresh()
- }
+ var templateRows by refreshOnUpdate(templateRows)
/**
* CSS grid areas template.
*/
- var templateAreas = templateAreas
- set(value) {
- field = value
- refresh()
- }
+ var templateAreas by refreshOnUpdate(templateAreas)
/**
* CSS grid column gap.
*/
- var columnGap = columnGap
- set(value) {
- field = value
- refresh()
- }
+ var columnGap by refreshOnUpdate(columnGap)
/**
* CSS grid row gap.
*/
- var rowGap = rowGap
- set(value) {
- field = value
- refresh()
- }
+ var rowGap by refreshOnUpdate(rowGap)
/**
* CSS grid items justification.
*/
- var justifyItems = justifyItems
- set(value) {
- field = value
- refresh()
- }
+ var justifyItems by refreshOnUpdate(justifyItems)
/**
* CSS grid items alignment.
*/
- var alignItems = alignItems
- set(value) {
- field = value
- refresh()
- }
+ var alignItems by refreshOnUpdate(alignItems)
/**
* CSS grid content justification.
*/
- var justifyContent = justifyContent
- set(value) {
- field = value
- refresh()
- }
+ var justifyContent by refreshOnUpdate(justifyContent)
/**
* CSS grid content alignment.
*/
- var alignContent = alignContent
- set(value) {
- field = value
- refresh()
- }
+ var alignContent by refreshOnUpdate(alignContent)
init {
@Suppress("LeakingThis")
diff --git a/src/main/kotlin/pl/treksoft/kvision/panel/ResponsiveGridPanel.kt b/src/main/kotlin/pl/treksoft/kvision/panel/ResponsiveGridPanel.kt
index 4a82ed5b..a5b3d577 100644
--- a/src/main/kotlin/pl/treksoft/kvision/panel/ResponsiveGridPanel.kt
+++ b/src/main/kotlin/pl/treksoft/kvision/panel/ResponsiveGridPanel.kt
@@ -62,11 +62,7 @@ open class ResponsiveGridPanel(
/**
* Text align of grid cells.
*/
- var align = align
- set(value) {
- field = value
- refreshRowContainers()
- }
+ var align by refreshOnUpdate(align, { refreshRowContainers() })
internal val map = mutableMapOf<Int, MutableMap<Int, WidgetParam>>()
private var auto: Boolean = true
diff --git a/src/main/kotlin/pl/treksoft/kvision/panel/StackPanel.kt b/src/main/kotlin/pl/treksoft/kvision/panel/StackPanel.kt
index bac027fe..da41db9f 100644
--- a/src/main/kotlin/pl/treksoft/kvision/panel/StackPanel.kt
+++ b/src/main/kotlin/pl/treksoft/kvision/panel/StackPanel.kt
@@ -44,11 +44,7 @@ open class StackPanel(
/**
* The index of active (visible) child.
*/
- var activeIndex = -1
- set(value) {
- field = value
- refresh()
- }
+ var activeIndex by refreshOnUpdate(-1)
init {
@Suppress("LeakingThis")
diff --git a/src/test/kotlin/test/pl/treksoft/kvision/html/ImageSpec.kt b/src/test/kotlin/test/pl/treksoft/kvision/html/ImageSpec.kt
index e44d4b44..45ea9eb0 100644
--- a/src/test/kotlin/test/pl/treksoft/kvision/html/ImageSpec.kt
+++ b/src/test/kotlin/test/pl/treksoft/kvision/html/ImageSpec.kt
@@ -37,6 +37,7 @@ class ImageSpec : DomSpec {
run {
val root = Root("test")
val res = require("./img/placeholder.png")
+ @Suppress("UnsafeCastFromDynamic")
val image = Image(res, "Image", true, IMAGESHAPE.ROUNDED, true)
root.add(image)
val element = document.getElementById("test")