@@ -5,9 +5,11 @@ import org.jetbrains.kotlinx.dataframe.AnyCol
5
5
import org.jetbrains.kotlinx.dataframe.AnyFrame
6
6
import org.jetbrains.kotlinx.dataframe.AnyRow
7
7
import org.jetbrains.kotlinx.dataframe.DataFrame
8
+ import org.jetbrains.kotlinx.dataframe.api.CellAttributes
8
9
import org.jetbrains.kotlinx.dataframe.api.FormattedFrame
9
10
import org.jetbrains.kotlinx.dataframe.api.FormattingDsl
10
11
import org.jetbrains.kotlinx.dataframe.api.RowColFormatter
12
+ import org.jetbrains.kotlinx.dataframe.api.and
11
13
import org.jetbrains.kotlinx.dataframe.api.asColumnGroup
12
14
import org.jetbrains.kotlinx.dataframe.api.asNumbers
13
15
import org.jetbrains.kotlinx.dataframe.api.format
@@ -24,6 +26,7 @@ import org.jetbrains.kotlinx.dataframe.columns.FrameColumn
24
26
import org.jetbrains.kotlinx.dataframe.dataTypes.IFRAME
25
27
import org.jetbrains.kotlinx.dataframe.dataTypes.IMG
26
28
import org.jetbrains.kotlinx.dataframe.impl.DataFrameSize
29
+ import org.jetbrains.kotlinx.dataframe.impl.columns.addParentPath
27
30
import org.jetbrains.kotlinx.dataframe.impl.columns.addPath
28
31
import org.jetbrains.kotlinx.dataframe.impl.io.resizeKeepingAspectRatio
29
32
import org.jetbrains.kotlinx.dataframe.impl.renderType
@@ -161,7 +164,11 @@ internal fun AnyFrame.toHtmlData(
161
164
val scripts = mutableListOf<String >()
162
165
val queue = LinkedList <RenderingQueueItem >()
163
166
164
- fun AnyFrame.columnToJs (col : AnyCol , rowsLimit : Int? , configuration : DisplayConfiguration ): ColumnDataForJs {
167
+ fun AnyFrame.columnToJs (
168
+ col : ColumnWithPath <* >,
169
+ rowsLimit : Int? ,
170
+ configuration : DisplayConfiguration ,
171
+ ): ColumnDataForJs {
165
172
val values = if (rowsLimit != null ) rows().take(rowsLimit) else rows()
166
173
val scale = if (col.isNumber()) col.asNumbers().scale() else 1
167
174
val format = if (scale > 0 ) {
@@ -170,23 +177,40 @@ internal fun AnyFrame.toHtmlData(
170
177
RendererDecimalFormat .of(" %e" )
171
178
}
172
179
val renderConfig = configuration.copy(decimalFormat = format)
173
- val contents = values.map {
174
- val value = col[it ]
175
- val content = value.toDataFrameLikeOrNull()
176
- if (content != null ) {
177
- val df = content .df()
180
+ val contents = values.map { row ->
181
+ val value = col[row ]
182
+ val dfLikeContent = value.toDataFrameLikeOrNull()
183
+ if (dfLikeContent != null ) {
184
+ val df = dfLikeContent .df()
178
185
if (df.isEmpty()) {
179
186
HtmlContent (" " , null )
180
187
} else {
181
188
val id = nextTableId()
182
- queue + = RenderingQueueItem (df, id, content .configuration(defaultConfiguration))
189
+ queue + = RenderingQueueItem (df, id, dfLikeContent .configuration(defaultConfiguration))
183
190
DataFrameReference (id, df.size)
184
191
}
185
192
} else {
186
- val html =
187
- formatter.format(downsizeBufferedImageIfNeeded(value, renderConfig), cellRenderer, renderConfig)
188
- val style = renderConfig.cellFormatter
189
- ?.invoke(FormattingDsl , it, col)
193
+ val html = formatter.format(
194
+ value = downsizeBufferedImageIfNeeded(value, renderConfig),
195
+ renderer = cellRenderer,
196
+ configuration = renderConfig,
197
+ )
198
+
199
+ val formatter = renderConfig.cellFormatter
200
+ ? : return @map HtmlContent (html, null )
201
+
202
+ // ask formatter for all attributes defined for this cell or any of its parents (outer column groups)
203
+ val parentCols = col.path.indices
204
+ .map { i -> col.path.take(i + 1 ) }
205
+ .dropLast(1 )
206
+ .map { ColumnWithPath (this @toHtmlData[it], it) }
207
+ val parentAttributes = parentCols
208
+ .map { formatter(FormattingDsl , row, it) }
209
+ .reduceOrNull(CellAttributes ? ::and )
210
+
211
+ val cellAttributes = formatter(FormattingDsl , row, col)
212
+
213
+ val style = (parentAttributes and cellAttributes)
190
214
?.attributes()
191
215
?.ifEmpty { null }
192
216
?.flatMap {
@@ -204,12 +228,16 @@ internal fun AnyFrame.toHtmlData(
204
228
listOf (it)
205
229
}
206
230
}
207
- ?.joinToString(" ;" ) { " ${it.first} :${it.second} " }
231
+ ?.toMap() // removing duplicate keys, allowing only the final one to be applied
232
+ ?.entries
233
+ ?.joinToString(" ;" ) { " ${it.key} :${it.value} " }
208
234
HtmlContent (html, style)
209
235
}
210
236
}
211
237
val nested = if (col is ColumnGroup <* >) {
212
- col.columns().map { col.columnToJs(it, rowsLimit, configuration) }
238
+ col.columns().map {
239
+ col.columnToJs(it.addParentPath(col.path), rowsLimit, configuration)
240
+ }
213
241
} else {
214
242
emptyList()
215
243
}
@@ -226,7 +254,9 @@ internal fun AnyFrame.toHtmlData(
226
254
while (! queue.isEmpty()) {
227
255
val (nextDf, nextId, configuration) = queue.pop()
228
256
val rowsLimit = if (nextId == rootId) configuration.rowsLimit else configuration.nestedRowsLimit
229
- val preparedColumns = nextDf.columns().map { nextDf.columnToJs(it, rowsLimit, configuration) }
257
+ val preparedColumns = nextDf.columns().map {
258
+ nextDf.columnToJs(it.addPath(), rowsLimit, configuration)
259
+ }
230
260
val js = tableJs(preparedColumns, nextId, rootId, nextDf.nrow)
231
261
scripts.add(js)
232
262
}
0 commit comments