diff --git a/docs/_quarto.yml b/docs/_quarto.yml
index 7b7ae55..6799772 100644
--- a/docs/_quarto.yml
+++ b/docs/_quarto.yml
@@ -1,6 +1,7 @@
project:
type: website
+notebook-links: false
format:
html:
@@ -20,11 +21,7 @@ website:
- text: Get Started
file: get-started/index.qmd
- text: Examples
- menu:
- - demos/twitter-followers.qmd
- - demos/pypi-downloads/index.qmd
- - demos/great-tables.qmd
- - demos/cookbook/index.qmd
+ file: demos/index.qmd
- text: Reference
file: reference/index.qmd
#- href: reference/index.qmd
diff --git a/docs/demos/cookbook/images/beaver.png b/docs/demos/cookbook/images/beaver.png
new file mode 100644
index 0000000..f035ecd
Binary files /dev/null and b/docs/demos/cookbook/images/beaver.png differ
diff --git a/docs/demos/cookbook/images/cow.png b/docs/demos/cookbook/images/cow.png
new file mode 100644
index 0000000..f24a03e
Binary files /dev/null and b/docs/demos/cookbook/images/cow.png differ
diff --git a/docs/demos/cookbook/images/goat.png b/docs/demos/cookbook/images/goat.png
new file mode 100644
index 0000000..b25cefb
Binary files /dev/null and b/docs/demos/cookbook/images/goat.png differ
diff --git a/docs/demos/cookbook/images/wolf.png b/docs/demos/cookbook/images/wolf.png
new file mode 100644
index 0000000..9b1d09c
Binary files /dev/null and b/docs/demos/cookbook/images/wolf.png differ
diff --git a/docs/demos/cookbook/index.qmd b/docs/demos/cookbook/index.qmd
new file mode 100644
index 0000000..c0009c9
--- /dev/null
+++ b/docs/demos/cookbook/index.qmd
@@ -0,0 +1,645 @@
+---
+title: Cookbook
+resources:
+ - "images/beaver.png"
+ - "images/cow.png"
+ - "images/goat.png"
+ - "images/wolf.png"
+jupyter: python3
+
+---
+
+
+```{python}
+# | label: setup
+from reactable import embed_css
+
+embed_css()
+```
+
+## Insert links
+
+
+```{python}
+# | label: insert-links
+import polars as pl
+import htmltools as ht
+
+from reactable import Reactable, Column
+
+data = pl.DataFrame(
+ {
+ "Address": ["https://google.com", "https://yahoo.com", "https://duckduckgo.com"],
+ "Site": ["Google", "Yahoo", "DuckDuckGo"],
+ }
+)
+
+Reactable(
+ data,
+ columns={
+ "Address": Column(
+ cell=lambda info: ht.a(info.value, href=info.value, target="_blank"),
+ ),
+ "Site": Column(
+ html=True,
+ cell=lambda info: f'{info.value}',
+ ),
+ },
+)
+
+```
+
+## Format color scales
+
+### Single column
+
+```{python}
+# | label: format-color-scales
+from reactable import Reactable, Column, CellInfo
+from reactable.data import cars_93
+
+from mizani.palettes import gradient_n_pal
+
+
+data = cars_93[["manufacturer", "model", "price"]]
+
+pal = gradient_n_pal(["#ffe4cc", "#ff9500"])
+
+
+def fmt_fill(ci: CellInfo):
+ val_range = max(data["price"]) - min(data["price"])
+ normalized = (ci.value - min(data["price"])) / val_range
+ return {"background": pal(normalized)}
+
+
+Reactable(
+ data,
+ columns={"price": Column(style=fmt_fill)},
+ default_page_size=5,
+)
+```
+
+### Grid
+
+```{python}
+# | label: format-color-scales2
+from reactable import Reactable, Column, ColFormat, CellInfo
+from reactable.data import nottem
+
+from mizani.palettes import gradient_n_pal
+
+pal = gradient_n_pal(["#7fb7d7", "#ffffbf", "#fc8d59"])
+
+# flatten out monthly columns into a single list
+# this lets us calculate the overall min and max
+flat_vals = sum(nottem[:, 1:].to_dict().values(), [])
+
+
+def fmt_fill(ci: CellInfo):
+ if not isinstance(ci.value, float):
+ return
+
+ val_range = max(flat_vals) - min(flat_vals)
+ normalized = (ci.value - min(flat_vals)) / val_range
+ color = pal(normalized)
+
+ return {"background": color}
+
+
+Reactable(
+ nottem,
+ default_col_def=Column(
+ style=fmt_fill,
+ format=ColFormat(digits=1),
+ min_width=50,
+ ),
+ # TODO: make year rowname
+ columns={
+ "year": Column(
+ format=ColFormat(digits=0),
+ row_header=True,
+ ),
+ },
+ bordered=True,
+ default_page_size=5,
+)
+
+```
+
+## Format changes
+
+```{python}
+# | label: format-changes
+import polars as pl
+from reactable import Reactable, Column, CellInfo
+
+data = pl.DataFrame(
+ {
+ "Symbol": ["GOOG", "FB", "AMZN", "NFLX", "TSLA"],
+ "Price": [1265.13, 187.89, 1761.33, 276.82, 328.13],
+ "Change": [4.14, 1.51, -19.45, 5.32, -12.45],
+ }
+)
+
+Reactable(
+ data,
+ columns={
+ "Change": Column(
+ # TODO: we should stringify, so people can
+ # return ci.value directly
+ cell=lambda ci: f"+{ci.value}" if ci.value >= 0 else str(ci.value),
+ style=lambda ci: {
+ "font-weight": 600,
+ "color": "#008000" if ci.value > 0 else "#e00000",
+ },
+ )
+ },
+)
+```
+
+## Format tags and badges
+
+
+```{python}
+import polars as pl
+from reactable import Reactable, Column, CellInfo
+
+orders = pl.DataFrame(
+ {
+ "Order": [2300, 2301, 2302, 2303, 2304],
+ "Created": ["2019-04-01", "2019-04-02", "2019-04-03", "2019-04-04", "2019-04-05"],
+ # "Customer": ["Degas", "Cezanne", "Monet", "Manet", "Renoir"],
+ "Status": ["Pending", "Paid", "Canceled", "Pending", "Paid"],
+ }
+)
+
+tbl = Reactable(
+ orders,
+ columns={
+ "Status": Column(
+ cell=lambda ci: f'{ci.value}',
+ html=True,
+ )
+ },
+)
+```
+
+```{python}
+# | label: format-tags
+from IPython.display import display, HTML
+
+display(
+ HTML(
+ """
+
+"""
+ )
+)
+
+tbl
+```
+
+
+
+
+
+```{python}
+# | label: format-badge
+import htmltools
+
+
+def status_badge(color="#aaa", width="0.55rem", height=None):
+ height = height or width
+ return htmltools.span(
+ style=(
+ "display: inline-block;"
+ "margin-right: 0.5rem;"
+ f"width: {width};"
+ f"height: {height};"
+ f"background-color: {color};"
+ "border-radius: 50%"
+ )
+ )
+
+
+status_hsl = {
+ "Paid": "hsl(214, 45%, 50%)",
+ "Pending": "hsl(30, 97%, 70%)",
+ "Canceled": "hsl(3, 69%, 50%)",
+}
+
+Reactable(
+ orders,
+ columns={
+ "Status": Column(
+ cell=lambda ci: htmltools.div(status_badge(color=status_hsl[ci.value]), str(ci.value)),
+ html=True,
+ )
+ },
+)
+```
+
+## Bar charts
+
+
+```{python}
+# | label: bar-charts
+import htmltools
+
+from reactable import Reactable, Column, CellInfo
+from reactable.data import cars_93
+
+data = cars_93[:5, ["make", "mpg_city", "mpg_highway"]]
+
+
+def html_barchart(label, width="100%", height="1rem", fill="#00bfc4", background=None):
+ """Create general purpose html fill bar."""
+
+ bar = htmltools.div(style=f"background: {fill}; width: {width}; height: {height}")
+ chart = htmltools.div(
+ bar,
+ style=htmltools.css(
+ flex_grow=1,
+ margin_left="0.5rem",
+ background=background,
+ ),
+ )
+ return htmltools.div(
+ label,
+ chart,
+ style=htmltools.css(
+ display="flex",
+ align_items="center",
+ ),
+ )
+
+
+def fmt_barchart(ci: CellInfo, **kwargs):
+ """Format cell value into html fill bar."""
+
+ width = f"{ci.value / max(data['mpg_city']) * 100}%"
+ return html_barchart(ci.value, width=width, **kwargs)
+
+
+Reactable(
+ data,
+ columns={
+ "mpg_city": Column(
+ name="MPG (city)",
+ align="left",
+ cell=fmt_barchart,
+ ),
+ "mpg_highway": Column(
+ name="MPG (highway)",
+ align="left",
+ cell=lambda ci: fmt_barchart(ci, fill="#fc5185", background="#e1e1e1"),
+ ),
+ },
+ default_page_size=5,
+)
+```
+
+
+
+### Positive and negative values
+
+TODO
+
+### Background bar charts
+
+TODO
+
+## Embed images
+
+
+```{python}
+# | label: embed-images
+import polars as pl
+import htmltools
+
+from reactable import Reactable, Column, CellInfo
+
+data = pl.DataFrame(
+ {
+ "Animal": ["beaver", "cow", "wolf", "goat"],
+ "Body": [1.35, 465, 36.33, 27.66],
+ "Brain": [8.1, 423, 119.5, 115],
+ }
+)
+
+
+def fmt_image(ci: CellInfo):
+ image = htmltools.img(
+ src=f"/demos/cookbook/images/{ci.value}.png",
+ style="height: 24px;",
+ alt=ci.value,
+ )
+ return htmltools.TagList(
+ htmltools.div(
+ image,
+ style="display: inline-block; width: 45px;",
+ ),
+ ci.value,
+ )
+
+
+Reactable(
+ data,
+ columns={
+ "Animal": Column(cell=fmt_image),
+ "Body": Column(name="Body (kg)"),
+ "Brain": Column(name="Brain (g)"),
+ },
+)
+
+```
+
+Note that this example assumes the images are available (we did that by setting the `resources:` field in quarto).
+
+## Rating stars
+
+
+```{python}
+# | label: rating-stars
+# pip install faicons
+import polars as pl
+import htmltools
+
+from faicons import icon_svg
+from reactable import Reactable, Column, CellInfo
+
+ratings = pl.DataFrame(
+ {
+ "Movie": [
+ "Silent Serpent",
+ "Nowhere to Hyde",
+ "The Ape-Man Goes to Mars",
+ "A Menace in Venice",
+ ],
+ "Rating": [3.65, 2.35, 4.5, 1.4],
+ "Votes": [115, 37, 60, 99],
+ }
+)
+
+
+def rating_stars(ci: CellInfo):
+ to_fill = round(ci.value)
+ # TODO: how to set aria?
+ stars = [
+ icon_svg(
+ "star", stroke="orange" if ii <= to_fill else "#edf0f2", stroke_width=100, fill="white"
+ )
+ for ii in range(1, 6)
+ ]
+ return htmltools.div(*stars, title="{ci.value} out of 5 stars")
+
+
+Reactable(
+ ratings,
+ columns={
+ "Rating": Column(
+ cell=rating_stars,
+ html=True,
+ )
+ },
+)
+
+
+
+```
+
+
+## Show data from other columns
+
+```{python}
+#| label: show-data-from-other-columns
+import htmltools
+
+from reactable import Reactable, Column, CellInfo
+from reactable.data import starwars
+
+data = starwars[["name", "height", "mass", "gender", "homeworld", "species"]]
+
+
+def fmt_name(ci: CellInfo):
+ species = data["species"][ci.row_index]
+ species = species if species is not None else "Unknown"
+
+ return htmltools.div(
+ htmltools.div(ci.value, style="font-weight: 600;"),
+ htmltools.div(species, style="font-size: 0.75rem;"),
+ )
+
+
+Reactable(
+ data,
+ columns={
+ "name": Column(
+ cell=fmt_name,
+ name="Character",
+ ),
+ "species": Column(show=False),
+ },
+ default_col_def=Column(v_align="center"),
+ default_page_size=6,
+)
+```
+
+
+```{python}
+from reactable import Reactable, Column, JS
+from reactable.data import starwars
+
+data = starwars[["name", "height", "mass", "gender", "homeworld", "species"]]
+
+js_name = JS(
+ """
+ function(cellInfo) {
+ const species = cellInfo.row["species"] || "Unknown"
+ return `
+
+
${cellInfo.value}
+
${species}
+
+ `
+ }
+ """
+)
+
+Reactable(
+ data,
+ columns={
+ "name": Column(
+ cell=js_name,
+ html=True,
+ ),
+ "species": Column(show=False),
+ },
+ default_col_def=Column(v_align="center"),
+ default_page_size=6,
+)
+
+```
+
+## Total rows
+
+### Fixed
+
+```{python}
+# | label: total-rows
+from reactable import Reactable, Column
+from reactable.data import cars_93
+
+data = cars_93[["manufacturer", "model", "type", "price"]]
+
+Reactable(
+ data,
+ default_page_size=5,
+ columns={
+ "manufacturer": Column(footer="Total"),
+ "price": Column(footer=f"${sum(data['price']):.2f}"),
+ },
+ default_col_def=Column(footer_style={"font-weight": "bold"}),
+)
+```
+
+### Dynamic
+
+```{python}
+from reactable import JS
+
+js_sum_price = JS(
+ """
+ function(column, state) {
+ let total = 0
+ state.sortedData.forEach(function(row) {
+ total += row[column.id]
+ })
+ return total.toLocaleString('en-US', { style: 'currency', currency: 'USD' })
+ }
+ """
+)
+
+Reactable(
+ data,
+ searchable=True,
+ default_page_size=5,
+ min_rows=5,
+ columns={
+ "manufacturer": Column(footer="Total"),
+ "price": Column(footer=js_sum_price),
+ },
+ default_col_def=Column(footer_style={"font-weight": "bold"}),
+)
+```
+
+## Nested tables
+
+```{python}
+# | label: nested-tables
+import polars as pl
+import polars.selectors as cs
+
+from reactable import Reactable, Column
+from reactable.data import us_expenditures
+
+data = (
+ us_expenditures.to_polars()
+ # tidy years from columns into rows
+ .unpivot(cs.starts_with("19"), index="index")
+)
+
+year_dfs = list(g for k, g in data.group_by("variable"))
+summary_df = data.group_by("variable").agg(n=pl.count())
+
+Reactable(
+ summary_df,
+ # TODO: details should accept a function
+ details=Column(
+ details=lambda ri: Reactable(year_dfs[ri.row_index]).to_widget(),
+ ),
+)
+```
+
+## Units on first row only
+
+```{python}
+# | label: units-on-first-row
+from reactable.data import cars_93
+from reactable import Reactable, Column
+
+data = cars_93[40:44, ["make", "length", "luggage_room"]]
+
+
+def fmt_length(ci):
+ return f"{ci.value}″"
+
+
+def fmt_ft(ci):
+ return f"{ci.value} ft³
"
+
+
+Reactable(
+ data,
+ class_="car-specs",
+ columns={
+ "length": Column(
+ cell=lambda ci: fmt_length(ci) if ci.row_index == 0 else str(ci.value),
+ class_="number",
+ ),
+ "luggage_room": Column(
+ name="Luggage Room",
+ cell=lambda ci: fmt_ft(ci) if ci.row_index == 0 else str(ci.value),
+ html=True,
+ ),
+ },
+)
+```
+
+## Tooltips
+
+## Highlight cells
+
+## Highlight columns
+
+## Highlight rows
+
+## Highlight sorted headers
+
+## Highlight sorted columns
+
+## Borders between groups of data
+
+## Merge cells
+
+## Borders between columns
+
+## Style nested rows
+
+## Custom fonts
+
+## Custom sort indicators
+
diff --git a/docs/demos/demo_snippets/pypi-downloads.qmd b/docs/demos/demo_snippets/pypi-downloads.qmd
new file mode 100644
index 0000000..2e03160
--- /dev/null
+++ b/docs/demos/demo_snippets/pypi-downloads.qmd
@@ -0,0 +1,10 @@
+---
+pagetitle: PyPI Downloads
+jupyter: python3
+navbar: false
+---
+
+:::{.shrink-example}
+{{< embed ../pypi-downloads/index.qmd#setup >}}
+{{< embed ../pypi-downloads/index.qmd#table >}}
+:::
\ No newline at end of file
diff --git a/docs/demos/demo_snippets/twitter-followers.qmd b/docs/demos/demo_snippets/twitter-followers.qmd
new file mode 100644
index 0000000..afe31af
--- /dev/null
+++ b/docs/demos/demo_snippets/twitter-followers.qmd
@@ -0,0 +1,11 @@
+---
+pagetitle: Twitter followers
+navbar: false
+jupyter: python3
+---
+
+:::{.shrink-example}
+{{< embed ../twitter-followers.qmd#setup >}}
+{{< embed ../twitter-followers.qmd#css >}}
+{{< embed ../twitter-followers.qmd#table >}}
+:::
diff --git a/docs/demos/index.qmd b/docs/demos/index.qmd
new file mode 100644
index 0000000..87f2df2
--- /dev/null
+++ b/docs/demos/index.qmd
@@ -0,0 +1,125 @@
+---
+pagetitle: Examples
+notebook-links: false
+---
+
+
+
+
+
+
+
+
+{{< embed cookbook/index.qmd#setup >}}
+
+:::::: {.column-page}
+
+## Demos
+
+::::: {.grid}
+:::{.g-col-lg-6 .g-col-12 .example}
+### [PyPI downloads](./pypi-downloads)
+
+:::
+
+:::{.g-col-lg-6 .g-col-12 .example}
+### [Twitter followers](./twitter-followers.qmd)
+
+:::
+
+:::::
+
+::::: {.grid}
+
+:::{.g-col-lg-3 .g-col-12 .example .shrink-example}
+
+
+
+:::
+
+:::::
+
+
+## Cookbook examples
+
+::::: {.grid}
+
+:::{.g-col-lg-3 .g-col-12 .example .shrink-example}
+## [Insert links](./cookbook/index.qmd#insert-links)
+{{< embed cookbook/index.qmd#insert-links >}}
+:::
+
+:::{.g-col-lg-3 .g-col-12 .example .shrink-example}
+## [Format colors](./cookbook/index.qmd#single-column)
+{{< embed cookbook/index.qmd#format-color-scales >}}
+:::
+
+:::{.g-col-lg-3 .g-col-12 .example .shrink-example}
+## [Format colors (2)](./cookbook/index.qmd#grid)
+{{< embed cookbook/index.qmd#format-color-scales2 >}}
+:::
+
+
+:::{.g-col-lg-3 .g-col-12 .example .shrink-example}
+## [Format changes](./cookbook/index.qmd#format-changes)
+{{< embed cookbook/index.qmd#format-changes >}}
+:::
+
+:::{.g-col-lg-3 .g-col-12 .example .shrink-example}
+## [Format tags](./cookbook/index.qmd#format-tags-and-badges)
+{{< embed cookbook/index.qmd#format-tags >}}
+:::
+
+:::{.g-col-lg-3 .g-col-12 .example .shrink-example}
+## [Format badge](./cookbook/index.qmd#format-tags-and-badges)
+{{< embed cookbook/index.qmd#format-badge >}}
+:::
+
+:::{.g-col-lg-3 .g-col-12 .example .shrink-example}
+## [Bar charts](./cookbook/index.qmd#bar-charts)
+{{< embed cookbook/index.qmd#bar-charts >}}
+:::
+
+:::{.g-col-lg-3 .g-col-12 .example .shrink-example}
+## [Embed images](./cookbook/index.qmd#embed-images)
+{{< embed cookbook/index.qmd#embed-images >}}
+:::
+
+:::{.g-col-lg-3 .g-col-12 .example .shrink-example}
+## [Rating stars](./cookbook/index.qmd#rating-stars)
+{{< embed cookbook/index.qmd#rating-stars >}}
+:::
+
+:::{.g-col-lg-3 .g-col-12 .example .shrink-example}
+## [Combine cols](./cookbook/index.qmd#show-data-from-other-columns)
+{{< embed cookbook/index.qmd#show-data-from-other-columns >}}
+:::
+
+:::{.g-col-lg-3 .g-col-12 .example .shrink-example}
+## [Total rows](./cookbook/index.qmd#total-rows)
+{{< embed cookbook/index.qmd#total-rows >}}
+:::
+
+:::{.g-col-lg-3 .g-col-12 .example .shrink-example}
+## [Nested tables](./cookbook/index.qmd#nested-tables)
+{{< embed cookbook/index.qmd#nested-tables >}}
+:::
+
+:::{.g-col-lg-3 .g-col-12 .example .shrink-example}
+## [Units on first row](./cookbook/index.qmd#units-on-first-row)
+{{< embed cookbook/index.qmd#units-on-first-row >}}
+:::
+
+
+:::::
+::::::
+
+
diff --git a/docs/demos/pypi-downloads/index.qmd b/docs/demos/pypi-downloads/index.qmd
index da4691a..2f11cf0 100644
--- a/docs/demos/pypi-downloads/index.qmd
+++ b/docs/demos/pypi-downloads/index.qmd
@@ -7,6 +7,7 @@ format:
---
```{python}
+# | label: setup
# | echo: false
import json
import polars as pl
@@ -159,18 +160,26 @@ html = """
```
```{python}
-#| echo: false
+# | label: css
+# | echo: false
from IPython.display import HTML, display
display(HTML(html))
```
+
```{python}
# | echo: false
+# | label: table
+
+from IPython.display import HTML, display
+
+display(HTML(html))
+
tbl = Reactable(
outer.head(50),
default_sorted=["downloads_month"],
- default_page_size=20,
+ default_page_size=10,
show_page_size_options=True,
page_size_options=[10, 20, 50, 100],
on_click="expand",
@@ -199,7 +208,6 @@ tbl = Reactable(
theme=Theme(cell_padding="8px 12px"),
)
-
to_widget(
ht.div(
# ht.h2("Top PyPI Monthly Downloads (Aug 1, 2024)"),
@@ -225,3 +233,10 @@ print_pre("python", code_cell)
print_pre("html", html)
```
+
+
+```{python}
+# | label: zzz
+print("yo2")
+
+```
\ No newline at end of file
diff --git a/docs/demos/twitter-followers.qmd b/docs/demos/twitter-followers.qmd
index 18b004d..10dce3a 100644
--- a/docs/demos/twitter-followers.qmd
+++ b/docs/demos/twitter-followers.qmd
@@ -6,6 +6,11 @@ execute:
daemon: false
---
+```{python}
+# | label: css
+from IPython.display import display, HTML
+
+html = """
+"""
+
+display(HTML(html))
+```
+
```{python}
+# | label: setup
+from reactable import embed_css
+
+embed_css()
-from reactable import bigblock, embed_css
-from reactable.models import Column, Props, CellInfo, JS
+```
+```{python}
+# | label: table
+from reactable import Reactable, embed_css
+from reactable.tags import to_widget
+from reactable.models import Column, CellInfo, JS
from htmltools import tags
import polars as pl
-embed_css()
data = pl.read_csv("twitter_followers.csv")
@@ -106,71 +123,72 @@ def f_followers(ci: CellInfo):
)
-tbl = bigblock(
- Props(
- data,
- pagination=False,
- default_sorted=["exclusive_followers_pct"],
- default_col_def=Column(header_class="header", align="left"),
- columns=dict(
- account=Column(
- cell=lambda ci: (
- tags.a(
- f"@{ci.value}",
- href=f"https://twitter.com/{ci.value}",
- target="_blank",
- )
- ),
- width=150,
- ),
- followers=Column(default_sort_order="desc", cell=f_followers),
- exclusive_followers_pct=Column(
- name="Exclusive Followers",
- default_sort_order="desc",
- cell=JS(
- """function(cellInfo) {
- // Format as percentage
- const pct = (cellInfo.value * 100).toFixed(1) + "%"
- // Pad single-digit numbers
- let value = pct.padStart(5)
- // Show % on first row only
- if (cellInfo.viewIndex > 0) {
- value = value.replace("%", " ")
- }
- // Render bar chart
- return `
-
-
${value}
-
+js_exclusive_percent = JS(
+ """
+ function(cellInfo) {
+ // Format as percentage
+ const pct = (cellInfo.value * 100).toFixed(1) + "%"
+ // Pad single-digit numbers
+ let value = pct.padStart(5)
+ // Show % on first row only
+ if (cellInfo.viewIndex > 0) {
+ value = value.replace("%", " ")
+ }
+ // Render bar chart
+ return `
+
+
${value}
+
- `
- }"""
- ),
- html=True,
+
+ `
+ }"""
+)
+
+tbl = Reactable(
+ data,
+ pagination=False,
+ default_sorted=["exclusive_followers_pct"],
+ default_col_def=Column(header_class="header", align="left"),
+ columns=dict(
+ account=Column(
+ cell=lambda ci: (
+ tags.a(
+ f"@{ci.value}",
+ href=f"https://twitter.com/{ci.value}",
+ target="_blank",
+ )
),
+ width=150,
),
- compact=True,
- class_="followers-tbl",
- static=True,
- )
+ followers=Column(default_sort_order="desc", cell=f_followers),
+ exclusive_followers_pct=Column(
+ name="Exclusive Followers",
+ default_sort_order="desc",
+ cell=js_exclusive_percent,
+ html=True,
+ ),
+ ),
+ compact=True,
+ class_="followers-tbl",
+ static=True,
)
-# tags.div(
-# tags.div(
-# tags.h2("Candidates whose followers are loyal only to them", class_="followers-title"),
-# "Share of each 2020 candidate's followers who don't follow any other candidates",
-# class_="followers-header",
-# ),
-# tbl,
-# class_="twitter-followers",
-# )
-
-tbl
+to_widget(
+ tags.div(
+ tags.div(
+ tags.h2("Candidates whose followers are loyal only to them", class_="followers-title"),
+ "Share of each 2020 candidate's followers who don't follow any other candidates",
+ class_="followers-header",
+ ),
+ tbl,
+ class_="twitter-followers",
+ )
+)
```
----
+
Source: [FiveThirtyEight](https://fivethirtyeight.com/features/which-2020-candidates-have-the-most-in-common-on-twitter/)
@@ -180,4 +198,17 @@ How it was made: [Building the Twitter Followers Demo](../building-twitter-follo
Source Code
-* TODO: how to show code and css down here?
\ No newline at end of file
+
+```{python}
+# | output: asis
+# | echo: false
+code_cell = _ih[-2]
+
+
+def print_pre(name, code):
+ print(f"\n```{name}\n{code}\n```\n\n")
+
+
+print_pre("python", code_cell)
+print_pre("html", html)
+```
\ No newline at end of file
diff --git a/docs/styles.css b/docs/styles.css
index fa137bd..c907082 100644
--- a/docs/styles.css
+++ b/docs/styles.css
@@ -70,4 +70,10 @@ div.sidebar-item-container .active {
.sidebar-section {
padding-left: 0px !important;
+}
+
+/* Examples */
+
+.shrink-example .cell-output {
+ zoom: 75%;
}
\ No newline at end of file
diff --git a/reactable/data/nottem.csv b/reactable/data/nottem.csv
new file mode 100644
index 0000000..c4cdfeb
--- /dev/null
+++ b/reactable/data/nottem.csv
@@ -0,0 +1,21 @@
+year,Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec
+1920,40.6,40.8,44.4,46.7,54.1,58.5,57.7,56.4,54.3,50.5,42.9,39.8
+1921,44.2,39.8,45.1,47,54.1,58.7,66.3,59.9,57,54.2,39.7,42.8
+1922,37.5,38.7,39.5,42.1,55.7,57.8,56.8,54.3,54.3,47.1,41.8,41.7
+1923,41.8,40.1,42.9,45.8,49.2,52.7,64.2,59.6,54.4,49.2,36.3,37.6
+1924,39.3,37.5,38.3,45.5,53.2,57.7,60.8,58.2,56.4,49.8,44.4,43.6
+1925,40,40.5,40.8,45.1,53.8,59.4,63.5,61,53,50,38.1,36.3
+1926,39.2,43.4,43.4,48.9,50.6,56.8,62.5,62,57.5,46.7,41.6,39.8
+1927,39.4,38.5,45.3,47.1,51.7,55,60.4,60.5,54.7,50.3,42.3,35.2
+1928,40.8,41.1,42.8,47.3,50.9,56.4,62.2,60.5,55.4,50.2,43,37.3
+1929,34.8,31.3,41,43.9,53.1,56.9,62.5,60.3,59.8,49.2,42.9,41.9
+1930,41.6,37.1,41.2,46.9,51.2,60.4,60.1,61.6,57,50.9,43,38.8
+1931,37.1,38.4,38.4,46.5,53.5,58.4,60.6,58.2,53.8,46.6,45.5,40.6
+1932,42.4,38.4,40.3,44.6,50.9,57,62.1,63.5,56.3,47.3,43.6,41.8
+1933,36.2,39.3,44.5,48.7,54.2,60.8,65.5,64.9,60.1,50.2,42.1,35.8
+1934,39.4,38.2,40.4,46.9,53.4,59.6,66.5,60.4,59.2,51.2,42.8,45.8
+1935,40,42.6,43.5,47.1,50,60.5,64.6,64,56.8,48.6,44.2,36.4
+1936,37.3,35,44,43.9,52.7,58.6,60,61.1,58.1,49.6,41.6,41.3
+1937,40.8,41,38.4,47.4,54.1,58.6,61.4,61.8,56.3,50.9,41.4,37.1
+1938,42.1,41.2,47.3,46.6,52.4,59,59.6,60.4,57,50.7,47.8,39.2
+1939,39.4,40.9,42.4,47.8,52.4,58,60.7,61.8,58.2,46.7,46.6,37.8