aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/main/kotlin/pl/treksoft/kvision/html/Tag.kt9
-rw-r--r--src/main/kotlin/pl/treksoft/kvision/table/Cell.kt50
-rw-r--r--src/main/kotlin/pl/treksoft/kvision/table/HeaderCell.kt50
-rw-r--r--src/main/kotlin/pl/treksoft/kvision/table/Row.kt39
-rw-r--r--src/main/kotlin/pl/treksoft/kvision/table/Table.kt174
-rw-r--r--src/test/kotlin/test/pl/treksoft/kvision/table/CellSpec.kt25
-rw-r--r--src/test/kotlin/test/pl/treksoft/kvision/table/HeaderCellSpec.kt29
-rw-r--r--src/test/kotlin/test/pl/treksoft/kvision/table/RowSpec.kt28
-rw-r--r--src/test/kotlin/test/pl/treksoft/kvision/table/TableSpec.kt46
9 files changed, 449 insertions, 1 deletions
diff --git a/src/main/kotlin/pl/treksoft/kvision/html/Tag.kt b/src/main/kotlin/pl/treksoft/kvision/html/Tag.kt
index 33ef2cb7..897daa60 100644
--- a/src/main/kotlin/pl/treksoft/kvision/html/Tag.kt
+++ b/src/main/kotlin/pl/treksoft/kvision/html/Tag.kt
@@ -65,7 +65,14 @@ enum class TAG(internal val tagName: String) {
VAR("var"),
SAMP("samp"),
SPAN("span"),
- LI("li")
+ LI("li"),
+
+ CAPTION("caption"),
+ THEAD("thead"),
+ TH("th"),
+ TBODY("tbody"),
+ TR("tr"),
+ TD("td")
}
/**
diff --git a/src/main/kotlin/pl/treksoft/kvision/table/Cell.kt b/src/main/kotlin/pl/treksoft/kvision/table/Cell.kt
new file mode 100644
index 00000000..ea59ed81
--- /dev/null
+++ b/src/main/kotlin/pl/treksoft/kvision/table/Cell.kt
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2018. Robert Jaros
+ */
+package pl.treksoft.kvision.table
+
+import pl.treksoft.kvision.html.Align
+import pl.treksoft.kvision.html.TAG
+import pl.treksoft.kvision.html.Tag
+
+/**
+ * HTML table cell component.
+ *
+ * @constructor
+ * @param text text content of the cell
+ * @param rich determines if [text] can contain HTML code
+ * @param align text align
+ * @param classes a set of CSS class names
+ * @param init an initializer extension function
+ */
+open class Cell(
+ text: String? = null,
+ rich: Boolean = false,
+ align: Align? = null,
+ classes: Set<String> = setOf(),
+ init: (Cell.() -> Unit)? = null
+) : Tag(TAG.TD, text, rich, align, classes) {
+
+ init {
+ init?.invoke(this)
+ }
+
+ companion object {
+ /**
+ * DSL builder extension function.
+ *
+ * It takes the same parameters as the constructor of the built component.
+ */
+ fun Row.cell(
+ text: String? = null,
+ rich: Boolean = false,
+ align: Align? = null,
+ classes: Set<String> = setOf(), init: (Cell.() -> Unit)? = null
+ ): Cell {
+ val cell = Cell(text, rich, align, classes, init)
+ this.add(cell)
+ return cell
+ }
+ }
+
+}
diff --git a/src/main/kotlin/pl/treksoft/kvision/table/HeaderCell.kt b/src/main/kotlin/pl/treksoft/kvision/table/HeaderCell.kt
new file mode 100644
index 00000000..26ea24d2
--- /dev/null
+++ b/src/main/kotlin/pl/treksoft/kvision/table/HeaderCell.kt
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2018. Robert Jaros
+ */
+package pl.treksoft.kvision.table
+
+import pl.treksoft.kvision.html.Align
+import pl.treksoft.kvision.html.TAG
+import pl.treksoft.kvision.html.Tag
+
+/**
+ * HTML table header cell component.
+ *
+ * @constructor
+ * @param text text content of the cell
+ * @param rich determines if [text] can contain HTML code
+ * @param align text align
+ * @param classes a set of CSS class names
+ * @param init an initializer extension function
+ */
+open class HeaderCell(
+ text: String? = null,
+ rich: Boolean = false,
+ align: Align? = null,
+ classes: Set<String> = setOf(),
+ init: (HeaderCell.() -> Unit)? = null
+) : Tag(TAG.TH, text, rich, align, classes) {
+
+ init {
+ init?.invoke(this)
+ }
+
+ companion object {
+ /**
+ * DSL builder extension function.
+ *
+ * It takes the same parameters as the constructor of the built component.
+ */
+ fun Row.headerCell(
+ text: String? = null,
+ rich: Boolean = false,
+ align: Align? = null,
+ classes: Set<String> = setOf(), init: (HeaderCell.() -> Unit)? = null
+ ): HeaderCell {
+ val cell = HeaderCell(text, rich, align, classes, init)
+ this.add(cell)
+ return cell
+ }
+ }
+
+}
diff --git a/src/main/kotlin/pl/treksoft/kvision/table/Row.kt b/src/main/kotlin/pl/treksoft/kvision/table/Row.kt
new file mode 100644
index 00000000..d408f699
--- /dev/null
+++ b/src/main/kotlin/pl/treksoft/kvision/table/Row.kt
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2018. Robert Jaros
+ */
+package pl.treksoft.kvision.table
+
+import pl.treksoft.kvision.html.TAG
+import pl.treksoft.kvision.html.Tag
+
+/**
+ * HTML table row component.
+ *
+ * @constructor
+ * @param classes a set of CSS class names
+ * @param init an initializer extension function
+ */
+open class Row(classes: Set<String> = setOf(), init: (Row.() -> Unit)? = null) : Tag(
+ TAG.TR, classes = classes
+) {
+
+ init {
+ init?.invoke(this)
+ }
+
+ companion object {
+ /**
+ * DSL builder extension function.
+ *
+ * It takes the same parameters as the constructor of the built component.
+ */
+ fun Table.row(
+ classes: Set<String> = setOf(), init: (Row.() -> Unit)? = null
+ ): Row {
+ val row = Row(classes, init)
+ this.add(row)
+ return row
+ }
+ }
+
+}
diff --git a/src/main/kotlin/pl/treksoft/kvision/table/Table.kt b/src/main/kotlin/pl/treksoft/kvision/table/Table.kt
new file mode 100644
index 00000000..31a0913a
--- /dev/null
+++ b/src/main/kotlin/pl/treksoft/kvision/table/Table.kt
@@ -0,0 +1,174 @@
+/*
+ * Copyright (c) 2018. Robert Jaros
+ */
+package pl.treksoft.kvision.table
+
+import com.github.snabbdom.VNode
+import com.github.snabbdom.h
+import pl.treksoft.kvision.core.Component
+import pl.treksoft.kvision.core.Container
+import pl.treksoft.kvision.core.StringBoolPair
+import pl.treksoft.kvision.html.TAG
+import pl.treksoft.kvision.html.Tag
+import pl.treksoft.kvision.panel.SimplePanel
+import pl.treksoft.kvision.utils.snClasses
+import pl.treksoft.kvision.utils.snOpt
+
+/**
+ * HTML table types.
+ */
+enum class TableType(internal val type: String) {
+ STRIPED("table-striped"),
+ BORDERED("table-bordered"),
+ HOVER("table-hover"),
+ CONDENSED("table-condensed")
+}
+
+/**
+ * HTML table component.
+ *
+ * @constructor
+ * @param headerNames a list of table headers names
+ * @param types a set of table types
+ * @param caption table caption
+ * @param responsive determines if the table is responsive
+ * @param classes a set of CSS class names
+ * @param init an initializer extension function
+ */
+open class Table(
+ headerNames: List<String>? = null,
+ types: Set<TableType> = setOf(), caption: String? = null, responsive: Boolean = false,
+ classes: Set<String> = setOf(), init: (Table.() -> Unit)? = null
+) : SimplePanel(classes + "table") {
+
+ /**
+ * Table headers names.
+ */
+ var headerNames by refreshOnUpdate(headerNames, { refreshHeaders() })
+ /**
+ * Table types.
+ */
+ var types by refreshOnUpdate(types)
+ /**
+ * Table caption.
+ */
+ var caption by refreshOnUpdate(caption)
+ /**
+ * Determines if the table is responsive.
+ */
+ var responsive by refreshOnUpdate(responsive)
+
+ private val theadRow = Tag(TAG.TR)
+ private val thead = Tag(TAG.THEAD).add(theadRow)
+ private val tbody = Tag(TAG.TBODY)
+
+ init {
+ refreshHeaders()
+ @Suppress("LeakingThis")
+ init?.invoke(this)
+ }
+
+ private fun refreshHeaders() {
+ theadRow.removeAll()
+ headerNames?.forEach {
+ theadRow.add(HeaderCell(it))
+ }
+ }
+
+ /**
+ * Adds new header cell to the table.
+ * @param cell header cell
+ * @return this table
+ */
+ fun addHeaderCell(cell: HeaderCell): Table {
+ theadRow.add(cell)
+ return this
+ }
+
+ /**
+ * Removes given header cell from the table.
+ * @param cell header cell
+ * @return this table
+ */
+ fun removeHeaderCell(cell: HeaderCell): Table {
+ theadRow.remove(cell)
+ return this
+ }
+
+ /**
+ * Removes all header cells from table.
+ * @return this table
+ */
+ fun removeHeaderCells(): Table {
+ theadRow.removeAll()
+ return this
+ }
+
+ override fun render(): VNode {
+ return if (responsive) {
+ val opt = snOpt {
+ `class` = snClasses(listOf("table-responsive" to true))
+ }
+ h("div", opt, arrayOf(render("table", childrenVNodes())))
+ } else {
+ render("table", childrenVNodes())
+ }
+ }
+
+ override fun childrenVNodes(): Array<VNode> {
+ val captionElement = caption?.let {
+ Tag(TAG.CAPTION, it)
+ }
+ return listOf(captionElement, thead, tbody).mapNotNull { it?.renderVNode() }.toTypedArray()
+ }
+
+ override fun getSnClass(): List<StringBoolPair> {
+ val cl = super.getSnClass().toMutableList()
+ types.forEach {
+ cl.add(it.type to true)
+ }
+ return cl
+ }
+
+ override fun add(child: Component): SimplePanel {
+ tbody.add(child)
+ return this
+ }
+
+ override fun addAll(children: List<Component>): SimplePanel {
+ tbody.addAll(children)
+ return this
+ }
+
+ override fun remove(child: Component): SimplePanel {
+ tbody.remove(child)
+ return this
+ }
+
+ override fun removeAll(): SimplePanel {
+ tbody.removeAll()
+ return this
+ }
+
+ override fun getChildren(): List<Component> {
+ return tbody.getChildren()
+ }
+
+ companion object {
+ /**
+ * DSL builder extension function.
+ *
+ * It takes the same parameters as the constructor of the built component.
+ */
+ fun Container.table(
+ headerNames: List<String>? = null,
+ types: Set<TableType> = setOf(), caption: String? = null, responsive: Boolean = false,
+ classes: Set<String> = setOf(), init: (Table.() -> Unit)? = null
+ ): Table {
+ val table =
+ Table(headerNames, types, caption, responsive, classes, init)
+ this.add(table)
+ return table
+ }
+ }
+}
diff --git a/src/test/kotlin/test/pl/treksoft/kvision/table/CellSpec.kt b/src/test/kotlin/test/pl/treksoft/kvision/table/CellSpec.kt
new file mode 100644
index 00000000..7f39f133
--- /dev/null
+++ b/src/test/kotlin/test/pl/treksoft/kvision/table/CellSpec.kt
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2018. Robert Jaros
+ */
+package test.pl.treksoft.kvision.table
+
+import pl.treksoft.kvision.panel.Root
+import pl.treksoft.kvision.table.Cell
+import test.pl.treksoft.kvision.DomSpec
+import kotlin.browser.document
+import kotlin.test.Test
+
+class CellSpec : DomSpec {
+
+ @Test
+ fun render() {
+ run {
+ val root = Root("test")
+ val cell = Cell("This is a cell")
+ root.add(cell)
+ val element = document.getElementById("test")
+ assertEqualsHtml("<td>This is a cell</td>", element?.innerHTML, "Should render correct table cell")
+ }
+ }
+
+}
diff --git a/src/test/kotlin/test/pl/treksoft/kvision/table/HeaderCellSpec.kt b/src/test/kotlin/test/pl/treksoft/kvision/table/HeaderCellSpec.kt
new file mode 100644
index 00000000..ef417fc8
--- /dev/null
+++ b/src/test/kotlin/test/pl/treksoft/kvision/table/HeaderCellSpec.kt
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2018. Robert Jaros
+ */
+package test.pl.treksoft.kvision.table
+
+import pl.treksoft.kvision.panel.Root
+import pl.treksoft.kvision.table.HeaderCell
+import test.pl.treksoft.kvision.DomSpec
+import kotlin.browser.document
+import kotlin.test.Test
+
+class HeaderCellSpec : DomSpec {
+
+ @Test
+ fun render() {
+ run {
+ val root = Root("test")
+ val cell = HeaderCell("This is a header cell")
+ root.add(cell)
+ val element = document.getElementById("test")
+ assertEqualsHtml(
+ "<th>This is a header cell</th>",
+ element?.innerHTML,
+ "Should render correct table header cell"
+ )
+ }
+ }
+
+}
diff --git a/src/test/kotlin/test/pl/treksoft/kvision/table/RowSpec.kt b/src/test/kotlin/test/pl/treksoft/kvision/table/RowSpec.kt
new file mode 100644
index 00000000..04cd5a2d
--- /dev/null
+++ b/src/test/kotlin/test/pl/treksoft/kvision/table/RowSpec.kt
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2018. Robert Jaros
+ */
+package test.pl.treksoft.kvision.table
+
+import pl.treksoft.kvision.panel.Root
+import pl.treksoft.kvision.table.Cell.Companion.cell
+import pl.treksoft.kvision.table.Row
+import test.pl.treksoft.kvision.DomSpec
+import kotlin.browser.document
+import kotlin.test.Test
+
+class RowSpec : DomSpec {
+
+ @Test
+ fun render() {
+ run {
+ val root = Root("test")
+ val row = Row {
+ cell("A")
+ }
+ root.add(row)
+ val element = document.getElementById("test")
+ assertEqualsHtml("<tr><td>A</td></tr>", element?.innerHTML, "Should render correct table row")
+ }
+ }
+
+}
diff --git a/src/test/kotlin/test/pl/treksoft/kvision/table/TableSpec.kt b/src/test/kotlin/test/pl/treksoft/kvision/table/TableSpec.kt
new file mode 100644
index 00000000..b544a346
--- /dev/null
+++ b/src/test/kotlin/test/pl/treksoft/kvision/table/TableSpec.kt
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2018. Robert Jaros
+ */
+package test.pl.treksoft.kvision.table
+
+import pl.treksoft.kvision.panel.Root
+import pl.treksoft.kvision.table.Cell.Companion.cell
+import pl.treksoft.kvision.table.Row.Companion.row
+import pl.treksoft.kvision.table.Table
+import pl.treksoft.kvision.table.TableType
+import test.pl.treksoft.kvision.DomSpec
+import kotlin.browser.document
+import kotlin.test.Test
+
+class TableSpec : DomSpec {
+
+ @Test
+ fun render() {
+ run {
+ val root = Root("test")
+ val table = Table(listOf("a", "b")) {
+ row {
+ cell("A")
+ cell("B")
+ }
+ }
+ root.add(table)
+ val element = document.getElementById("test")
+ assertEqualsHtml(
+ "<table class=\"table\"><thead><tr><th>a</th><th>b</th></tr></thead><tbody><tr><td>A</td><td>B</td></tr></tbody></table>",
+ element?.innerHTML,
+ "Should render correct table"
+ )
+ table.caption = "Caption"
+ table.responsive = true
+ table.types = setOf(TableType.BORDERED)
+ val element2 = document.getElementById("test")
+ assertEqualsHtml(
+ "<div class=\"table-responsive\"><table class=\"table table-bordered\"><caption>Caption</caption><thead><tr><th>a</th><th>b</th></tr></thead><tbody><tr><td>A</td><td>B</td></tr></tbody></table></div>",
+ element2?.innerHTML,
+ "Should render correct responsive table"
+ )
+ }
+ }
+
+}