diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml
index 61b03c5..ac99773 100644
--- a/.github/workflows/docs.yml
+++ b/.github/workflows/docs.yml
@@ -18,6 +18,7 @@ jobs:
python-version: "3.10"
- name: Install dependencies
run: |
+ python -m pip install "pip3 install git+https://github.com/posit-dev/great-tables.git@feat-interactive"
python -m pip install ".[dev]"
- uses: quarto-dev/quarto-actions/setup@v2
with:
diff --git a/docs/_quarto.yml b/docs/_quarto.yml
index 0d796d4..b8cfc17 100644
--- a/docs/_quarto.yml
+++ b/docs/_quarto.yml
@@ -12,6 +12,7 @@ website:
- text: Demos
menu:
- demos/twitter-followers.qmd
+ - demos/great-tables.qmd
#- href: reference/index.qmd
# text: Reference
right:
diff --git a/docs/demos/coffee-sales/aeropress.png b/docs/demos/coffee-sales/aeropress.png
new file mode 100644
index 0000000..739f435
Binary files /dev/null and b/docs/demos/coffee-sales/aeropress.png differ
diff --git a/docs/demos/coffee-sales/cezve.png b/docs/demos/coffee-sales/cezve.png
new file mode 100644
index 0000000..ffacaef
Binary files /dev/null and b/docs/demos/coffee-sales/cezve.png differ
diff --git a/docs/demos/coffee-sales/chemex.png b/docs/demos/coffee-sales/chemex.png
new file mode 100644
index 0000000..c3a2f2a
Binary files /dev/null and b/docs/demos/coffee-sales/chemex.png differ
diff --git a/docs/demos/coffee-sales/coffee-sales.json b/docs/demos/coffee-sales/coffee-sales.json
new file mode 100644
index 0000000..43db02c
--- /dev/null
+++ b/docs/demos/coffee-sales/coffee-sales.json
@@ -0,0 +1 @@
+{"columns":[{"name":"icon","datatype":"String","bit_settings":"","values":["grinder.png","moka-pot.png","cold-brew.png","filter.png","drip-machine.png","aeropress.png","pour-over.png","french-press.png","cezve.png","chemex.png","scale.png","kettle.png","espresso-machine.png",null]},{"name":"product","datatype":"String","bit_settings":"","values":["Grinder","Moka pot","Cold brew","Filter","Drip machine","AeroPress","Pour over","French press","Cezve","Chemex","Scale","Kettle","Espresso Machine","Total"]},{"name":"revenue_dollars","datatype":"Float64","bit_settings":"","values":[904500.0,2045250.0,288750.0,404250.0,2632000.0,2601500.0,846000.0,1113250.0,2512500.0,3137250.0,3801000.0,756250.0,8406000.0,29448500.0]},{"name":"revenue_pct","datatype":"Float64","bit_settings":"","values":[0.03,0.07,0.01,0.01,0.09,0.09,0.03,0.04,0.09,0.11,0.13,0.03,0.29,1.0]},{"name":"profit_dollars","datatype":"Float64","bit_settings":"","values":[567960.0,181080.0,241770.0,70010.0,1374450.0,1293780.0,364530.0,748120.0,1969520.0,817680.0,2910290.0,617520.0,3636440.0,14793150.0]},{"name":"profit_pct","datatype":"Float64","bit_settings":"","values":[0.04,0.01,0.02,0.0,0.09,0.09,0.02,0.05,0.13,0.06,0.2,0.04,0.25,1.0]},{"name":"monthly_sales","datatype":{"List":"Int64"},"bit_settings":"","values":[{"name":"","datatype":"Int64","bit_settings":"","values":[521,494,596,613,667,748,765,686,607,594,568,751]},{"name":"","datatype":"Int64","bit_settings":"","values":[4726,4741,4791,5506,6156,6619,6868,6026,5304,4884,4648,6283]},{"name":"","datatype":"Int64","bit_settings":"","values":[244,249,438,981,1774,2699,2606,2348,1741,896,499,244]},{"name":"","datatype":"Int64","bit_settings":"","values":[2067,1809,1836,2123,2252,2631,2562,2367,2164,2195,2070,2744]},{"name":"","datatype":"Int64","bit_settings":"","values":[2137,1623,1971,2097,2580,2456,2336,2316,2052,1967,1837,2328]},{"name":"","datatype":"Int64","bit_settings":"","values":[6332,5199,6367,7024,7906,8704,8693,7797,6828,6963,6877,9270]},{"name":"","datatype":"Int64","bit_settings":"","values":[1562,1291,1511,1687,1940,2177,2141,1856,1715,1806,1601,2165]},{"name":"","datatype":"Int64","bit_settings":"","values":[3507,2880,3346,3792,3905,4095,4184,4428,3279,3420,3297,4819]},{"name":"","datatype":"Int64","bit_settings":"","values":[12171,11469,11788,13630,15391,16532,17090,14433,12985,12935,11598,15895]},{"name":"","datatype":"Int64","bit_settings":"","values":[4938,4167,5235,6000,6358,6768,7112,6249,5605,6076,4980,7220]},{"name":"","datatype":"Int64","bit_settings":"","values":[1542,1566,1681,2028,2425,2549,2569,2232,2036,2089,1693,3180]},{"name":"","datatype":"Int64","bit_settings":"","values":[1139,1023,1087,1131,1414,1478,1456,1304,1140,1233,1193,1529]},{"name":"","datatype":"Int64","bit_settings":"","values":[686,840,618,598,2148,533,797,996,1002,668,858,2577]},null]}]}
\ No newline at end of file
diff --git a/docs/demos/coffee-sales/coffee-sales.parquet b/docs/demos/coffee-sales/coffee-sales.parquet
new file mode 100644
index 0000000..7399ec8
Binary files /dev/null and b/docs/demos/coffee-sales/coffee-sales.parquet differ
diff --git a/docs/demos/coffee-sales/coffee-table.png b/docs/demos/coffee-sales/coffee-table.png
new file mode 100644
index 0000000..093380f
Binary files /dev/null and b/docs/demos/coffee-sales/coffee-table.png differ
diff --git a/docs/demos/coffee-sales/cold-brew.png b/docs/demos/coffee-sales/cold-brew.png
new file mode 100644
index 0000000..b506857
Binary files /dev/null and b/docs/demos/coffee-sales/cold-brew.png differ
diff --git a/docs/demos/coffee-sales/drip-machine.png b/docs/demos/coffee-sales/drip-machine.png
new file mode 100644
index 0000000..a0b6914
Binary files /dev/null and b/docs/demos/coffee-sales/drip-machine.png differ
diff --git a/docs/demos/coffee-sales/espresso-machine.png b/docs/demos/coffee-sales/espresso-machine.png
new file mode 100644
index 0000000..3a09e5c
Binary files /dev/null and b/docs/demos/coffee-sales/espresso-machine.png differ
diff --git a/docs/demos/coffee-sales/filter.png b/docs/demos/coffee-sales/filter.png
new file mode 100644
index 0000000..1e6b84d
Binary files /dev/null and b/docs/demos/coffee-sales/filter.png differ
diff --git a/docs/demos/coffee-sales/french-press.png b/docs/demos/coffee-sales/french-press.png
new file mode 100644
index 0000000..1c30b65
Binary files /dev/null and b/docs/demos/coffee-sales/french-press.png differ
diff --git a/docs/demos/coffee-sales/grinder.png b/docs/demos/coffee-sales/grinder.png
new file mode 100644
index 0000000..575439a
Binary files /dev/null and b/docs/demos/coffee-sales/grinder.png differ
diff --git a/docs/demos/coffee-sales/kettle.png b/docs/demos/coffee-sales/kettle.png
new file mode 100644
index 0000000..ac1588c
Binary files /dev/null and b/docs/demos/coffee-sales/kettle.png differ
diff --git a/docs/demos/coffee-sales/moka-pot.png b/docs/demos/coffee-sales/moka-pot.png
new file mode 100644
index 0000000..c03f78f
Binary files /dev/null and b/docs/demos/coffee-sales/moka-pot.png differ
diff --git a/docs/demos/coffee-sales/noun-drip-machine-6065915.png b/docs/demos/coffee-sales/noun-drip-machine-6065915.png
new file mode 100644
index 0000000..13ac350
Binary files /dev/null and b/docs/demos/coffee-sales/noun-drip-machine-6065915.png differ
diff --git a/docs/demos/coffee-sales/pour-over.png b/docs/demos/coffee-sales/pour-over.png
new file mode 100644
index 0000000..cbb5489
Binary files /dev/null and b/docs/demos/coffee-sales/pour-over.png differ
diff --git a/docs/demos/coffee-sales/scale.png b/docs/demos/coffee-sales/scale.png
new file mode 100644
index 0000000..21d0817
Binary files /dev/null and b/docs/demos/coffee-sales/scale.png differ
diff --git a/docs/demos/coffee-sales/total.png b/docs/demos/coffee-sales/total.png
new file mode 100644
index 0000000..62fafec
Binary files /dev/null and b/docs/demos/coffee-sales/total.png differ
diff --git a/docs/demos/great-tables.qmd b/docs/demos/great-tables.qmd
new file mode 100644
index 0000000..0f9d9a5
--- /dev/null
+++ b/docs/demos/great-tables.qmd
@@ -0,0 +1,337 @@
+---
+title: Great Tables Gallery
+format:
+ html:
+ code-fold: true
+ code-summary: "Show the Code"
+html-table-processing: none
+jupyter: python3
+---
+
+```{python}
+from react_tables import embed_css, render
+
+embed_css()
+
+```
+
+:::::: {.column-page}
+::::: {.grid}
+
+:::{.g-col-lg-6 .g-col-12}
+
+```{python}
+import polars as pl
+from great_tables import GT, md, html
+from great_tables.data import islands
+from react_tables import render
+
+
+islands_mini = pl.from_pandas(islands).sort("size", descending=True).head(10)
+
+gt = (
+ GT(islands_mini)
+ .tab_header(title="Large Landmasses of the World", subtitle="The top ten largest are presented")
+ .tab_stub(rowname_col="name")
+ .tab_source_note(source_note="Source: The World Almanac and Book of Facts, 1975, page 406.")
+ .tab_source_note(
+ source_note=md("Reference: McNeil, D. R. (1977) *Interactive Data Analysis*. Wiley.")
+ )
+ .tab_stubhead(label="landmass")
+ .fmt_integer(columns="size")
+)
+
+render(gt)
+```
+
+
+:::
+
+:::{.g-col-lg-6 .g-col-12}
+
+```{python}
+import polars as pl
+
+from great_tables import GT, html
+from great_tables.data import airquality
+
+airquality_mini = airquality.head(10).assign(Year=1973)
+
+gt = (
+ GT(pl.from_pandas(airquality_mini))
+ .tab_header(
+ title="New York Air Quality Measurements",
+ subtitle="Daily measurements in New York City (May 1-10, 1973)",
+ )
+ .tab_spanner(label="Time", columns=["Year", "Month", "Day"])
+ .tab_spanner(label="Measurement", columns=["Ozone", "Solar_R", "Wind", "Temp"])
+ .cols_move_to_start(columns=["Year", "Month", "Day"])
+ .cols_label(
+ Ozone=html("Ozone,
ppbV"),
+ Solar_R=html("Solar R.,
cal/m2"),
+ Wind=html("Wind,
mph"),
+ Temp=html("Temp,
°F"),
+ )
+)
+
+render(gt)
+```
+
+:::
+
+:::{.g-col-lg-6 .g-col-12}
+
+```{python}
+from great_tables import GT
+from great_tables.data import countrypops
+import polars as pl
+import polars.selectors as cs
+
+# Get vectors of 2-letter country codes for each region of Oceania
+oceania = {
+ "Australasia": ["AU", "NZ"],
+ "Melanesia": ["NC", "PG", "SB", "VU"],
+ "Micronesia": ["FM", "GU", "KI", "MH", "MP", "NR", "PW"],
+ "Polynesia": ["PF", "WS", "TO", "TV"],
+}
+
+# Create a dictionary mapping country to region (e.g. AU -> Australasia)
+country_to_region = {
+ country: region for region, countries in oceania.items() for country in countries
+}
+
+wide_pops = (
+ pl.from_pandas(countrypops)
+ .filter(
+ pl.col("country_code_2").is_in(list(country_to_region))
+ & pl.col("year").is_in([2000, 2010, 2020])
+ )
+ .with_columns(pl.col("country_code_2").replace(country_to_region).alias("region"))
+ .filter(pl.col("region").is_in(["Australasia", "Melanesia"]))
+ .pivot(index=["country_name", "region"], columns="year", values="population")
+ .sort("2020", descending=True)
+)
+
+gt = (
+ GT(wide_pops, id="some-table")
+ .tab_header(title="Some title", subtitle="Some subtitle")
+ .tab_spanner(label="Spanner label", columns=cs.all())
+ .tab_stub(rowname_col="country_name", groupname_col="region")
+ .tab_stubhead("Stubhead label")
+ .tab_source_note("footnote1")
+ .tab_source_note("footnote2")
+ .fmt_integer()
+)
+
+gt
+```
+
+:::
+
+:::{.g-col-lg-6 .g-col-12}
+
+```{python}
+from great_tables import GT, html
+from great_tables.data import towny
+
+towny_mini = (
+ towny[["name", "website", "density_2021", "land_area_km2", "latitude", "longitude"]]
+ .sort_values("density_2021", ascending=False)
+ .head(10)
+)
+
+towny_mini["url_name"] = ["["] + towny_mini["name"] + ["]"] + ["("] + towny_mini["website"] + [")"]
+
+towny_mini["location"] = (
+ ["[map](http://maps.google.com/?ie=UTF8&hq=&ll="]
+ + towny_mini["latitude"].astype(str)
+ + [","]
+ + towny_mini["longitude"].astype(str)
+ + ["&z=13)"]
+)
+
+pl_towny = pl.from_pandas(towny_mini[["url_name", "location", "land_area_km2", "density_2021"]])
+
+gt = (
+ GT(
+ pl_towny,
+ rowname_col="url_name",
+ )
+ .tab_header(
+ title="The Municipalities of Ontario",
+ subtitle="The top 10 highest population density in 2021",
+ )
+ .tab_stubhead(label="Municipality")
+ .fmt_markdown(columns=["url_name", "location"])
+ .fmt_number(columns=["land_area_km2", "density_2021"], decimals=1)
+ .cols_label(
+ land_area_km2=html("land area,
km2"),
+ density_2021=html("density,
people/km2"),
+ )
+)
+
+render(gt)
+```
+
+
+:::
+
+:::{.g-col-lg-6 .g-col-12}
+
+```{python}
+from great_tables import GT, html
+from great_tables.data import sza
+import polars as pl
+import polars.selectors as cs
+
+sza_pivot = (
+ pl.from_pandas(sza)
+ .filter((pl.col("latitude") == "20") & (pl.col("tst") <= "1200"))
+ .select(pl.col("*").exclude("latitude"))
+ .drop_nulls()
+ .pivot(values="sza", index="month", columns="tst", sort_columns=True)
+)
+
+gt = (
+ GT(sza_pivot, rowname_col="month")
+ .data_color(
+ domain=[90, 0],
+ palette=["rebeccapurple", "white", "orange"],
+ na_color="white",
+ )
+ .tab_header(
+ title="Solar Zenith Angles from 05:30 to 12:00",
+ subtitle=html("Average monthly values at latitude of 20°N."),
+ )
+ .sub_missing(missing_text="")
+)
+
+render(gt)
+```
+
+:::
+
+:::{.g-col-lg-6 .g-col-12}
+
+```{python}
+import polars as pl
+import polars.selectors as cs
+from great_tables import GT, md
+
+
+def create_bar(prop_fill: float, max_width: int, height: int) -> str:
+ """Create divs to represent prop_fill as a bar."""
+ width = round(max_width * prop_fill, 2)
+ px_width = f"{width}px"
+ return f"""\
+