From 4f8d983832cd7b5f359bd698d452c6198452fcd1 Mon Sep 17 00:00:00 2001 From: Jolan Rensen Date: Mon, 11 Aug 2025 20:11:31 +0200 Subject: [PATCH 1/4] migrated the format operation to use ColumnWithPath, instead of just columns by name, so column groups can now also be targeted and used. I turned the gathered attributes into a map, such that duplicate keys are removed. Only the latest ones stay, meaning you can overwrite previous attributes for a cell. --- .../jetbrains/kotlinx/dataframe/api/format.kt | 17 +++--- .../kotlinx/dataframe/impl/api/format.kt | 29 ++++------ .../jetbrains/kotlinx/dataframe/io/html.kt | 58 ++++++++++++++----- .../testSets/person/FormattingTests.kt | 7 ++- 4 files changed, 69 insertions(+), 42 deletions(-) diff --git a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/format.kt b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/format.kt index b2962defa0..aeecba0c14 100644 --- a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/format.kt +++ b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/format.kt @@ -1,7 +1,6 @@ package org.jetbrains.kotlinx.dataframe.api import org.jetbrains.kotlinx.dataframe.ColumnsSelector -import org.jetbrains.kotlinx.dataframe.DataColumn import org.jetbrains.kotlinx.dataframe.DataFrame import org.jetbrains.kotlinx.dataframe.DataRow import org.jetbrains.kotlinx.dataframe.RowColumnExpression @@ -16,6 +15,7 @@ import org.jetbrains.kotlinx.dataframe.api.FormattingDsl.linearBg import org.jetbrains.kotlinx.dataframe.api.FormattingDsl.rgb import org.jetbrains.kotlinx.dataframe.api.FormattingDsl.textColor import org.jetbrains.kotlinx.dataframe.columns.ColumnReference +import org.jetbrains.kotlinx.dataframe.columns.ColumnWithPath import org.jetbrains.kotlinx.dataframe.columns.toColumnSet import org.jetbrains.kotlinx.dataframe.dataTypes.IFRAME import org.jetbrains.kotlinx.dataframe.dataTypes.IMG @@ -66,6 +66,9 @@ import kotlin.reflect.KProperty * * You can continue formatting the [FormattedFrame] by calling [format][FormattedFrame.format] on it again. * + * Formatting is done additively, meaning you can add more formatting to a cell that's already formatted or + * override certain attributes inherited from its outer group. + * * Check out the [Grammar]. * * For more information: {@include [DocumentationUrls.Format]} @@ -169,7 +172,7 @@ internal interface FormatDocs { interface CellFormatterDef /** - * `rowColFormatter: `{@include [FormattingDslGrammarRef]}`.(row: `[DataRow][DataRow]`, col: `[DataColumn][DataColumn]`) -> `[CellAttributes][CellAttributes]`?` + * `rowColFormatter: `{@include [FormattingDslGrammarRef]}`.(row: `[DataRow][DataRow]`, col: `[ColumnWithPath][ColumnWithPath]`) -> `[CellAttributes][CellAttributes]`?` */ interface RowColFormatterDef @@ -348,7 +351,7 @@ public fun FormattedFrame.format(vararg columns: String): FormatClause FormattedFrame.format(): FormatClause = FormatClause(df, null, formatter) +public fun FormattedFrame.format(): FormatClause = FormatClause(df = df, oldFormatter = formatter) // endregion @@ -470,7 +473,7 @@ public fun FormatClause.perRowCol(formatter: RowColFormatter) */ @Suppress("UNCHECKED_CAST") public fun FormatClause.with(formatter: CellFormatter): FormattedFrame = - formatImpl { row, col -> formatter(row[col.name] as C) } + formatImpl { row, col -> formatter(col[row] as C) } /** * Creates a new [FormattedFrame] that uses the specified [CellFormatter] to format selected non-null cells of the dataframe. @@ -734,14 +737,14 @@ public object FormattingDsl { /** * A lambda function expecting a [CellAttributes] or `null` given an instance of - * [DataRow][DataRow]`<`[T][T]`>` and [DataColumn][DataColumn]`<`[C][C]`>`. + * [DataRow][DataRow]`<`[T][T]`>` and [ColumnWithPath][ColumnWithPath]`<`[C][C]`>`. * * This is similar to a [RowColumnExpression], except that you also have access * to the [FormattingDsl] in the context. * * @include [FormattingDsl] */ -public typealias RowColFormatter = FormattingDsl.(row: DataRow, col: DataColumn) -> CellAttributes? +public typealias RowColFormatter = FormattingDsl.(row: DataRow, col: ColumnWithPath) -> CellAttributes? /** * A lambda function expecting a [CellAttributes] or `null` given an instance of a cell: [C] of the dataframe. @@ -838,7 +841,7 @@ public class FormattedFrame(internal val df: DataFrame, internal val forma */ public class FormatClause( internal val df: DataFrame, - internal val columns: ColumnsSelector? = null, + internal val columns: ColumnsSelector = { all().cast() }, internal val oldFormatter: RowColFormatter? = null, internal val filter: RowValueFilter = { true }, ) { diff --git a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/impl/api/format.kt b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/impl/api/format.kt index 579c48213f..8b78bdfe5e 100644 --- a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/impl/api/format.kt +++ b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/impl/api/format.kt @@ -8,9 +8,10 @@ import org.jetbrains.kotlinx.dataframe.api.RgbColor import org.jetbrains.kotlinx.dataframe.api.RowColFormatter import org.jetbrains.kotlinx.dataframe.api.and import org.jetbrains.kotlinx.dataframe.api.cast -import org.jetbrains.kotlinx.dataframe.api.getColumnsWithPaths -import org.jetbrains.kotlinx.dataframe.api.name -import org.jetbrains.kotlinx.dataframe.columns.depth +import org.jetbrains.kotlinx.dataframe.api.getColumnPaths +import org.jetbrains.kotlinx.dataframe.columns.UnresolvedColumnsPolicy +import org.jetbrains.kotlinx.dataframe.impl.getColumnPaths +import org.jetbrains.kotlinx.dataframe.path internal class SingleAttribute(val key: String, val value: String) : CellAttributes { override fun attributes() = listOf(key to value) @@ -53,25 +54,17 @@ internal inline fun FormatClause.formatImpl( crossinline formatter: RowColFormatter, ): FormattedFrame { val clause = this - val columns = - if (clause.columns != null) { - clause.df.getColumnsWithPaths(clause.columns) - .mapNotNull { if (it.depth == 0) it.name else null } // TODO Causes #1356 - .toSet() - } else { - null - } + val columns = clause.df.getColumnPaths(UnresolvedColumnsPolicy.Skip, clause.columns).toSet() + return FormattedFrame(clause.df) { row, col -> val oldAttributes = clause.oldFormatter?.invoke(FormattingDsl, row, col.cast()) - if (columns == null || columns.contains(col.name())) { - val value = row[col.name] as C + if (col.path in columns) { + val value = col[row] as C if (clause.filter(row, value)) { - oldAttributes and formatter(FormattingDsl, row.cast(), col.cast()) - } else { - oldAttributes + return@FormattedFrame oldAttributes and formatter(FormattingDsl, row.cast(), col.cast()) } - } else { - oldAttributes } + + oldAttributes } } 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 bd1ab19e60..73e12b5b3e 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 @@ -5,9 +5,11 @@ import org.jetbrains.kotlinx.dataframe.AnyCol import org.jetbrains.kotlinx.dataframe.AnyFrame import org.jetbrains.kotlinx.dataframe.AnyRow import org.jetbrains.kotlinx.dataframe.DataFrame +import org.jetbrains.kotlinx.dataframe.api.CellAttributes import org.jetbrains.kotlinx.dataframe.api.FormattedFrame import org.jetbrains.kotlinx.dataframe.api.FormattingDsl import org.jetbrains.kotlinx.dataframe.api.RowColFormatter +import org.jetbrains.kotlinx.dataframe.api.and import org.jetbrains.kotlinx.dataframe.api.asColumnGroup import org.jetbrains.kotlinx.dataframe.api.asNumbers import org.jetbrains.kotlinx.dataframe.api.format @@ -24,6 +26,7 @@ import org.jetbrains.kotlinx.dataframe.columns.FrameColumn import org.jetbrains.kotlinx.dataframe.dataTypes.IFRAME import org.jetbrains.kotlinx.dataframe.dataTypes.IMG import org.jetbrains.kotlinx.dataframe.impl.DataFrameSize +import org.jetbrains.kotlinx.dataframe.impl.columns.addParentPath import org.jetbrains.kotlinx.dataframe.impl.columns.addPath import org.jetbrains.kotlinx.dataframe.impl.io.resizeKeepingAspectRatio import org.jetbrains.kotlinx.dataframe.impl.renderType @@ -161,7 +164,11 @@ internal fun AnyFrame.toHtmlData( val scripts = mutableListOf() val queue = LinkedList() - fun AnyFrame.columnToJs(col: AnyCol, rowsLimit: Int?, configuration: DisplayConfiguration): ColumnDataForJs { + fun AnyFrame.columnToJs( + col: ColumnWithPath<*>, + rowsLimit: Int?, + configuration: DisplayConfiguration, + ): ColumnDataForJs { val values = if (rowsLimit != null) rows().take(rowsLimit) else rows() val scale = if (col.isNumber()) col.asNumbers().scale() else 1 val format = if (scale > 0) { @@ -170,23 +177,40 @@ internal fun AnyFrame.toHtmlData( RendererDecimalFormat.of("%e") } val renderConfig = configuration.copy(decimalFormat = format) - val contents = values.map { - val value = col[it] - val content = value.toDataFrameLikeOrNull() - if (content != null) { - val df = content.df() + val contents = values.map { row -> + val value = col[row] + val dfLikeContent = value.toDataFrameLikeOrNull() + if (dfLikeContent != null) { + val df = dfLikeContent.df() if (df.isEmpty()) { HtmlContent("", null) } else { val id = nextTableId() - queue += RenderingQueueItem(df, id, content.configuration(defaultConfiguration)) + queue += RenderingQueueItem(df, id, dfLikeContent.configuration(defaultConfiguration)) DataFrameReference(id, df.size) } } else { - val html = - formatter.format(downsizeBufferedImageIfNeeded(value, renderConfig), cellRenderer, renderConfig) - val style = renderConfig.cellFormatter - ?.invoke(FormattingDsl, it, col) + val html = formatter.format( + value = downsizeBufferedImageIfNeeded(value, renderConfig), + renderer = cellRenderer, + configuration = renderConfig, + ) + + val formatter = renderConfig.cellFormatter + ?: return@map HtmlContent(html, null) + + // ask formatter for all attributes defined for this cell or any of its parents (outer column groups) + val parentCols = col.path.indices + .map { i -> col.path.take(i + 1) } + .dropLast(1) + .map { ColumnWithPath(this@toHtmlData[it], it) } + val parentAttributes = parentCols + .map { formatter(FormattingDsl, row, it) } + .reduceOrNull(CellAttributes?::and) + + val cellAttributes = formatter(FormattingDsl, row, col) + + val style = (parentAttributes and cellAttributes) ?.attributes() ?.ifEmpty { null } ?.flatMap { @@ -204,12 +228,16 @@ internal fun AnyFrame.toHtmlData( listOf(it) } } - ?.joinToString(";") { "${it.first}:${it.second}" } + ?.toMap() // removing duplicate keys, allowing only the final one to be applied + ?.entries + ?.joinToString(";") { "${it.key}:${it.value}" } HtmlContent(html, style) } } val nested = if (col is ColumnGroup<*>) { - col.columns().map { col.columnToJs(it, rowsLimit, configuration) } + col.columns().map { + col.columnToJs(it.addParentPath(col.path), rowsLimit, configuration) + } } else { emptyList() } @@ -226,7 +254,9 @@ internal fun AnyFrame.toHtmlData( while (!queue.isEmpty()) { val (nextDf, nextId, configuration) = queue.pop() val rowsLimit = if (nextId == rootId) configuration.rowsLimit else configuration.nestedRowsLimit - val preparedColumns = nextDf.columns().map { nextDf.columnToJs(it, rowsLimit, configuration) } + val preparedColumns = nextDf.columns().map { + nextDf.columnToJs(it.addPath(), rowsLimit, configuration) + } val js = tableJs(preparedColumns, nextId, rootId, nextDf.nrow) scripts.add(js) } diff --git a/core/src/test/kotlin/org/jetbrains/kotlinx/dataframe/testSets/person/FormattingTests.kt b/core/src/test/kotlin/org/jetbrains/kotlinx/dataframe/testSets/person/FormattingTests.kt index 0bb1476577..e6ac5471e6 100644 --- a/core/src/test/kotlin/org/jetbrains/kotlinx/dataframe/testSets/person/FormattingTests.kt +++ b/core/src/test/kotlin/org/jetbrains/kotlinx/dataframe/testSets/person/FormattingTests.kt @@ -14,6 +14,7 @@ import org.jetbrains.kotlinx.dataframe.api.where import org.jetbrains.kotlinx.dataframe.api.with import org.jetbrains.kotlinx.dataframe.impl.api.encode import org.jetbrains.kotlinx.dataframe.impl.api.linearGradient +import org.jetbrains.kotlinx.dataframe.impl.columns.addPath import org.jetbrains.kotlinx.dataframe.index import org.jetbrains.kotlinx.dataframe.io.DisplayConfiguration import org.jetbrains.kotlinx.dataframe.nrow @@ -33,7 +34,7 @@ class FormattingTests : BaseTest() { val formatter = formattedFrame.formatter!! for (row in 0 until typed.nrow) { - FormattingDsl.formatter(typed[row], typed.age)!!.attributes().size shouldBe + FormattingDsl.formatter(typed[row], typed.age.addPath())!!.attributes().size shouldBe if (typed[row].age > 10) 3 else 2 } @@ -48,12 +49,12 @@ class FormattingTests : BaseTest() { .formatter!! for (row in 0 until typed.nrow step 2) { - FormattingDsl.formatter(typed[row], typed.age)!!.attributes() shouldBe + FormattingDsl.formatter(typed[row], typed.age.addPath())!!.attributes() shouldBe listOf("background-color" to gray.encode()) } for (row in 1 until typed.nrow step 2) { - FormattingDsl.formatter(typed[row], typed.age)!!.attributes() shouldBe + FormattingDsl.formatter(typed[row], typed.age.addPath())!!.attributes() shouldBe listOf("background-color" to linearGradient(typed[row].age.toDouble(), 20.0, green, 80.0, red).encode()) } } From c33e89ecbe84d76f728bbe63e274cdbb89b4bab8 Mon Sep 17 00:00:00 2001 From: Jolan Rensen Date: Mon, 11 Aug 2025 20:59:14 +0200 Subject: [PATCH 2/4] fixed format tests --- .../jetbrains/kotlinx/dataframe/api/format.kt | 85 +++++++++++-------- 1 file changed, 51 insertions(+), 34 deletions(-) diff --git a/core/src/test/kotlin/org/jetbrains/kotlinx/dataframe/api/format.kt b/core/src/test/kotlin/org/jetbrains/kotlinx/dataframe/api/format.kt index 055b4e01ca..e3d4d8b795 100644 --- a/core/src/test/kotlin/org/jetbrains/kotlinx/dataframe/api/format.kt +++ b/core/src/test/kotlin/org/jetbrains/kotlinx/dataframe/api/format.kt @@ -2,7 +2,6 @@ package org.jetbrains.kotlinx.dataframe.api import io.kotest.matchers.shouldBe import io.kotest.matchers.shouldNotBe -import io.kotest.matchers.string.shouldContain import org.jetbrains.kotlinx.dataframe.api.FormattingDsl.blue import org.jetbrains.kotlinx.dataframe.api.FormattingDsl.red import org.jetbrains.kotlinx.dataframe.api.FormattingDsl.rgb @@ -13,7 +12,6 @@ import org.jetbrains.kotlinx.dataframe.samples.api.firstName import org.jetbrains.kotlinx.dataframe.samples.api.isHappy import org.jetbrains.kotlinx.dataframe.samples.api.name import org.jetbrains.kotlinx.dataframe.samples.api.weight -import org.junit.Ignore import org.junit.Test class FormatTests : TestBase() { @@ -24,7 +22,7 @@ class FormatTests : TestBase() { val html = formatted.toHtml().toString() // Should contain CSS background-color styling - html shouldContain "background-color:#ff0000" + (html.split("background-color:#ff0000").size - 1) shouldBe 7 // Format operation should produce a FormattedFrame formatted::class.simpleName shouldBe "FormattedFrame" } @@ -34,7 +32,7 @@ class FormatTests : TestBase() { val formatted = df.format { age }.with { textColor(blue) } val html = formatted.toHtml().toString() - html shouldContain "color:#0000ff" + (html.split("color:#0000ff").size - 1) shouldBe 14 formatted::class.simpleName shouldBe "FormattedFrame" } @@ -43,10 +41,10 @@ class FormatTests : TestBase() { val formatted = df.format { age }.with { background(white) and textColor(black) and bold } val html = formatted.toHtml().toString() - html shouldContain "background-color:#ffffff" - html shouldContain "color" - html shouldContain "font-weight" - html shouldContain "bold" + (html.split("background-color:#ffffff").size - 1) shouldBe 7 + (html.split("color:#000000").size - 1) shouldBe 14 + (html.split("font-weight:bold").size - 1) shouldBe 7 + (html.split("font-weight:bold").size - 1) shouldBe 7 } @Test @@ -54,23 +52,42 @@ class FormatTests : TestBase() { val formatted = df.format { age }.with { italic and underline } val html = formatted.toHtml().toString() - html shouldContain "font-style" - html shouldContain "italic" - html shouldContain "text-decoration" - html shouldContain "underline" + (html.split("font-style:italic").size - 1) shouldBe 7 + (html.split("font-style:italic").size - 1) shouldBe 7 + (html.split("text-decoration:underline").size - 1) shouldBe 7 + (html.split("text-decoration:underline").size - 1) shouldBe 7 } - // TODO #1356 - @Ignore @Test - fun `format with italic and underline in nested group`() { + fun `format with italic and underline nested in group`() { val formatted = df.format { name.firstName }.with { italic and underline } val html = formatted.toHtml().toString() - html shouldContain "font-style" - html shouldContain "italic" - html shouldContain "text-decoration" - html shouldContain "underline" + (html.split("font-style:italic").size - 1) shouldBe 7 + (html.split("font-style:italic").size - 1) shouldBe 7 + (html.split("text-decoration:underline").size - 1) shouldBe 7 + (html.split("text-decoration:underline").size - 1) shouldBe 7 + } + + @Test + fun `format with italic and underline for entire group`() { + val formatted = df + .format().with { background(white) } + .format { age }.with { background(blue) and textColor(white) } + .format { name }.with { background(green) } + .format { name.firstName }.with { italic and underline } + .format { name.colsOf() }.where { it.startsWith("C") }.with { background(red) } + + val html = formatted.toHtml().toString() + + (html.split("background-color:#ffffff").size - 1) shouldBe 21 + (html.split("background-color:#00ff00").size - 1) shouldBe 16 + (html.split("background-color:#0000ff").size - 1) shouldBe 7 + (html.split("background-color:#ff0000").size - 1) shouldBe 5 + (html.split("font-style:italic").size - 1) shouldBe 7 + (html.split("font-style:italic").size - 1) shouldBe 7 + (html.split("text-decoration:underline").size - 1) shouldBe 7 + (html.split("text-decoration:underline").size - 1) shouldBe 7 } @Test @@ -80,7 +97,7 @@ class FormatTests : TestBase() { val html = formatted.toHtml().toString() // Custom color should be applied - html shouldContain "background-color:#8040c0" + (html.split("background-color:#8040c0").size - 1) shouldBe 7 } @Test @@ -158,13 +175,13 @@ class FormatTests : TestBase() { val formatted = df.format { age }.linearBg(15 to blue, 45 to red) val html = formatted.toHtml().toString() - html shouldContain "background-color:#0000ff" - html shouldContain "background-color:#2a00d4" - html shouldContain "background-color:#d4002a" - html shouldContain "background-color:#7f007f" - html shouldContain "background-color:#2a00d4" - html shouldContain "background-color:#7f007f" - html shouldContain "background-color:#ff0000" + (html.split("background-color:#0000ff").size - 1) shouldBe 1 + (html.split("background-color:#2a00d4").size - 1) shouldBe 2 + (html.split("background-color:#d4002a").size - 1) shouldBe 1 + (html.split("background-color:#7f007f").size - 1) shouldBe 2 + (html.split("background-color:#2a00d4").size - 1) shouldBe 2 + (html.split("background-color:#7f007f").size - 1) shouldBe 2 + (html.split("background-color:#ff0000").size - 1) shouldBe 1 formatted::class.simpleName shouldBe "FormattedFrame" } @@ -175,13 +192,13 @@ class FormatTests : TestBase() { } val html = formatted.toHtml().toString() - html shouldContain "color:#0000ff" - html shouldContain "color:#2a00d4" - html shouldContain "color:#d4002a" - html shouldContain "color:#7f007f" - html shouldContain "color:#2a00d4" - html shouldContain "color:#7f007f" - html shouldContain "color:#ff0000" + (html.split("color:#0000ff").size - 1) shouldBe 2 + (html.split("color:#2a00d4").size - 1) shouldBe 4 + (html.split("color:#d4002a").size - 1) shouldBe 2 + (html.split("color:#7f007f").size - 1) shouldBe 4 + (html.split("color:#2a00d4").size - 1) shouldBe 4 + (html.split("color:#7f007f").size - 1) shouldBe 4 + (html.split("color:#ff0000").size - 1) shouldBe 2 formatted::class.simpleName shouldBe "FormattedFrame" } From 37ecc695439b08ff9433054ef0bfb5505ebf8313 Mon Sep 17 00:00:00 2001 From: Jolan Rensen Date: Tue, 12 Aug 2025 12:44:28 +0200 Subject: [PATCH 3/4] updated kdocs of format with information about colgroups and framecols --- .../org/jetbrains/kotlinx/dataframe/api/format.kt | 14 ++++++++++++++ ...x.dataframe.api.FormatDocs.Grammar.ForHtml.html | 2 +- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/format.kt b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/format.kt index aeecba0c14..2d7e6c6110 100644 --- a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/format.kt +++ b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/format.kt @@ -14,8 +14,10 @@ import org.jetbrains.kotlinx.dataframe.api.FormattingDsl.linear import org.jetbrains.kotlinx.dataframe.api.FormattingDsl.linearBg import org.jetbrains.kotlinx.dataframe.api.FormattingDsl.rgb import org.jetbrains.kotlinx.dataframe.api.FormattingDsl.textColor +import org.jetbrains.kotlinx.dataframe.columns.ColumnGroup import org.jetbrains.kotlinx.dataframe.columns.ColumnReference import org.jetbrains.kotlinx.dataframe.columns.ColumnWithPath +import org.jetbrains.kotlinx.dataframe.columns.FrameColumn import org.jetbrains.kotlinx.dataframe.columns.toColumnSet import org.jetbrains.kotlinx.dataframe.dataTypes.IFRAME import org.jetbrains.kotlinx.dataframe.dataTypes.IMG @@ -66,9 +68,21 @@ import kotlin.reflect.KProperty * * You can continue formatting the [FormattedFrame] by calling [format][FormattedFrame.format] on it again. * + * Specifying a [column group][ColumnGroup] makes all of its inner columns be formatted in the same way unless + * overridden. + * * Formatting is done additively, meaning you can add more formatting to a cell that's already formatted or * override certain attributes inherited from its outer group. * + * Specifying a [frame column][FrameColumn] at the moment does nothing + * ([Issue #1375](https://github.com/Kotlin/dataframe/issues/1375)), + * convert each nested [DataFrame] to a [FormattedFrame] instead: + * ```kt + * df.convert { myFrameCol }.with { + * it.format { someCol }.with { background(green) } + * }.toStandaloneHtml() + * ``` + * * Check out the [Grammar]. * * For more information: {@include [DocumentationUrls.Format]} diff --git a/docs/StardustDocs/resources/snippets/kdocs/org.jetbrains.kotlinx.dataframe.api.FormatDocs.Grammar.ForHtml.html b/docs/StardustDocs/resources/snippets/kdocs/org.jetbrains.kotlinx.dataframe.api.FormatDocs.Grammar.ForHtml.html index a8ecf17deb..717b98861b 100644 --- a/docs/StardustDocs/resources/snippets/kdocs/org.jetbrains.kotlinx.dataframe.api.FormatDocs.Grammar.ForHtml.html +++ b/docs/StardustDocs/resources/snippets/kdocs/org.jetbrains.kotlinx.dataframe.api.FormatDocs.Grammar.ForHtml.html @@ -85,7 +85,7 @@ window.addEventListener('resize', sendHeight); -

Definitions:

cellFormatter: FormattingDsl.(cell: C) -> CellAttributes?

    

rowColFormatter: FormattingDsl.(row: DataRow<T>, col: DataColumn<C>) -> CellAttributes?

Notation:

format { columns }

     +

Definitions:

cellFormatter: FormattingDsl.(cell: C) -> CellAttributes?

    

rowColFormatter: FormattingDsl.(row: DataRow<T>, col: ColumnWithPath<C>) -> CellAttributes?

Notation:

format { columns }

     [ .where { filter: RowValueFilter } ]

     [ .at(rowIndices: Collection<Int> | IntRange|vararg Int) ]

     [ .notNull() ]

     From 7e12d7ae630848f00c403d3b313340a10d3d9b29 Mon Sep 17 00:00:00 2001 From: Jolan Rensen Date: Tue, 12 Aug 2025 16:46:14 +0200 Subject: [PATCH 4/4] updated docs of format wrt colgroups --- .../operations/formatExample_properties.html | 12 +++++------ .../operations/formatExample_strings.html | 12 +++++------ ...x.dataframe.samples.api.Modify.update.html | 8 ++++---- ...rame.samples.api.Modify.updateAsFrame.html | 4 ++-- docs/StardustDocs/topics/format.md | 20 +++++++++++++++++++ .../kotlinx/dataframe/samples/api/Modify.kt | 4 ++++ 6 files changed, 42 insertions(+), 18 deletions(-) diff --git a/docs/StardustDocs/resources/modify/operations/formatExample_properties.html b/docs/StardustDocs/resources/modify/operations/formatExample_properties.html index 1cd67c82d1..4c28a12f37 100644 --- a/docs/StardustDocs/resources/modify/operations/formatExample_properties.html +++ b/docs/StardustDocs/resources/modify/operations/formatExample_properties.html @@ -177,7 +177,7 @@ -
+

@@ -457,17 +457,17 @@ })() /**/ -call_DataFrame(function() { DataFrame.renderTable(-788529152) }); +call_DataFrame(function() { DataFrame.renderTable(2046820354) }); function sendHeight() { const table = document.querySelector('table.dataframe'); diff --git a/docs/StardustDocs/resources/modify/operations/formatExample_strings.html b/docs/StardustDocs/resources/modify/operations/formatExample_strings.html index 4edefab106..94117df20f 100644 --- a/docs/StardustDocs/resources/modify/operations/formatExample_strings.html +++ b/docs/StardustDocs/resources/modify/operations/formatExample_strings.html @@ -177,7 +177,7 @@ -
+

@@ -457,17 +457,17 @@ })() /**/ -call_DataFrame(function() { DataFrame.renderTable(-788529150) }); +call_DataFrame(function() { DataFrame.renderTable(-503316478) }); function sendHeight() { const table = document.querySelector('table.dataframe'); diff --git a/docs/StardustDocs/resources/snippets/org.jetbrains.kotlinx.dataframe.samples.api.Modify.update.html b/docs/StardustDocs/resources/snippets/org.jetbrains.kotlinx.dataframe.samples.api.Modify.update.html index 0bb71306cc..623f05c4b7 100644 --- a/docs/StardustDocs/resources/snippets/org.jetbrains.kotlinx.dataframe.samples.api.Modify.update.html +++ b/docs/StardustDocs/resources/snippets/org.jetbrains.kotlinx.dataframe.samples.api.Modify.update.html @@ -706,8 +706,8 @@ call_DataFrame(function() { DataFrame.renderTable(3) }); /*