From 18a42c41fd28d82df1a582444050d053b2c5a582 Mon Sep 17 00:00:00 2001 From: Michael Chow Date: Tue, 6 Aug 2024 17:14:43 -0400 Subject: [PATCH] add examples gallery, nottem dataset --- docs/_quarto.yml | 7 +- docs/demos/cookbook/images/beaver.png | Bin 0 -> 15295 bytes docs/demos/cookbook/images/cow.png | Bin 0 -> 17412 bytes docs/demos/cookbook/images/goat.png | Bin 0 -> 5623 bytes docs/demos/cookbook/images/wolf.png | Bin 0 -> 6124 bytes docs/demos/cookbook/index.qmd | 645 ++++++++++++++++++ docs/demos/demo_snippets/pypi-downloads.qmd | 10 + .../demos/demo_snippets/twitter-followers.qmd | 11 + docs/demos/index.qmd | 125 ++++ docs/demos/pypi-downloads/index.qmd | 21 +- docs/demos/twitter-followers.qmd | 155 +++-- docs/styles.css | 6 + reactable/data/nottem.csv | 21 + 13 files changed, 931 insertions(+), 70 deletions(-) create mode 100644 docs/demos/cookbook/images/beaver.png create mode 100644 docs/demos/cookbook/images/cow.png create mode 100644 docs/demos/cookbook/images/goat.png create mode 100644 docs/demos/cookbook/images/wolf.png create mode 100644 docs/demos/cookbook/index.qmd create mode 100644 docs/demos/demo_snippets/pypi-downloads.qmd create mode 100644 docs/demos/demo_snippets/twitter-followers.qmd create mode 100644 docs/demos/index.qmd create mode 100644 reactable/data/nottem.csv 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 0000000000000000000000000000000000000000..f035ecd36b3df9672e6e4876a6da80fb43a64deb GIT binary patch literal 15295 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 Rh3NnQ002ovPDHLkV1jB+B&z@b literal 0 HcmV?d00001 diff --git a/docs/demos/cookbook/images/cow.png b/docs/demos/cookbook/images/cow.png new file mode 100644 index 0000000000000000000000000000000000000000..f24a03ebc669200d67cf223df7199ab3aef76f60 GIT binary patch literal 17412 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 RYcv1=002ovPDHLkV1l%Dtr`FT literal 0 HcmV?d00001 diff --git a/docs/demos/cookbook/images/wolf.png b/docs/demos/cookbook/images/wolf.png new file mode 100644 index 0000000000000000000000000000000000000000..9b1d09c21bd2cf1b019f0e70b8449d418b969840 GIT binary patch literal 6124 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.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