Skip to content

Commit 9384a3e

Browse files
committed
Expand HTML rendering documentation
1 parent a24868a commit 9384a3e

File tree

4 files changed

+502
-51
lines changed

4 files changed

+502
-51
lines changed

core/src/test/kotlin/org/jetbrains/kotlinx/dataframe/samples/api/Render.kt

Lines changed: 0 additions & 48 deletions
This file was deleted.

docs/StardustDocs/topics/toHTML.md

Lines changed: 228 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,221 @@
22

33
<!---IMPORT org.jetbrains.kotlinx.dataframe.samples.api.Render-->
44

5+
## HTML rendering
6+
57
`DataFrame` instances can be rendered to HTML.
68
Rendering of hierarchical tables in HTML is supported by JS and CSS definitions
79
that can be found in project resources.
810

911
Dataframes can also be formatted before being converted to HTML.
1012
See [](format.md) for how to do this.
1113

14+
Besides that, DataFrame provides multiple APIs to customize HTML output.
15+
16+
### Display images
17+
Values of the `org.jetbrains.kotlinx.dataframe.datatypes.IMG` class are rendered as an `<img>` tag
18+
19+
<!---FUN displayImg-->
20+
21+
```kotlin
22+
val htmlData = dataFrameOf(
23+
"kotlinLogo" to columnOf(
24+
IMG("https://kotlin.github.io/dataframe/images/kotlin-logo.svg"),
25+
),
26+
).toStandaloneHtml()
27+
```
28+
29+
<!---END-->
30+
31+
### Embed pages
32+
33+
Values of the `org.jetbrains.kotlinx.dataframe.datatypes.IFRAME` class are rendered as an `<iframe>` tag
34+
35+
<!---FUN displayIFrame-->
36+
37+
```kotlin
38+
val htmlData = dataFrameOf(
39+
"documentationPages" to columnOf(
40+
IFRAME(
41+
src = "https://kotlin.github.io/dataframe/tohtml.html",
42+
width = 850,
43+
height = 500,
44+
),
45+
),
46+
).toStandaloneHtml()
47+
```
48+
49+
<!---END-->
50+
51+
### Render clickable links
52+
53+
Values of `java.net.URL` are rendered as `<a>` tag
54+
55+
<!---FUN displayURL-->
56+
57+
```kotlin
58+
val htmlData = dataFrameOf(
59+
"documentationPages" to columnOf(
60+
URI("https://kotlin.github.io/dataframe/format.html").toURL(),
61+
URI("https://kotlin.github.io/dataframe/tohtml.html").toURL(),
62+
URI("https://kotlin.github.io/dataframe/jupyterrendering.html").toURL(),
63+
),
64+
).toStandaloneHtml()
65+
```
66+
67+
<!---END-->
68+
69+
### Render any HTML inside a cell
70+
71+
Wrap cell values in custom HTML using `RenderedContent.media`
72+
73+
<!---FUN displayMediaContent-->
74+
<tabs>
75+
<tab title="Properties">
76+
77+
```kotlin
78+
val htmlData = dataFrameOf(
79+
"documentationPages" to columnOf(
80+
"https://kotlin.github.io/dataframe/format.html",
81+
"https://kotlin.github.io/dataframe/tohtml.html",
82+
"https://kotlin.github.io/dataframe/jupyterrendering.html",
83+
),
84+
)
85+
.convert { documentationPages }.with {
86+
val uri = URI(it)
87+
RenderedContent.media("""<a href='$uri'>${uri.path}</a>""")
88+
}
89+
.toStandaloneHtml()
90+
```
91+
92+
</tab>
93+
<tab title="Strings">
94+
95+
```kotlin
96+
val htmlData = dataFrameOf(
97+
"documentationPages" to columnOf(
98+
"https://kotlin.github.io/dataframe/format.html",
99+
"https://kotlin.github.io/dataframe/tohtml.html",
100+
"https://kotlin.github.io/dataframe/jupyterrendering.html",
101+
),
102+
)
103+
.convert { "documentationPages"<String>() }.with {
104+
val uri = URI(it)
105+
RenderedContent.media("""<a href='$uri'>${uri.path}</a>""")
106+
}
107+
.toStandaloneHtml()
108+
```
109+
110+
</tab></tabs>
111+
<!---END-->
112+
113+
### Sample data
114+
This dataframe is used in the following examples
115+
116+
<!---FUN df-->
117+
118+
```kotlin
119+
val df = dataFrameOf(
120+
"name" to columnOf(
121+
"firstName" to columnOf("Alice", "Bob", "Charlie", "Charlie", "Bob", "Alice", "Charlie"),
122+
"lastName" to columnOf("Cooper", "Dylan", "Daniels", "Chaplin", "Marley", "Wolf", "Byrd"),
123+
),
124+
"age" to columnOf(15, 45, 20, 40, 30, 20, 30),
125+
"city" to columnOf("London", "Dubai", "Moscow", "Milan", "Tokyo", null, "Moscow"),
126+
"weight" to columnOf(54, 87, null, null, 68, 55, 90),
127+
"isHappy" to columnOf(true, true, false, true, true, false, true),
128+
)
129+
```
130+
131+
<!---END-->
132+
133+
### Reusable rendering logic
134+
135+
Generic approach to custom cell rendering. Useful if you want to apply the same rendering to different dataframes.
136+
137+
<!---FUN cellRenderer-->
138+
139+
```kotlin
140+
class CustomArrayCellRenderer : ChainedCellRenderer(DefaultCellRenderer) {
141+
override fun maybeContent(value: Any?, configuration: DisplayConfiguration): RenderedContent? {
142+
if (value is Boolean) {
143+
return RenderedContent.text(if (value) "" else "")
144+
}
145+
// return null to delegate work to parent renderer: DefaultCellRenderer
146+
return null
147+
}
148+
149+
override fun maybeTooltip(value: Any?, configuration: DisplayConfiguration): String? {
150+
// return null to delegate work to parent renderer: DefaultCellRenderer
151+
return null
152+
}
153+
}
154+
155+
val htmlData = df.toStandaloneHtml(cellRenderer = CustomArrayCellRenderer())
156+
```
157+
158+
<!---END-->
159+
160+
### Custom HTML outside the table
161+
162+
The result of `toHtml` can be composed with other HTML, CSS, or JS definitions.
163+
Let's build an alternative to displaying all rows in one table: custom pagination across multiple files
164+
165+
<!---FUN appendCustomHtml-->
166+
167+
```kotlin
168+
val pages = df.duplicateRows(10).chunked(20)
169+
val files = pages.indices.map { i -> File("page$i.html") }
170+
val navLinks = files.mapIndexed { i, file ->
171+
"""<a href="${file.name}">Page ${i + 1}</a>"""
172+
}.joinToString(" | ")
173+
174+
pages.forEachIndexed { i, page ->
175+
val output = files[i]
176+
page.toStandaloneHtml().plus(DataFrameHtmlData(body = navLinks))
177+
// uncomment
178+
// .writeHtml(output)
179+
}
180+
```
181+
182+
<!---END-->
183+
184+
### Custom style and scripts
185+
186+
Let's add a hover effect and click listener for table cells.
187+
See [init.js](https://github.com/Kotlin/dataframe/blob/704200cb86e7bdc07b800a7cfef48de408bd5fe8/core/src/main/resources/init.js) and [table.css](https://github.com/Kotlin/dataframe/blob/ead4f8666df5cf24e5bf45d245cda3200e150e93/core/src/main/resources/table.css) for reference.
188+
189+
<!---FUN interactiveJs-->
190+
191+
```kotlin
192+
val selectCellInteraction = DataFrameHtmlData(
193+
style =
194+
"""
195+
td:hover {
196+
background-color: rgba(0, 123, 255, 0.15);
197+
cursor: pointer;
198+
}
199+
""".trimIndent(),
200+
script =
201+
"""
202+
(function() {
203+
let cells = document.querySelectorAll('td');
204+
cells.forEach(function(cell) {
205+
cell.addEventListener('click', function(e) {
206+
let content = cell.textContent;
207+
alert(content);
208+
});
209+
});
210+
})();
211+
""".trimIndent(),
212+
)
213+
214+
// keep in mind JS script initialization order.
215+
val htmlData = df.toStandaloneHtml().plus(selectCellInteraction)
216+
```
217+
218+
<!---END-->
219+
12220
Depending on your environment, there can be different ways to use the result of `toHtml` functions.
13221

14222
## IntelliJ IDEA
@@ -21,9 +229,10 @@ It can be displayed in the browser and has parameters for customization.
21229
<!---FUN useRenderingResult-->
22230

23231
```kotlin
24-
df.toStandaloneHtml(DisplayConfiguration(rowsLimit = null)).openInBrowser()
25-
df.toStandaloneHtml(DisplayConfiguration(rowsLimit = null)).writeHtml(File("/path/to/file"))
26-
df.toStandaloneHtml(DisplayConfiguration(rowsLimit = null)).writeHtml(Path("/path/to/file"))
232+
val configuration = DisplayConfiguration(rowsLimit = null)
233+
df.toStandaloneHtml(configuration).openInBrowser()
234+
df.toStandaloneHtml(configuration).writeHtml(File("/path/to/file"))
235+
df.toStandaloneHtml(configuration).writeHtml(Path("/path/to/file"))
27236
```
28237

29238
<!---END-->
@@ -35,6 +244,8 @@ which you can use to include additional scripts, elements,
35244
or styles at the end of the page or just to merge multiple tables into one HTML snippet.
36245

37246
<!---FUN composeTables-->
247+
<tabs>
248+
<tab title="Properties">
38249

39250
```kotlin
40251
val df1 = df.reorderColumnsByName()
@@ -44,6 +255,20 @@ val df3 = df.sortByDesc { age }
44255
listOf(df1, df2, df3).fold(DataFrameHtmlData.tableDefinitions()) { acc, df -> acc + df.toHtml() }
45256
```
46257

258+
</tab>
259+
<tab title="Strings">
260+
261+
```kotlin
262+
val df1 = df.reorderColumnsByName()
263+
val df2 = df.sortBy("age")
264+
val df3 = df.sortByDesc("age")
265+
266+
listOf(df1, df2, df3).fold(DataFrameHtmlData.tableDefinitions()) { acc, df ->
267+
acc + df.toHtml()
268+
}
269+
```
270+
271+
</tab></tabs>
47272
<!---END-->
48273

49274
## Jupyter Notebooks

samples/build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@ korro {
110110
include("docs/StardustDocs/topics/write.md")
111111
include("docs/StardustDocs/topics/rename.md")
112112
include("docs/StardustDocs/topics/format.md")
113+
include("docs/StardustDocs/topics/toHTML.md")
113114
include("docs/StardustDocs/topics/guides/*.md")
114115
include("docs/StardustDocs/topics/operations/utils/*.md")
115116
include("docs/StardustDocs/topics/operations/multiple/*.md")

0 commit comments

Comments
 (0)