aboutsummaryrefslogtreecommitdiff
path: root/kvision-modules/kvision-tabulator
diff options
context:
space:
mode:
authorRobbie Cronin <robert.cronin@uqconnect.edu.au>2019-06-21 13:17:30 +0800
committerGitHub <noreply@github.com>2019-06-21 13:17:30 +0800
commit2f8e0060308b0f5b1eeac9541600bfed3ad5acc9 (patch)
tree80d40364c5f5957da8c82578dd1152adac06912e /kvision-modules/kvision-tabulator
parent9038555f9e46a32a0a725112304e64cdd408bf8e (diff)
parentd9f1a90c772719d14540eb2bf7bc3b8384fa7a72 (diff)
downloadkvision-2f8e0060308b0f5b1eeac9541600bfed3ad5acc9.tar.gz
kvision-2f8e0060308b0f5b1eeac9541600bfed3ad5acc9.tar.bz2
kvision-2f8e0060308b0f5b1eeac9541600bfed3ad5acc9.zip
Merge pull request #1 from rjaros/master
merge
Diffstat (limited to 'kvision-modules/kvision-tabulator')
-rw-r--r--kvision-modules/kvision-tabulator/build.gradle1
-rw-r--r--kvision-modules/kvision-tabulator/src/main/kotlin/pl/treksoft/kvision/tabulator/Options.kt152
-rw-r--r--kvision-modules/kvision-tabulator/src/main/kotlin/pl/treksoft/kvision/tabulator/Tabulator.kt38
-rw-r--r--kvision-modules/kvision-tabulator/src/test/kotlin/test/pl/treksoft/kvision/TestUtil.kt2
-rw-r--r--kvision-modules/kvision-tabulator/src/test/kotlin/test/pl/treksoft/kvision/tabulator/TabulatorSpec.kt2
5 files changed, 164 insertions, 31 deletions
diff --git a/kvision-modules/kvision-tabulator/build.gradle b/kvision-modules/kvision-tabulator/build.gradle
index d461e542..5ea97e50 100644
--- a/kvision-modules/kvision-tabulator/build.gradle
+++ b/kvision-modules/kvision-tabulator/build.gradle
@@ -11,6 +11,7 @@ kotlinFrontend {
dependency("tabulator-tables", "4.2.5")
devDependency("karma", "3.1.4")
devDependency("karma-chrome-launcher", "2.2.0")
+ devDependency("karma-webpack", "3.0.5")
devDependency("qunit", "2.8.0")
}
diff --git a/kvision-modules/kvision-tabulator/src/main/kotlin/pl/treksoft/kvision/tabulator/Options.kt b/kvision-modules/kvision-tabulator/src/main/kotlin/pl/treksoft/kvision/tabulator/Options.kt
index dac29ab9..08f2603b 100644
--- a/kvision-modules/kvision-tabulator/src/main/kotlin/pl/treksoft/kvision/tabulator/Options.kt
+++ b/kvision-modules/kvision-tabulator/src/main/kotlin/pl/treksoft/kvision/tabulator/Options.kt
@@ -22,8 +22,19 @@
package pl.treksoft.kvision.tabulator
+import kotlinx.serialization.KSerializer
+import org.w3c.dom.HTMLElement
+import pl.treksoft.kvision.core.Component
+import pl.treksoft.kvision.form.FormControl
+import pl.treksoft.kvision.form.FormInput
+import pl.treksoft.kvision.panel.Root
+import pl.treksoft.kvision.tabulator.EditorRoot.disposeTimer
+import pl.treksoft.kvision.tabulator.EditorRoot.root
import pl.treksoft.kvision.tabulator.js.Tabulator
+import pl.treksoft.kvision.utils.JSON
import pl.treksoft.kvision.utils.obj
+import kotlin.browser.document
+import kotlin.browser.window
import kotlin.js.Promise
/**
@@ -248,7 +259,7 @@ fun DownloadConfig.toJs(): Tabulator.DownloadConfig {
/**
* Column definition options.
*/
-data class ColumnDefinition(
+data class ColumnDefinition<T : Any>(
val title: String,
val field: String? = null,
val visible: Boolean? = null,
@@ -265,18 +276,36 @@ data class ColumnDefinition(
val rowHandle: Boolean? = null,
val hideInHtml: Boolean? = null,
val sorter: Sorter? = null,
+ val sorterFunction: ((
+ a: dynamic, b: dynamic, aRow: Tabulator.RowComponent, bRow: Tabulator.RowComponent,
+ column: Tabulator.ColumnComponent, dir: SortingDir, sorterParams: dynamic
+ ) -> Number)? = null,
val sorterParams: dynamic = null,
val formatter: Formatter? = null,
val formatterFunction: ((
cell: Tabulator.CellComponent, formatterParams: dynamic,
onRendered: (callback: () -> Unit) -> Unit
) -> dynamic)? = null,
+ val formatterComponentFunction: ((
+ cell: Tabulator.CellComponent, onRendered: (callback: () -> Unit) -> Unit, data: T
+ ) -> Component)? = null,
val formatterParams: dynamic = null,
val variableHeight: Boolean? = null,
val editable: ((cell: Tabulator.CellComponent) -> Boolean)? = null,
val editor: Editor? = null,
+ val editorFunction: ((
+ cell: Tabulator.CellComponent,
+ onRendered: (callback: () -> Unit) -> Unit,
+ success: (value: dynamic) -> Unit, cancel: (value: dynamic) -> Unit, editorParams: dynamic
+ ) -> dynamic)? = null,
+ val editorComponentFunction: ((
+ cell: Tabulator.CellComponent,
+ onRendered: (callback: () -> Unit) -> Unit,
+ success: (value: dynamic) -> Unit, cancel: (value: dynamic) -> Unit, data: T
+ ) -> Component)? = null,
val editorParams: dynamic = null,
val validator: Validator? = null,
+ val validatorFunction: dynamic = null,
val validatorParams: String? = null,
val download: Boolean? = null,
val downloadTitle: String? = null,
@@ -325,11 +354,78 @@ data class ColumnDefinition(
val cellEditCancelled: ((cell: Tabulator.CellComponent) -> Unit)? = null
)
+internal object EditorRoot {
+ internal var root: Root? = null
+ internal var disposeTimer: Int? = null
+}
+
/**
* An extension function to convert column definition class to JS object.
*/
-@Suppress("UNCHECKED_CAST_TO_EXTERNAL_INTERFACE", "ComplexMethod")
-fun ColumnDefinition.toJs(i18nTranslator: (String) -> (String)): Tabulator.ColumnDefinition {
+@Suppress("UNCHECKED_CAST_TO_EXTERNAL_INTERFACE", "ComplexMethod", "MagicNumber")
+fun <T : Any> ColumnDefinition<T>.toJs(
+ i18nTranslator: (String) -> (String),
+ dataSerializer: KSerializer<T>? = null
+): Tabulator.ColumnDefinition {
+ val tmpEditorFunction = editorComponentFunction?.let {
+ { cell: Tabulator.CellComponent,
+ onRendered: (callback: () -> Unit) -> Unit,
+ success: (value: dynamic) -> Unit, cancel: (value: dynamic) -> Unit, _: dynamic ->
+ var onRenderedCallback: (() -> Unit)? = null
+ val str = kotlin.js.JSON.stringify(cell.getData())
+ @Suppress("UNCHECKED_CAST") val data = dataSerializer?.let {
+ JSON.plain.parse(it, str)
+ } ?: cell.getData() as T
+ val component = it(cell, { callback ->
+ onRenderedCallback = callback
+ }, { value ->
+ success(value)
+ disposeTimer = window.setTimeout({
+ root?.dispose()
+ disposeTimer = null
+ root = null
+ }, 500)
+ }, cancel, data)
+ val rootElement = document.createElement("div") as HTMLElement
+ onRendered {
+ if (root != null) {
+ disposeTimer?.let { window.clearTimeout(it) }
+ root?.dispose()
+ }
+ root = Root(element = rootElement)
+ @Suppress("UnsafeCastFromDynamic")
+ root?.add(component)
+ (component as? FormControl)?.focus()
+ (component as? FormInput)?.focus()
+ cell.checkHeight()
+ onRenderedCallback?.invoke()
+ }
+ rootElement
+ }
+ }
+
+ val tmpFormatterFunction = formatterComponentFunction?.let {
+ { cell: Tabulator.CellComponent, _: dynamic,
+ onRendered: (callback: () -> Unit) -> Unit ->
+ var onRenderedCallback: (() -> Unit)? = null
+ val str = kotlin.js.JSON.stringify(cell.getData())
+ @Suppress("UNCHECKED_CAST") val data =
+ dataSerializer?.let { JSON.plain.parse(it, str) } ?: cell.getData() as T
+ val component = it(cell, { callback ->
+ onRenderedCallback = callback
+ }, data)
+ val rootElement = document.createElement("div") as HTMLElement
+ onRendered {
+ val root = Root(element = rootElement)
+ @Suppress("UnsafeCastFromDynamic")
+ root.add(component)
+ cell.checkHeight()
+ onRenderedCallback?.invoke()
+ }
+ rootElement
+ }
+ }
+
return obj {
this.title = i18nTranslator(title)
if (field != null) this.field = field
@@ -346,17 +442,25 @@ fun ColumnDefinition.toJs(i18nTranslator: (String) -> (String)): Tabulator.Colum
if (cssClass != null) this.cssClass = cssClass
if (rowHandle != null) this.rowHandle = rowHandle
if (hideInHtml != null) this.hideInHtml = hideInHtml
- if (sorter != null) this.sorter = sorter.sorter
+ if (sorterFunction != null) {
+ this.sorter = sorterFunction
+ } else if (sorter != null) {
+ this.sorter = sorter.sorter
+ }
if (sorterParams != null) this.sorterParams = sorterParams
- if (formatterFunction != null) {
- this.formatter = formatterFunction
- } else if (formatter != null) {
- this.formatter = formatter.formatter
+ when {
+ tmpFormatterFunction != null -> this.formatter = tmpFormatterFunction
+ formatterFunction != null -> this.formatter = formatterFunction
+ formatter != null -> this.formatter = formatter.formatter
}
if (formatterParams != null) this.formatterParams = formatterParams
if (variableHeight != null) this.variableHeight = variableHeight
if (editable != null) this.editable = editable
- if (editor != null) this.editor = editor.editor
+ when {
+ tmpEditorFunction != null -> this.editor = tmpEditorFunction
+ editorFunction != null -> this.editor = editorFunction
+ editor != null -> this.editor = editor.editor
+ }
if (editorParams != null) this.editorParams = editorParams
if (validator != null) this.validator = validator.validator
if (validatorParams != null) this.validatorParams = validatorParams
@@ -404,14 +508,20 @@ fun ColumnDefinition.toJs(i18nTranslator: (String) -> (String)): Tabulator.Colum
if (cellMouseMove != null) this.cellMouseMove = cellMouseMove
if (cellEditing != null) this.cellEditing = cellEditing
if (cellEdited != null) this.cellEdited = cellEdited
- if (cellEditCancelled != null) this.cellEditCancelled = cellEditCancelled
+ if (cellEditCancelled != null) {
+ this.cellEditCancelled = cellEditCancelled
+ } else if (tmpEditorFunction != null) {
+ this.cellEditCancelled = { cell: Tabulator.CellComponent ->
+ cell.checkHeight()
+ }
+ }
} as Tabulator.ColumnDefinition
}
/**
* Tabulator options.
*/
-data class TabulatorOptions(
+data class TabulatorOptions<T : Any>(
val height: String? = null,
val virtualDom: Boolean? = null,
val virtualDomBuffer: Int? = null,
@@ -425,7 +535,7 @@ data class TabulatorOptions(
val downloadConfig: DownloadConfig? = null,
val reactiveData: Boolean? = null,
val autoResize: Boolean? = null,
- val columns: List<ColumnDefinition>? = null,
+ val columns: List<ColumnDefinition<T>>? = null,
val autoColumns: Boolean? = null,
val layout: Layout? = null,
val layoutColumnsOnNewData: Boolean? = null,
@@ -572,7 +682,17 @@ data class TabulatorOptions(
* An extension function to convert tabulator options class to JS object.
*/
@Suppress("UNCHECKED_CAST_TO_EXTERNAL_INTERFACE", "ComplexMethod")
-fun TabulatorOptions.toJs(i18nTranslator: (String) -> (String)): Tabulator.Options {
+fun <T : Any> TabulatorOptions<T>.toJs(
+ i18nTranslator: (String) -> (String),
+ dataSerializer: KSerializer<T>? = null
+): Tabulator.Options {
+ val tmpCellEditCancelled = this.columns?.find { it.editorComponentFunction != null }?.let {
+ { cell: Tabulator.CellComponent ->
+ cellEditCancelled?.invoke(cell)
+ cell.getTable().redraw(true)
+ }
+ } ?: cellEditCancelled
+
return obj {
if (height != null) this.height = height
if (virtualDom != null) this.virtualDom = virtualDom
@@ -587,7 +707,7 @@ fun TabulatorOptions.toJs(i18nTranslator: (String) -> (String)): Tabulator.Optio
if (downloadConfig != null) this.downloadConfig = downloadConfig.toJs()
if (reactiveData != null) this.reactiveData = reactiveData
if (autoResize != null) this.autoResize = autoResize
- if (columns != null) this.columns = columns.map { it.toJs(i18nTranslator) }.toTypedArray()
+ if (columns != null) this.columns = columns.map { it.toJs(i18nTranslator, dataSerializer) }.toTypedArray()
if (autoColumns != null) {
this.autoColumns = autoColumns
} else if (columns == null) {
@@ -700,7 +820,9 @@ fun TabulatorOptions.toJs(i18nTranslator: (String) -> (String)): Tabulator.Optio
if (cellMouseMove != null) this.cellMouseMove = cellMouseMove
if (cellEditing != null) this.cellEditing = cellEditing
if (cellEdited != null) this.cellEdited = cellEdited
- if (cellEditCancelled != null) this.cellEditCancelled = cellEditCancelled
+ if (tmpCellEditCancelled != null) {
+ this.cellEditCancelled = tmpCellEditCancelled
+ }
if (columnMoved != null) this.columnMoved = columnMoved
if (columnResized != null) this.columnResized = columnResized
if (columnVisibilityChanged != null) this.columnVisibilityChanged = columnVisibilityChanged
diff --git a/kvision-modules/kvision-tabulator/src/main/kotlin/pl/treksoft/kvision/tabulator/Tabulator.kt b/kvision-modules/kvision-tabulator/src/main/kotlin/pl/treksoft/kvision/tabulator/Tabulator.kt
index 249c578f..72a2809a 100644
--- a/kvision-modules/kvision-tabulator/src/main/kotlin/pl/treksoft/kvision/tabulator/Tabulator.kt
+++ b/kvision-modules/kvision-tabulator/src/main/kotlin/pl/treksoft/kvision/tabulator/Tabulator.kt
@@ -38,6 +38,7 @@ import pl.treksoft.kvision.table.TableType
import pl.treksoft.kvision.utils.JSON
import pl.treksoft.kvision.utils.createInstance
import pl.treksoft.kvision.utils.obj
+import pl.treksoft.kvision.utils.syncWithList
import redux.RAction
import pl.treksoft.kvision.tabulator.js.Tabulator as JsTabulator
@@ -47,14 +48,17 @@ import pl.treksoft.kvision.tabulator.js.Tabulator as JsTabulator
* @constructor
* @param T serializable type
* @param data a list of serializable objects
+ * @param dataUpdateOnEdit determines if the data model is automatically updated after tabulator edit action
* @param options tabulator options
+ * @param types a set of table types
* @param classes a set of CSS class names
* @param dataSerializer a serializer for class T
*/
@Suppress("LargeClass", "TooManyFunctions")
open class Tabulator<T : Any>(
protected val data: List<T>? = null,
- val options: TabulatorOptions = TabulatorOptions(),
+ protected val dataUpdateOnEdit: Boolean = true,
+ val options: TabulatorOptions<T> = TabulatorOptions(),
types: Set<TableType> = setOf(),
classes: Set<String> = setOf(),
protected val dataSerializer: KSerializer<T>? = null
@@ -191,6 +195,9 @@ open class Tabulator<T : Any>(
val d = nativeToData(data, dataSerializer)
@Suppress("UnsafeCastFromDynamic")
this.dispatchEvent("tabulatorDataEdited", obj { detail = d })
+ if (dataUpdateOnEdit && this.data is MutableList<T>) {
+ this.data.syncWithList(d)
+ }
}
}
counter++
@@ -228,7 +235,7 @@ open class Tabulator<T : Any>(
(this.getElement() as? HTMLElement)?.let {
jsTabulator =
KVManagerTabulator.getConstructor()
- .createInstance(it, options.toJs(this::translate))
+ .createInstance(it, options.toJs(this::translate, dataSerializer))
if (currentPage != null) {
jsTabulator?.setPageSize(pageSize ?: 0)
jsTabulator?.setPage(currentPage)
@@ -561,12 +568,13 @@ open class Tabulator<T : Any>(
*/
inline fun <reified T : Any> Container.tabulator(
data: List<T>? = null,
- options: TabulatorOptions = TabulatorOptions(),
+ dataUpdateOnEdit: Boolean = true,
+ options: TabulatorOptions<T> = TabulatorOptions(),
types: Set<TableType> = setOf(),
classes: Set<String> = setOf(),
noinline init: (Tabulator<T>.() -> Unit)? = null
): Tabulator<T> {
- val tabulator = create(data, options, types, classes)
+ val tabulator = create(data, dataUpdateOnEdit, options, types, classes)
init?.invoke(tabulator)
this.add(tabulator)
return tabulator
@@ -578,7 +586,7 @@ open class Tabulator<T : Any>(
inline fun <reified T : Any, S : Any, A : RAction> Container.tabulator(
store: ReduxStore<S, A>,
noinline dataFactory: (S) -> List<T>,
- options: TabulatorOptions = TabulatorOptions(),
+ options: TabulatorOptions<T> = TabulatorOptions(),
types: Set<TableType> = setOf(),
classes: Set<String> = setOf(),
noinline init: (Tabulator<T>.() -> Unit)? = null
@@ -594,7 +602,7 @@ open class Tabulator<T : Any>(
*/
inline fun <reified T : Any, A : RAction> Container.tabulator(
store: ReduxStore<List<T>, A>,
- options: TabulatorOptions = TabulatorOptions(),
+ options: TabulatorOptions<T> = TabulatorOptions(),
types: Set<TableType> = setOf(),
classes: Set<String> = setOf(),
noinline init: (Tabulator<T>.() -> Unit)? = null
@@ -609,12 +617,12 @@ open class Tabulator<T : Any>(
* DSL builder extension function for dynamic data (send within options parameter).
*/
fun <T : Any> Container.tabulator(
- options: TabulatorOptions = TabulatorOptions(),
+ options: TabulatorOptions<T> = TabulatorOptions(),
types: Set<TableType> = setOf(),
classes: Set<String> = setOf(),
init: (Tabulator<T>.() -> Unit)? = null
): Tabulator<T> {
- val tabulator = Tabulator<T>(options = options, types = types, classes = classes)
+ val tabulator = Tabulator(dataUpdateOnEdit = false, options = options, types = types, classes = classes)
init?.invoke(tabulator)
this.add(tabulator)
return tabulator
@@ -625,12 +633,14 @@ open class Tabulator<T : Any>(
*/
@UseExperimental(ImplicitReflectionSerializer::class)
inline fun <reified T : Any> create(
- data: List<T>? = null, options: TabulatorOptions = TabulatorOptions(),
+ data: List<T>? = null,
+ dataUpdateOnEdit: Boolean = true,
+ options: TabulatorOptions<T> = TabulatorOptions(),
types: Set<TableType> = setOf(),
classes: Set<String> = setOf(),
noinline init: (Tabulator<T>.() -> Unit)? = null
): Tabulator<T> {
- val tabulator = Tabulator(data, options, types, classes, T::class.serializer())
+ val tabulator = Tabulator(data, dataUpdateOnEdit, options, types, classes, T::class.serializer())
init?.invoke(tabulator)
return tabulator
}
@@ -642,13 +652,13 @@ open class Tabulator<T : Any>(
inline fun <reified T : Any, S : Any, A : RAction> create(
store: ReduxStore<S, A>,
noinline dataFactory: (S) -> List<T>,
- options: TabulatorOptions = TabulatorOptions(),
+ options: TabulatorOptions<T> = TabulatorOptions(),
types: Set<TableType> = setOf(),
classes: Set<String> = setOf(),
noinline init: (Tabulator<T>.() -> Unit)? = null
): Tabulator<T> {
val data = dataFactory(store.getState())
- val tabulator = Tabulator(data, options, types, classes, T::class.serializer())
+ val tabulator = Tabulator(data, false, options, types, classes, T::class.serializer())
init?.invoke(tabulator)
store.subscribe { s ->
tabulator.replaceData(dataFactory(s))
@@ -662,13 +672,13 @@ open class Tabulator<T : Any>(
@UseExperimental(ImplicitReflectionSerializer::class)
inline fun <reified T : Any, A : RAction> create(
store: ReduxStore<List<T>, A>,
- options: TabulatorOptions = TabulatorOptions(),
+ options: TabulatorOptions<T> = TabulatorOptions(),
types: Set<TableType> = setOf(),
classes: Set<String> = setOf(),
noinline init: (Tabulator<T>.() -> Unit)? = null
): Tabulator<T> {
val data = store.getState()
- val tabulator = Tabulator(data, options, types, classes, T::class.serializer())
+ val tabulator = Tabulator(data, false, options, types, classes, T::class.serializer())
init?.invoke(tabulator)
store.subscribe { s ->
tabulator.replaceData(s)
diff --git a/kvision-modules/kvision-tabulator/src/test/kotlin/test/pl/treksoft/kvision/TestUtil.kt b/kvision-modules/kvision-tabulator/src/test/kotlin/test/pl/treksoft/kvision/TestUtil.kt
index 37d7a9df..13c8531b 100644
--- a/kvision-modules/kvision-tabulator/src/test/kotlin/test/pl/treksoft/kvision/TestUtil.kt
+++ b/kvision-modules/kvision-tabulator/src/test/kotlin/test/pl/treksoft/kvision/TestUtil.kt
@@ -86,7 +86,7 @@ interface WSpec : DomSpec {
fun runW(code: (widget: Widget, element: Element?) -> Unit) {
run {
- val root = Root("test", true)
+ val root = Root("test", fixed = true)
val widget = Widget()
widget.id = "test_id"
root.add(widget)
diff --git a/kvision-modules/kvision-tabulator/src/test/kotlin/test/pl/treksoft/kvision/tabulator/TabulatorSpec.kt b/kvision-modules/kvision-tabulator/src/test/kotlin/test/pl/treksoft/kvision/tabulator/TabulatorSpec.kt
index d6b33a78..1f49ee93 100644
--- a/kvision-modules/kvision-tabulator/src/test/kotlin/test/pl/treksoft/kvision/tabulator/TabulatorSpec.kt
+++ b/kvision-modules/kvision-tabulator/src/test/kotlin/test/pl/treksoft/kvision/tabulator/TabulatorSpec.kt
@@ -34,7 +34,7 @@ class TabulatorSpec : DomSpec {
@Test
fun render() {
run {
- val root = Root("test", true)
+ val root = Root("test", fixed = true)
val tabulator = Tabulator<Any>(options = TabulatorOptions(data = arrayOf(obj {
id = 1
name = "Name"