diff options
author | Robert Jaros <rjaros@finn.pl> | 2020-03-27 19:30:06 +0100 |
---|---|---|
committer | Robert Jaros <rjaros@finn.pl> | 2020-03-27 19:39:01 +0100 |
commit | d7cb91657c5751d285afe51af9494ed362d9b5ca (patch) | |
tree | 91916682b7d640c3895a1ffb726519242c881d0c /src/main/kotlin/pl/treksoft/kvision | |
parent | 4f111976f842b9a63363181ad2182091bb810c46 (diff) | |
download | kvision-d7cb91657c5751d285afe51af9494ed362d9b5ca.tar.gz kvision-d7cb91657c5751d285afe51af9494ed362d9b5ca.tar.bz2 kvision-d7cb91657c5751d285afe51af9494ed362d9b5ca.zip |
Add support for custom html elements and custom css styles (#144)
Diffstat (limited to 'src/main/kotlin/pl/treksoft/kvision')
-rw-r--r-- | src/main/kotlin/pl/treksoft/kvision/core/StyledComponent.kt | 98 | ||||
-rw-r--r-- | src/main/kotlin/pl/treksoft/kvision/html/CustomTag.kt | 81 |
2 files changed, 177 insertions, 2 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 +} |