diff --git a/core/generated-sources/src/main/kotlin/org/jetbrains/kotlinx/dataframe/io/html.kt b/core/generated-sources/src/main/kotlin/org/jetbrains/kotlinx/dataframe/io/html.kt index bedaee0da8..260eae32d3 100644 --- a/core/generated-sources/src/main/kotlin/org/jetbrains/kotlinx/dataframe/io/html.kt +++ b/core/generated-sources/src/main/kotlin/org/jetbrains/kotlinx/dataframe/io/html.kt @@ -18,6 +18,7 @@ import org.jetbrains.kotlinx.dataframe.impl.renderType import org.jetbrains.kotlinx.dataframe.impl.scale import org.jetbrains.kotlinx.dataframe.impl.truncate import org.jetbrains.kotlinx.dataframe.jupyter.CellRenderer +import org.jetbrains.kotlinx.dataframe.jupyter.DefaultCellRenderer import org.jetbrains.kotlinx.dataframe.jupyter.RenderedContent import org.jetbrains.kotlinx.dataframe.name import org.jetbrains.kotlinx.dataframe.nrow @@ -174,6 +175,70 @@ internal fun AnyFrame.toHtmlData( return DataFrameHtmlData("", body, script) } +internal fun AnyFrame.toStaticHtml( + configuration: DisplayConfiguration = DisplayConfiguration.DEFAULT, + cellRenderer: CellRenderer, +): DataFrameHtmlData { + val df = this + val id = "static_df_${nextTableId()}" + val columnsToRender = columns() + + fun StringBuilder.emitTag(tag: String, attributes: String = "", tagContents: StringBuilder.() -> Unit) { + append("<") + append(tag) + if (attributes.isNotEmpty()) { + append(" ") + append(attributes) + } + append(">") + + tagContents() + + append("") + } + + fun StringBuilder.emitHeader() = emitTag("thead") { + emitTag("tr") { + columnsToRender.forEach { col -> + emitTag("th") { + append(col.name()) + } + } + } + } + + fun StringBuilder.emitCell(cellValue: Any?) = emitTag("td") { + append(cellRenderer.content(cellValue, configuration).truncatedContent) + } + + fun StringBuilder.emitRow(row: AnyRow) = emitTag("tr") { + columnsToRender.forEach { col -> + emitCell(row[col.path()]) + } + } + + fun StringBuilder.emitBody() = emitTag("tbody") { + val rowsCountToRender = minOf(rowsCount(), configuration.rowsLimit ?: Int.MAX_VALUE) + for (rowIndex in 0.. DataFrame.html(): String = toStandaloneHTML().toString() */ public fun DataFrame.toStandaloneHTML( configuration: DisplayConfiguration = DisplayConfiguration.DEFAULT, - cellRenderer: CellRenderer = org.jetbrains.kotlinx.dataframe.jupyter.DefaultCellRenderer, + cellRenderer: CellRenderer = DefaultCellRenderer, getFooter: (DataFrame) -> String? = { "DataFrame [${it.size}]" }, ): DataFrameHtmlData = toHTML(configuration, cellRenderer, getFooter).withTableDefinitions() @@ -197,7 +262,7 @@ public fun DataFrame.toStandaloneHTML( */ public fun DataFrame.toHTML( configuration: DisplayConfiguration = DisplayConfiguration.DEFAULT, - cellRenderer: CellRenderer = org.jetbrains.kotlinx.dataframe.jupyter.DefaultCellRenderer, + cellRenderer: CellRenderer = DefaultCellRenderer, getFooter: (DataFrame) -> String? = { "DataFrame [${it.size}]" }, ): DataFrameHtmlData { val limit = configuration.rowsLimit ?: Int.MAX_VALUE @@ -218,6 +283,8 @@ public fun DataFrame.toHTML( var tableHtml = toHtmlData(configuration, cellRenderer) + tableHtml += toStaticHtml(configuration, DefaultCellRenderer) + if (bodyFooter != null) { tableHtml += DataFrameHtmlData("", bodyFooter, "") } diff --git a/core/generated-sources/src/test/kotlin/org/jetbrains/kotlinx/dataframe/rendering/RenderingTests.kt b/core/generated-sources/src/test/kotlin/org/jetbrains/kotlinx/dataframe/rendering/RenderingTests.kt index ed6f5c9f1d..17fb3759f0 100644 --- a/core/generated-sources/src/test/kotlin/org/jetbrains/kotlinx/dataframe/rendering/RenderingTests.kt +++ b/core/generated-sources/src/test/kotlin/org/jetbrains/kotlinx/dataframe/rendering/RenderingTests.kt @@ -125,4 +125,24 @@ class RenderingTests { df.toHTML().script shouldContain rendered } } + + @Test + fun `static rendering should be present`() { + val df = dataFrameOf("a", "b")(listOf(1, 1), listOf(2, 4)) + val actualHtml = df.toHTML() + + actualHtml.body shouldContain """ + + + ab + + + + + [1, 1][2, 4] + + + + """.trimIndent().replace("\n", "") + } } diff --git a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/io/html.kt b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/io/html.kt index bedaee0da8..260eae32d3 100644 --- a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/io/html.kt +++ b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/io/html.kt @@ -18,6 +18,7 @@ import org.jetbrains.kotlinx.dataframe.impl.renderType import org.jetbrains.kotlinx.dataframe.impl.scale import org.jetbrains.kotlinx.dataframe.impl.truncate import org.jetbrains.kotlinx.dataframe.jupyter.CellRenderer +import org.jetbrains.kotlinx.dataframe.jupyter.DefaultCellRenderer import org.jetbrains.kotlinx.dataframe.jupyter.RenderedContent import org.jetbrains.kotlinx.dataframe.name import org.jetbrains.kotlinx.dataframe.nrow @@ -174,6 +175,70 @@ internal fun AnyFrame.toHtmlData( return DataFrameHtmlData("", body, script) } +internal fun AnyFrame.toStaticHtml( + configuration: DisplayConfiguration = DisplayConfiguration.DEFAULT, + cellRenderer: CellRenderer, +): DataFrameHtmlData { + val df = this + val id = "static_df_${nextTableId()}" + val columnsToRender = columns() + + fun StringBuilder.emitTag(tag: String, attributes: String = "", tagContents: StringBuilder.() -> Unit) { + append("<") + append(tag) + if (attributes.isNotEmpty()) { + append(" ") + append(attributes) + } + append(">") + + tagContents() + + append("") + } + + fun StringBuilder.emitHeader() = emitTag("thead") { + emitTag("tr") { + columnsToRender.forEach { col -> + emitTag("th") { + append(col.name()) + } + } + } + } + + fun StringBuilder.emitCell(cellValue: Any?) = emitTag("td") { + append(cellRenderer.content(cellValue, configuration).truncatedContent) + } + + fun StringBuilder.emitRow(row: AnyRow) = emitTag("tr") { + columnsToRender.forEach { col -> + emitCell(row[col.path()]) + } + } + + fun StringBuilder.emitBody() = emitTag("tbody") { + val rowsCountToRender = minOf(rowsCount(), configuration.rowsLimit ?: Int.MAX_VALUE) + for (rowIndex in 0.. DataFrame.html(): String = toStandaloneHTML().toString() */ public fun DataFrame.toStandaloneHTML( configuration: DisplayConfiguration = DisplayConfiguration.DEFAULT, - cellRenderer: CellRenderer = org.jetbrains.kotlinx.dataframe.jupyter.DefaultCellRenderer, + cellRenderer: CellRenderer = DefaultCellRenderer, getFooter: (DataFrame) -> String? = { "DataFrame [${it.size}]" }, ): DataFrameHtmlData = toHTML(configuration, cellRenderer, getFooter).withTableDefinitions() @@ -197,7 +262,7 @@ public fun DataFrame.toStandaloneHTML( */ public fun DataFrame.toHTML( configuration: DisplayConfiguration = DisplayConfiguration.DEFAULT, - cellRenderer: CellRenderer = org.jetbrains.kotlinx.dataframe.jupyter.DefaultCellRenderer, + cellRenderer: CellRenderer = DefaultCellRenderer, getFooter: (DataFrame) -> String? = { "DataFrame [${it.size}]" }, ): DataFrameHtmlData { val limit = configuration.rowsLimit ?: Int.MAX_VALUE @@ -218,6 +283,8 @@ public fun DataFrame.toHTML( var tableHtml = toHtmlData(configuration, cellRenderer) + tableHtml += toStaticHtml(configuration, DefaultCellRenderer) + if (bodyFooter != null) { tableHtml += DataFrameHtmlData("", bodyFooter, "") } diff --git a/core/src/test/kotlin/org/jetbrains/kotlinx/dataframe/rendering/RenderingTests.kt b/core/src/test/kotlin/org/jetbrains/kotlinx/dataframe/rendering/RenderingTests.kt index ed6f5c9f1d..17fb3759f0 100644 --- a/core/src/test/kotlin/org/jetbrains/kotlinx/dataframe/rendering/RenderingTests.kt +++ b/core/src/test/kotlin/org/jetbrains/kotlinx/dataframe/rendering/RenderingTests.kt @@ -125,4 +125,24 @@ class RenderingTests { df.toHTML().script shouldContain rendered } } + + @Test + fun `static rendering should be present`() { + val df = dataFrameOf("a", "b")(listOf(1, 1), listOf(2, 4)) + val actualHtml = df.toHTML() + + actualHtml.body shouldContain """ + + + ab + + + + + [1, 1][2, 4] + + + + """.trimIndent().replace("\n", "") + } }