aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/main/kotlin/pl/treksoft/kvision/core/StyledComponent.kt98
-rw-r--r--src/main/kotlin/pl/treksoft/kvision/html/CustomTag.kt81
-rw-r--r--src/test/kotlin/test/pl/treksoft/kvision/core/StyleSpec.kt3
-rw-r--r--src/test/kotlin/test/pl/treksoft/kvision/html/CustomTagSpec.kt48
4 files changed, 227 insertions, 3 deletions
diff --git a/src/main/kotlin/pl/treksoft/kvision/core/StyledComponent.kt b/src/main/kotlin/pl/treksoft/kvision/core/StyledComponent.kt
index 80a2b0c4..9da4f1d1 100644
--- a/src/main/kotlin/pl/treksoft/kvision/core/StyledComponent.kt
+++ b/src/main/kotlin/pl/treksoft/kvision/core/StyledComponent.kt
@@ -31,135 +31,168 @@ import kotlin.reflect.KProperty
@Suppress("LargeClass")
abstract class StyledComponent {
private val propertyValues: MutableMap<String, Any?> = mutableMapOf()
+ internal val customStyles: MutableMap<String, String> = mutableMapOf()
/**
* Width of the current component.
*/
open var width: CssSize? by refreshOnUpdate()
+
/**
* Minimal width of the current component.
*/
open var minWidth: CssSize? by refreshOnUpdate()
+
/**
* Maximal width of the current component.
*/
open var maxWidth: CssSize? by refreshOnUpdate()
+
/**
* Height of the current component.
*/
open var height: CssSize? by refreshOnUpdate()
+
/**
* Minimal height of the current component.
*/
open var minHeight: CssSize? by refreshOnUpdate()
+
/**
* Maximal height of the current component.
*/
open var maxHeight: CssSize? by refreshOnUpdate()
+
/**
* CSS display of the current component.
*/
open var display: Display? by refreshOnUpdate()
+
/**
* CSS position of the current component.
*/
open var position: Position? by refreshOnUpdate()
+
/**
* Top edge of the current component.
*/
open var top: CssSize? by refreshOnUpdate()
+
/**
* Left edge of the current component.
*/
open var left: CssSize? by refreshOnUpdate()
+
/**
* Right edge of the current component.
*/
open var right: CssSize? by refreshOnUpdate()
+
/**
* Bottom edge of the current component.
*/
open var bottom: CssSize? by refreshOnUpdate()
+
/**
* Z-index of the current component.
*/
open var zIndex: Int? by refreshOnUpdate()
+
/**
* CSS overflow of the current component.
*/
open var overflow: Overflow? by refreshOnUpdate()
+
/**
* CSS overflow-wrap of the current component.
*/
open var overflowWrap: OverflowWrap? by refreshOnUpdate()
+
/**
* CSS resize of the current component.
*/
open var resize: Resize? by refreshOnUpdate()
+
/**
* Border of the current component.
*/
open var border: Border? by refreshOnUpdate()
+
/**
* Top border of the current component.
*/
open var borderTop: Border? by refreshOnUpdate()
+
/**
* Right border of the current component.
*/
open var borderRight: Border? by refreshOnUpdate()
+
/**
* Bottom border of the current component.
*/
open var borderBottom: Border? by refreshOnUpdate()
+
/**
* Left border of the current component.
*/
open var borderLeft: Border? by refreshOnUpdate()
+
/**
* Margin of the current component.
*/
open var margin: CssSize? by refreshOnUpdate()
+
/**
* Top margin of the current component.
*/
open var marginTop: CssSize? by refreshOnUpdate()
+
/**
* Right margin of the current component.
*/
open var marginRight: CssSize? by refreshOnUpdate()
+
/**
* Bottom margin of the current component.
*/
open var marginBottom: CssSize? by refreshOnUpdate()
+
/**
* Left margin of the current component.
*/
open var marginLeft: CssSize? by refreshOnUpdate()
+
/**
* Padding of the current component.
*/
open var padding: CssSize? by refreshOnUpdate()
+
/**
* Top padding of the current component.
*/
open var paddingTop: CssSize? by refreshOnUpdate()
+
/**
* Right padding of the current component.
*/
open var paddingRight: CssSize? by refreshOnUpdate()
+
/**
* Bottom padding of the current component.
*/
open var paddingBottom: CssSize? by refreshOnUpdate()
+
/**
* Left padding of the current component.
*/
open var paddingLeft: CssSize? by refreshOnUpdate()
+
/**
* Text color for the current component.
*/
open var color: Color? by refreshOnUpdate()
+
/**
* Text color for the current component given in hex format (write only).
*
@@ -174,6 +207,7 @@ abstract class StyledComponent {
set(value) {
color = if (value != null) Color.hex(value) else null
}
+
/**
* Text color for the current component given with named constant (write only).
*
@@ -188,102 +222,127 @@ abstract class StyledComponent {
set(value) {
color = if (value != null) Color.name(value) else null
}
+
/**
* Opacity of the current component.
*/
open var opacity: Double? by refreshOnUpdate()
+
/**
* Background of the current component.
*/
open var background: Background? by refreshOnUpdate()
+
/**
* CSS Text direction of the current component.
*/
open var textDirection: Direction? by refreshOnUpdate()
+
/**
* CSS Text letter spacing of the current component.
*/
open var letterSpacing: CssSize? by refreshOnUpdate()
+
/**
* CSS Text line height of the current component.
*/
open var lineHeight: CssSize? by refreshOnUpdate()
+
/**
* CSS Text align of the current component.
*/
open var textAlign: TextAlign? by refreshOnUpdate()
+
/**
* CSS Text decoration of the current component.
*/
open var textDecoration: TextDecoration? by refreshOnUpdate()
+
/**
* CSS Text indent of the current component.
*/
open var textIndent: CssSize? by refreshOnUpdate()
+
/**
* CSS Text shadow of the current component.
*/
open var textShadow: TextShadow? by refreshOnUpdate()
+
/**
* CSS Text transform of the current component.
*/
open var textTransform: TextTransform? by refreshOnUpdate()
+
/**
* CSS Text overflow of the current component.
*/
open var textOverflow: TextOverflow? by refreshOnUpdate()
+
/**
* CSS Text unicode-bidi of the current component.
*/
open var unicodeBidi: UnicodeBidi? by refreshOnUpdate()
+
/**
* CSS Text vertical align of the current component.
*/
open var verticalAlign: VerticalAlign? by refreshOnUpdate()
+
/**
* CSS Text white space of the current component.
*/
open var whiteSpace: WhiteSpace? by refreshOnUpdate()
+
/**
* CSS Text word spacing of the current component.
*/
open var wordSpacing: CssSize? by refreshOnUpdate()
+
/**
* CSS font family of the current component.
*/
open var fontFamily: String? by refreshOnUpdate()
+
/**
* CSS font size of the current component.
*/
open var fontSize: CssSize? by refreshOnUpdate()
+
/**
* CSS font style of the current component.
*/
open var fontStyle: FontStyle? by refreshOnUpdate()
+
/**
* CSS font weight of the current component.
*/
open var fontWeight: FontWeight? by refreshOnUpdate()
+
/**
* CSS font variant of the current component.
*/
open var fontVariant: FontVariant? by refreshOnUpdate()
+
/**
* CSS position float of the current component.
*/
open var float: PosFloat? by refreshOnUpdate()
+
/**
* CSS clear float of the current component.
*/
open var clear: Clear? by refreshOnUpdate()
+
/**
* CSS word break of the current component.
*/
open var wordBreak: WordBreak? by refreshOnUpdate()
+
/**
* CSS line break of the current component.
*/
open var lineBreak: LineBreak? by refreshOnUpdate()
+
/**
* CSS cursor shape over the current component.
*/
@@ -490,15 +549,50 @@ abstract class StyledComponent {
cursor?.let {
snstyle.add("cursor" to it.cursor)
}
+ if (customStyles.isNotEmpty()) {
+ snstyle += customStyles.toList()
+ }
globalStyleCache[cacheKey] = snstyle
return snstyle
}
}
+ /**
+ * Returns the value of a custom CSS style.
+ * @param name the name of the style
+ * @return the value of the style
+ */
+ fun getStyle(name: String): String? {
+ return this.customStyles[name]
+ }
+
+ /**
+ * Sets the value of a custom CSS style.
+ * @param name the name of the style
+ * @param value the value of the style
+ */
+ fun setStyle(name: String, value: String): StyledComponent {
+ this.customStyles[name] = value
+ refresh()
+ return this
+ }
+
+ /**
+ * Removes the value of a custom CSS style.
+ * @param name the name of the style
+ */
+ fun removeStyle(name: String): StyledComponent {
+ this.customStyles.remove(name)
+ refresh()
+ return this
+ }
+
protected open fun getCacheKey(): String {
- return propertyValues.map {
+ return ((propertyValues.map {
+ it.toString()
+ }) + (customStyles.map {
it.toString()
- }.joinToString("###KvSep###")
+ })).joinToString("###KvSep###")
}
private fun <T> refreshOnUpdate(refreshFunction: ((T) -> Unit) = { this.refresh() }) =
diff --git a/src/main/kotlin/pl/treksoft/kvision/html/CustomTag.kt b/src/main/kotlin/pl/treksoft/kvision/html/CustomTag.kt
new file mode 100644
index 00000000..34910205
--- /dev/null
+++ b/src/main/kotlin/pl/treksoft/kvision/html/CustomTag.kt
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2017-present Robert Jaros
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+package pl.treksoft.kvision.html
+
+import com.github.snabbdom.VNode
+import pl.treksoft.kvision.core.Container
+
+/**
+ * HTML custom tag component.
+ *
+ * @constructor
+ * @param elementName element name
+ * @param content element text
+ * @param rich determines if [content] can contain HTML code
+ * @param align content align
+ * @param classes a set of CSS class names
+ * @param attributes a map of additional attributes
+ * @param init an initializer extension function
+ */
+open class CustomTag(
+ elementName: String,
+ content: String? = null,
+ rich: Boolean = false,
+ align: Align? = null,
+ classes: Set<String> = setOf(), attributes: Map<String, String> = mapOf(),
+ init: (CustomTag.() -> Unit)? = null
+) :
+ Tag(TAG.DIV, content, rich, align, classes, attributes) {
+
+ /**
+ * HTML element name.
+ */
+ var elementName by refreshOnUpdate(elementName)
+
+ init {
+ @Suppress("LeakingThis")
+ init?.invoke(this)
+ }
+
+ override fun render(elementName: String, children: Array<dynamic>): VNode {
+ return super.render(this.elementName, children)
+ }
+}
+
+/**
+ * DSL builder extension function.
+ *
+ * It takes the same parameters as the constructor of the built component.
+ */
+fun Container.customTag(
+ elementName: String,
+ content: String? = null,
+ rich: Boolean = false,
+ align: Align? = null,
+ classes: Set<String> = setOf(),
+ attributes: Map<String, String> = mapOf(),
+ init: (CustomTag.() -> Unit)? = null
+): CustomTag {
+ val customTag = CustomTag(elementName, content, rich, align, classes, attributes).apply { init?.invoke(this) }
+ this.add(customTag)
+ return customTag
+}
diff --git a/src/test/kotlin/test/pl/treksoft/kvision/core/StyleSpec.kt b/src/test/kotlin/test/pl/treksoft/kvision/core/StyleSpec.kt
index cac53e90..33e08c1e 100644
--- a/src/test/kotlin/test/pl/treksoft/kvision/core/StyleSpec.kt
+++ b/src/test/kotlin/test/pl/treksoft/kvision/core/StyleSpec.kt
@@ -43,12 +43,13 @@ class StyleSpec : DomSpec {
margin = 2.px
color = Color.name(Col.SILVER)
overflow = Overflow.SCROLL
+ setStyle("box-shadow", "10px 10px")
}
}
}
val element = document.getElementById("test")
assertEqualsHtml(
- "<style>.kv_styleclass_0 {\noverflow: scroll;\nmargin: 2px;\ncolor: silver;\n}</style><div class=\"kv_styleclass_0\"></div>",
+ "<style>.kv_styleclass_0 {\noverflow: scroll;\nmargin: 2px;\ncolor: silver;\nbox-shadow: 10px 10px;\n}</style><div class=\"kv_styleclass_0\"></div>",
element?.innerHTML,
"Should render correct style element"
)
diff --git a/src/test/kotlin/test/pl/treksoft/kvision/html/CustomTagSpec.kt b/src/test/kotlin/test/pl/treksoft/kvision/html/CustomTagSpec.kt
new file mode 100644
index 00000000..d1261fa6
--- /dev/null
+++ b/src/test/kotlin/test/pl/treksoft/kvision/html/CustomTagSpec.kt
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2017-present Robert Jaros
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+package test.pl.treksoft.kvision.html
+
+import pl.treksoft.kvision.html.Align
+import pl.treksoft.kvision.html.CustomTag
+import pl.treksoft.kvision.panel.Root
+import test.pl.treksoft.kvision.DomSpec
+import kotlin.browser.document
+import kotlin.test.Test
+
+class CustomTagSpec : DomSpec {
+
+ @Test
+ fun render() {
+ run {
+ val root = Root("test", fixed = true)
+ val tag = CustomTag("custom-element", "This is <b>custom element</b>", rich = false, align = Align.CENTER)
+ root.add(tag)
+ val element = document.getElementById("test")
+ assertEqualsHtml(
+ "<custom-element class=\"text-center\">This is &lt;b&gt;custom element&lt;/b&gt;</custom-element>",
+ element?.innerHTML,
+ "Should render correct custom html tag"
+ )
+ }
+ }
+
+}