From 41ac80f7ade5cb487150cdc5d0db8eb7aab02d08 Mon Sep 17 00:00:00 2001 From: Robert Jaros Date: Wed, 24 Apr 2019 20:14:55 +0200 Subject: New kvision-tabulator module. --- .../pl/treksoft/kvision/KVManagerTabulator.kt | 47 ++ .../pl/treksoft/kvision/tabulator/Options.kt | 727 ++++++++++++++++++ .../pl/treksoft/kvision/tabulator/Tabulator.kt | 679 ++++++++++++++++ .../pl/treksoft/kvision/tabulator/js/Tabulator.kt | 855 +++++++++++++++++++++ 4 files changed, 2308 insertions(+) create mode 100644 kvision-modules/kvision-tabulator/src/main/kotlin/pl/treksoft/kvision/KVManagerTabulator.kt create mode 100644 kvision-modules/kvision-tabulator/src/main/kotlin/pl/treksoft/kvision/tabulator/Options.kt create mode 100644 kvision-modules/kvision-tabulator/src/main/kotlin/pl/treksoft/kvision/tabulator/Tabulator.kt create mode 100644 kvision-modules/kvision-tabulator/src/main/kotlin/pl/treksoft/kvision/tabulator/js/Tabulator.kt (limited to 'kvision-modules/kvision-tabulator/src/main') diff --git a/kvision-modules/kvision-tabulator/src/main/kotlin/pl/treksoft/kvision/KVManagerTabulator.kt b/kvision-modules/kvision-tabulator/src/main/kotlin/pl/treksoft/kvision/KVManagerTabulator.kt new file mode 100644 index 00000000..ebd1b9f7 --- /dev/null +++ b/kvision-modules/kvision-tabulator/src/main/kotlin/pl/treksoft/kvision/KVManagerTabulator.kt @@ -0,0 +1,47 @@ +/* + * 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 + +internal val kVManagerTabulatorInit = KVManagerTabulator.init() + +/** + * Internal singleton object which initializes and configures KVision Tabulator module. + */ +@Suppress("EmptyCatchBlock", "TooGenericExceptionCaught") +internal object KVManagerTabulator { + fun init() {} + + private val tabulatorCss = try { + require("tabulator-tables/dist/css/bootstrap/tabulator_bootstrap.min.css") + } catch (e: Throwable) { + } + + private val tabulator = try { + require("tabulator-tables/dist/js/tabulator.min.js") + } catch (e: Throwable) { + } + + @Suppress("UnsafeCastFromDynamic") + fun getConstructor(): Any { + return tabulator + } +} 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 new file mode 100644 index 00000000..c00af9cb --- /dev/null +++ b/kvision-modules/kvision-tabulator/src/main/kotlin/pl/treksoft/kvision/tabulator/Options.kt @@ -0,0 +1,727 @@ +/* + * 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.tabulator + +import pl.treksoft.kvision.tabulator.js.Tabulator +import pl.treksoft.kvision.utils.obj +import kotlin.js.Promise + +/** + * Tooltip generation mode. + */ +enum class TooltipGenerationMode(internal val mode: String) { + LOAD("load"), + HOVER("hover") +} + +/** + * Column align. + */ +enum class Align(internal val align: String) { + LEFT("left"), + CENTER("center"), + RIGHT("right") +} + +/** + * Built-in sorters. + */ +enum class Sorter(internal val sorter: String) { + STRING("string"), + NUMBER("number"), + ALPHANUM("alphanum"), + BOOLEAN("boolean"), + EXISTS("exists"), + DATE("date"), + TIME("time"), + DATETIME("datetime"), + ARRAY("array") +} + +/** + * Built-in formatters. + */ +enum class Formatter(internal val formatter: String) { + PLAINTEXT("plaintext"), + TEXTAREA("textarea"), + HTML("html"), + MONEY("money"), + IMAGE("image"), + LINK("link"), + DATETIME("datetime"), + DATETIMEDIFF("datetimediff"), + TICKCROSS("tickCross"), + COLOR("color"), + STAR("star"), + TRAFFIC("traffic"), + PROGRESS("progress"), + LOOKUP("lookup"), + BUTTONTICK("buttonTick"), + BUTTONCROSS("buttonCross"), + ROWNUM("rownum"), + HANDLE("handle") +} + +/** + * Built-in editors. + */ +enum class Editor(internal val editor: String) { + INPUT("input"), + TEXTAREA("textarea"), + NUMBER("number"), + RANGE("range"), + TICK("tick"), + STAR("star"), + SELECT("select"), + AUTOCOMPLETE("autocomplete") +} + +/** + * Built-in validators. + */ +enum class Validator(internal val validator: String) { + REQUIRED("required"), + UNIQUE("unique"), + INTEGER("integer"), + FLOAT("float"), + NUMERIC("numeric"), + STRING("string"), + MIN("min"), + MAX("max"), + MINLENGTH("minLength"), + MAXLENGTH("maxLength"), + IN("in"), + REGEX("regex") +} + +/** + * Built-in calc functions. + */ +enum class Calc(internal val calc: String) { + AVG("avg"), + MAX("max"), + MIN("min"), + SUM("sum"), + CONCAT("concat"), + COUNT("count") +} + +/** + * Sorting directions. + */ +enum class SortingDir(internal val dir: String) { + ASC("asc"), + DESC("desc") +} + +/** + * Filters. + */ +enum class Filter(internal val filter: String) { + EQUAL("="), + NOTEQUAL("!="), + LIKE("like"), + LESS("<"), + LESSEQ("<="), + GREATER(">"), + GREATEREQ(">="), + IN("in"), + REGEX("regex") +} + +/** + * Table layouts. + */ +enum class Layout(internal val layout: String) { + FITDATA("fitData"), + FITDATAFILL("fitDataFill"), + FITCOLUMNS("fitColumns") +} + +/** + * Responsive layout modes. + */ +enum class ResponsiveLayout(internal val layout: String) { + HIDE("hide"), + COLLAPSE("collapse") +} + +/** + * Column positions. + */ +enum class ColumnPosition(internal val position: String) { + MIDDLE("middle"), + LEFT("left"), + RIGHT("right") +} + +/** + * Row scroll positions . + */ +enum class RowPosition(internal val position: String) { + BOTTOM("bottom"), + TOP("top"), + CENTER("center"), + NEAREST("nearest") +} + +/** + * Row positions. + */ +enum class RowPos(internal val position: String) { + BOTTOM("bottom"), + TOP("top") +} + +/** + * Range select modes. + */ +enum class RangeMode(internal val mode: String) { + CLICK("click") +} + +/** + * Progressive modes. + */ +enum class ProgressiveMode(internal val mode: String) { + LOAD("load"), + SCROLL("scroll") +} + +/** + * Pagination modes. + */ +enum class PaginationMode(internal val mode: String) { + LOCAL("local"), + REMOTE("remote") +} + +/** + * Add row modes. + */ +enum class AddRowMode(internal val mode: String) { + TABLE("table"), + PAGE("page") +} + +/** + * Download config options. + */ +data class DownloadConfig( + val columnGroups: Boolean? = null, + val rowGroups: Boolean? = null, + val columnCalcs: Boolean? = null +) + +/** + * An extension function to convert download config class to JS object. + */ +@Suppress("UNCHECKED_CAST_TO_EXTERNAL_INTERFACE") +fun DownloadConfig.toJs(): Tabulator.DownloadConfig { + return obj { + if (columnGroups != null) this.columnGroups = columnGroups + if (rowGroups != null) this.rowGroups = rowGroups + if (columnCalcs != null) this.columnCalcs = columnCalcs + } as Tabulator.DownloadConfig +} + +/** + * Column definition options. + */ +data class ColumnDefinition( + val title: String, + val field: String? = null, + val visible: Boolean? = null, + val align: Align? = null, + val width: String? = null, + val minWidth: Int? = null, + val widthGrow: Int? = null, + val widthShrink: Int? = null, + val resizable: Boolean? = null, + val frozen: Boolean? = null, + val responsive: Int? = null, + val tooltip: ((cell: Tabulator.CellComponent) -> String)? = null, + val cssClass: String? = null, + val rowHandle: Boolean? = null, + val hideInHtml: Boolean? = null, + val sorter: Sorter? = null, + val sorterParams: dynamic = null, + val formatter: Formatter? = null, + val formatterFunction: (( + cell: Tabulator.CellComponent, formatterParams: dynamic, + onRendered: (callback: () -> Unit) -> Unit + ) -> dynamic)? = null, + val formatterParams: dynamic = null, + val variableHeight: Boolean? = null, + val editable: ((cell: Tabulator.CellComponent) -> Boolean)? = null, + val editor: Editor? = null, + val editorParams: dynamic = null, + val validator: Validator? = null, + val validatorParams: String? = null, + val download: Boolean? = null, + val downloadTitle: String? = null, + val topCalc: Calc? = null, + val topCalcParams: dynamic = null, + val topCalcFormatter: Formatter? = null, + val topCalcFormatterParams: dynamic = null, + val bottomCalc: Calc? = null, + val bottomCalcParams: dynamic = null, + val bottomCalcFormatter: Formatter? = null, + val bottomCalcFormatterParams: dynamic = null, + val headerSort: Boolean? = null, + val headerSortStartingDir: SortingDir? = null, + val headerSortTristate: Boolean? = null, + val headerClick: ((e: dynamic, column: Tabulator.ColumnComponent) -> Unit)? = null, + val headerDblClick: ((e: dynamic, column: Tabulator.ColumnComponent) -> Unit)? = null, + val headerContext: ((e: dynamic, column: Tabulator.ColumnComponent) -> Unit)? = null, + val headerTap: ((e: dynamic, column: Tabulator.ColumnComponent) -> Unit)? = null, + val headerDblTap: ((e: dynamic, column: Tabulator.ColumnComponent) -> Unit)? = null, + val headerTapHold: ((e: dynamic, column: Tabulator.ColumnComponent) -> Unit)? = null, + val headerTooltip: ((column: Tabulator.ColumnComponent) -> String)? = null, + val headerVertical: Boolean? = null, + val editableTitle: Boolean? = null, + val titleFormatter: Formatter? = null, + val titleFormatterParams: dynamic = null, + val headerFilter: Editor? = null, + val headerFilterParams: dynamic = null, + val headerFilterPlaceholder: String? = null, + val headerFilterEmptyCheck: ((value: Any) -> Boolean)? = null, + val headerFilterFunc: Filter? = null, + val headerFilterFuncParams: dynamic = null, + val headerFilterLiveFilter: Boolean? = null, + val cellClick: ((e: dynamic, cell: Tabulator.CellComponent) -> Unit)? = null, + val cellDblClick: ((e: dynamic, cell: Tabulator.CellComponent) -> Unit)? = null, + val cellContext: ((e: dynamic, cell: Tabulator.CellComponent) -> Unit)? = null, + val cellTap: ((e: dynamic, cell: Tabulator.CellComponent) -> Unit)? = null, + val cellDblTap: ((e: dynamic, cell: Tabulator.CellComponent) -> Unit)? = null, + val cellTapHold: ((e: dynamic, cell: Tabulator.CellComponent) -> Unit)? = null, + val cellMouseEnter: ((e: dynamic, cell: Tabulator.CellComponent) -> Unit)? = null, + val cellMouseLeave: ((e: dynamic, cell: Tabulator.CellComponent) -> Unit)? = null, + val cellMouseOver: ((e: dynamic, cell: Tabulator.CellComponent) -> Unit)? = null, + val cellMouseOut: ((e: dynamic, cell: Tabulator.CellComponent) -> Unit)? = null, + val cellMouseMove: ((e: dynamic, cell: Tabulator.CellComponent) -> Unit)? = null, + val cellEditing: ((cell: Tabulator.CellComponent) -> Unit)? = null, + val cellEdited: ((cell: Tabulator.CellComponent) -> Unit)? = null, + val cellEditCancelled: ((cell: Tabulator.CellComponent) -> Unit)? = 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 { + return obj { + this.title = i18nTranslator(title) + if (field != null) this.field = field + if (visible != null) this.visible = visible + if (align != null) this.align = align.align + if (width != null) this.width = width + if (minWidth != null) this.minWidth = minWidth + if (widthGrow != null) this.widthGrow = widthGrow + if (widthShrink != null) this.widthShrink = widthShrink + if (resizable != null) this.resizable = resizable + if (frozen != null) this.frozen = frozen + if (responsive != null) this.responsive = responsive + if (tooltip != null) this.tooltip = tooltip + 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 (sorterParams != null) this.sorterParams = sorterParams + if (formatterFunction != null) { + this.formatter = formatterFunction + } else if (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 + if (editorParams != null) this.editorParams = editorParams + if (validator != null) this.validator = validator.validator + if (validatorParams != null) this.validatorParams = validatorParams + if (download != null) this.download = download + if (downloadTitle != null) this.downloadTitle = i18nTranslator(downloadTitle) + if (topCalc != null) this.topCalc = topCalc.calc + if (topCalcParams != null) this.topCalcParams = topCalcParams + if (topCalcFormatter != null) this.topCalcFormatter = topCalcFormatter.formatter + if (topCalcFormatterParams != null) this.topCalcFormatterParams = topCalcFormatterParams + if (bottomCalc != null) this.bottomCalc = bottomCalc.calc + if (bottomCalcParams != null) this.bottomCalcParams = bottomCalcParams + if (bottomCalcFormatter != null) this.bottomCalcFormatter = bottomCalcFormatter.formatter + if (bottomCalcFormatterParams != null) this.bottomCalcFormatterParams = bottomCalcFormatterParams + if (headerSort != null) this.headerSort = headerSort + if (headerSortStartingDir != null) this.headerSortStartingDir = headerSortStartingDir.dir + if (headerSortTristate != null) this.headerSortTristate = headerSortTristate + if (headerClick != null) this.headerClick = headerClick + if (headerDblClick != null) this.headerDblClick = headerDblClick + if (headerContext != null) this.headerContext = headerContext + if (headerTap != null) this.headerTap = headerTap + if (headerDblTap != null) this.headerDblTap = headerDblTap + if (headerTapHold != null) this.headerTapHold = headerTapHold + if (headerTooltip != null) this.headerTooltip = headerTooltip + if (headerVertical != null) this.headerVertical = headerVertical + if (editableTitle != null) this.editableTitle = editableTitle + if (titleFormatter != null) this.titleFormatter = titleFormatter.formatter + if (titleFormatterParams != null) this.titleFormatterParams = titleFormatterParams + if (headerFilter != null) this.headerFilter = headerFilter.editor + if (headerFilterParams != null) this.headerFilterParams = headerFilterParams + if (headerFilterPlaceholder != null) this.headerFilterPlaceholder = i18nTranslator(headerFilterPlaceholder) + if (headerFilterEmptyCheck != null) this.headerFilterEmptyCheck = headerFilterEmptyCheck + if (headerFilterFunc != null) this.headerFilterFunc = headerFilterFunc.filter + if (headerFilterFuncParams != null) this.headerFilterFuncParams = headerFilterFuncParams + if (headerFilterLiveFilter != null) this.headerFilterLiveFilter = headerFilterLiveFilter + if (cellClick != null) this.cellClick = cellClick + if (cellDblClick != null) this.cellDblClick = cellDblClick + if (cellContext != null) this.cellContext = cellContext + if (cellTap != null) this.cellTap = cellTap + if (cellDblTap != null) this.cellDblTap = cellDblTap + if (cellTapHold != null) this.cellTapHold = cellTapHold + if (cellMouseEnter != null) this.cellMouseEnter = cellMouseEnter + if (cellMouseLeave != null) this.cellMouseLeave = cellMouseLeave + if (cellMouseOver != null) this.cellMouseOver = cellMouseOver + if (cellMouseOut != null) this.cellMouseOut = cellMouseOut + if (cellMouseMove != null) this.cellMouseMove = cellMouseMove + if (cellEditing != null) this.cellEditing = cellEditing + if (cellEdited != null) this.cellEdited = cellEdited + if (cellEditCancelled != null) this.cellEditCancelled = cellEditCancelled + } as Tabulator.ColumnDefinition +} + +/** + * Tabulator options. + */ +data class Options( + val height: String? = null, + val virtualDom: Boolean? = null, + val virtualDomBuffer: Int? = null, + val placeholder: String? = null, + val footerElement: String? = null, + val tooltips: ((cell: Tabulator.CellComponent) -> String)? = null, + val tooltipGenerationMode: TooltipGenerationMode? = null, + val history: Boolean? = null, + val keybindings: dynamic = null, + val downloadDataFormatter: dynamic = null, + val downloadConfig: DownloadConfig? = null, + val reactiveData: Boolean? = null, + val autoResize: Boolean? = null, + val columns: List? = null, + val autoColumns: Boolean? = null, + val layout: Layout? = null, + val layoutColumnsOnNewData: Boolean? = null, + val responsiveLayout: ResponsiveLayout? = null, + val responsiveLayoutCollapseStartOpen: Boolean? = null, + val responsiveLayoutCollapseUseFormatters: Boolean? = null, + val columnMinWidth: Int? = null, + val resizableColumns: Boolean? = null, + val movableColumns: Boolean? = null, + val tooltipsHeader: Boolean? = null, + val headerFilterPlaceholder: String? = null, + val scrollToColumnPosition: ColumnPosition? = null, + val scrollToColumnIfVisible: Boolean? = null, + val rowFormatter: ((row: Tabulator.RowComponent) -> Unit)? = null, + val addRowPos: RowPos? = null, + val selectable: dynamic = null, + val selectableRangeMode: RangeMode? = null, + val selectableRollingSelection: Boolean? = null, + val selectablePersistence: Boolean? = null, + val selectableCheck: ((row: Tabulator.RowComponent) -> Boolean)? = null, + val movableRows: Boolean? = null, + val movableRowsConnectedTables: dynamic = null, + val movableRowsSender: dynamic = null, + val movableRowsReceiver: dynamic = null, + val resizableRows: Boolean? = null, + val scrollToRowPosition: RowPosition? = null, + val scrollToRowIfVisible: Boolean? = null, + val index: String? = null, + @Suppress("ArrayInDataClass") var data: Array? = null, + val ajaxURL: String? = null, + val ajaxParams: dynamic = null, + val ajaxConfig: dynamic = null, + val ajaxContentType: dynamic = null, + val ajaxURLGenerator: ((url: String, config: dynamic, params: dynamic) -> String)? = null, + val ajaxRequestFunc: ((url: String, config: dynamic, params: dynamic) -> Promise)? = null, + val ajaxFiltering: Boolean? = null, + val ajaxSorting: Boolean? = null, + val ajaxProgressiveLoad: ProgressiveMode? = null, + val ajaxProgressiveLoadDelay: Int? = null, + val ajaxProgressiveLoadScrollMargin: Int? = null, + val ajaxLoader: Boolean? = null, + val ajaxLoaderLoading: String? = null, + val ajaxLoaderError: String? = null, + val initialSort: List? = null, + val sortOrderReverse: Boolean? = null, + val initialFilter: List? = null, + val initialHeaderFilter: List? = null, + val pagination: PaginationMode? = null, + val paginationSize: Int? = null, + val paginationSizeSelector: Boolean? = null, + val paginationElement: dynamic = null, + val paginationDataReceived: dynamic = null, + val paginationDataSent: dynamic = null, + val paginationAddRow: AddRowMode? = null, + val paginationButtonCount: Int? = null, + var persistenceID: String? = null, + var persistenceMode: Boolean? = null, + var persistentLayout: Boolean? = null, + var persistentSort: Boolean? = null, + var persistentFilter: Boolean? = null, + val locale: String? = null, + var langs: dynamic = null, + val localized: ((locale: String, lang: dynamic) -> Unit)? = null, + val dataTreeRowExpanded: ((row: Tabulator.RowComponent, level: Number) -> Unit)? = null, + val dataTreeRowCollapsed: ((row: Tabulator.RowComponent, level: Number) -> Unit)? = null, + val movableRowsSendingStart: ((toTables: Array) -> Unit)? = null, + val movableRowsSent: (( + fromRow: Tabulator.RowComponent, + toRow: Tabulator.RowComponent, toTable: Tabulator + ) -> Unit)? = null, + val movableRowsSentFailed: (( + fromRow: Tabulator.RowComponent, + toRow: Tabulator.RowComponent, toTable: Tabulator + ) -> Unit)? = null, + val movableRowsSendingStop: ((toTables: Array) -> Unit)? = null, + val movableRowsReceivingStart: ((fromRow: Tabulator.RowComponent, toTable: Tabulator) -> Unit)? = null, + val movableRowsReceived: (( + fromRow: Tabulator.RowComponent, + toRow: Tabulator.RowComponent, fromTable: Tabulator + ) -> Unit)? = null, + val movableRowsReceivedFailed: (( + fromRow: Tabulator.RowComponent, + toRow: Tabulator.RowComponent, fromTable: Tabulator + ) -> Unit)? = null, + val movableRowsReceivingStop: ((fromTable: Tabulator) -> Unit)? = null, + var rowClick: ((e: dynamic, row: Tabulator.RowComponent) -> Unit)? = null, + var rowDblClick: ((e: dynamic, row: Tabulator.RowComponent) -> Unit)? = null, + var rowContext: ((e: dynamic, row: Tabulator.RowComponent) -> Unit)? = null, + var rowTap: ((e: dynamic, row: Tabulator.RowComponent) -> Unit)? = null, + var rowDblTap: ((e: dynamic, row: Tabulator.RowComponent) -> Unit)? = null, + var rowTapHold: ((e: dynamic, row: Tabulator.RowComponent) -> Unit)? = null, + var rowMouseEnter: ((e: dynamic, row: Tabulator.RowComponent) -> Unit)? = null, + var rowMouseLeave: ((e: dynamic, row: Tabulator.RowComponent) -> Unit)? = null, + var rowMouseOver: ((e: dynamic, row: Tabulator.RowComponent) -> Unit)? = null, + var rowMouseOut: ((e: dynamic, row: Tabulator.RowComponent) -> Unit)? = null, + var rowMouseMove: ((e: dynamic, row: Tabulator.RowComponent) -> Unit)? = null, + var rowAdded: ((row: Tabulator.RowComponent) -> Unit)? = null, + var rowUpdated: ((row: Tabulator.RowComponent) -> Unit)? = null, + var rowDeleted: ((row: Tabulator.RowComponent) -> Unit)? = null, + var rowMoved: ((row: Tabulator.RowComponent) -> Unit)? = null, + var rowResized: ((row: Tabulator.RowComponent) -> Unit)? = null, + var rowSelectionChanged: ((data: Array, rows: Array) -> Unit)? = null, + var rowSelected: ((row: Tabulator.RowComponent) -> Unit)? = null, + var rowDeselected: ((row: Tabulator.RowComponent) -> Unit)? = null, + var cellClick: ((e: Any, cell: Tabulator.CellComponent) -> Unit)? = null, + var cellDblClick: ((e: Any, cell: Tabulator.CellComponent) -> Unit)? = null, + var cellContext: ((e: Any, cell: Tabulator.CellComponent) -> Unit)? = null, + var cellTap: ((e: Any, cell: Tabulator.CellComponent) -> Unit)? = null, + var cellDblTap: ((e: Any, cell: Tabulator.CellComponent) -> Unit)? = null, + var cellTapHold: ((e: Any, cell: Tabulator.CellComponent) -> Unit)? = null, + var cellMouseEnter: ((e: Any, cell: Tabulator.CellComponent) -> Unit)? = null, + var cellMouseLeave: ((e: Any, cell: Tabulator.CellComponent) -> Unit)? = null, + var cellMouseOver: ((e: Any, cell: Tabulator.CellComponent) -> Unit)? = null, + var cellMouseOut: ((e: Any, cell: Tabulator.CellComponent) -> Unit)? = null, + var cellMouseMove: ((e: Any, cell: Tabulator.CellComponent) -> Unit)? = null, + var cellEditing: ((cell: Tabulator.CellComponent) -> Unit)? = null, + var cellEdited: ((cell: Tabulator.CellComponent) -> Unit)? = null, + var cellEditCancelled: ((cell: Tabulator.CellComponent) -> Unit)? = null, + var columnMoved: ((column: Tabulator.ColumnComponent, columns: Array) -> Unit)? = null, + var columnResized: ((column: Tabulator.ColumnComponent) -> Unit)? = null, + var columnVisibilityChanged: ((column: Tabulator.ColumnComponent, visible: Boolean) -> Unit)? = null, + var columnTitleChanged: ((column: Tabulator.ColumnComponent) -> Unit)? = null, + var tableBuilding: (() -> Unit)? = null, + var tableBuilt: (() -> Unit)? = null, + var renderStarted: (() -> Unit)? = null, + var renderComplete: (() -> Unit)? = null, + var htmlImporting: (() -> Unit)? = null, + var htmlImported: (() -> Unit)? = null, + var dataLoading: ((data: Any) -> Unit)? = null, + var dataLoaded: ((data: Any) -> Unit)? = null, + var dataEdited: ((data: Any) -> Unit)? = null, + var pageLoaded: ((pageno: Int) -> Unit)? = null, + var dataSorting: ((sorters: Array) -> Unit)? = null, + var dataSorted: ((sorters: Array, rows: Array) -> Unit)? = null, + var dataFiltering: ((filters: Array) -> Unit)? = null, + var dataFiltered: ((filters: Array, rows: Array) -> Unit)? = null, + var validationFailed: ((cell: Tabulator.CellComponent, value: Any, validators: dynamic) -> Unit)? = null, + var ajaxRequesting: ((url: String, params: dynamic) -> Boolean)? = null, + var ajaxResponse: ((url: String, params: dynamic, response: dynamic) -> Any)? = null, + var ajaxError: ((xhr: dynamic, textStatus: String, errorThrown: dynamic) -> Unit)? = null +) + +/** + * An extension function to convert tabulator options class to JS object. + */ +@Suppress("UNCHECKED_CAST_TO_EXTERNAL_INTERFACE", "ComplexMethod") +fun Options.toJs(i18nTranslator: (String) -> (String)): Tabulator.Options { + return obj { + if (height != null) this.height = height + if (virtualDom != null) this.virtualDom = virtualDom + if (virtualDomBuffer != null) this.virtualDomBuffer = virtualDomBuffer + if (placeholder != null) this.placeholder = i18nTranslator(placeholder) + if (footerElement != null) this.footerElement = i18nTranslator(footerElement) + if (tooltips != null) this.tooltips = tooltips + if (tooltipGenerationMode != null) this.tooltipGenerationMode = tooltipGenerationMode.mode + if (history != null) this.history = history + if (keybindings != null) this.keybindings = keybindings + if (downloadDataFormatter != null) this.downloadDataFormatter = downloadDataFormatter + 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 (autoColumns != null) { + this.autoColumns = autoColumns + } else if (columns == null) { + this.autoColumns = true + } + if (layout != null) this.layout = layout.layout + if (layoutColumnsOnNewData != null) this.layoutColumnsOnNewData = layoutColumnsOnNewData + if (responsiveLayout != null) this.responsiveLayout = responsiveLayout.layout + if (responsiveLayoutCollapseStartOpen != null) this.responsiveLayoutCollapseStartOpen = + responsiveLayoutCollapseStartOpen + if (responsiveLayoutCollapseUseFormatters != null) this.responsiveLayoutCollapseUseFormatters = + responsiveLayoutCollapseUseFormatters + if (columnMinWidth != null) this.columnMinWidth = columnMinWidth + if (resizableColumns != null) this.resizableColumns = resizableColumns + if (movableColumns != null) this.movableColumns = movableColumns + if (tooltipsHeader != null) this.tooltipsHeader = tooltipsHeader + if (headerFilterPlaceholder != null) this.headerFilterPlaceholder = i18nTranslator(headerFilterPlaceholder) + if (scrollToColumnPosition != null) this.scrollToColumnPosition = scrollToColumnPosition.position + if (scrollToColumnIfVisible != null) this.scrollToColumnIfVisible = scrollToColumnIfVisible + if (rowFormatter != null) this.rowFormatter = rowFormatter + if (addRowPos != null) this.addRowPos = addRowPos.position + if (selectable != null) this.selectable = selectable + if (selectableRangeMode != null) this.selectableRangeMode = selectableRangeMode.mode + if (selectableRollingSelection != null) this.selectableRollingSelection = selectableRollingSelection + if (selectablePersistence != null) this.selectablePersistence = selectablePersistence + if (selectableCheck != null) this.selectableCheck = selectableCheck + if (movableRows != null) this.movableRows = movableRows + if (movableRowsConnectedTables != null) this.movableRowsConnectedTables = movableRowsConnectedTables + if (movableRowsSender != null) this.movableRowsSender = movableRowsSender + if (movableRowsReceiver != null) this.movableRowsReceiver = movableRowsReceiver + if (resizableRows != null) this.resizableRows = resizableRows + if (scrollToRowPosition != null) this.scrollToRowPosition = scrollToRowPosition.position + if (scrollToRowIfVisible != null) this.scrollToRowIfVisible = scrollToRowIfVisible + if (index != null) this.index = index + if (data != null) this.data = data + if (ajaxURL != null) this.ajaxURL = ajaxURL + if (ajaxParams != null) this.ajaxParams = ajaxParams + if (ajaxConfig != null) this.ajaxConfig = ajaxConfig + if (ajaxContentType != null) this.ajaxContentType = ajaxContentType + if (ajaxURLGenerator != null) this.ajaxURLGenerator = ajaxURLGenerator + if (ajaxRequestFunc != null) this.ajaxRequestFunc = ajaxRequestFunc + if (ajaxFiltering != null) this.ajaxFiltering = ajaxFiltering + if (ajaxSorting != null) this.ajaxSorting = ajaxSorting + if (ajaxProgressiveLoad != null) this.ajaxProgressiveLoad = ajaxProgressiveLoad.mode + if (ajaxProgressiveLoadDelay != null) this.ajaxProgressiveLoadDelay = ajaxProgressiveLoadDelay + if (ajaxProgressiveLoadScrollMargin != null) this.ajaxProgressiveLoadScrollMargin = + ajaxProgressiveLoadScrollMargin + if (ajaxLoader != null) this.ajaxLoader = ajaxLoader + if (ajaxLoaderLoading != null) this.ajaxLoaderLoading = i18nTranslator(ajaxLoaderLoading) + if (ajaxLoaderError != null) this.ajaxLoaderError = i18nTranslator(ajaxLoaderError) + if (initialSort != null) this.initialSort = initialSort + if (sortOrderReverse != null) this.sortOrderReverse = sortOrderReverse + if (initialFilter != null) this.initialFilter = initialFilter + if (initialHeaderFilter != null) this.initialHeaderFilter = initialHeaderFilter + if (pagination != null) this.pagination = pagination.mode + if (paginationSize != null) this.paginationSize = paginationSize + if (paginationSizeSelector != null) this.paginationSizeSelector = paginationSizeSelector + if (paginationElement != null) this.paginationElement = paginationElement + if (paginationDataReceived != null) this.paginationDataReceived = paginationDataReceived + if (paginationDataSent != null) this.paginationDataSent = paginationDataSent + if (paginationAddRow != null) this.paginationAddRow = paginationAddRow.mode + if (paginationButtonCount != null) this.paginationButtonCount = paginationButtonCount + if (persistenceID != null) this.persistenceID = persistenceID + if (persistenceMode != null) this.persistenceMode = persistenceMode + if (persistentLayout != null) this.persistentLayout = persistentLayout + if (persistentSort != null) this.persistentSort = persistentSort + if (persistentFilter != null) this.persistentFilter = persistentFilter + if (locale != null) this.locale = locale + if (langs != null) this.langs = langs + if (localized != null) this.localized = localized + if (dataTreeRowExpanded != null) this.dataTreeRowExpanded = dataTreeRowExpanded + if (dataTreeRowCollapsed != null) this.dataTreeRowCollapsed = dataTreeRowCollapsed + if (movableRowsSendingStart != null) this.movableRowsSendingStart = movableRowsSendingStart + if (movableRowsSent != null) this.movableRowsSent = movableRowsSent + if (movableRowsSentFailed != null) this.movableRowsSentFailed = movableRowsSentFailed + if (movableRowsSendingStop != null) this.movableRowsSendingStop = movableRowsSendingStop + if (movableRowsReceivingStart != null) this.movableRowsReceivingStart = movableRowsReceivingStart + if (movableRowsReceived != null) this.movableRowsReceived = movableRowsReceived + if (movableRowsReceivedFailed != null) this.movableRowsReceivedFailed = movableRowsReceivedFailed + if (movableRowsReceivingStop != null) this.movableRowsReceivingStop = movableRowsReceivingStop + if (rowClick != null) this.rowClick = rowClick + if (rowDblClick != null) this.rowDblClick = rowDblClick + if (rowContext != null) this.rowContext = rowContext + if (rowTap != null) this.rowTap = rowTap + if (rowDblTap != null) this.rowDblTap = rowDblTap + if (rowTapHold != null) this.rowTapHold = rowTapHold + if (rowMouseEnter != null) this.rowMouseEnter = rowMouseEnter + if (rowMouseLeave != null) this.rowMouseLeave = rowMouseLeave + if (rowMouseOver != null) this.rowMouseOver = rowMouseOver + if (rowMouseOut != null) this.rowMouseOut = rowMouseOut + if (rowMouseMove != null) this.rowMouseMove = rowMouseMove + if (rowAdded != null) this.rowAdded = rowAdded + if (rowUpdated != null) this.rowUpdated = rowUpdated + if (rowDeleted != null) this.rowDeleted = rowDeleted + if (rowMoved != null) this.rowMoved = rowMoved + if (rowResized != null) this.rowResized = rowResized + if (rowSelectionChanged != null) this.rowSelectionChanged = rowSelectionChanged + if (rowSelected != null) this.rowSelected = rowSelected + if (rowDeselected != null) this.rowDeselected = rowDeselected + if (cellClick != null) this.cellClick = cellClick + if (cellDblClick != null) this.cellDblClick = cellDblClick + if (cellContext != null) this.cellContext = cellContext + if (cellTap != null) this.cellTap = cellTap + if (cellDblTap != null) this.cellDblTap = cellDblTap + if (cellTapHold != null) this.cellTapHold = cellTapHold + if (cellMouseEnter != null) this.cellMouseEnter = cellMouseEnter + if (cellMouseLeave != null) this.cellMouseLeave = cellMouseLeave + if (cellMouseOver != null) this.cellMouseOver = cellMouseOver + if (cellMouseOut != null) this.cellMouseOut = cellMouseOut + 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 (columnMoved != null) this.columnMoved = columnMoved + if (columnResized != null) this.columnResized = columnResized + if (columnVisibilityChanged != null) this.columnVisibilityChanged = columnVisibilityChanged + if (columnTitleChanged != null) this.columnTitleChanged = columnTitleChanged + if (tableBuilding != null) this.tableBuilding = tableBuilding + if (tableBuilt != null) this.tableBuilt = tableBuilt + if (renderStarted != null) this.renderStarted = renderStarted + if (renderComplete != null) this.renderComplete = renderComplete + if (htmlImporting != null) this.htmlImporting = htmlImporting + if (htmlImported != null) this.htmlImported = htmlImported + if (dataLoading != null) this.dataLoading = dataLoading + if (dataLoaded != null) this.dataLoaded = dataLoaded + if (dataEdited != null) this.dataEdited = dataEdited + if (pageLoaded != null) this.pageLoaded = pageLoaded + if (dataSorting != null) this.dataSorting = dataSorting + if (dataSorted != null) this.dataSorted = dataSorted + if (dataFiltering != null) this.dataFiltering = dataFiltering + if (dataFiltered != null) this.dataFiltered = dataFiltered + if (validationFailed != null) this.validationFailed = validationFailed + if (ajaxRequesting != null) this.ajaxRequesting = ajaxRequesting + if (ajaxResponse != null) this.ajaxResponse = ajaxResponse + if (ajaxError != null) this.ajaxError = ajaxError + } as Tabulator.Options +} 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 new file mode 100644 index 00000000..1b8becff --- /dev/null +++ b/kvision-modules/kvision-tabulator/src/main/kotlin/pl/treksoft/kvision/tabulator/Tabulator.kt @@ -0,0 +1,679 @@ +/* + * 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.tabulator + +import com.github.snabbdom.VNode +import com.lightningkite.kotlin.observable.list.ObservableList +import kotlinx.serialization.ImplicitReflectionSerializer +import kotlinx.serialization.KSerializer +import kotlinx.serialization.list +import kotlinx.serialization.serializer +import org.w3c.dom.HTMLElement +import pl.treksoft.kvision.KVManagerTabulator +import pl.treksoft.kvision.core.Container +import pl.treksoft.kvision.core.StringBoolPair +import pl.treksoft.kvision.core.Widget +import pl.treksoft.kvision.i18n.I18n +import pl.treksoft.kvision.redux.ReduxStore +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 redux.RAction +import pl.treksoft.kvision.tabulator.js.Tabulator as JsTabulator + +/** + * Tabulator component. + * + * @constructor + * @param T serializable type + * @param data a list of serializable objects + * @param options tabulator options + * @param classes a set of CSS class names + * @param dataSerializer a serializer for class T + */ +@Suppress("LargeClass", "TooManyFunctions") +open class Tabulator( + protected val data: List? = null, + val options: Options = Options(), + types: Set = setOf(), + classes: Set = setOf(), + protected val dataSerializer: KSerializer? = null +) : + Widget(classes) { + + /** + * Table types. + */ + var types by refreshOnUpdate(types) + + protected var jsTabulator: JsTabulator? = null + + private var pageSize: Number? = null + private var currentPage: Number? = null + + protected var filter: ((T) -> Boolean)? = null + + init { + this.vnkey = "kv_tabulator_$counter" + if (data != null && dataSerializer != null) { + @Suppress("UnsafeCastFromDynamic") + options.data = dataToNative(data, dataSerializer) + if (data is ObservableList) { + data.onUpdate += { + replaceData(data) + } + } + } + if (options.langs == null) { + options.langs = obj { + default = obj { + groups = obj { + item = "" + items = "" + } + ajax = obj { + loading = "..." + error = "!!!" + } + pagination = obj { + page_size = "↕" + first = "<<" + first_title = "<<" + last = ">>" + last_title = ">>" + prev = "<" + prev_title = "<" + next = ">" + next_title = ">" + } + headerFilters = obj { + default = "..." + } + } + } + } + if (options.rowClick == null) { + options.rowClick = { _, row -> + @Suppress("UnsafeCastFromDynamic") + this.dispatchEvent("tabulatorRowClick", obj { detail = row }) + } + } + if (options.rowDblClick == null) { + options.rowDblClick = { _, row -> + @Suppress("UnsafeCastFromDynamic") + this.dispatchEvent("tabulatorRowDblClick", obj { detail = row }) + } + } + if (options.rowSelectionChanged == null) { + options.rowSelectionChanged = { _, rows -> + @Suppress("UnsafeCastFromDynamic") + this.dispatchEvent("tabulatorRowSelectionChanged", obj { detail = rows }) + } + } + if (options.rowSelected == null) { + options.rowSelected = { row -> + @Suppress("UnsafeCastFromDynamic") + this.dispatchEvent("tabulatorRowSelected", obj { detail = row }) + } + } + if (options.rowDeselected == null) { + options.rowDeselected = { row -> + @Suppress("UnsafeCastFromDynamic") + this.dispatchEvent("tabulatorRowDeselected", obj { detail = row }) + } + } + if (options.cellClick == null) { + options.cellClick = { _, cell -> + @Suppress("UnsafeCastFromDynamic") + this.dispatchEvent("tabulatorCellClick", obj { detail = cell }) + } + } + if (options.cellDblClick == null) { + options.cellDblClick = { _, cell -> + @Suppress("UnsafeCastFromDynamic") + this.dispatchEvent("tabulatorCellDblClick", obj { detail = cell }) + } + } + if (options.cellEditing == null) { + options.cellEditing = { cell -> + @Suppress("UnsafeCastFromDynamic") + this.dispatchEvent("tabulatorCellEditing", obj { detail = cell }) + } + } + if (options.cellEdited == null) { + options.cellEdited = { cell -> + @Suppress("UnsafeCastFromDynamic") + this.dispatchEvent("tabulatorCellEdited", obj { detail = cell }) + } + } + if (options.cellEditCancelled == null) { + options.cellEditCancelled = { cell -> + @Suppress("UnsafeCastFromDynamic") + this.dispatchEvent("tabulatorCellEditCancelled", obj { detail = cell }) + } + } + if (options.dataLoading == null && dataSerializer != null) { + options.dataLoading = { data -> + val d = nativeToData(data, dataSerializer) + @Suppress("UnsafeCastFromDynamic") + this.dispatchEvent("tabulatorDataLoading", obj { detail = d }) + } + } + if (options.dataLoaded == null && dataSerializer != null) { + options.dataLoaded = { data -> + val d = nativeToData(data, dataSerializer) + @Suppress("UnsafeCastFromDynamic") + this.dispatchEvent("tabulatorDataLoaded", obj { detail = d }) + } + } + if (options.dataEdited == null && dataSerializer != null) { + options.dataEdited = { data -> + val d = nativeToData(data, dataSerializer) + @Suppress("UnsafeCastFromDynamic") + this.dispatchEvent("tabulatorDataEdited", obj { detail = d }) + } + } + counter++ + } + + override fun getSnClass(): List { + val cl = super.getSnClass().toMutableList() + types.forEach { + cl.add(it.type to true) + } + return cl + } + + /** + * Converts a list of objects of type T to native JS array + */ + protected fun dataToNative(data: List, dataSerializer: KSerializer): dynamic { + val json = JSON.plain.stringify(dataSerializer.list, data) + return kotlin.js.JSON.parse(json) + } + + /** + * Converts a native JS array to the list of objects of type T + */ + protected fun nativeToData(data: dynamic, dataSerializer: KSerializer): List { + @Suppress("UnsafeCastFromDynamic") + val str = kotlin.js.JSON.stringify(data) + return JSON.plain.parse(dataSerializer.list, str) + } + + /** + * Creates internal JS Tabulator object + */ + protected fun createJsTabulator() { + (this.getElement() as? HTMLElement)?.let { + jsTabulator = + KVManagerTabulator.getConstructor() + .createInstance(it, options.toJs(this::translate)) + if (currentPage != null) { + jsTabulator?.setPageSize(pageSize ?: 0) + jsTabulator?.setPage(currentPage) + } + } + } + + override fun render(): VNode { + if (lastLanguage != null && lastLanguage != I18n.language) { + jsTabulator?.destroy() + createJsTabulator() + } + return render("div") + } + + override fun afterInsert(node: VNode) { + createJsTabulator() + } + + override fun afterDestroy() { + val page = jsTabulator?.getPage() + if (page != false) { + pageSize = jsTabulator?.getPageSize() + currentPage = page as Number + } + jsTabulator?.destroy() + jsTabulator = null + } + + /** + * Silently replaces the data in a table. + * @param data new data + */ + open fun replaceData(data: List) { + if (dataSerializer != null) { + val native = dataToNative(data, dataSerializer) + @Suppress("UnsafeCastFromDynamic") + options.data = native + jsTabulator?.replaceData(native, null, null) + } + } + + /** + * Sets new data in a table. + * @param data new data + */ + open fun setData(data: List) { + if (dataSerializer != null) { + val native = dataToNative(data, dataSerializer) + @Suppress("UnsafeCastFromDynamic") + options.data = native + jsTabulator?.setData(native, null, null) + } + } + + /** + * Returns the current data in the table. + * @param active return only visible data + * @return current data + */ + open fun getData(active: Boolean): List? { + return if (jsTabulator != null && dataSerializer != null) { + val native = jsTabulator?.getData(active) + nativeToData(native, dataSerializer) + } else { + data + } + } + + /** + * Returns the selected data in the table. + * @return selected data + */ + open fun getSelectedData(): List { + return if (jsTabulator != null && dataSerializer != null) { + val native = jsTabulator?.getSelectedData() + nativeToData(native, dataSerializer) + } else { + listOf() + } + } + + /** + * Clears the data in the table. + */ + open fun clearData() = jsTabulator?.clearData() + + /** + * Undo the last user action. + */ + open fun undo(): Boolean = jsTabulator?.undo() ?: false + + /** + * Redo the last undone user action. + */ + open fun redo(): Boolean = jsTabulator?.redo() ?: false + + /** + * Get the number of history undo actions available. + */ + @Suppress("UnsafeCastFromDynamic") + open fun getHistoryUndoSize(): Int = jsTabulator?.getHistoryUndoSize() ?: 0 + + /** + * Get the number of history redo actions available. + */ + @Suppress("UnsafeCastFromDynamic") + open fun getHistoryRedoSize(): Int = jsTabulator?.getHistoryRedoSize() ?: 0 + + /** + * Get the number of data rows. + * @param activeOnly return only the number of visible rows + * @return the number of data rows + */ + open fun getDataCount(activeOnly: Boolean = false): Int = jsTabulator?.getDataCount(activeOnly)?.toInt() ?: 0 + + /** + * Get the HTML code of the table. + * @param activeOnly include only visible rows + * @return the HTML code of the table + */ + open fun getHtml(activeOnly: Boolean = false): String? = jsTabulator?.getHtml(activeOnly) + + /** + * Scroll to the row given by id. + * @param row id of the row + * @param position the scrolling position + * @param ifVisible scroll to already visible row + */ + open fun scrollToRow( + row: Int, + position: RowPosition? = null, + ifVisible: Boolean? = null + ) { + jsTabulator?.scrollToRow(row, position, ifVisible) + } + + /** + * Redraw the table (e.g. after a resize). + * @param force rerender all rows and columns + */ + open fun redraw(force: Boolean = false) { + jsTabulator?.redraw(force) + } + + /** + * Change the height of the table. + * @param height new heigth of the table + */ + open fun setHeight(height: Int) { + jsTabulator?.setHeight(height) + } + + /** + * Clears current sort. + */ + open fun clearSort() { + jsTabulator?.clearSort() + } + + /** + * Sets the external filter for the data. + * @param filter a filtering function + */ + open fun setFilter(filter: (T) -> Boolean) { + this.filter = filter + applyFilter() + } + + /** + * Applies the current filter. + */ + open fun applyFilter() { + if (dataSerializer != null && filter != null) { + jsTabulator?.setFilter({ data: dynamic, _: dynamic -> + val str = kotlin.js.JSON.stringify(data) + filter?.let { it(JSON.plain.parse(dataSerializer, str)) } + }, null, null) + } + } + + /** + * Clears current filters. + * @param includeHeaderFilters clear also the header filters + */ + open fun clearFilter(includeHeaderFilters: Boolean = true) { + jsTabulator?.clearFilter(includeHeaderFilters) + } + + /** + * Clears header filters. + */ + open fun clearHeaderFilter() { + jsTabulator?.clearHeaderFilter() + } + + /** + * Select the row given by id. + * @param row row id + */ + open fun selectRow(row: Int) { + jsTabulator?.selectRow(row) + } + + /** + * Deselect the row given by id. + * @param row row id + */ + open fun deselectRow(row: Int) { + jsTabulator?.deselectRow(row) + } + + /** + * Toggle selection status of the row given by id. + * @param row row id + */ + open fun toggleSelectRow(row: Int) { + jsTabulator?.toggleSelectRow(row) + } + + /** + * Returns the selected rows. + */ + open fun getSelectedRows(): List { + return jsTabulator?.getSelectedRows()?.asList() ?: listOf() + } + + /** + * Shows given page. + * @param page page number + */ + open fun setPage(page: Int) { + jsTabulator?.setPage(page) + } + + /** + * Shows page with a row given by id. + * @param row row id + */ + open fun setPageToRow(row: Int) { + jsTabulator?.setPageToRow(row) + } + + /** + * Set the size of a page. + * @param size page size + */ + open fun setPageSize(size: Int) { + jsTabulator?.setPageSize(size) + } + + /** + * Returns the size of a page. + */ + open fun getPageSize(): Int = jsTabulator?.getPageSize()?.toInt() ?: 0 + + /** + * Navigate to the previous page. + */ + open fun previousPage() { + jsTabulator?.previousPage() + } + + /** + * Navigate to the next page. + */ + open fun nextPage() { + jsTabulator?.nextPage() + } + + /** + * Returns current page number. + */ + open fun getPage(): Int = (jsTabulator?.getPage() as? Number)?.toInt() ?: -1 + + /** + * Returns number of pages. + */ + open fun getPageMax(): Int = (jsTabulator?.getPageMax() as? Number)?.toInt() ?: -1 + + /** + * Navigate to the previous cell. + */ + open fun navigatePrev() { + jsTabulator?.navigatePrev() + } + + /** + * Navigate to the next cell. + */ + open fun navigateNext() { + jsTabulator?.navigateNext() + } + + /** + * Navigate to the cell on the left. + */ + open fun navigateLeft() { + jsTabulator?.navigateLeft() + } + + /** + * Navigate to the cell on the right. + */ + open fun navigateRight() { + jsTabulator?.navigateRight() + } + + /** + * Navigate to the same cell in the row above. + */ + open fun navigateUp() { + jsTabulator?.navigateUp() + } + + /** + * Navigate to the same cell in the row below. + */ + open fun navigateDown() { + jsTabulator?.navigateDown() + } + + companion object { + internal var counter = 0 + + /** + * DSL builder extension function. + * + * It takes the same parameters as the constructor of the built component. + */ + inline fun Container.tabulator( + data: List? = null, + options: Options = Options(), + types: Set = setOf(), + classes: Set = setOf(), + noinline init: (Tabulator.() -> Unit)? = null + ): Tabulator { + val tabulator = create(data, options, types, classes) + init?.invoke(tabulator) + this.add(tabulator) + return tabulator + } + + /** + * DSL builder extension function for general redux store. + */ + inline fun Container.tabulator( + store: ReduxStore, + noinline dataFactory: (S) -> List, + options: Options = Options(), + types: Set = setOf(), + classes: Set = setOf(), + noinline init: (Tabulator.() -> Unit)? = null + ): Tabulator { + val tabulator = create(store, dataFactory, options, types, classes) + init?.invoke(tabulator) + this.add(tabulator) + return tabulator + } + + /** + * DSL builder extension function for dedicated redux store (backed with a list). + */ + inline fun Container.tabulator( + store: ReduxStore, A>, + options: Options = Options(), + types: Set = setOf(), + classes: Set = setOf(), + noinline init: (Tabulator.() -> Unit)? = null + ): Tabulator { + val tabulator = create(store, options, types, classes) + init?.invoke(tabulator) + this.add(tabulator) + return tabulator + } + + /** + * DSL builder extension function for dynamic data (send within options parameter). + */ + fun Container.tabulator( + options: Options = Options(), + types: Set = setOf(), + classes: Set = setOf(), + init: (Tabulator.() -> Unit)? = null + ): Tabulator { + val tabulator = Tabulator(options = options, types = types, classes = classes) + init?.invoke(tabulator) + this.add(tabulator) + return tabulator + } + + /** + * A helper function to create a Tabulator object with correct serializer. + */ + @UseExperimental(ImplicitReflectionSerializer::class) + inline fun create( + data: List? = null, options: Options = Options(), + types: Set = setOf(), + classes: Set = setOf(), + noinline init: (Tabulator.() -> Unit)? = null + ): Tabulator { + val tabulator = Tabulator(data, options, types, classes, T::class.serializer()) + init?.invoke(tabulator) + return tabulator + } + + /** + * A helper function to create a Tabulator object with correct serializer and general redux store. + */ + @UseExperimental(ImplicitReflectionSerializer::class) + inline fun create( + store: ReduxStore, + noinline dataFactory: (S) -> List, + options: Options = Options(), + types: Set = setOf(), + classes: Set = setOf(), + noinline init: (Tabulator.() -> Unit)? = null + ): Tabulator { + val data = dataFactory(store.getState()) + val tabulator = Tabulator(data, options, types, classes, T::class.serializer()) + init?.invoke(tabulator) + store.subscribe { s -> + tabulator.replaceData(dataFactory(s)) + } + return tabulator + } + + /** + * A helper function to create a Tabulator object with correct serializer and dedicated redux store. + */ + @UseExperimental(ImplicitReflectionSerializer::class) + inline fun create( + store: ReduxStore, A>, + options: Options = Options(), + types: Set = setOf(), + classes: Set = setOf(), + noinline init: (Tabulator.() -> Unit)? = null + ): Tabulator { + val data = store.getState() + val tabulator = Tabulator(data, options, types, classes, T::class.serializer()) + init?.invoke(tabulator) + store.subscribe { s -> + tabulator.replaceData(s) + } + return tabulator + } + } +} diff --git a/kvision-modules/kvision-tabulator/src/main/kotlin/pl/treksoft/kvision/tabulator/js/Tabulator.kt b/kvision-modules/kvision-tabulator/src/main/kotlin/pl/treksoft/kvision/tabulator/js/Tabulator.kt new file mode 100644 index 00000000..11e37266 --- /dev/null +++ b/kvision-modules/kvision-tabulator/src/main/kotlin/pl/treksoft/kvision/tabulator/js/Tabulator.kt @@ -0,0 +1,855 @@ +@file:Suppress( + "INTERFACE_WITH_SUPERCLASS", + "OVERRIDING_FINAL_MEMBER", + "RETURN_TYPE_MISMATCH_ON_OVERRIDE", + "CONFLICTING_OVERLOADS", + "EXTERNAL_DELEGATION", + "NESTED_CLASS_IN_EXTERNAL_INTERFACE", + "unused", "PropertyName", "TooManyFunctions", "VariableNaming", "MaxLineLength" +) + +package pl.treksoft.kvision.tabulator.js + +import org.w3c.dom.HTMLElement +import kotlin.js.Promise + +@Suppress("UNREACHABLE_CODE", "LargeClass") +open external class Tabulator { + constructor(selector: String, options: Options? = definedExternally /* null */) + constructor(selector: HTMLElement, options: Options? = definedExternally /* null */) + + open var columnManager: Any = definedExternally + open var rowManager: Any = definedExternally + open var footerManager: Any = definedExternally + open var browser: String = definedExternally + open var browserSlow: Boolean = definedExternally + open var modules: Any = definedExternally + open var options: Options = definedExternally + open fun download( + downloadType: dynamic /* String /* "json" */ | String /* "csv" */ | String /* "xlsx" */ | String /* "pdf" */ | (columns: Array, data: Any, options: Any, setFileContents: Any) -> Any */, + fileName: String, + params: DownloadOptions? /*= null*/ + ): Unit = + definedExternally + + open fun downloadToTab( + downloadType: dynamic /* String /* "json" */ | String /* "csv" */ | String /* "xlsx" */ | String /* "pdf" */ */, + fileName: String, + params: DownloadOptions? /*= null*/ + ): Unit = + definedExternally + + open fun copyToClipboard(type: dynamic /* String /* "table" */ | String /* "selection" */ */): Unit = + definedExternally + + open fun undo(): Boolean = definedExternally + open fun getHistoryUndoSize(): dynamic /* Number | Boolean */ = definedExternally + open fun redo(): Boolean = definedExternally + open fun getHistoryRedoSize(): dynamic /* Number | Boolean */ = definedExternally + open fun destroy(): Unit = definedExternally + open fun setDataFromLocalFile(extensions: String): Unit = definedExternally + open fun setData(data: dynamic, params: Any? /*= null*/, config: Any? /*= null*/): Promise = definedExternally + open fun clearData(): Unit = definedExternally + open fun getData(activeOnly: Boolean? /*= null*/): Array = definedExternally + open fun getDataCount(activeOnly: Boolean? /*= null*/): Number = definedExternally + open fun searchRows( + field: String, + type: dynamic /* String /* "=" */ | String /* "!=" */ | String /* "like" */ | String /* "<" */ | String /* ">" */ | String /* "<=" */ | String /* ">=" */ | String /* "in" */ | String /* "regex" */ */, + value: Any + ): Array = + definedExternally + + open fun searchData( + field: String, + type: dynamic /* String /* "=" */ | String /* "!=" */ | String /* "like" */ | String /* "<" */ | String /* ">" */ | String /* "<=" */ | String /* ">=" */ | String /* "in" */ | String /* "regex" */ */, + val