├── .gitignore ├── .nojekyll ├── 1-welcome ├── 1-welcome.qmd ├── images │ ├── bulk-density.jpg │ ├── cascadia-logo.png │ ├── lydia-gibson.jpeg │ ├── mts.webp │ ├── rangeland.jpg │ └── soil-sampling.jpg └── index.qmd ├── 2-parameters ├── 2-parameters.qmd ├── images │ ├── driver-quality.png │ ├── find-replace-hard-coded-values.png │ ├── ocean-floor.png │ ├── snowshoe-graph.png │ ├── soils-report-html.png │ ├── template1.png │ ├── template2.png │ ├── template3.png │ └── urban-institute.png └── index.qmd ├── 3-render ├── 3-render.qmd ├── images │ ├── other-formats.png │ └── render-button.png └── index.qmd ├── 4-conditionals ├── 4-conditionals.qmd └── index.qmd ├── 5-style ├── 5-style.qmd ├── images │ ├── browser-tools-modified.png │ └── browser-tools.png └── index.qmd ├── 6-summary ├── 6-summary.qmd ├── images │ └── mts.jpg └── index.qmd ├── README.md ├── _extensions └── quarto-ext │ └── fontawesome │ ├── _extension.yml │ ├── assets │ ├── css │ │ ├── all.css │ │ └── latex-fontsize.css │ └── webfonts │ │ ├── FontAwesome6Brands-Regular-400.ttf │ │ ├── FontAwesome6Brands-Regular-400.woff2 │ │ ├── FontAwesome6Free-Regular-400.ttf │ │ ├── FontAwesome6Free-Regular-400.woff2 │ │ ├── FontAwesome6Free-Solid-900.ttf │ │ ├── FontAwesome6Free-Solid-900.woff2 │ │ ├── fa-brands-400.ttf │ │ ├── fa-brands-400.woff2 │ │ ├── fa-regular-400.ttf │ │ ├── fa-regular-400.woff2 │ │ ├── fa-solid-900.ttf │ │ ├── fa-solid-900.woff2 │ │ ├── fa-v4compatibility.ttf │ │ └── fa-v4compatibility.woff2 │ └── fontawesome.lua ├── _freeze ├── 1-welcome │ └── 1-welcome │ │ └── execute-results │ │ └── html.json ├── 2-parameters │ └── 2-parameters │ │ └── execute-results │ │ └── html.json ├── 3-render │ └── 3-render │ │ └── execute-results │ │ └── html.json ├── 4-conditionals │ └── 4-conditionals │ │ └── execute-results │ │ └── html.json ├── 5-style │ └── 5-style │ │ └── execute-results │ │ └── html.json ├── 6-summary │ └── 6-summary │ │ └── execute-results │ │ └── html.json ├── prework │ └── execute-results │ │ └── html.json └── site_libs │ ├── clipboard │ └── clipboard.min.js │ ├── countdown-0.4.0 │ ├── countdown.css │ ├── countdown.js │ └── smb_stage_clear.mp3 │ └── revealjs │ ├── dist │ ├── reset.css │ ├── reveal.css │ ├── reveal.esm.js │ ├── reveal.esm.js.map │ ├── reveal.js │ ├── reveal.js.map │ └── theme │ │ ├── fonts │ │ ├── league-gothic │ │ │ ├── LICENSE │ │ │ ├── league-gothic.css │ │ │ ├── league-gothic.eot │ │ │ ├── league-gothic.ttf │ │ │ └── league-gothic.woff │ │ └── source-sans-pro │ │ │ ├── LICENSE │ │ │ ├── source-sans-pro-italic.eot │ │ │ ├── source-sans-pro-italic.ttf │ │ │ ├── source-sans-pro-italic.woff │ │ │ ├── source-sans-pro-regular.eot │ │ │ ├── source-sans-pro-regular.ttf │ │ │ ├── source-sans-pro-regular.woff │ │ │ ├── source-sans-pro-semibold.eot │ │ │ ├── source-sans-pro-semibold.ttf │ │ │ ├── source-sans-pro-semibold.woff │ │ │ ├── source-sans-pro-semibolditalic.eot │ │ │ ├── source-sans-pro-semibolditalic.ttf │ │ │ ├── source-sans-pro-semibolditalic.woff │ │ │ └── source-sans-pro.css │ │ └── quarto.css │ └── plugin │ ├── highlight │ ├── highlight.esm.js │ ├── highlight.js │ ├── monokai.css │ ├── plugin.js │ └── zenburn.css │ ├── markdown │ ├── markdown.esm.js │ ├── markdown.js │ └── plugin.js │ ├── math │ ├── katex.js │ ├── math.esm.js │ ├── math.js │ ├── mathjax2.js │ ├── mathjax3.js │ └── plugin.js │ ├── notes │ ├── notes.esm.js │ ├── notes.js │ ├── plugin.js │ └── speaker-view.html │ ├── pdf-export │ ├── pdfexport.js │ └── plugin.yml │ ├── quarto-line-highlight │ ├── line-highlight.css │ ├── line-highlight.js │ └── plugin.yml │ ├── quarto-support │ ├── footer.css │ ├── plugin.yml │ └── support.js │ ├── reveal-menu │ ├── menu.css │ ├── menu.js │ ├── plugin.yml │ ├── quarto-menu.css │ └── quarto-menu.js │ ├── search │ ├── plugin.js │ ├── search.esm.js │ └── search.js │ └── zoom │ ├── plugin.js │ ├── zoom.esm.js │ └── zoom.js ├── _publish.yml ├── _quarto.yml ├── data ├── data.R ├── pet-reports.RDS └── pets.RDS ├── exercises └── intermediate-exercises.zip ├── images ├── cascadia-jr-quarto.webp ├── jadey.jpg ├── jr-logo-circle.webp └── jr-logo-quarto.webp ├── index.qmd ├── license.qmd ├── parameterized-quarto-workshop.Rproj ├── prework.qmd ├── slides.scss └── theme.scss /.gitignore: -------------------------------------------------------------------------------- 1 | .Rproj.user 2 | .Rhistory 3 | .RData 4 | .Ruserdata 5 | .DS_Store 6 | /.quarto/ 7 | _site/ 8 | docs/ 9 | .html 10 | -------------------------------------------------------------------------------- /.nojekyll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jadeynryan/parameterized-quarto-workshop/b7ca2fc86f2a1d0c1c7328395657577a0ab47cd3/.nojekyll -------------------------------------------------------------------------------- /1-welcome/1-welcome.qmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Welcome!
![](../images/cascadia-jr-quarto.webp){width=5em}" 3 | format: revealjs 4 | --- 5 | 6 | ## While waiting for us to begin... 7 | 8 | Open the workshop website and make sure you've completed the pre-work 9 | and have the required software, packages, and exercises. 10 | 11 | [**jadeyryan.quarto.pub/cascadia-quarto/prework**](https://jadeyryan.quarto.pub/cascadia-quarto/prework). 12 | 13 | ```{r} 14 | #| eval: false 15 | 16 | usethis::use_course( 17 | "https://github.com/jadeynryan/parameterized-quarto-workshop/raw/cascadia/exercises/intermediate-exercises.zip" 18 | # , destdir = "C:/Users/jryan/Documents/R/projects" 19 | ) 20 | 21 | install.packages(c("dplyr", "fs", "ggplot2", "here", "janitor", "knitr", "lubridate", 22 | "plotly", "purrr", "quarto", "readr", "rmarkdown", "stringr", "tidyr")) 23 | ``` 24 | 25 | If `use_course()` didn't work, manually download the zip file from 26 | GitHub: 27 | [github.com/jadeynryan/parameterized-quarto-workshop/tree/cascadia/exercises](https://github.com/jadeynryan/parameterized-quarto-workshop/tree/cascadia/exercises). 28 | 29 | ::: callout-tip 30 | ## Backup option: Posit Cloud 31 | 32 | Join the Posit Cloud [Quarto Cascadia 33 | workspace](https://posit.cloud/spaces/519059/join?access_code=ckv18LHEcSw3QtQ904uvpzLVlh4L2rk6RhKdpwyp) 34 | and open the [Intermediate Exercises 35 | project](https://posit.cloud/spaces/519059/content/8351107). 36 | ::: 37 | 38 | ## Acknowledgements 39 | 40 | ![](images/cascadia-logo.png){.center fig-alt="Cascadia R Conf logo" 41 | height="350px"} 42 | 43 | \ 44 | 45 | **Workshop structure adapted from:** 46 | 47 | [R/Medicine Data Cleaning 2023 48 | Workshop](https://shannonpileggi.github.io/rmedicine-data-cleaning-2023/) 49 | taught by [Crystal Lewis](https://cghlewis.com/), [Shannon 50 | Pileggi](https://www.pipinghotdata.com/), and [Peter 51 | Higgins](https://bookdown.org/pdr_higgins/rmrwr/) 52 | 53 | \ 54 | 55 | [ASA Traveling Courses on 56 | Quarto](https://quarto.org/docs/blog/posts/2023-12-05-asa-traveling-courses/) 57 | taught by [Mine Çetinkaya-Rundel](https://mine-cr.com/) and [Andrew 58 | Bray](https://andrewpbray.github.io/) 59 | 60 | ## Code of Conduct 61 | 62 | Please review and abide by: 63 | [cascadiarconf.com/policies/](https://cascadiarconf.com/policies/) 64 | 65 | \ 66 | 67 | 💙 Treat everyone with respect and kindness. 68 | 69 | \ 70 | 71 | 💜 Everyone should feel welcome and safe. 72 | 73 | ## Disclaimer and license 74 | 75 | Opinions expressed are solely my own and do not express the views of my 76 | employer or any organizations I am associated with. 77 | 78 | \ 79 | 80 | This work is licensed under [Creative Commons 81 | Attribution-NonCommercial-ShareAlike 4.0 International (CC 82 | BY-NC-SA)](https://creativecommons.org/licenses/by-nc-sa/4.0/). 83 | 84 | {{< fa brands creative-commons size=2x >}} {{< fa brands creative-commons-by size=2x >}} {{< fa brands creative-commons-nc size=2x >}} {{< fa brands creative-commons-sa size=2x >}} 85 | 86 | 87 | ## Meet Jadey Ryan 88 | 89 | ::: columns 90 | ::: {.column width="44%"} 91 | Data scientist at WA Dept of Agriculture 92 | 93 | The Coding Cats: cat & code themed merch 94 | 95 | {{< fa link size=xl >}} [jadeyryan.com](https://jadeyryan.com) 96 | 97 | {{< fa brands mastodon size=xl >}} [\@jadeynryan](https://fosstodon.org/@jadeynryan) 98 | 99 | {{< fa brands linkedin size=xl >}} [linkedin.com/in/jadey-ryan](https://www.linkedin.com/in/jadey-ryan) 100 | 101 | {{< fa brands github size=xl >}} [jadeynryan](https://github.com/jadeynryan/) 102 | 103 | {{< fa brands etsy size=xl >}} [thecodingcats.etsy.com](https://thecodingcats.etsy.com/) 104 | 105 | ![](images/mts.webp){fig-alt="Three snowshoe siamese cats in loaf mode. From left to right: Tai, Mai, and Skye" style="border-radius:1em;margin-top:auto"} 106 | ::: 107 | 108 | ::: {.column width="28%"} 109 | ![](images/bulk-density.jpg){fig-alt="Jadey collecting a bulk density soil sample in a field of wheat stubble." style="border-radius:1em;margin-top:auto"} 110 | ::: 111 | 112 | ::: {.column width="28%"} 113 | ::: {layout-nrow="2"} 114 | ![](images/soil-sampling.jpg){style="border-radius:1em;margin-top:auto" 115 | fig-alt="Jadey standing in a field of wheat stubble holding a 3 foot long soil sampling probe over her shoulder."} 116 | 117 | ![](images/rangeland.jpg){fig-alt="Jadey standing in a grazed wildflower meadow with two colleagues collecting soil samples." 118 | style="border-radius:1em;margin-top:auto"} 119 | ::: 120 | 121 | {{< fa camera title="Photo credit" >}} [@leslie.mmichel](https://twitter.com/leslie_mmichel) 122 | ::: 123 | ::: 124 | 125 | ## Meet our TA: Lydia Gibson 126 | 127 | ![](images/lydia-gibson.jpeg){.center fig-alt="Photo of Lydia Gibson" style="border-radius:1em;"} 128 | 129 | ## Learning objectives 130 | 131 | ::: incremental 132 | - Understand what parameterized reporting is and when it is useful. 133 | - Convert a Quarto document into a parameterized template and render 134 | all variations. 135 | - Generate multiple format outputs from the same template using: 136 | - conditional content 137 | - conditional code execution, and 138 | - custom styling. 139 | ::: 140 | 141 | ## Schedule 142 | 143 | | Time | Topic | 144 | |-------------|------------------------------------------------------| 145 | | 1:30 - 2:00 | Welcome & [Parameterizing reports](../2-parameters/) | 146 | | 2:00 - 3:00 | [Rendering reports](../3-render/) | 147 | | 3:00 - 3:30 | Break | 148 | | 3:30 - 4:00 | [Conditional content & code](../4-conditionals/) | 149 | | 4:00 - 4:20 | [Styling reports](../5-styling/) | 150 | | 4:20 - 4:30 | [Summary](../6-summary/) | 151 | 152 | ## Workshop structure 153 | 154 | Presentation 155 | 156 | 💃🏻 Demos 157 | 158 | 💪🏻 Exercises 159 | 160 | \ 161 | 162 | . . . 163 | 164 | Let us know how you're doing by displaying your stickies! 165 | 166 | 🟦 I'm all good; I'm done. 167 | 168 | 🟧 I'm a little lost; I could use some help. 169 | 170 | # 💪🏼 Exercise {.exercise} 171 | 172 | **Meet your neighbors**: 173 | 174 | - Your name 175 | 176 | - Your favorite snack 🍎🧀🍦🍫🍿 177 | 178 | - What kinds of reports are you wanting to create after today's 179 | workshop? 180 | 181 | ```{r} 182 | #| echo: false 183 | countdown::countdown(minutes = 5, top = 0) 184 | ``` 185 | -------------------------------------------------------------------------------- /1-welcome/images/bulk-density.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jadeynryan/parameterized-quarto-workshop/b7ca2fc86f2a1d0c1c7328395657577a0ab47cd3/1-welcome/images/bulk-density.jpg -------------------------------------------------------------------------------- /1-welcome/images/cascadia-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jadeynryan/parameterized-quarto-workshop/b7ca2fc86f2a1d0c1c7328395657577a0ab47cd3/1-welcome/images/cascadia-logo.png -------------------------------------------------------------------------------- /1-welcome/images/lydia-gibson.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jadeynryan/parameterized-quarto-workshop/b7ca2fc86f2a1d0c1c7328395657577a0ab47cd3/1-welcome/images/lydia-gibson.jpeg -------------------------------------------------------------------------------- /1-welcome/images/mts.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jadeynryan/parameterized-quarto-workshop/b7ca2fc86f2a1d0c1c7328395657577a0ab47cd3/1-welcome/images/mts.webp -------------------------------------------------------------------------------- /1-welcome/images/rangeland.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jadeynryan/parameterized-quarto-workshop/b7ca2fc86f2a1d0c1c7328395657577a0ab47cd3/1-welcome/images/rangeland.jpg -------------------------------------------------------------------------------- /1-welcome/images/soil-sampling.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jadeynryan/parameterized-quarto-workshop/b7ca2fc86f2a1d0c1c7328395657577a0ab47cd3/1-welcome/images/soil-sampling.jpg -------------------------------------------------------------------------------- /1-welcome/index.qmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Welcome" 3 | format: html 4 | --- 5 | 6 | [View slides in full screen](1-welcome.qmd). 7 | 8 | ```{=html} 9 | 10 | ``` 11 | -------------------------------------------------------------------------------- /2-parameters/2-parameters.qmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Parameterizing reports
![](../images/cascadia-jr-quarto.webp){width=5em}" 3 | format: revealjs 4 | --- 5 | 6 | ## Examples of parameterized reports 7 | 8 | ::: columns 9 | ::: {.column width="25%"} 10 | ![](images/ocean-floor.png){.lightbox group="examples" 11 | fig-alt="RStudio R Markdown parameterized report project for mapping ocean floors."} 12 | 13 | [Ocean floor maps](https://rmarkdown.rstudio.com/lesson-6.html) 14 | ::: 15 | 16 | ::: {.column width="25%"} 17 | ![](images/urban-institute.png){.lightbox group="examples" 18 | fig-alt="Report for Alabama spending with text and plots generated from parameterized R Markdown."} 19 | 20 | [Fiscal 21 | briefs](https://book.rwithoutstatistics.com/parameterized-reports-chapter.html) 22 | ::: 23 | 24 | ::: {.column width="25%"} 25 | ![](images/driver-quality.png){.lightbox group="examples" 26 | fig-alt="Report for Alaska driver quality with text, plot, and table generated from parameterized RMarkdown."} 27 | 28 | [Bad 29 | drivers](https://urban-institute.medium.com/iterated-fact-sheets-with-r-markdown-d685eb4eafce) 30 | ::: 31 | 32 | ::: {.column width="25%"} 33 | ![](images/soils-report-html.png){.lightbox group="examples" 34 | fig-alt="HTML report for soil health survey participant, generated from a parameterized Quarto report."} 35 | 36 | [Soil health](https://wa-department-of-agriculture.github.io/soils/) 37 | ::: 38 | ::: 39 | 40 | . . . 41 | 42 | \ 43 | 44 | **Another use case: different audiences, different reports** 45 | 46 | Show code for technical staff and hide code for everyone else ([StackOverflow 47 | example](https://stackoverflow.com/questions/73571919/how-to-pass-logical-parameters-with-the-quarto-r-package-to-the-knitr-chunk-opti)). 48 | 49 | ## Like a custom function 50 | 51 | ::: r-stack 52 | ![](images/template1.png){.fragment 53 | fig-alt="File with the word '.qmd' inside and the word 'Function' above." 54 | width="1300" fig-align="left" style="margin-top:-1em"} 55 | 56 | ![](images/template2.png){.fragment 57 | fig-alt="An arrow points from 'Input' with 'params$year' to the previous image with 'Function' and '.qmd' file." 58 | width="1300" fig-align="left" style="margin-top:-1em"} 59 | 60 | ![](images/template3.png){.fragment 61 | fig-alt="In addition to the previous two images, arrows point to five reports with years 2019 through 2023 on them in a flow chart." 62 | width="1300" fig-align="left" style="margin-top:-1em"} 63 | ::: 64 | 65 | ## What makes a report "parameterized"? 66 | 67 | - YAML header with `params` key-value pairs 68 | 69 | - Use these `params` to create different variations of a report from a single `.qmd` document. 70 | 71 | . . . 72 | 73 | ::: callout-important 74 | - Valid parameter values are strings, numbers, or Boolean. 75 | 76 | - Must serialize a dataframe to pass it as a parameter, then un-serialize it 77 | back to a dataframe within the `.qmd` content. 78 | 79 | - See [Christophe Dervieux's answer in Posit 80 | Community](https://community.rstudio.com/t/param-converted-from-data-frame-to-list/155556/9) 81 | to understand why. 82 | 83 | - See [John Paul Helveston's blog 84 | post](https://www.jhelvy.com/posts/2023-02-28-parameterized-pdfs-with-quarto/#passing-data-frames-as-parameters) 85 | to learn how to use {jsonlite} as a workaround. 86 | ::: 87 | 88 | ## Workflow 89 | 90 | ::: incremental 91 | 1. Write report template with default values hard-coded, and then render & review. 92 | 93 | 2. Set default `params` key-value pairs in YAML. 94 | 95 | 3. Replace hard-coded values with the `params` variables. 96 | 97 | 4. Render the single report and review. 98 | 99 | 5. Render extreme cases and review. 100 | 101 | - Parameter values with barely any data and with tons of data. 102 | 103 | 6. Render all variations of the report at once. 104 | ::: 105 | 106 | # 💪🏼 Exercise {.exercise} 107 | 108 | **Explore a report without parameters and see where we could add them.** 109 | 110 | 1. Open `1-swiss-cats.qmd`. 111 | 112 | 2. Click the 113 | ![](https://quarto.org/docs/get-started/hello/images/rstudio-render-button.png){fig-alt="Quarto render button in RStudio" style="vertical-align:middle;" width="49"} **Render** button. 114 | 115 | 3. Look at the source markdown & code and the rendered report. 116 | 117 | 4. 💬 **Chat**: What variables could we set as parameters? 118 | 119 | 💡 **Hint**: run the `setup` chunk and look at the `pets` dataframe to see 120 | what variables it has. 121 | 122 | ```{r} 123 | #| echo: false 124 | countdown::countdown(minutes = 5, top = 0) 125 | ``` 126 | 127 | ## Set `params` in YAML header 128 | 129 | ```{.yaml code-line-numbers="|2|3,4,6|5|7-8|11"} 130 | --- 131 | title: "Swiss Cats" # Metadata 132 | format: # Set format types 133 | html: 134 | toc: true # Set additional options 135 | docx: default 136 | params: # Set default parameter key-value pairs 137 | fave_breed: "Snowshoe" 138 | --- 139 | 140 | Report content goes here. # Write narrative and code 141 | ``` 142 | 143 | . . . 144 | 145 | ::: callout-important 146 | Your default `params` key-value pairs must be found in your dataset. Otherwise, code will error or output will be blank. 147 | 148 | The variable name for `params` can be anything you choose. Often, it's a column name in your dataset. 149 | ::: 150 | 151 | ## Access `params` 152 | 153 | ```{r} 154 | #| include: false 155 | 156 | # Set params for slide 157 | params <- list( 158 | fave_breed = "Snowshoe" 159 | ) 160 | ``` 161 | 162 | Run any line or chunk to add `params` to your environment. 163 | 164 | . . . 165 | 166 | `params` object is a list. 167 | 168 | ```{r} 169 | str(params) 170 | ``` 171 | 172 | \ 173 | 174 | . . . 175 | 176 | Access with `$` notation. 177 | 178 | ```{r} 179 | params$fave_breed 180 | ``` 181 | 182 | \ 183 | 184 | . . . 185 | 186 | For inline code in YAML or report content, enclose the expression in `` `r ` ``. 187 | 188 | ```markdown 189 | My favorite cat breed is the **``r I("r params$fave_breed")``**. 190 | ``` 191 | 192 | My favorite cat breed is the **`r params$fave_breed`**. 193 | 194 | ## Replace hard-coded values with `params` 195 | 196 | `Cmd`/`Ctrl` + `F` to find where to replace hard-coded values with `params`. 197 | 198 | ![](images/find-replace-hard-coded-values.png){fig-alt="Find and replace toolbar with "pet_type in the Search field highlighted by a purple box and "params$pet_type in the Replace field highlighted by a blue box. The .qmd file shows a filter statement with the "pet_type highlighted by RStudio as a match for the Find tool. This filter statement is highlighted by a purple box with an arrow pointing to a blue box that has the filter statement with the hard-coded "cats string replaced with "params$pet_type."} 199 | 200 | ## Replace hard-coded values with `params` 201 | 202 | **Use `$` list notation in code** for plot/table titles and labels, filtering, etc. 203 | 204 | \ 205 | 206 | `paste()` syntax: 207 | 208 | ```{r} 209 | #| eval: false 210 | 211 | # ggplot2 code + 212 | labs(title = paste(params$fave_breed, "population")) 213 | ``` 214 | 215 | . . . 216 | 217 | \ 218 | 219 | `glue::glue()` syntax: 220 | 221 | ```{r} 222 | #| eval: false 223 | 224 | # ggplot2 code + 225 | labs(title = glue::glue("{params$fave_breed} population")) 226 | ``` 227 | 228 | \ 229 | 230 | . . . 231 | 232 | **Use inline R code** for markdown. 233 | 234 | ```markdown 235 | ## My favorite breed: **``r I("r params$fave_breed")``**. 236 | ``` 237 | 238 | ## 239 | 240 | ![](images/snowshoe-graph.png){fig-align="center"} 241 | 242 | # 💃🏻 Demo {.demo} 243 | 244 | Modify `1-swiss-cats-demo.qmd` to add `pet_type` and `fave_breed` parameters. 245 | 246 | \ 247 | 248 | This parameterized version of `1-swiss-cats.qmd` is the starting point for the next section's exercises (`2-quarto-render.qmd`). 249 | -------------------------------------------------------------------------------- /2-parameters/images/driver-quality.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jadeynryan/parameterized-quarto-workshop/b7ca2fc86f2a1d0c1c7328395657577a0ab47cd3/2-parameters/images/driver-quality.png -------------------------------------------------------------------------------- /2-parameters/images/find-replace-hard-coded-values.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jadeynryan/parameterized-quarto-workshop/b7ca2fc86f2a1d0c1c7328395657577a0ab47cd3/2-parameters/images/find-replace-hard-coded-values.png -------------------------------------------------------------------------------- /2-parameters/images/ocean-floor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jadeynryan/parameterized-quarto-workshop/b7ca2fc86f2a1d0c1c7328395657577a0ab47cd3/2-parameters/images/ocean-floor.png -------------------------------------------------------------------------------- /2-parameters/images/snowshoe-graph.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jadeynryan/parameterized-quarto-workshop/b7ca2fc86f2a1d0c1c7328395657577a0ab47cd3/2-parameters/images/snowshoe-graph.png -------------------------------------------------------------------------------- /2-parameters/images/soils-report-html.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jadeynryan/parameterized-quarto-workshop/b7ca2fc86f2a1d0c1c7328395657577a0ab47cd3/2-parameters/images/soils-report-html.png -------------------------------------------------------------------------------- /2-parameters/images/template1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jadeynryan/parameterized-quarto-workshop/b7ca2fc86f2a1d0c1c7328395657577a0ab47cd3/2-parameters/images/template1.png -------------------------------------------------------------------------------- /2-parameters/images/template2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jadeynryan/parameterized-quarto-workshop/b7ca2fc86f2a1d0c1c7328395657577a0ab47cd3/2-parameters/images/template2.png -------------------------------------------------------------------------------- /2-parameters/images/template3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jadeynryan/parameterized-quarto-workshop/b7ca2fc86f2a1d0c1c7328395657577a0ab47cd3/2-parameters/images/template3.png -------------------------------------------------------------------------------- /2-parameters/images/urban-institute.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jadeynryan/parameterized-quarto-workshop/b7ca2fc86f2a1d0c1c7328395657577a0ab47cd3/2-parameters/images/urban-institute.png -------------------------------------------------------------------------------- /2-parameters/index.qmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Parameterizing reports" 3 | format: html 4 | --- 5 | 6 | [View slides in full screen](2-parameters.qmd). 7 | 8 | ```{=html} 9 | 10 | ``` 11 | -------------------------------------------------------------------------------- /3-render/3-render.qmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Rendering reports
![](../images/cascadia-jr-quarto.webp){width=5em}" 3 | format: revealjs 4 | --- 5 | 6 | ## Three ways to render 7 | 8 | ::: incremental 9 | 1. **RStudio/Quarto integration**: 10 | 11 | ![](https://quarto.org/docs/get-started/hello/images/rstudio-render-button.png){fig-alt="Quarto render button in RStudio" style="vertical-align:middle;" width="49"} **Render** button in RStudio or `Cmd`/`Ctrl` + `Shift` + `K` keyboard shortcut 12 | 13 | 2. ✨ **Quarto R package** ✨ 14 | 15 | ```{.r filename="Console or R script"} 16 | quarto::quarto_render( 17 | input = here::here("2-quarto-render.qmd"), # Input Quarto file 18 | execute_params = list( # Named list of params 19 | pet_type = "cats", 20 | fave_breed = "Snowshoe")) 21 | ``` 22 | 23 | 3. **Quarto CLI** 24 | 25 | ```{.bash filename="Terminal"} 26 | quarto render 2-quarto-render.qmd -P pet_type:'cats' -P fave_breed:'Snowshoe' 27 | ``` 28 | 29 | ::: 30 | 31 | # 💪🏼 Exercise {.exercise} 32 | 33 | **Change parameters in the YAML and render using ![](https://quarto.org/docs/get-started/hello/images/rstudio-render-button.png){fig-alt="Quarto render button in RStudio" style="vertical-align:middle;" width="49"} Render button.** 34 | 35 | 1. Look at the unique pet breeds and pick your favorite. 36 | ```{.r} 37 | # Run in the console 38 | pets <- readr::read_rds(here::here("data", "pets.RDS")) |> 39 | dplyr::distinct(pet_type, breed) |> View() 40 | ``` 41 | 42 | 2. In `2-quarto-render.qmd`, change the default parameters in the YAML to your favorite pet type and breed. Render using the ![](https://quarto.org/docs/get-started/hello/images/rstudio-render-button.png){fig-alt="Quarto render button in RStudio" style="vertical-align:middle;" width="49"} **Render** button. 43 | 44 | ```{.yaml} 45 | params: 46 | pet_type: "___" 47 | fave_breed: "___" 48 | ``` 49 | 50 | 3. 💬 **Chat**: Which breed did you pick and why? What do you think will happen if you set the `pet_type` default parameter to "cats" and the `fave_breed` parameter to "American Bulldog"? 51 | 52 | Try it out and discuss what happend! 53 | 54 | ```{r} 55 | #| echo: false 56 | countdown::countdown(minutes = 7, top = 0) 57 | ``` 58 | 59 | # 💪🏼 Exercise {.exercise} 60 | 61 | **Change parameters and render using `quarto_render()`.** 62 | 63 | 1. Render with `quarto::quarto_render()`. 64 | 65 | ```r 66 | # Run in the console 67 | quarto::quarto_render( 68 | input = here::here("2-quarto-render.qmd"), 69 | execute_params = list( 70 | pet_type = "___", 71 | fave_breed = "___")) 72 | ``` 73 | 74 | 2. 💬 **Chat**: What kinds of variables will you use as parameters for your reports? 75 | 76 | ```{r} 77 | #| echo: false 78 | countdown::countdown(minutes = 7, top = 0) 79 | ``` 80 | 81 | ## Render all 538 reports 82 | 83 | One HTML report for each cat breed and each dog breed. 84 | 85 | :::: panel-tabset 86 | 87 | ## 📊 Data 88 | 89 | ```{r} 90 | pets <- readr::read_rds(here::here("data", "pets.RDS")) 91 | 92 | pets |> 93 | dplyr::distinct(pet_type, breed) |> 94 | dplyr::count(pet_type) |> 95 | janitor::adorn_totals() 96 | ``` 97 | 98 | ## 👷🏼‍♂️ Manual 1 99 | 100 | 1. Change the default `params` in the YAML. 101 | 102 | 2. **Render** button or `Cmd`/`Ctrl` + `Shift` + `K` keyboard shortcut. 103 | 104 | 3. Rename the rendered report to include the parameter & prevent overwriting. 105 | 106 | 4. Repeat 537 times. 107 | 108 | 😭 109 | 110 | ## 👷🏻‍♀️ Manual 2 111 | 112 | ```{r} 113 | #| eval: false 114 | quarto::quarto_render( 115 | input = here::here("2-quarto-render.qmd"), 116 | output_file = "dogs-affenpinscher-report.html", 117 | execute_params = list( 118 | pet_type = "dogs", 119 | fave_breed = "Affenpinscher")) 120 | 121 | quarto::quarto_render( 122 | input = here::here("2-quarto-render.qmd"), 123 | output_file = "dogs-afghan-hound-report.html", 124 | execute_params = list( 125 | pet_type = "dogs", 126 | fave_breed = "Afghan Hound")) 127 | 128 | quarto::quarto_render( 129 | input = here::here("2-quarto-render.qmd"), 130 | output_file = "dogs-aidi-chien-de-montagne-de-l-atlas-report.html", 131 | execute_params = list( 132 | pet_type = "dogs", 133 | fave_breed = "Aidi Chien De Montagne De L Atlas")) 134 | 135 | quarto::quarto_render( 136 | input = here::here("2-quarto-render.qmd"), 137 | output_file = "dogs-akita-report.html", 138 | execute_params = list( 139 | pet_type = "dogs", 140 | fave_breed = "Akita")) 141 | 142 | # + 534 more times... 143 | # 😭 144 | ``` 145 | 146 | ## ⚡ Programatically 147 | 148 | **Create a dataframe with three columns that match `quarto_render()` args:** 149 | 150 | - `output_format`: file type (html, revealjs, pdf, docx, etc.) 151 | 152 | - `output_file`: file name with extension 153 | 154 | - `execute_params`: named list of parameters 155 | 156 | **Map over each row:** 157 | 158 | - `purrr::pwalk(dataframe, quarto_render, )` 😎 159 | :::: 160 | 161 | ## Primer on `purrr` functions for iteration 162 | 163 | :::: {.columns} 164 | 165 | ::: {.column width="60%"} 166 | **Map functions** apply the same action/function to each element of an object. 167 | 168 | - Base R `apply()` functions are map functions. 169 | 170 | - `purrr` map functions have consistent syntax and the output data type is predictable. 171 | 172 | `for loops` → `lapply()` → `purrr::map()` 173 | ::: 174 | 175 | ::: {.column width="5%"} 176 | ::: 177 | 178 | ::: {.column width="35%"} 179 | ![](https://purrr.tidyverse.org/logo.png){fig-alt="purrr R package logo"} 180 | ::: 181 | :::: 182 | 183 | . . . 184 | 185 | **Learn more**: 186 | 187 | - [Iteration chapter of *R for Data Science*](https://r4ds.had.co.nz/iteration.html) 188 | 189 | - R-Ladies Baltimore presentation [*Make your R Code purr with `purrr`*](https://www.youtube.com/watch?v=IewsPpjKElc) 190 | 191 | - Jenny Bryan's [tutorial](https://jennybc.github.io/purrr-tutorial/) and [workshop](https://github.com/jennybc/row-oriented-workflows) 192 | 193 | ## Create dataframe to iterate over 194 | 195 | ```{r} 196 | #| code-line-numbers: "|2|4|5-9|11-14|" 197 | pet_reports <- pets |> 198 | dplyr::distinct(pet_type, breed) |> # Get distinct pet/breed combos 199 | dplyr::mutate( 200 | output_format = "html", # Make output_format column 201 | output_file = paste( # Make output_file column: 202 | tolower(pet_type), # cats-abyssiniane-report.html 203 | tolower(gsub(" ", "-", breed)), 204 | "report.html", 205 | sep = "-" 206 | ), 207 | execute_params = purrr::map2( # Make execute_params column 208 | pet_type, 209 | breed, 210 | \(pet_type, breed) list(pet_type = pet_type, breed = breed))) 211 | ``` 212 | 213 | ```{r} 214 | #| echo: false 215 | saveRDS(pet_reports, "../data/pet-reports.RDS") 216 | ``` 217 | 218 | ## Subset to first 2 cat/dog breeds 219 | 220 | ```{r} 221 | pet_reports_subset <- pet_reports |> 222 | dplyr::slice_head(n = 2, by = pet_type) |> 223 | dplyr::select(output_file, execute_params) 224 | 225 | pet_reports_subset 226 | ``` 227 | 228 | ## Map over each row 229 | 230 | ::: incremental 231 | - `purrr::pwalk()` iterates over multiple arguments simultaneously. 232 | 233 | - First `.l` argument is a *list of vectors*. 234 | 235 | - Dataframe is a special case of `.l` that iterates over rows. 236 | ::: 237 | . . . 238 | 239 | ```{r} 240 | #| eval: false 241 | #| code-line-numbers: "|2|3|4|5|" 242 | 243 | purrr::pwalk( 244 | .l = pet_reports_subset, # Dataframe to map over 245 | .f = quarto::quarto_render, # Function we are applying to each row 246 | input = here::here("2-quarto-render.qmd"), # Named arguments of .f 247 | .progress = TRUE # Show a progress bar :) 248 | ) 249 | ``` 250 | 251 | . . . 252 | 253 | ::: callout-note 254 | `input` is the only named argument of `quarto_render()` included in `pwalk()`. 255 | 256 | 257 | `output_format`, `output_file`, and `execute_params` are already passed in through the dataframe. 258 | ::: 259 | 260 | # Multiple formats {.section} 261 | 262 | ## Render all reports to all formats 263 | 264 | Add to the `format:` YAML option to render additional output formats from the same `.qmd` file. 265 | 266 | ``` {.yaml} 267 | --- 268 | format: 269 | html: 270 | embed-resources: true # Makes the report self-contained 271 | pdf: default # If not using any additional format options, 272 | docx: default # set value to `default` 273 | --- 274 | ``` 275 | 276 | . . . 277 | 278 | **Note**: the Render button now has a dropdown! 279 | 280 | ![](images/render-button.png){width=60% fig-alt="Screenshot of Quarto file with the Render dropdown showing options for HTML, PDF, and MS Word formats."} 281 | 282 | [Quarto docs on multiple formats](https://quarto.org/docs/get-started/authoring/rstudio.html#multiple-formats) 283 | 284 | ## Format links for HTML output 285 | 286 | Links to download the other formats will automatically appear in HTML documents. 287 | 288 | ![](images/other-formats.png){fig-alt="Screenshot of a HTML page that includes links to the PDF and MS Word formats in the table of contents under the heading Other Formats." style="border-radius:1em"} 289 | 290 | ## Format link options 291 | 292 | **Choose which format links to include:** 293 | 294 | ```{yaml} 295 | #| code-line-numbers: "7" 296 | --- 297 | format: 298 | html: 299 | embed-resources: true 300 | pdf: default 301 | docx: default 302 | format-links: [pdf, docx] 303 | --- 304 | ``` 305 | 306 | . . . 307 | 308 | **Or hide all links:** 309 | 310 | ```{yaml} 311 | #| code-line-numbers: "7" 312 | --- 313 | format: 314 | html: 315 | embed-resources: true 316 | pdf: default 317 | docx: default 318 | format-links: false 319 | --- 320 | ``` 321 | 322 | # 💃🏻 Demo {.demo} 323 | 324 | Programmatically render all reports in all formats in `3-purrr-render-demo.qmd` and `3-purrr-render-demo.R`. 325 | 326 | \ 327 | 328 | 💡 Use these files as a template and modify them for your own projects! 329 | 330 | ## Directory limitations 331 | 332 | ::: incremental 333 | 334 | :::: callout-important 335 | ## Can't render reports to another directory. 336 | 337 | [`output-dir` YAML option](https://quarto.org/docs/books/book-output.html#output-path) only works for *Quarto projects* that contain a `_quarto.yml` config file. 338 | 339 | **Workaround**: use [`{fs}`](https://fs.r-lib.org/) to move files after rendering. 340 | 341 | See `3-purrr-render-demo.R` for example. 342 | 343 | **More info**: [GitHub discussion](https://github.com/quarto-dev/quarto-cli/discussions/2171#discussioncomment-4865286) and [GitHub issue](https://github.com/quarto-dev/quarto-r/issues/81). 344 | :::: 345 | 346 | \ 347 | 348 | :::: callout-important 349 | ## Can't embed resources for a Quarto document in a subfolder. 350 | 351 | If using `embed-resources: true` YAML option, `.qmd` can't be in subfolder, otherwise: 352 | 353 | [[WARNING] Could not fetch resource ...]{.warning} 354 | 355 | **More info**: [GitHub discussion](https://github.com/quarto-dev/quarto-cli/discussions/4041#discussioncomment-6052000) and [GitHub issue](https://github.com/quarto-dev/quarto-cli/issues/5765). 356 | :::: 357 | ::: 358 | 359 | # 🚶🏻‍♀ 30-min break 🧘🏾‍♂️️ {.section} 360 | 361 | ```{r} 362 | #| echo: false 363 | countdown::countdown(minutes = 30, top = 0) 364 | ``` 365 | -------------------------------------------------------------------------------- /3-render/images/other-formats.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jadeynryan/parameterized-quarto-workshop/b7ca2fc86f2a1d0c1c7328395657577a0ab47cd3/3-render/images/other-formats.png -------------------------------------------------------------------------------- /3-render/images/render-button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jadeynryan/parameterized-quarto-workshop/b7ca2fc86f2a1d0c1c7328395657577a0ab47cd3/3-render/images/render-button.png -------------------------------------------------------------------------------- /3-render/index.qmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Rendering reports" 3 | format: html 4 | --- 5 | 6 | [View slides in full screen](3-render.qmd). 7 | 8 | ```{=html} 9 | 10 | ``` 11 | -------------------------------------------------------------------------------- /4-conditionals/4-conditionals.qmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Conditional content & code
![](../images/cascadia-jr-quarto.webp){width=4em}" 3 | format: revealjs 4 | --- 5 | 6 | # Conditional content {.section} 7 | 8 | ## Control content visibility 9 | 10 | Create a div, span, or non-executable code block with one option from each of the below columns: 11 | 12 | `{.class attribute="format"}` 13 | 14 | :::: {.columns} 15 | 16 | ::: {.column width="30%"} 17 | **Class** 18 | 19 | - `.content-visible` 20 | - `.content-hidden` 21 | ::: 22 | 23 | ::: {.column width="30%"} 24 | **Attribute** 25 | 26 | - `when-format="___"` 27 | - `unless-format="___"` 28 | ::: 29 | 30 | ::: {.column width="30%"} 31 | **Format** 32 | 33 | - `latex` or `pdf` 34 | - `epub` 35 | - `html` or `revealjs` 36 | - `markdown` 37 | ::: 38 | 39 | :::: 40 | 41 | :::: {.fragment style="margin-top:-3.5em"} 42 | 43 | **Examples:** 44 | 45 | ::: panel-tabset 46 | 47 | ## Divs 48 | 49 | ```markdown 50 | ::: {.content-visible when-format="html"} 51 | 52 | Will only appear in HTML. 53 | 54 | ::: 55 | ``` 56 | 57 | ## Spans 58 | 59 | ``` markdown 60 | Some text 61 | [in HTML.]{.content-visible when-format="html"} 62 | [in PDF.]{.content-visible when-format="pdf"} 63 | ``` 64 | 65 | ## Non-executable code 66 | 67 | Fenced code blocks purely for documentation. 68 | 69 | ````markdown 70 | ```{.python .content-visible when-format="html"} 71 | # code shown only in HTML 72 | 2 + 2 73 | ``` 74 | ```` 75 | ::: 76 | :::: 77 | 78 | . . . 79 | 80 | [Conditional Quarto docs](https://quarto.org/docs/authoring/conditional.html) & [Format aliases](https://quarto.org/docs/authoring/conditional.html#format-matching) 81 | 82 | ## Useful for static/interactive features 83 | 84 | Pairs well with the `{{< include >}}` shortcode to re-use content without copying/pasting. 85 | 86 | . . . 87 | 88 | ::::: panel-tabset 89 | 90 | ## Static 91 | 92 | ```markdown 93 | :::: {.content-visible unless-format="html"} 94 | 95 | ## Cats 96 | 97 | {{{< include _cats.qmd >}}} 98 | 99 | ## Dogs 100 | 101 | {{{< include _dogs.qmd >}}} 102 | 103 | :::: 104 | ``` 105 | 106 | ## Interactive 107 | 108 | ```markdown 109 | :::: {.content-visible when-format="html"} 110 | ::: panel-tabset 111 | 112 | ## Cats 113 | 114 | {{{< include _cats.qmd >}}} 115 | 116 | ## Dogs 117 | 118 | {{{< include _dogs.qmd >}}} 119 | 120 | ::: 121 | :::: 122 | ``` 123 | ::::: 124 | 125 | . . . 126 | 127 | ::: callout-tip 128 | ## Good practice 129 | Use an underscore prefix for included files so they are automatically ignored by a Quarto render of a project ([Include shortcode Quarto docs](https://quarto.org/docs/authoring/includes.html)). 130 | ::: 131 | 132 | # 💪🏼 Exercise {.exercise} 133 | 134 | **Use conditional content divs to control when tabsets are shown.** 135 | 136 | 1. Modify `3-conditional-content.qmd` so that the `panel-tabset` is visible for HTML reports and hidden for all other formats. 137 | 138 | \ 139 | 140 | 2. Try another way to get the same result. 141 | 142 | `{.content-visible when-format="html"}` is essentially the same as `{.content-hidden unless-format="html"}`. 143 | 144 | \ 145 | 146 | 3. 💬 **Chat**: Besides tabsets, what other kinds of content might you want to make visible for only a certain format? 147 | 148 | ```{r} 149 | #| echo: false 150 | countdown::countdown(minutes = 5, top = 0) 151 | ``` 152 | 153 | # Conditional code execution {.section} 154 | 155 | ## Conditionally execute a code chunk 156 | 157 | ::: incremental 158 | 159 | - More efficient to not execute code that generates interactive outputs for static reports. 160 | 161 | - Useful for executing interactive plot code (e.g., `plotly` or `ggiraph`) for HTML reports and static `ggplot2` code for all other formats. 162 | 163 | - Useful for executing different code based on a parameter value. 164 | 165 | - Not currently a feature of Quarto v1.4. Follow along with this [GitHub discussion](https://github.com/quarto-dev/quarto-cli/discussions/3260#discussioncomment-4573926). 166 | 167 | - Chunk options can use R code for option values with `!expr`. Learn about the limitations to this YAML "tag" literal syntax in the [Quarto Chunk Options reference](https://quarto.org/docs/computations/r.html#chunk-options). 168 | 169 | ::: 170 | 171 | ## Conditional code based on output 172 | 173 | Get the format of the Pandoc output by including the following in the setup chunk of your `.qmd` file: 174 | 175 | ```{r} 176 | #| label: setup 177 | #| echo: fenced 178 | 179 | # Get output format 180 | format <- knitr::opts_knit$get("rmarkdown.pandoc.to") 181 | ``` 182 | 183 | ## Use `eval: !expr` chunk option 184 | 185 | ::: panel-tabset 186 | 187 | ## Static plot 188 | 189 | ```markdown 190 | #| echo: fenced 191 | #| eval: !expr format %in% c("latex", "docx") 192 | 193 | # code to create static {ggplot2} 194 | ``` 195 | 196 | :::: callout-important 197 | ## Use `latex` not `pdf` 198 | 199 | `format` comes from `knitr::opts_knit$get("rmarkdown.pandoc.to")`. Pandoc uses LaTeX to create PDFs. 200 | 201 | Quarto [format aliases](https://quarto.org/docs/authoring/conditional.html#format-matching) won't work here. 202 | :::: 203 | 204 | ## Interactive plot 205 | 206 | ```markdown 207 | #| echo: fenced 208 | #| eval: !expr format == "html" 209 | 210 | # code to create interactive {plotly} 211 | ``` 212 | 213 | ::: 214 | 215 | # 💪🏼 Exercise {.exercise} 216 | 217 | **Conditionally execute `ggplot2` code for static reports & `plotly` code for interactive reports.** 218 | 219 | 1. Open `5-conditional-code.qmd`. 220 | 221 | 2. In the `ggplot2` code chunks and `plotly` code chunks, fill in the blanks for the `eval: ` option. 222 | 223 | ```{r} 224 | # Replace ___ with html, latex, or docx. 225 | 226 | #| eval: !expr format == "___" 227 | 228 | #| eval: !expr format %in% c("___", "___") 229 | ``` 230 | 231 | 3. 💬 **Chat**: How would you change the `eval: ` option to execute a chunk based on a parameter value rather than the output format? 232 | 233 | ```{r} 234 | #| echo: false 235 | countdown::countdown(minutes = 5, top = 0) 236 | ``` 237 | 238 | ## Conditional code based on parameter 239 | 240 | Use `params` in `!expr`: 241 | 242 | ``` r 243 | #| eval: !expr params$fave_breed == "Snowshoe" 244 | 245 | # Code for a special plot for my favorite cat breed. 246 | ``` 247 | 248 | \ 249 | 250 | . . . 251 | 252 | ``` r 253 | #| eval: !expr !params$fave_breed == "Snowshoe" 254 | 255 | # Code for a different plot for all other breeds. 256 | # Note the ! in front of params. 257 | ``` 258 | -------------------------------------------------------------------------------- /4-conditionals/index.qmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Conditional content & code" 3 | format: html 4 | --- 5 | 6 | [View slides in full screen](4-conditionals.qmd). 7 | 8 | ```{=html} 9 | 10 | ``` 11 | -------------------------------------------------------------------------------- /5-style/5-style.qmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Styling your reports
![](../images/cascadia-jr-quarto.webp){width=5em}" 3 | format: revealjs 4 | --- 5 | 6 | # HTML {.section} 7 | 8 | ## Bootswatch themes 9 | 10 | Choose from or customize one of 25 [Bootswatch](https://bootswatch.com/) themes. 11 | 12 | \ 13 | 14 | . . . 15 | 16 | **Set the theme** in the YAML under the `html` key: 17 | 18 | ``` yaml 19 | format: 20 | html: 21 | theme: flatly 22 | ``` 23 | 24 | \ 25 | 26 | . . . 27 | 28 | **Customize a theme** by including a custom `.scss` file under the `theme` key: 29 | 30 | ``` yaml 31 | format: 32 | html: 33 | theme: [flatly, theme.scss] 34 | ``` 35 | 36 | \ 37 | 38 | [HTML theming Quarto docs](https://quarto.org/docs/output-formats/html-themes.html) 39 | 40 | 41 | ## SCSS files 42 | 43 | SCSS files have the following form: 44 | 45 | ``` scss 46 | /*-- scss:defaults --*/ 47 | $h2-font-size: 1.6rem !default; 48 | $headings-font-weight: 500 !default; 49 | $body-color: $gray-700 !default; 50 | 51 | /*-- scss:rules --*/ 52 | h1, h2, h3, h4, h5, h6 { 53 | text-shadow: -1px -1px 0 rgba(0, 0, 0, .3); 54 | } 55 | ``` 56 | 57 | \ 58 | 59 | Define [**SASS variables**](https://quarto.org/docs/output-formats/html-themes.html#sass-variables) in the `defaults` section. 60 | 61 | Declare [**CSS rules**](https://www.w3schools.com/css/css_syntax.ASP) in the `rules` section. 62 | 63 | \ 64 | 65 | [Bootstrap docs](https://getbootstrap.com/docs/5.1/customize/sass/) 66 | 67 | [Default Bootstrap variables](https://github.com/twbs/bootstrap/blob/main/scss/_variables.scss) 68 | 69 | ## Browser developer tools 70 | 71 | ::: r-stack 72 | ![](images/browser-tools.png){.fragment 73 | fig-alt="Screenshot of browser developer tools inspecting the Quarto title meta heading of an HTML report." 74 | width="1300" fig-align="left"} 75 | 76 | ![](images/browser-tools-modified.png){.fragment 77 | fig-alt="Screenshot of browser developer tools with the CSS declaration modified to test style changes." 78 | width="1300" fig-align="left"} 79 | ::: 80 | 81 | # MS Word {.section} 82 | 83 | ## Reference document 84 | 85 | Create and modify a *reference document*, which is a special kind of template. 86 | 87 | . . . 88 | 89 | 1. Run the following in the Terminal to create the reference doc: 90 | 91 | ```{.bash filename="Terminal"} 92 | quarto pandoc -o word-template.docx --print-default-data-file reference.docx 93 | ``` 94 | 95 | . . . 96 | 97 | 2. Open `word-template.docx` and modify the styles. 98 | 99 | . . . 100 | 101 | 3. Set this template in the YAML under the `reference-doc:` key: 102 | 103 | ```yaml 104 | format: 105 | docx: 106 | reference-doc: word-template.docx 107 | ``` 108 | 109 | . . . 110 | 111 | [MS Word template Quarto docs](https://quarto.org/docs/output-formats/ms-word-templates.html) 112 | 113 | [MS documentation on modifying styles](https://support.microsoft.com/en-us/office/customize-or-create-new-styles-d38d6e47-f6fc-48eb-a607-1eb120dec563) 114 | 115 | ## Need your report as a PDF? 116 | 117 | Format in MS Word and then convert `.docx` to `.pdf` with Adobe Acrobat. 118 | 119 | or... 120 | 121 | - Learn how to format PDFs in the Quarto docs: [PDF basics](https://quarto.org/docs/output-formats/pdf-basics.html) & [PDF options](https://quarto.org/docs/reference/formats/pdf.html). 122 | 123 | - Try [Typst](https://quarto.org/docs/output-formats/typst.html), an open-source markup-based typesetting system, new in Quarto 1.4. 124 | 125 | # 💪🏼 Exercise {.exercise} 126 | 127 | **Play around with the HTML and MS Word styling of a report of your choosing.** 128 | 129 | 1. Open a new `.qmd` or choose one from a previous exercise. 130 | 2. Add a [Bootswatch](https://bootswatch.com/) theme to the YAML and re-render. 131 | 3. Create a MS Word reference doc, [modify a style](https://support.microsoft.com/en-us/office/customize-or-create-new-styles-d38d6e47-f6fc-48eb-a607-1eb120dec563), add it to the YAML, and re-render. 132 | 133 | ```{.bash} 134 | # Run in the Terminal 135 | 136 | quarto pandoc -o word-template.docx --print-default-data-file reference.docx 137 | ``` 138 | 139 | ```{r} 140 | #| echo: false 141 | countdown::countdown(minutes = 7, top = 0) 142 | ``` 143 | -------------------------------------------------------------------------------- /5-style/images/browser-tools-modified.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jadeynryan/parameterized-quarto-workshop/b7ca2fc86f2a1d0c1c7328395657577a0ab47cd3/5-style/images/browser-tools-modified.png -------------------------------------------------------------------------------- /5-style/images/browser-tools.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jadeynryan/parameterized-quarto-workshop/b7ca2fc86f2a1d0c1c7328395657577a0ab47cd3/5-style/images/browser-tools.png -------------------------------------------------------------------------------- /5-style/index.qmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Styling reports" 3 | format: html 4 | --- 5 | 6 | [View slides in full screen](5-style.qmd). 7 | 8 | ```{=html} 9 | 10 | ``` 11 | -------------------------------------------------------------------------------- /6-summary/6-summary.qmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: "🏁 Summary
![](../images/cascadia-jr-quarto.webp){width=5em}" 3 | format: revealjs 4 | --- 5 | 6 | ## Learning objective 1 7 | 8 | **Understand what parameterized reporting is and when it is useful.** 9 | 10 | . . . 11 | 12 | **Like very fancy custom functions:** 13 | 14 | ::: incremental 15 | 16 | - Function → `.qmd` template 17 | 18 | - Input → parameters 19 | 20 | - Output → rendered reports 21 | ::: 22 | 23 | . . . 24 | 25 | **Useful for creating variations of the same report:** 26 | 27 | - Spatial: country, state, county, or city 28 | 29 | - Temporal: year, month or other time period 30 | 31 | - Anything you can filter by: breeds, species, diseases, water bodies, customers, trials, etc. 32 | 33 | . . . 34 | 35 | ::: callout-note 36 | We only covered reports, but you can also parameterize `revealjs` presentations! See this [Jumping Rivers blog post](https://www.jumpingrivers.com/blog/r-parameterised-presentations-quarto/) about it. 37 | ::: 38 | 39 | ## Learning objective 2a 40 | 41 | **Convert a Quarto document into a parameterized template.** 42 | 43 | ::: incremental 44 | - Include default `params:` in YAML 45 | 46 | - Replace hard-coded values with `params$pet_type` 47 | 48 | - **YAML**: 49 | 50 | ``` {yaml} 51 | --- 52 | title: "Report about `r params$pet_type`" 53 | params: 54 | pet_type: "cats" 55 | --- 56 | ``` 57 | 58 | - **Inline R code**: 59 | 60 | ```{markdown} 61 | I like **`r params$pet_type`**. 62 | ``` 63 | 64 | - **Code chunks**: 65 | 66 | ``` r 67 | pets |> 68 | dplyr::filter(pet_type == params$pet_type) 69 | ``` 70 | ::: 71 | 72 | ## Learning objective 2b 73 | 74 | **Render all variations of the report at once using [{quarto}](https://quarto-dev.github.io/quarto-r/) and [{purrr}](https://purrr.tidyverse.org/).** 75 | 76 | ::: {.fragment} 77 | 1. Get all unique parameter combinations into a dataframe: 78 | 79 | ```{r} 80 | #| echo: false 81 | pet_reports <- readRDS("../data/pet-reports.RDS") 82 | ``` 83 | 84 | 85 | ```{r} 86 | head(pet_reports, 2) 87 | ``` 88 | 89 | ::: 90 | 91 | ::: {.fragment} 92 | 2. Use dataframe in `pwalk()` with `quarto_render()`: 93 | 94 | ```{r} 95 | #| eval: false 96 | purrr::pwalk( 97 | pet_reports, 98 | quarto::quarto_render, 99 | input = here::here("pet_template.qmd"), 100 | .progress = TRUE 101 | ) 102 | ``` 103 | ::: 104 | 105 | ## Learning objective 3a 106 | 107 | **Generate multiple format outputs from the same .qmd using [conditional content]{style="font-size:larger;text-decoration: underline;"}.** 108 | 109 | Create a div, span, or non-executable code block with one option from each of the below columns: 110 | 111 | :::: {.columns} 112 | 113 | ::: {.column width="30%"} 114 | **Class** 115 | 116 | - `.content-visible` 117 | - `.content-hidden` 118 | ::: 119 | 120 | ::: {.column width="30%"} 121 | **Attribute** 122 | 123 | - `when-format="___"` 124 | - `unless-format="___"` 125 | ::: 126 | 127 | ::: {.column width="30%"} 128 | **Format** 129 | 130 | - `latex` or `pdf` 131 | - `epub` 132 | - `html` or `revealjs` 133 | - `markdown` 134 | ::: 135 | :::: 136 | 137 | :::: {.fragment style="margin-top:-2em"} 138 | 139 | **Example to show tabset only for HTML reports:** 140 | 141 | ```markdown 142 | :::: {.content-visible when-format="html"} 143 | ::: panel-tabset 144 | {{{< include _4-report-content.qmd >}}} 145 | ::: 146 | :::: 147 | ``` 148 | :::: 149 | 150 | ## Learning objective 3b 151 | 152 | **Generate multiple format outputs from the same .qmd using [conditional code execution]{style="font-size:larger;text-decoration: underline;"}**. 153 | 154 | ::: panel-tabset 155 | 156 | ## Conditional on output 157 | 158 | 1. Get the Pandoc output format. 159 | 160 | ```r 161 | #| label: setup 162 | 163 | format <- knitr::opts_knit$get("rmarkdown.pandoc.to") 164 | ``` 165 | 166 | 2. Use `format` in the `eval: !expr` chunk option. 167 | 168 | ```r 169 | #| label: interactive-plot 170 | #| eval: !expr format == "html" 171 | 172 | # plotly code 173 | ``` 174 | 175 | ## Conditional on parameter 176 | 177 | Use `params` in the `eval: !expr` chunk option. 178 | 179 | ```r 180 | #| eval: !expr params$fave_breed == "Snowshoe" 181 | 182 | # Code for a special plot for my favorite cat breed. 183 | ``` 184 | ::: 185 | 186 | ## Learning objective 3c 187 | 188 | **Generate multiple format outputs from the same .qmd using [custom styling]{style="font-size:larger;text-decoration: underline;"}.** 189 | 190 | ::: panel-tabset 191 | 192 | ## HTML: Bootswatch & SCSS file 193 | 194 | 1. Pick a [Bootswatch](https://bootswatch.com/) theme. 195 | 196 | 2. Customize with a `.scss` file. 197 | 198 | 3. Use browser developer tools to find/test more styling. 199 | 200 | ``` yaml 201 | format: 202 | html: 203 | theme: [flatly, theme.scss] 204 | ``` 205 | 206 | ## MS Word: reference doc 207 | 208 | 1. Create the reference doc. 209 | 210 | ```{.bash filename="Terminal"} 211 | quarto pandoc -o word-template.docx --print-default-data-file reference.docx 212 | ``` 213 | 214 | 2. Open `word-template.docx` and modify the styles. 215 | 216 | 3. Set this template in the YAML under the `reference-doc:` key: 217 | 218 | ```yaml 219 | format: 220 | docx: 221 | reference-doc: word-template.docx 222 | ``` 223 | ::: 224 | 225 | ## Thank you! 226 | 227 | ::: columns 228 | ::: {.column width="65%"} 229 | 🏡 **Home for all workshop materials**: 230 | [jadeyryan.quarto.pub/cascadia-quarto/](https://jadeyryan.quarto.pub/cascadia-quarto/) 231 | 232 | \ 233 | 234 | 🎥 **Recordings from previous workshops & talks**: 235 | [links in GitHub repo](https://github.com/jadeynryan/parameterized-quarto-workshop?tab=readme-ov-file#workshops) or [my YouTube playlist](https://youtube.com/playlist?list=PLzjGoNexcyYaDUVCg4MQDtMXLl2F5HE3B&si=-y0jQfCYyRAQjI1B) 236 | 237 | ![](images/mts.jpg){width="600" 238 | fig-alt="From left to right, Mai, Tai, and Skye. Three snowshoe cats cuddling in their warming beds." 239 | style="border-radius:1em"} 240 | ::: 241 | 242 | ::: {.column width="35%"} 243 | **Let's stay connected!** 244 | 245 | {{< fa link size=xl >}} [jadeyryan.com](https://jadeyryan.com) 246 | 247 | {{< fa brands mastodon size=xl >}} [\@jadeynryan](https://fosstodon.org/@jadeynryan) 248 | 249 | {{< fa brands linkedin size=xl >}} [linkedin.com/in/jadey-ryan](https://www.linkedin.com/in/jadey-ryan) 250 | 251 | {{< fa brands github size=xl >}} [jadeynryan](https://github.com/jadeynryan/) 252 | 253 | {{< fa brands etsy size=xl >}} [thecodingcats.etsy.com](https://thecodingcats.etsy.com/) 254 | ::: 255 | ::: 256 | -------------------------------------------------------------------------------- /6-summary/images/mts.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jadeynryan/parameterized-quarto-workshop/b7ca2fc86f2a1d0c1c7328395657577a0ab47cd3/6-summary/images/mts.jpg -------------------------------------------------------------------------------- /6-summary/index.qmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Summary" 3 | format: html 4 | --- 5 | 6 | [View slides in full screen](6-summary.qmd). 7 | 8 | ```{=html} 9 | 10 | ``` 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Reproducible and Parameterized Quarto Workshops 2 | 3 | ------------------------------------------------------------------------ 4 | 5 | ## Workshops 6 | 7 | ### Upcoming 8 | 9 | ### Past 10 | 11 | #### Intermediate Quarto: Parameterized Reports 12 | 13 | [Cascadia R Conference](https://cascadiarconf.com/) 2024 Workshop 14 | 15 | 📆 June 21, 2024 // 1:30 pm - 4:30 pm PT\ 16 | 🏫 University of Washington, South Lake Union, Building C\ 17 | 🧾 [EventBrite 18 | registration](https://www.eventbrite.com/e/cascadia-r-conf-2024-tickets-851137203287)\ 19 | 🏡 [Workshop website](https://jadeyryan.quarto.pub/cascadia-quarto)\ 20 | 🐙 [GitHub release](https://github.com/jadeynryan/parameterized-quarto-workshop/tree/cascadia) 21 | 22 | The [Intro to Quarto workshop](https://charlotte.quarto.pub/cascadia/) 23 | takes you through the basics of authoring a reproducible report using 24 | Quarto. This workshop builds on those concepts and teaches you how to 25 | level up your reproducible reports by using parameters, conditional 26 | content, conditional code execution, and custom styling sheets for HTML 27 | and Microsoft Word formats. Additionally, you will learn how to render 28 | all variations of a parameterized report at once using `quarto` and 29 | `purrr`. 30 | 31 | #### Reproducible Reporting with Quarto 32 | 33 | Hosted by the [Community Engaged Data 34 | Science](https://coa-community-data-science.netlify.app/) undergraduate 35 | course taught by [Dr. Laurie Baker](https://lauriebaker.rbind.io/). 36 | 37 | 📆 April 25, 2024 // 1:00 pm - 4:00 pm ET\ 38 | 🏫 Hybrid // College of the Atlantic\ 39 | 🆓 **FREE** with 40 | [registration](https://www.meetup.com/maine-r-users-group/events/300459430/)\ 41 | 🏡 [Workshop 42 | website](https://jadeyryan.quarto.pub/ceds-quarto-workshop)\ 43 | 🐙 [GitHub 44 | release](https://github.com/jadeynryan/parameterized-quarto-workshop/tree/coa-ceds) 45 | 46 | Join this interactive workshop to learn what Quarto is and its impact on 47 | data communication and sharing. In this session, we will 1) explore how 48 | Quarto seamlessly integrates code and text to create reproducible 49 | reports and presentations; 2) dive into real-world applications at the 50 | Washington State Department of Agriculture; and 3) learn and practice 51 | Quarto functionality through demonstrations and hands-on exercises 52 | covering static and dynamic output formats, markdown syntax, code chunk 53 | options, output theming, and parameters for document variations. 54 | 55 | #### Parameterized Reporting with Quarto 56 | 57 | Hosted by R-Ladies Abuja 58 | 59 | 📆 February 21, 2024 // 4:30 pm - 6:30 pm WAT\ 60 | 🏨 Virtual\ 61 | 🆓 **FREE** with 62 | [registration](https://www.meetup.com/rladies-abuja/events/298688371/)\ 63 | 🏡 [Workshop 64 | website](https://jadeyryan.quarto.pub/rladies-abuja-quarto-params)\ 65 | 🐙 [GitHub 66 | release](https://github.com/jadeynryan/parameterized-quarto-workshop/tree/rladies-abuja)\ 67 | 🎥 [Recording](https://youtu.be/kQn82pa04jQ?si=Ksvdp0Hdgs1crOD0) 68 | 69 | Tired of manually adjusting Quarto reports for different regions, time 70 | periods, or clients? Dreaming of using just one template to generate 71 | both interactive HTML and static Word/PDF versions of your reports? 72 | 73 | Join our workshop to unlock the power of parameterized reporting with 74 | Quarto and leave with your own template and examples to modify for your 75 | own projects. 76 | 77 | #### Parameterized Reporting with Quarto 78 | 79 | Hosted by R-Ladies Washington DC 80 | 81 | 📆 January 18, 2024 // 6:30 pm - 8:30 pm ET\ 82 | 🏨 Virtual\ 83 | 🆓 **FREE** with 84 | [registration](https://www.meetup.com/rladies-dc/events/297344107/)\ 85 | 🏡 [Workshop 86 | website](https://jadeyryan.quarto.pub/rladies-dc-quarto-params/)\ 87 | 🐙 [GitHub 88 | release](https://github.com/jadeynryan/parameterized-quarto-workshop/tree/rladies-dc)\ 89 | 🎥 [Recording](https://youtu.be/MKjz_xkMgxY) 90 | 91 | Tired of manually adjusting Quarto reports for different regions, time 92 | periods, or clients? Dreaming of using just one template to generate 93 | both interactive HTML and static Word/PDF versions of your reports? 94 | 95 | Join our workshop to unlock the power of parameterized reporting with 96 | Quarto and leave with your own template and examples to modify for your 97 | own projects. 98 | 99 | ## Speaker 100 | 101 | **Jadey Ryan** 102 | 103 | Jadey is a data scientist at the [Natural Resources and Agricultural 104 | Sciences section](https://agr.wa.gov/AgScience) of the WA Dept. of 105 | Agriculture. She is an R enthusiast obsessed with efficiently creating 106 | beautiful data products and decision-support tools, especially with 107 | Quarto. Away from the computer, catch her trying new foods with her 108 | husband, snuggling with her three cats, or taking leisurely strolls in 109 | the great outdoors. 110 | 111 | Learn more at [jadeyryan.com](https://jadeyryan.com). 112 | 113 | ## Acknowledgements 114 | 115 | This workshop structure has been adapted from: 116 | 117 | - [R/Medicine Data Cleaning 2023 118 | Workshop](https://shannonpileggi.github.io/rmedicine-data-cleaning-2023/) 119 | taught by [Crystal Lewis](https://cghlewis.com/), [Shannon 120 | Pileggi](https://www.pipinghotdata.com/), and [Peter 121 | Higgins](https://bookdown.org/pdr_higgins/rmrwr/) 122 | 123 | - [ASA Traveling Courses on 124 | Quarto](https://quarto.org/docs/blog/posts/2023-12-05-asa-traveling-courses/) 125 | taught by [Mine Çetinkaya-Rundel](https://mine-cr.com/) and [Andrew 126 | Bray](https://andrewpbray.github.io/) 127 | 128 | ------------------------------------------------------------------------ 129 | 130 | ![](https://licensebuttons.net/l/by-nc-sa/4.0/88x31.png) 131 | 132 | This work is released under the [Creative Commons 133 | Attribution-NonCommercial-ShareAlike 4.0 International (CC BY-NC-SA) 134 | license](https://creativecommons.org/licenses/by-nc-sa/4.0/). 135 | -------------------------------------------------------------------------------- /_extensions/quarto-ext/fontawesome/_extension.yml: -------------------------------------------------------------------------------- 1 | title: Font Awesome support 2 | author: Carlos Scheidegger 3 | version: 1.1.0 4 | quarto-required: ">=1.2.269" 5 | contributes: 6 | shortcodes: 7 | - fontawesome.lua 8 | -------------------------------------------------------------------------------- /_extensions/quarto-ext/fontawesome/assets/css/latex-fontsize.css: -------------------------------------------------------------------------------- 1 | .fa-tiny { 2 | font-size: 0.5em; 3 | } 4 | .fa-scriptsize { 5 | font-size: 0.7em; 6 | } 7 | .fa-footnotesize { 8 | font-size: 0.8em; 9 | } 10 | .fa-small { 11 | font-size: 0.9em; 12 | } 13 | .fa-normalsize { 14 | font-size: 1em; 15 | } 16 | .fa-large { 17 | font-size: 1.2em; 18 | } 19 | .fa-Large { 20 | font-size: 1.5em; 21 | } 22 | .fa-LARGE { 23 | font-size: 1.75em; 24 | } 25 | .fa-huge { 26 | font-size: 2em; 27 | } 28 | .fa-Huge { 29 | font-size: 2.5em; 30 | } 31 | -------------------------------------------------------------------------------- /_extensions/quarto-ext/fontawesome/assets/webfonts/FontAwesome6Brands-Regular-400.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jadeynryan/parameterized-quarto-workshop/b7ca2fc86f2a1d0c1c7328395657577a0ab47cd3/_extensions/quarto-ext/fontawesome/assets/webfonts/FontAwesome6Brands-Regular-400.ttf -------------------------------------------------------------------------------- /_extensions/quarto-ext/fontawesome/assets/webfonts/FontAwesome6Brands-Regular-400.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jadeynryan/parameterized-quarto-workshop/b7ca2fc86f2a1d0c1c7328395657577a0ab47cd3/_extensions/quarto-ext/fontawesome/assets/webfonts/FontAwesome6Brands-Regular-400.woff2 -------------------------------------------------------------------------------- /_extensions/quarto-ext/fontawesome/assets/webfonts/FontAwesome6Free-Regular-400.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jadeynryan/parameterized-quarto-workshop/b7ca2fc86f2a1d0c1c7328395657577a0ab47cd3/_extensions/quarto-ext/fontawesome/assets/webfonts/FontAwesome6Free-Regular-400.ttf -------------------------------------------------------------------------------- /_extensions/quarto-ext/fontawesome/assets/webfonts/FontAwesome6Free-Regular-400.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jadeynryan/parameterized-quarto-workshop/b7ca2fc86f2a1d0c1c7328395657577a0ab47cd3/_extensions/quarto-ext/fontawesome/assets/webfonts/FontAwesome6Free-Regular-400.woff2 -------------------------------------------------------------------------------- /_extensions/quarto-ext/fontawesome/assets/webfonts/FontAwesome6Free-Solid-900.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jadeynryan/parameterized-quarto-workshop/b7ca2fc86f2a1d0c1c7328395657577a0ab47cd3/_extensions/quarto-ext/fontawesome/assets/webfonts/FontAwesome6Free-Solid-900.ttf -------------------------------------------------------------------------------- /_extensions/quarto-ext/fontawesome/assets/webfonts/FontAwesome6Free-Solid-900.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jadeynryan/parameterized-quarto-workshop/b7ca2fc86f2a1d0c1c7328395657577a0ab47cd3/_extensions/quarto-ext/fontawesome/assets/webfonts/FontAwesome6Free-Solid-900.woff2 -------------------------------------------------------------------------------- /_extensions/quarto-ext/fontawesome/assets/webfonts/fa-brands-400.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jadeynryan/parameterized-quarto-workshop/b7ca2fc86f2a1d0c1c7328395657577a0ab47cd3/_extensions/quarto-ext/fontawesome/assets/webfonts/fa-brands-400.ttf -------------------------------------------------------------------------------- /_extensions/quarto-ext/fontawesome/assets/webfonts/fa-brands-400.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jadeynryan/parameterized-quarto-workshop/b7ca2fc86f2a1d0c1c7328395657577a0ab47cd3/_extensions/quarto-ext/fontawesome/assets/webfonts/fa-brands-400.woff2 -------------------------------------------------------------------------------- /_extensions/quarto-ext/fontawesome/assets/webfonts/fa-regular-400.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jadeynryan/parameterized-quarto-workshop/b7ca2fc86f2a1d0c1c7328395657577a0ab47cd3/_extensions/quarto-ext/fontawesome/assets/webfonts/fa-regular-400.ttf -------------------------------------------------------------------------------- /_extensions/quarto-ext/fontawesome/assets/webfonts/fa-regular-400.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jadeynryan/parameterized-quarto-workshop/b7ca2fc86f2a1d0c1c7328395657577a0ab47cd3/_extensions/quarto-ext/fontawesome/assets/webfonts/fa-regular-400.woff2 -------------------------------------------------------------------------------- /_extensions/quarto-ext/fontawesome/assets/webfonts/fa-solid-900.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jadeynryan/parameterized-quarto-workshop/b7ca2fc86f2a1d0c1c7328395657577a0ab47cd3/_extensions/quarto-ext/fontawesome/assets/webfonts/fa-solid-900.ttf -------------------------------------------------------------------------------- /_extensions/quarto-ext/fontawesome/assets/webfonts/fa-solid-900.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jadeynryan/parameterized-quarto-workshop/b7ca2fc86f2a1d0c1c7328395657577a0ab47cd3/_extensions/quarto-ext/fontawesome/assets/webfonts/fa-solid-900.woff2 -------------------------------------------------------------------------------- /_extensions/quarto-ext/fontawesome/assets/webfonts/fa-v4compatibility.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jadeynryan/parameterized-quarto-workshop/b7ca2fc86f2a1d0c1c7328395657577a0ab47cd3/_extensions/quarto-ext/fontawesome/assets/webfonts/fa-v4compatibility.ttf -------------------------------------------------------------------------------- /_extensions/quarto-ext/fontawesome/assets/webfonts/fa-v4compatibility.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jadeynryan/parameterized-quarto-workshop/b7ca2fc86f2a1d0c1c7328395657577a0ab47cd3/_extensions/quarto-ext/fontawesome/assets/webfonts/fa-v4compatibility.woff2 -------------------------------------------------------------------------------- /_extensions/quarto-ext/fontawesome/fontawesome.lua: -------------------------------------------------------------------------------- 1 | local function ensureLatexDeps() 2 | quarto.doc.use_latex_package("fontawesome5") 3 | end 4 | 5 | local function ensureHtmlDeps() 6 | quarto.doc.add_html_dependency({ 7 | name = 'fontawesome6', 8 | version = '0.1.0', 9 | stylesheets = {'assets/css/all.css', 'assets/css/latex-fontsize.css'} 10 | }) 11 | end 12 | 13 | local function isEmpty(s) 14 | return s == nil or s == '' 15 | end 16 | 17 | local function isValidSize(size) 18 | local validSizes = { 19 | "tiny", 20 | "scriptsize", 21 | "footnotesize", 22 | "small", 23 | "normalsize", 24 | "large", 25 | "Large", 26 | "LARGE", 27 | "huge", 28 | "Huge" 29 | } 30 | for _, v in ipairs(validSizes) do 31 | if v == size then 32 | return size 33 | end 34 | end 35 | return "" 36 | end 37 | 38 | return { 39 | ["fa"] = function(args, kwargs) 40 | 41 | local group = "solid" 42 | local icon = pandoc.utils.stringify(args[1]) 43 | if #args > 1 then 44 | group = icon 45 | icon = pandoc.utils.stringify(args[2]) 46 | end 47 | 48 | local title = pandoc.utils.stringify(kwargs["title"]) 49 | if not isEmpty(title) then 50 | title = " title=\"" .. title .. "\"" 51 | end 52 | 53 | local label = pandoc.utils.stringify(kwargs["label"]) 54 | if isEmpty(label) then 55 | label = " aria-label=\"" .. icon .. "\"" 56 | else 57 | label = " aria-label=\"" .. label .. "\"" 58 | end 59 | 60 | local size = pandoc.utils.stringify(kwargs["size"]) 61 | 62 | -- detect html (excluding epub which won't handle fa) 63 | if quarto.doc.is_format("html:js") then 64 | ensureHtmlDeps() 65 | if not isEmpty(size) then 66 | size = " fa-" .. size 67 | end 68 | return pandoc.RawInline( 69 | 'html', 70 | "" 71 | ) 72 | -- detect pdf / beamer / latex / etc 73 | elseif quarto.doc.is_format("pdf") then 74 | ensureLatexDeps() 75 | if isEmpty(isValidSize(size)) then 76 | return pandoc.RawInline('tex', "\\faIcon{" .. icon .. "}") 77 | else 78 | return pandoc.RawInline('tex', "{\\" .. size .. "\\faIcon{" .. icon .. "}}") 79 | end 80 | else 81 | return pandoc.Null() 82 | end 83 | end 84 | } 85 | -------------------------------------------------------------------------------- /_freeze/1-welcome/1-welcome/execute-results/html.json: -------------------------------------------------------------------------------- 1 | { 2 | "hash": "75fa531d560a37bdae729ab7d050df0c", 3 | "result": { 4 | "engine": "knitr", 5 | "markdown": "---\ntitle: \"Welcome!
![](../images/cascadia-jr-quarto.webp){width=5em}\"\nformat: revealjs\n---\n\n\n\n## While waiting for us to begin...\n\nOpen the workshop website and make sure you've completed the pre-work\nand have the required software, packages, and exercises.\n\n[**jadeyryan.quarto.pub/cascadia-quarto/prework**](https://jadeyryan.quarto.pub/cascadia-quarto/prework).\n\n\n\n::: {.cell}\n\n```{.r .cell-code}\nusethis::use_course(\n \"https://github.com/jadeynryan/parameterized-quarto-workshop/raw/cascadia/exercises/intermediate-exercises.zip\"\n# , destdir = \"C:/Users/jryan/Documents/R/projects\"\n )\n\ninstall.packages(c(\"dplyr\", \"fs\", \"ggplot2\", \"here\", \"janitor\", \"knitr\", \"lubridate\",\n \"plotly\", \"purrr\", \"quarto\", \"readr\", \"rmarkdown\", \"stringr\", \"tidyr\"))\n```\n:::\n\n\n\nIf `use_course()` didn't work, manually download the zip file from\nGitHub:\n[github.com/jadeynryan/parameterized-quarto-workshop/tree/cascadia/exercises](https://github.com/jadeynryan/parameterized-quarto-workshop/tree/cascadia/exercises).\n\n::: callout-tip\n## Backup option: Posit Cloud\n\nJoin the Posit Cloud [Quarto Cascadia\nworkspace](https://posit.cloud/spaces/519059/join?access_code=ckv18LHEcSw3QtQ904uvpzLVlh4L2rk6RhKdpwyp)\nand open the [Intermediate Exercises\nproject](https://posit.cloud/spaces/519059/content/8351107).\n:::\n\n## Acknowledgements\n\n![](images/cascadia-logo.png){.center fig-alt=\"Cascadia R Conf logo\"\nheight=\"350px\"}\n\n\\\n\n**Workshop structure adapted from:**\n\n[R/Medicine Data Cleaning 2023\nWorkshop](https://shannonpileggi.github.io/rmedicine-data-cleaning-2023/)\ntaught by [Crystal Lewis](https://cghlewis.com/), [Shannon\nPileggi](https://www.pipinghotdata.com/), and [Peter\nHiggins](https://bookdown.org/pdr_higgins/rmrwr/)\n\n\\\n\n[ASA Traveling Courses on\nQuarto](https://quarto.org/docs/blog/posts/2023-12-05-asa-traveling-courses/)\ntaught by [Mine Çetinkaya-Rundel](https://mine-cr.com/) and [Andrew\nBray](https://andrewpbray.github.io/)\n\n## Code of Conduct\n\nPlease review and abide by:\n[cascadiarconf.com/policies/](https://cascadiarconf.com/policies/)\n\n\\\n\n💙 Treat everyone with respect and kindness.\n\n\\\n\n💜 Everyone should feel welcome and safe.\n\n## Disclaimer and license\n\nOpinions expressed are solely my own and do not express the views of my\nemployer or any organizations I am associated with.\n\n\\\n\nThis work is licensed under [Creative Commons\nAttribution-NonCommercial-ShareAlike 4.0 International (CC\nBY-NC-SA)](https://creativecommons.org/licenses/by-nc-sa/4.0/).\n\n\n\n{{< fa brands creative-commons size=2x >}} {{< fa brands creative-commons-by size=2x >}} {{< fa brands creative-commons-nc size=2x >}} {{< fa brands creative-commons-sa size=2x >}}\n\n\n\n\n\n## Meet Jadey Ryan\n\n::: columns\n::: {.column width=\"44%\"}\nData scientist at WA Dept of Agriculture\n\nThe Coding Cats: cat & code themed merch\n\n{{< fa link size=xl >}} [jadeyryan.com](https://jadeyryan.com)\n\n{{< fa brands mastodon size=xl >}} [\\@jadeynryan](https://fosstodon.org/@jadeynryan)\n\n{{< fa brands linkedin size=xl >}} [linkedin.com/in/jadey-ryan](https://www.linkedin.com/in/jadey-ryan)\n\n{{< fa brands github size=xl >}} [jadeynryan](https://github.com/jadeynryan/)\n\n{{< fa brands etsy size=xl >}} [thecodingcats.etsy.com](https://thecodingcats.etsy.com/)\n\n![](images/mts.webp){fig-alt=\"Three snowshoe siamese cats in loaf mode. From left to right: Tai, Mai, and Skye\" style=\"border-radius:1em;margin-top:auto\"}\n:::\n\n::: {.column width=\"28%\"}\n![](images/bulk-density.jpg){fig-alt=\"Jadey collecting a bulk density soil sample in a field of wheat stubble.\" style=\"border-radius:1em;margin-top:auto\"}\n:::\n\n::: {.column width=\"28%\"}\n::: {layout-nrow=\"2\"}\n![](images/soil-sampling.jpg){style=\"border-radius:1em;margin-top:auto\"\nfig-alt=\"Jadey standing in a field of wheat stubble holding a 3 foot long soil sampling probe over her shoulder.\"}\n\n![](images/rangeland.jpg){fig-alt=\"Jadey standing in a grazed wildflower meadow with two colleagues collecting soil samples.\"\nstyle=\"border-radius:1em;margin-top:auto\"}\n:::\n\n{{< fa camera title=\"Photo credit\" >}} [@leslie.mmichel](https://twitter.com/leslie_mmichel)\n:::\n:::\n\n## Meet our TA: Lydia Gibson\n\n![](images/lydia-gibson.jpeg){.center fig-alt=\"Photo of Lydia Gibson\" style=\"border-radius:1em;\"}\n\n## Learning objectives\n\n::: incremental\n- Understand what parameterized reporting is and when it is useful.\n- Convert a Quarto document into a parameterized template and render\n all variations.\n- Generate multiple format outputs from the same template using:\n - conditional content\n - conditional code execution, and\n - custom styling.\n:::\n\n## Schedule\n\n| Time | Topic |\n|-------------|------------------------------------------------------|\n| 1:30 - 2:00 | Welcome & [Parameterizing reports](../2-parameters/) |\n| 2:00 - 3:00 | [Rendering reports](../3-render/) |\n| 3:00 - 3:30 | Break |\n| 3:30 - 4:00 | [Conditional content & code](../4-conditionals/) |\n| 4:00 - 4:20 | [Styling reports](../5-styling/) |\n| 4:20 - 4:30 | [Summary](../6-summary/) |\n\n## Workshop structure\n\nPresentation\n\n💃🏻 Demos\n\n💪🏻 Exercises\n\n\\\n\n. . .\n\nLet us know how you're doing by displaying your stickies!\n\n🟦 I'm all good; I'm done.\n\n🟧 I'm a little lost; I could use some help.\n\n# 💪🏼 Exercise {.exercise}\n\n**Meet your neighbors**:\n\n- Your name\n\n- Your favorite snack 🍎🧀🍦🍫🍿\n\n- What kinds of reports are you wanting to create after today's\n workshop?\n\n\n\n::: {.cell}\n::: {.cell-output-display}\n\n```{=html}\n
\n
\n05:00\n
\n```\n\n:::\n:::\n", 6 | "supporting": [], 7 | "filters": [ 8 | "rmarkdown/pagebreak.lua" 9 | ], 10 | "includes": { 11 | "include-in-header": [ 12 | "\n\n" 13 | ], 14 | "include-after-body": [ 15 | "\n\n\n" 16 | ] 17 | }, 18 | "engineDependencies": {}, 19 | "preserve": {}, 20 | "postProcess": true 21 | } 22 | } -------------------------------------------------------------------------------- /_freeze/2-parameters/2-parameters/execute-results/html.json: -------------------------------------------------------------------------------- 1 | { 2 | "hash": "f4219177a054ba8a3b0b3a1b1b615355", 3 | "result": { 4 | "engine": "knitr", 5 | "markdown": "---\ntitle: \"Parameterizing reports
![](../images/cascadia-jr-quarto.webp){width=5em}\"\nformat: revealjs\n---\n\n\n\n## Examples of parameterized reports\n\n::: columns\n::: {.column width=\"25%\"}\n![](images/ocean-floor.png){.lightbox group=\"examples\"\nfig-alt=\"RStudio R Markdown parameterized report project for mapping ocean floors.\"}\n\n[Ocean floor maps](https://rmarkdown.rstudio.com/lesson-6.html)\n:::\n\n::: {.column width=\"25%\"}\n![](images/urban-institute.png){.lightbox group=\"examples\"\nfig-alt=\"Report for Alabama spending with text and plots generated from parameterized R Markdown.\"}\n\n[Fiscal\nbriefs](https://book.rwithoutstatistics.com/parameterized-reports-chapter.html)\n:::\n\n::: {.column width=\"25%\"}\n![](images/driver-quality.png){.lightbox group=\"examples\"\nfig-alt=\"Report for Alaska driver quality with text, plot, and table generated from parameterized RMarkdown.\"}\n\n[Bad\ndrivers](https://urban-institute.medium.com/iterated-fact-sheets-with-r-markdown-d685eb4eafce)\n:::\n\n::: {.column width=\"25%\"}\n![](images/soils-report-html.png){.lightbox group=\"examples\"\nfig-alt=\"HTML report for soil health survey participant, generated from a parameterized Quarto report.\"}\n\n[Soil health](https://wa-department-of-agriculture.github.io/soils/)\n:::\n:::\n\n. . .\n\n\\\n\n**Another use case: different audiences, different reports**\n\nShow code for technical staff and hide code for everyone else ([StackOverflow\nexample](https://stackoverflow.com/questions/73571919/how-to-pass-logical-parameters-with-the-quarto-r-package-to-the-knitr-chunk-opti)).\n\n## Like a custom function\n\n::: r-stack\n![](images/template1.png){.fragment\nfig-alt=\"File with the word '.qmd' inside and the word 'Function' above.\"\nwidth=\"1300\" fig-align=\"left\" style=\"margin-top:-1em\"}\n\n![](images/template2.png){.fragment\nfig-alt=\"An arrow points from 'Input' with 'params$year' to the previous image with 'Function' and '.qmd' file.\"\nwidth=\"1300\" fig-align=\"left\" style=\"margin-top:-1em\"}\n\n![](images/template3.png){.fragment\nfig-alt=\"In addition to the previous two images, arrows point to five reports with years 2019 through 2023 on them in a flow chart.\"\nwidth=\"1300\" fig-align=\"left\" style=\"margin-top:-1em\"}\n:::\n\n## What makes a report \"parameterized\"?\n\n- YAML header with `params` key-value pairs\n\n- Use these `params` to create different variations of a report from a single `.qmd` document.\n\n. . .\n\n::: callout-important\n- Valid parameter values are strings, numbers, or Boolean.\n\n- Must serialize a dataframe to pass it as a parameter, then un-serialize it\n back to a dataframe within the `.qmd` content.\n\n- See [Christophe Dervieux's answer in Posit\n Community](https://community.rstudio.com/t/param-converted-from-data-frame-to-list/155556/9)\n to understand why.\n\n- See [John Paul Helveston's blog\n post](https://www.jhelvy.com/posts/2023-02-28-parameterized-pdfs-with-quarto/#passing-data-frames-as-parameters)\n to learn how to use {jsonlite} as a workaround.\n:::\n\n## Workflow\n\n::: incremental\n1. Write report template with default values hard-coded, and then render & review.\n\n2. Set default `params` key-value pairs in YAML. \n\n3. Replace hard-coded values with the `params` variables.\n\n4. Render the single report and review.\n\n5. Render extreme cases and review.\n\n - Parameter values with barely any data and with tons of data.\n\n6. Render all variations of the report at once.\n:::\n\n# 💪🏼 Exercise {.exercise}\n\n**Explore a report without parameters and see where we could add them.**\n\n1. Open `1-swiss-cats.qmd`.\n\n2. Click the\n ![](https://quarto.org/docs/get-started/hello/images/rstudio-render-button.png){fig-alt=\"Quarto render button in RStudio\" style=\"vertical-align:middle;\" width=\"49\"} **Render** button.\n\n3. Look at the source markdown & code and the rendered report.\n\n4. 💬 **Chat**: What variables could we set as parameters?\n\n 💡 **Hint**: run the `setup` chunk and look at the `pets` dataframe to see\n what variables it has.\n\n\n\n::: {.cell}\n::: {.cell-output-display}\n\n```{=html}\n
\n
\n05:00\n
\n```\n\n:::\n:::\n\n\n\n## Set `params` in YAML header\n\n```{.yaml code-line-numbers=\"|2|3,4,6|5|7-8|11\"}\n---\ntitle: \"Swiss Cats\" # Metadata\nformat: # Set format types\n html:\n toc: true # Set additional options\n docx: default \nparams: # Set default parameter key-value pairs\n fave_breed: \"Snowshoe\" \n---\n \nReport content goes here. # Write narrative and code\n```\n\n. . .\n\n::: callout-important\nYour default `params` key-value pairs must be found in your dataset. Otherwise, code will error or output will be blank.\n\nThe variable name for `params` can be anything you choose. Often, it's a column name in your dataset.\n:::\n\n## Access `params`\n\n\n\n\n\n\n\nRun any line or chunk to add `params` to your environment.\n\n. . .\n\n`params` object is a list.\n\n\n\n::: {.cell}\n\n```{.r .cell-code}\nstr(params)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\nList of 1\n $ fave_breed: chr \"Snowshoe\"\n```\n\n\n:::\n:::\n\n\n\n\\\n\n. . .\n\nAccess with `$` notation.\n\n\n\n::: {.cell}\n\n```{.r .cell-code}\nparams$fave_breed\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n[1] \"Snowshoe\"\n```\n\n\n:::\n:::\n\n\n\n\\\n\n. . .\n\nFor inline code in YAML or report content, enclose the expression in `` `r ` ``.\n\n```markdown\nMy favorite cat breed is the **`r params$fave_breed`**.\n```\n\nMy favorite cat breed is the **Snowshoe**.\n\n## Replace hard-coded values with `params`\n\n`Cmd`/`Ctrl` + `F` to find where to replace hard-coded values with `params`.\n\n![](images/find-replace-hard-coded-values.png){fig-alt=\"Find and replace toolbar with "pet_type in the Search field highlighted by a purple box and "params$pet_type in the Replace field highlighted by a blue box. The .qmd file shows a filter statement with the "pet_type highlighted by RStudio as a match for the Find tool. This filter statement is highlighted by a purple box with an arrow pointing to a blue box that has the filter statement with the hard-coded "cats string replaced with "params$pet_type.\"}\n\n## Replace hard-coded values with `params`\n\n**Use `$` list notation in code** for plot/table titles and labels, filtering, etc.\n\n\\\n\n`paste()` syntax:\n\n\n\n::: {.cell}\n\n```{.r .cell-code}\n# ggplot2 code +\n labs(title = paste(params$fave_breed, \"population\"))\n```\n:::\n\n\n\n. . .\n\n\\\n\n`glue::glue()` syntax:\n\n\n\n::: {.cell}\n\n```{.r .cell-code}\n# ggplot2 code +\n labs(title = glue::glue(\"{params$fave_breed} population\"))\n```\n:::\n\n\n\n\\\n\n. . .\n\n**Use inline R code** for markdown.\n\n```markdown\n## My favorite breed: **`r params$fave_breed`**.\n```\n\n##\n\n![](images/snowshoe-graph.png){fig-align=\"center\"}\n\n# 💃🏻 Demo {.demo}\n\nModify `1-swiss-cats-demo.qmd` to add `pet_type` and `fave_breed` parameters.\n\n\\\n\nThis parameterized version of `1-swiss-cats.qmd` is the starting point for the next section's exercises (`2-quarto-render.qmd`).\n", 6 | "supporting": [], 7 | "filters": [ 8 | "rmarkdown/pagebreak.lua" 9 | ], 10 | "includes": { 11 | "include-in-header": [ 12 | "\n\n" 13 | ], 14 | "include-after-body": [ 15 | "\n\n\n" 16 | ] 17 | }, 18 | "engineDependencies": {}, 19 | "preserve": {}, 20 | "postProcess": true 21 | } 22 | } -------------------------------------------------------------------------------- /_freeze/4-conditionals/4-conditionals/execute-results/html.json: -------------------------------------------------------------------------------- 1 | { 2 | "hash": "b01822cd1d328b14ab03f3a3a6971f90", 3 | "result": { 4 | "engine": "knitr", 5 | "markdown": "---\ntitle: \"Conditional content & code
![](../images/cascadia-jr-quarto.webp){width=4em}\"\nformat: revealjs\n---\n\n\n\n# Conditional content {.section}\n\n## Control content visibility\n\nCreate a div, span, or non-executable code block with one option from each of the below columns:\n\n`{.class attribute=\"format\"}`\n\n:::: {.columns}\n\n::: {.column width=\"30%\"}\n**Class**\n\n- `.content-visible`\n- `.content-hidden`\n:::\n\n::: {.column width=\"30%\"}\n**Attribute**\n\n- `when-format=\"___\"`\n- `unless-format=\"___\"`\n:::\n\n::: {.column width=\"30%\"}\n**Format**\n\n- `latex` or `pdf`\n- `epub`\n- `html` or `revealjs`\n- `markdown`\n:::\n\n::::\n\n:::: {.fragment style=\"margin-top:-3.5em\"}\n\n**Examples:**\n\n::: panel-tabset\n\n## Divs\n\n```markdown\n::: {.content-visible when-format=\"html\"}\n\nWill only appear in HTML.\n\n:::\n```\n\n## Spans\n\n``` markdown\nSome text\n[in HTML.]{.content-visible when-format=\"html\"}\n[in PDF.]{.content-visible when-format=\"pdf\"}\n```\n\n## Non-executable code\n\nFenced code blocks purely for documentation.\n\n````markdown\n```{.python .content-visible when-format=\"html\"}\n# code shown only in HTML\n2 + 2\n```\n````\n:::\n::::\n\n. . .\n\n[Conditional Quarto docs](https://quarto.org/docs/authoring/conditional.html) & [Format aliases](https://quarto.org/docs/authoring/conditional.html#format-matching)\n\n## Useful for static/interactive features\n\nPairs well with the `{{< include >}}` shortcode to re-use content without copying/pasting.\n\n. . .\n\n::::: panel-tabset\n\n## Static\n\n```markdown\n:::: {.content-visible unless-format=\"html\"}\n\n## Cats\n\n{{{< include _cats.qmd >}}}\n\n## Dogs\n\n{{{< include _dogs.qmd >}}}\n\n::::\n```\n\n## Interactive\n\n```markdown\n:::: {.content-visible when-format=\"html\"}\n::: panel-tabset\n\n## Cats\n\n{{{< include _cats.qmd >}}}\n\n## Dogs\n\n{{{< include _dogs.qmd >}}}\n\n:::\n::::\n```\n:::::\n\n. . .\n\n::: callout-tip\n## Good practice\nUse an underscore prefix for included files so they are automatically ignored by a Quarto render of a project ([Include shortcode Quarto docs](https://quarto.org/docs/authoring/includes.html)).\n:::\n\n# 💪🏼 Exercise {.exercise}\n\n**Use conditional content divs to control when tabsets are shown.**\n\n1. Modify `3-conditional-content.qmd` so that the `panel-tabset` is visible for HTML reports and hidden for all other formats.\n\n\\\n\n2. Try another way to get the same result.\n\n `{.content-visible when-format=\"html\"}` is essentially the same as `{.content-hidden unless-format=\"html\"}`.\n\n\\\n\n3. 💬 **Chat**: Besides tabsets, what other kinds of content might you want to make visible for only a certain format?\n\n\n\n::: {.cell}\n::: {.cell-output-display}\n\n```{=html}\n
\n
\n05:00\n
\n```\n\n:::\n:::\n\n\n\n# Conditional code execution {.section}\n\n## Conditionally execute a code chunk\n\n::: incremental\n\n- More efficient to not execute code that generates interactive outputs for static reports.\n\n- Useful for executing interactive plot code (e.g., `plotly` or `ggiraph`) for HTML reports and static `ggplot2` code for all other formats. \n\n- Useful for executing different code based on a parameter value.\n\n- Not currently a feature of Quarto v1.4. Follow along with this [GitHub discussion](https://github.com/quarto-dev/quarto-cli/discussions/3260#discussioncomment-4573926).\n\n- Chunk options can use R code for option values with `!expr`. Learn about the limitations to this YAML \"tag\" literal syntax in the [Quarto Chunk Options reference](https://quarto.org/docs/computations/r.html#chunk-options).\n\n:::\n\n## Conditional code based on output\n\nGet the format of the Pandoc output by including the following in the setup chunk of your `.qmd` file:\n\n\n\n::: {.cell}\n\n````{.cell-code}\n```{{r}}\n#| label: setup\n\n# Get output format\nformat <- knitr::opts_knit$get(\"rmarkdown.pandoc.to\")\n```\n````\n:::\n\n\n\n## Use `eval: !expr` chunk option\n\n::: panel-tabset\n\n## Static plot\n\n```markdown\n#| echo: fenced\n#| eval: !expr format %in% c(\"latex\", \"docx\")\n\n# code to create static {ggplot2}\n```\n\n:::: callout-important\n## Use `latex` not `pdf`\n\n`format` comes from `knitr::opts_knit$get(\"rmarkdown.pandoc.to\")`. Pandoc uses LaTeX to create PDFs.\n\nQuarto [format aliases](https://quarto.org/docs/authoring/conditional.html#format-matching) won't work here.\n::::\n\n## Interactive plot\n\n```markdown\n#| echo: fenced\n#| eval: !expr format == \"html\"\n\n# code to create interactive {plotly}\n```\n\n:::\n\n# 💪🏼 Exercise {.exercise}\n\n**Conditionally execute `ggplot2` code for static reports & `plotly` code for interactive reports.**\n\n1. Open `5-conditional-code.qmd`.\n\n2. In the `ggplot2` code chunks and `plotly` code chunks, fill in the blanks for the `eval: ` option.\n\n\n\n::: {.cell}\n\n```{.r .cell-code}\n# Replace ___ with html, latex, or docx.\n\n#| eval: !expr format == \"___\"\n\n#| eval: !expr format %in% c(\"___\", \"___\")\n```\n:::\n\n\n\n3. 💬 **Chat**: How would you change the `eval: ` option to execute a chunk based on a parameter value rather than the output format?\n\n\n\n::: {.cell}\n::: {.cell-output-display}\n\n```{=html}\n
\n
\n05:00\n
\n```\n\n:::\n:::\n\n\n\n## Conditional code based on parameter\n\nUse `params` in `!expr`:\n\n``` r\n#| eval: !expr params$fave_breed == \"Snowshoe\"\n\n# Code for a special plot for my favorite cat breed.\n```\n\n\\\n\n. . .\n\n``` r\n#| eval: !expr !params$fave_breed == \"Snowshoe\"\n\n# Code for a different plot for all other breeds.\n# Note the ! in front of params.\n```\n", 6 | "supporting": [], 7 | "filters": [ 8 | "rmarkdown/pagebreak.lua" 9 | ], 10 | "includes": { 11 | "include-in-header": [ 12 | "\n\n" 13 | ], 14 | "include-after-body": [ 15 | "\n\n\n" 16 | ] 17 | }, 18 | "engineDependencies": {}, 19 | "preserve": {}, 20 | "postProcess": true 21 | } 22 | } -------------------------------------------------------------------------------- /_freeze/5-style/5-style/execute-results/html.json: -------------------------------------------------------------------------------- 1 | { 2 | "hash": "fc09e6ce8648753e60b40ae3abcce0bc", 3 | "result": { 4 | "engine": "knitr", 5 | "markdown": "---\ntitle: \"Styling your reports
![](../images/cascadia-jr-quarto.webp){width=5em}\"\nformat: revealjs\n---\n\n\n\n# HTML {.section}\n\n## Bootswatch themes\n\nChoose from or customize one of 25 [Bootswatch](https://bootswatch.com/) themes.\n\n\\\n\n. . .\n\n**Set the theme** in the YAML under the `html` key:\n\n``` yaml\nformat:\n html:\n theme: flatly\n```\n\n\\\n\n. . .\n\n**Customize a theme** by including a custom `.scss` file under the `theme` key:\n\n``` yaml\nformat:\n html:\n theme: [flatly, theme.scss]\n```\n\n\\\n\n[HTML theming Quarto docs](https://quarto.org/docs/output-formats/html-themes.html)\n\n\n## SCSS files\n\nSCSS files have the following form:\n\n``` scss\n/*-- scss:defaults --*/\n$h2-font-size: 1.6rem !default;\n$headings-font-weight: 500 !default;\n$body-color: $gray-700 !default;\n\n/*-- scss:rules --*/\nh1, h2, h3, h4, h5, h6 {\n text-shadow: -1px -1px 0 rgba(0, 0, 0, .3);\n}\n```\n\n\\\n\nDefine [**SASS variables**](https://quarto.org/docs/output-formats/html-themes.html#sass-variables) in the `defaults` section.\n\nDeclare [**CSS rules**](https://www.w3schools.com/css/css_syntax.ASP) in the `rules` section.\n\n\\\n\n[Bootstrap docs](https://getbootstrap.com/docs/5.1/customize/sass/)\n\n[Default Bootstrap variables](https://github.com/twbs/bootstrap/blob/main/scss/_variables.scss)\n\n## Browser developer tools\n\n::: r-stack\n![](images/browser-tools.png){.fragment\nfig-alt=\"Screenshot of browser developer tools inspecting the Quarto title meta heading of an HTML report.\"\nwidth=\"1300\" fig-align=\"left\"}\n\n![](images/browser-tools-modified.png){.fragment\nfig-alt=\"Screenshot of browser developer tools with the CSS declaration modified to test style changes.\"\nwidth=\"1300\" fig-align=\"left\"}\n:::\n\n# MS Word {.section}\n\n## Reference document\n\nCreate and modify a *reference document*, which is a special kind of template.\n\n. . .\n\n1. Run the following in the Terminal to create the reference doc:\n\n```{.bash filename=\"Terminal\"}\nquarto pandoc -o word-template.docx --print-default-data-file reference.docx\n```\n\n. . .\n\n2. Open `word-template.docx` and modify the styles.\n\n. . .\n\n3. Set this template in the YAML under the `reference-doc:` key:\n\n```yaml\nformat:\n docx:\n reference-doc: word-template.docx\n```\n\n. . .\n\n[MS Word template Quarto docs](https://quarto.org/docs/output-formats/ms-word-templates.html)\n\n[MS documentation on modifying styles](https://support.microsoft.com/en-us/office/customize-or-create-new-styles-d38d6e47-f6fc-48eb-a607-1eb120dec563)\n\n## Need your report as a PDF?\n\nFormat in MS Word and then convert `.docx` to `.pdf` with Adobe Acrobat.\n\nor...\n\n- Learn how to format PDFs in the Quarto docs: [PDF basics](https://quarto.org/docs/output-formats/pdf-basics.html) & [PDF options](https://quarto.org/docs/reference/formats/pdf.html).\n\n- Try [Typst](https://quarto.org/docs/output-formats/typst.html), an open-source markup-based typesetting system, new in Quarto 1.4.\n\n# 💪🏼 Exercise {.exercise}\n\n**Play around with the HTML and MS Word styling of a report of your choosing.**\n\n1. Open a new `.qmd` or choose one from a previous exercise.\n2. Add a [Bootswatch](https://bootswatch.com/) theme to the YAML and re-render.\n3. Create a MS Word reference doc, [modify a style](https://support.microsoft.com/en-us/office/customize-or-create-new-styles-d38d6e47-f6fc-48eb-a607-1eb120dec563), add it to the YAML, and re-render.\n\n```{.bash}\n# Run in the Terminal\n\nquarto pandoc -o word-template.docx --print-default-data-file reference.docx\n```\n\n\n\n::: {.cell}\n::: {.cell-output-display}\n\n```{=html}\n
\n
\n07:00\n
\n```\n\n:::\n:::\n", 6 | "supporting": [], 7 | "filters": [ 8 | "rmarkdown/pagebreak.lua" 9 | ], 10 | "includes": { 11 | "include-in-header": [ 12 | "\n\n" 13 | ], 14 | "include-after-body": [ 15 | "\n\n\n" 16 | ] 17 | }, 18 | "engineDependencies": {}, 19 | "preserve": {}, 20 | "postProcess": true 21 | } 22 | } -------------------------------------------------------------------------------- /_freeze/6-summary/6-summary/execute-results/html.json: -------------------------------------------------------------------------------- 1 | { 2 | "hash": "993cdb0e4530329379acf85c989fa40e", 3 | "result": { 4 | "engine": "knitr", 5 | "markdown": "---\ntitle: \"🏁 Summary
![](../images/cascadia-jr-quarto.webp){width=5em}\"\nformat: revealjs\n---\n\n\n\n## Learning objective 1\n\n**Understand what parameterized reporting is and when it is useful.**\n\n. . .\n\n**Like very fancy custom functions:**\n\n::: incremental\n\n- Function → `.qmd` template\n\n- Input → parameters\n\n- Output → rendered reports\n:::\n\n. . . \n\n**Useful for creating variations of the same report:**\n\n- Spatial: country, state, county, or city\n\n- Temporal: year, month or other time period\n\n- Anything you can filter by: breeds, species, diseases, water bodies, customers, trials, etc.\n\n. . .\n\n::: callout-note\nWe only covered reports, but you can also parameterize `revealjs` presentations! See this [Jumping Rivers blog post](https://www.jumpingrivers.com/blog/r-parameterised-presentations-quarto/) about it.\n:::\n\n## Learning objective 2a\n\n**Convert a Quarto document into a parameterized template.**\n\n::: incremental\n- Include default `params:` in YAML\n \n- Replace hard-coded values with `params$pet_type`\n\n - **YAML**: \n \n\n\n ::: {.cell}\n \n ```{.yaml .cell-code}\n ---\n title: \"Report about `r params$pet_type`\"\n params:\n pet_type: \"cats\"\n ---\n ```\n :::\n\n\n \n - **Inline R code**: \n \n\n\n ::: {.cell}\n \n ```{.markdown .cell-code}\n I like **`r params$pet_type`**.\n ```\n :::\n\n\n \n - **Code chunks**: \n \n ``` r\n pets |> \n dplyr::filter(pet_type == params$pet_type)\n ```\n:::\n\n## Learning objective 2b\n\n**Render all variations of the report at once using [{quarto}](https://quarto-dev.github.io/quarto-r/) and [{purrr}](https://purrr.tidyverse.org/).**\n\n::: {.fragment}\n1. Get all unique parameter combinations into a dataframe:\n\n\n\n::: {.cell}\n\n:::\n\n::: {.cell}\n\n```{.r .cell-code}\nhead(pet_reports, 2)\n```\n\n::: {.cell-output-display}\n
\n\n|pet_type |breed |output_format |output_file |execute_params |\n|:--------|:-----------|:-------------|:----------------------------|:------------------------|\n|cats |Abyssiniane |html |cats-abyssiniane-report.html |cats , Abyssiniane |\n|cats |Aegean Cat |html |cats-aegean-cat-report.html |cats , Aegean Cat |\n\n
\n:::\n:::\n\n\n\n:::\n\n::: {.fragment}\n2. Use dataframe in `pwalk()` with `quarto_render()`:\n\n\n\n::: {.cell}\n\n```{.r .cell-code}\npurrr::pwalk(\n pet_reports,\n quarto::quarto_render,\n input = here::here(\"pet_template.qmd\"),\n .progress = TRUE\n)\n```\n:::\n\n\n:::\n\n## Learning objective 3a\n\n**Generate multiple format outputs from the same .qmd using [conditional content]{style=\"font-size:larger;text-decoration: underline;\"}.**\n\nCreate a div, span, or non-executable code block with one option from each of the below columns:\n\n:::: {.columns}\n\n::: {.column width=\"30%\"}\n**Class**\n\n- `.content-visible`\n- `.content-hidden`\n:::\n\n::: {.column width=\"30%\"}\n**Attribute**\n\n- `when-format=\"___\"`\n- `unless-format=\"___\"`\n:::\n\n::: {.column width=\"30%\"}\n**Format**\n\n- `latex` or `pdf`\n- `epub`\n- `html` or `revealjs`\n- `markdown`\n:::\n::::\n\n:::: {.fragment style=\"margin-top:-2em\"}\n\n**Example to show tabset only for HTML reports:**\n\n```markdown\n:::: {.content-visible when-format=\"html\"}\n::: panel-tabset\n{{{< include _4-report-content.qmd >}}}\n:::\n::::\n```\n::::\n\n## Learning objective 3b\n\n**Generate multiple format outputs from the same .qmd using [conditional code execution]{style=\"font-size:larger;text-decoration: underline;\"}**.\n\n::: panel-tabset\n\n## Conditional on output\n\n1. Get the Pandoc output format.\n\n```r\n#| label: setup\n\nformat <- knitr::opts_knit$get(\"rmarkdown.pandoc.to\")\n```\n\n2. Use `format` in the `eval: !expr` chunk option.\n\n```r\n#| label: interactive-plot\n#| eval: !expr format == \"html\"\n\n# plotly code\n```\n\n## Conditional on parameter\n\nUse `params` in the `eval: !expr` chunk option.\n\n```r\n#| eval: !expr params$fave_breed == \"Snowshoe\"\n\n# Code for a special plot for my favorite cat breed.\n```\n:::\n\n## Learning objective 3c\n\n**Generate multiple format outputs from the same .qmd using [custom styling]{style=\"font-size:larger;text-decoration: underline;\"}.**\n\n::: panel-tabset\n\n## HTML: Bootswatch & SCSS file\n\n1. Pick a [Bootswatch](https://bootswatch.com/) theme.\n\n2. Customize with a `.scss` file.\n\n3. Use browser developer tools to find/test more styling.\n\n``` yaml\nformat:\n html:\n theme: [flatly, theme.scss]\n```\n\n## MS Word: reference doc\n\n1. Create the reference doc.\n\n```{.bash filename=\"Terminal\"}\nquarto pandoc -o word-template.docx --print-default-data-file reference.docx\n```\n\n2. Open `word-template.docx` and modify the styles.\n\n3. Set this template in the YAML under the `reference-doc:` key:\n\n```yaml\nformat:\n docx:\n reference-doc: word-template.docx\n```\n:::\n\n## Thank you!\n\n::: columns\n::: {.column width=\"65%\"}\n🏡 **Home for all workshop materials**:\n[jadeyryan.quarto.pub/cascadia-quarto/](https://jadeyryan.quarto.pub/cascadia-quarto/)\n\n\\\n\n🎥 **Recordings from previous workshops & talks**: \n[links in GitHub repo](https://github.com/jadeynryan/parameterized-quarto-workshop?tab=readme-ov-file#workshops) or [my YouTube playlist](https://youtube.com/playlist?list=PLzjGoNexcyYaDUVCg4MQDtMXLl2F5HE3B&si=-y0jQfCYyRAQjI1B)\n\n![](images/mts.jpg){width=\"600\"\nfig-alt=\"From left to right, Mai, Tai, and Skye. Three snowshoe cats cuddling in their warming beds.\"\nstyle=\"border-radius:1em\"}\n:::\n\n::: {.column width=\"35%\"}\n**Let's stay connected!**\n\n{{< fa link size=xl >}} [jadeyryan.com](https://jadeyryan.com)\n\n{{< fa brands mastodon size=xl >}} [\\@jadeynryan](https://fosstodon.org/@jadeynryan)\n\n{{< fa brands linkedin size=xl >}} [linkedin.com/in/jadey-ryan](https://www.linkedin.com/in/jadey-ryan)\n\n{{< fa brands github size=xl >}} [jadeynryan](https://github.com/jadeynryan/)\n\n{{< fa brands etsy size=xl >}} [thecodingcats.etsy.com](https://thecodingcats.etsy.com/)\n:::\n:::\n", 6 | "supporting": [], 7 | "filters": [ 8 | "rmarkdown/pagebreak.lua" 9 | ], 10 | "includes": { 11 | "include-after-body": [ 12 | "\n\n\n" 13 | ] 14 | }, 15 | "engineDependencies": {}, 16 | "preserve": {}, 17 | "postProcess": true 18 | } 19 | } -------------------------------------------------------------------------------- /_freeze/prework/execute-results/html.json: -------------------------------------------------------------------------------- 1 | { 2 | "hash": "9fd3dd02921181eb2f4bb0f8aee15f04", 3 | "result": { 4 | "engine": "knitr", 5 | "markdown": "---\ntitle: \"Pre-workshop instructions\"\nformat: html\nexecute: \n eval: false\n---\n\n\n\n\nPrior to the workshop, please complete the following steps:\n\n*If you have trouble with these steps, or you'd prefer not to mess with your current set up, you can also work in Posit Cloud. See [Backup option](#backup-option-posit-cloud) below.*\n\n\n#### 1. Software\n\nDownload and install the latest versions of R, RStudio, and Quarto:\n\n- R 4.2.3 or above: \n- RStudio 2024.04.0 or above: \n- Quarto 1.4 or above: \n\n#### 2. R Packages\n\nInstall the following packages by copying and pasting the following into the console in RStudio:\n\n\n\n\n::: {.cell}\n\n```{.r .cell-code}\ninstall.packages(c(\"dplyr\", \"fs\", \"ggplot2\", \"here\", \"janitor\", \"knitr\", \n \"lubridate\", \"plotly\", \"purrr\", \"quarto\", \"readr\", \n \"rmarkdown\", \"stringr\", \"tidyr\"))\n```\n:::\n\n\n\n\n#### 3. Exercises\n\nDownload and open the exercises for this workshop by copying and pasting the following into the console in RStudio:\n\nThis will download the RStudio project onto your Desktop. To choose a different destination, uncomment and use the `destdir` argument.\n\n\n\n\n::: {.cell}\n\n```{.r .cell-code}\n# install.packages(\"usethis\")\nusethis::use_course(\n \"https://github.com/jadeynryan/parameterized-quarto-workshop/raw/cascadia/exercises/intermediate-exercises.zip\"\n# , destdir = \"C:/Users/jryan/Documents/R/projects\"\n )\n```\n:::\n\n\n\n\nIf `use_course()` didn't work, manually download the zip file from GitHub: [github.com/jadeynryan/parameterized-quarto-workshop/tree/cascadia/exercises](https://github.com/jadeynryan/parameterized-quarto-workshop/tree/cascadia/exercises).\n\n## Backup option: Posit Cloud\n\nYou can choose to use Posit Cloud instead which has the required tools already installed, along with a copy of the exercise files. Join the Posit Cloud [Quarto Cascadia workspace](https://posit.cloud/spaces/519059/join?access_code=ckv18LHEcSw3QtQ904uvpzLVlh4L2rk6RhKdpwyp) and open the [Intermediate Exercises project](https://posit.cloud/spaces/519059/content/8351107).\n\n## `purrr` {#sec-purrr}\n\nIf you're new to `purrr`, we recommend watching this R-Ladies Baltimore\npresentation [*Make your R Code purr with\n`purrr`*](https://www.youtube.com/watch?v=IewsPpjKElc) and reviewing Jenny\nBryan's [tutorial](https://jennybc.github.io/purrr-tutorial/) and\n[workshop](https://github.com/jennybc/row-oriented-workflows).\n", 6 | "supporting": [], 7 | "filters": [ 8 | "rmarkdown/pagebreak.lua" 9 | ], 10 | "includes": {}, 11 | "engineDependencies": {}, 12 | "preserve": {}, 13 | "postProcess": true 14 | } 15 | } -------------------------------------------------------------------------------- /_freeze/site_libs/clipboard/clipboard.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * clipboard.js v2.0.11 3 | * https://clipboardjs.com/ 4 | * 5 | * Licensed MIT © Zeno Rocha 6 | */ 7 | !function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports.ClipboardJS=e():t.ClipboardJS=e()}(this,function(){return n={686:function(t,e,n){"use strict";n.d(e,{default:function(){return b}});var e=n(279),i=n.n(e),e=n(370),u=n.n(e),e=n(817),r=n.n(e);function c(t){try{return document.execCommand(t)}catch(t){return}}var a=function(t){t=r()(t);return c("cut"),t};function o(t,e){var n,o,t=(n=t,o="rtl"===document.documentElement.getAttribute("dir"),(t=document.createElement("textarea")).style.fontSize="12pt",t.style.border="0",t.style.padding="0",t.style.margin="0",t.style.position="absolute",t.style[o?"right":"left"]="-9999px",o=window.pageYOffset||document.documentElement.scrollTop,t.style.top="".concat(o,"px"),t.setAttribute("readonly",""),t.value=n,t);return e.container.appendChild(t),e=r()(t),c("copy"),t.remove(),e}var f=function(t){var e=1 button { 78 | font-size: 1.5rem; 79 | width: 1rem; 80 | height: 1rem; 81 | display: inline-block; 82 | display: flex; 83 | flex-direction: column; 84 | align-items: center; 85 | justify-content: center; 86 | font-family: monospace; 87 | padding: 10px; 88 | margin: 0; 89 | background: inherit; 90 | border: 2px solid; 91 | border-radius: 100%; 92 | transition: 50ms transform ease-in-out, 150ms opacity ease-in; 93 | --countdown-transition-distance: 10px; 94 | } 95 | 96 | .countdown .countdown-controls > button:last-child { 97 | transform: translate(calc(-1 * var(--countdown-transition-distance)), var(--countdown-transition-distance)); 98 | opacity: 0; 99 | color: #002F14FF; 100 | background-color: #43AC6A; 101 | border-color: #2A9B59FF; 102 | } 103 | 104 | .countdown .countdown-controls > button:first-child { 105 | transform: translate(var(--countdown-transition-distance), var(--countdown-transition-distance)); 106 | opacity: 0; 107 | color: #4A0900FF; 108 | background-color: #F04124; 109 | border-color: #DE3000FF; 110 | } 111 | 112 | .countdown.running:hover .countdown-controls > button, 113 | .countdown.running:focus-within .countdown-controls > button{ 114 | transform: translate(0, 0); 115 | opacity: 1; 116 | } 117 | 118 | .countdown.running:hover .countdown-controls > button:hover, 119 | .countdown.running:focus-within .countdown-controls > button:hover{ 120 | transform: translate(0, calc(var(--countdown-transition-distance) / -2)); 121 | box-shadow: 0px 2px 5px 0px rgba(50, 50, 50, 0.4); 122 | -webkit-box-shadow: 0px 2px 5px 0px rgba(50, 50, 50, 0.4); 123 | } 124 | 125 | .countdown.running:hover .countdown-controls > button:active, 126 | .countdown.running:focus-within .countdown-controls > button:active{ 127 | transform: translate(0, calc(var(--coutndown-transition-distance) / -5)); 128 | } 129 | 130 | /* ----- Fullscreen ----- */ 131 | .countdown.countdown-fullscreen { 132 | z-index: 0; 133 | } 134 | 135 | .countdown-fullscreen.running .countdown-controls { 136 | top: 1rem; 137 | left: 0; 138 | right: 0; 139 | justify-content: center; 140 | } 141 | 142 | .countdown-fullscreen.running .countdown-controls > button + button { 143 | margin-left: 1rem; 144 | } 145 | -------------------------------------------------------------------------------- /_freeze/site_libs/countdown-0.4.0/smb_stage_clear.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jadeynryan/parameterized-quarto-workshop/b7ca2fc86f2a1d0c1c7328395657577a0ab47cd3/_freeze/site_libs/countdown-0.4.0/smb_stage_clear.mp3 -------------------------------------------------------------------------------- /_freeze/site_libs/revealjs/dist/reset.css: -------------------------------------------------------------------------------- 1 | /* http://meyerweb.com/eric/tools/css/reset/ 2 | v4.0 | 20180602 3 | License: none (public domain) 4 | */ 5 | 6 | html, body, div, span, applet, object, iframe, 7 | h1, h2, h3, h4, h5, h6, p, blockquote, pre, 8 | a, abbr, acronym, address, big, cite, code, 9 | del, dfn, em, img, ins, kbd, q, s, samp, 10 | small, strike, strong, sub, sup, tt, var, 11 | b, u, i, center, 12 | dl, dt, dd, ol, ul, li, 13 | fieldset, form, label, legend, 14 | table, caption, tbody, tfoot, thead, tr, th, td, 15 | article, aside, canvas, details, embed, 16 | figure, figcaption, footer, header, hgroup, 17 | main, menu, nav, output, ruby, section, summary, 18 | time, mark, audio, video { 19 | margin: 0; 20 | padding: 0; 21 | border: 0; 22 | font-size: 100%; 23 | font: inherit; 24 | vertical-align: baseline; 25 | } 26 | /* HTML5 display-role reset for older browsers */ 27 | article, aside, details, figcaption, figure, 28 | footer, header, hgroup, main, menu, nav, section { 29 | display: block; 30 | } -------------------------------------------------------------------------------- /_freeze/site_libs/revealjs/dist/theme/fonts/league-gothic/LICENSE: -------------------------------------------------------------------------------- 1 | SIL Open Font License (OFL) 2 | http://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&id=OFL 3 | -------------------------------------------------------------------------------- /_freeze/site_libs/revealjs/dist/theme/fonts/league-gothic/league-gothic.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: 'League Gothic'; 3 | src: url('./league-gothic.eot'); 4 | src: url('./league-gothic.eot?#iefix') format('embedded-opentype'), 5 | url('./league-gothic.woff') format('woff'), 6 | url('./league-gothic.ttf') format('truetype'); 7 | 8 | font-weight: normal; 9 | font-style: normal; 10 | } 11 | -------------------------------------------------------------------------------- /_freeze/site_libs/revealjs/dist/theme/fonts/league-gothic/league-gothic.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jadeynryan/parameterized-quarto-workshop/b7ca2fc86f2a1d0c1c7328395657577a0ab47cd3/_freeze/site_libs/revealjs/dist/theme/fonts/league-gothic/league-gothic.eot -------------------------------------------------------------------------------- /_freeze/site_libs/revealjs/dist/theme/fonts/league-gothic/league-gothic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jadeynryan/parameterized-quarto-workshop/b7ca2fc86f2a1d0c1c7328395657577a0ab47cd3/_freeze/site_libs/revealjs/dist/theme/fonts/league-gothic/league-gothic.ttf -------------------------------------------------------------------------------- /_freeze/site_libs/revealjs/dist/theme/fonts/league-gothic/league-gothic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jadeynryan/parameterized-quarto-workshop/b7ca2fc86f2a1d0c1c7328395657577a0ab47cd3/_freeze/site_libs/revealjs/dist/theme/fonts/league-gothic/league-gothic.woff -------------------------------------------------------------------------------- /_freeze/site_libs/revealjs/dist/theme/fonts/source-sans-pro/LICENSE: -------------------------------------------------------------------------------- 1 | SIL Open Font License 2 | 3 | Copyright 2010, 2012 Adobe Systems Incorporated (http://www.adobe.com/), with Reserved Font Name ‘Source’. All Rights Reserved. Source is a trademark of Adobe Systems Incorporated in the United States and/or other countries. 4 | 5 | This Font Software is licensed under the SIL Open Font License, Version 1.1. 6 | This license is copied below, and is also available with a FAQ at: http://scripts.sil.org/OFL 7 | 8 | —————————————————————————————- 9 | SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 10 | —————————————————————————————- 11 | 12 | PREAMBLE 13 | The goals of the Open Font License (OFL) are to stimulate worldwide development of collaborative font projects, to support the font creation efforts of academic and linguistic communities, and to provide a free and open framework in which fonts may be shared and improved in partnership with others. 14 | 15 | The OFL allows the licensed fonts to be used, studied, modified and redistributed freely as long as they are not sold by themselves. The fonts, including any derivative works, can be bundled, embedded, redistributed and/or sold with any software provided that any reserved names are not used by derivative works. The fonts and derivatives, however, cannot be released under any other type of license. The requirement for fonts to remain under this license does not apply to any document created using the fonts or their derivatives. 16 | 17 | DEFINITIONS 18 | “Font Software” refers to the set of files released by the Copyright Holder(s) under this license and clearly marked as such. This may include source files, build scripts and documentation. 19 | 20 | “Reserved Font Name” refers to any names specified as such after the copyright statement(s). 21 | 22 | “Original Version” refers to the collection of Font Software components as distributed by the Copyright Holder(s). 23 | 24 | “Modified Version” refers to any derivative made by adding to, deleting, or substituting—in part or in whole—any of the components of the Original Version, by changing formats or by porting the Font Software to a new environment. 25 | 26 | “Author” refers to any designer, engineer, programmer, technical writer or other person who contributed to the Font Software. 27 | 28 | PERMISSION & CONDITIONS 29 | Permission is hereby granted, free of charge, to any person obtaining a copy of the Font Software, to use, study, copy, merge, embed, modify, redistribute, and sell modified and unmodified copies of the Font Software, subject to the following conditions: 30 | 31 | 1) Neither the Font Software nor any of its individual components, in Original or Modified Versions, may be sold by itself. 32 | 33 | 2) Original or Modified Versions of the Font Software may be bundled, redistributed and/or sold with any software, provided that each copy contains the above copyright notice and this license. These can be included either as stand-alone text files, human-readable headers or in the appropriate machine-readable metadata fields within text or binary files as long as those fields can be easily viewed by the user. 34 | 35 | 3) No Modified Version of the Font Software may use the Reserved Font Name(s) unless explicit written permission is granted by the corresponding Copyright Holder. This restriction only applies to the primary font name as presented to the users. 36 | 37 | 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font Software shall not be used to promote, endorse or advertise any Modified Version, except to acknowledge the contribution(s) of the Copyright Holder(s) and the Author(s) or with their explicit written permission. 38 | 39 | 5) The Font Software, modified or unmodified, in part or in whole, must be distributed entirely under this license, and must not be distributed under any other license. The requirement for fonts to remain under this license does not apply to any document created using the Font Software. 40 | 41 | TERMINATION 42 | This license becomes null and void if any of the above conditions are not met. 43 | 44 | DISCLAIMER 45 | THE FONT SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE FONT SOFTWARE. -------------------------------------------------------------------------------- /_freeze/site_libs/revealjs/dist/theme/fonts/source-sans-pro/source-sans-pro-italic.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jadeynryan/parameterized-quarto-workshop/b7ca2fc86f2a1d0c1c7328395657577a0ab47cd3/_freeze/site_libs/revealjs/dist/theme/fonts/source-sans-pro/source-sans-pro-italic.eot -------------------------------------------------------------------------------- /_freeze/site_libs/revealjs/dist/theme/fonts/source-sans-pro/source-sans-pro-italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jadeynryan/parameterized-quarto-workshop/b7ca2fc86f2a1d0c1c7328395657577a0ab47cd3/_freeze/site_libs/revealjs/dist/theme/fonts/source-sans-pro/source-sans-pro-italic.ttf -------------------------------------------------------------------------------- /_freeze/site_libs/revealjs/dist/theme/fonts/source-sans-pro/source-sans-pro-italic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jadeynryan/parameterized-quarto-workshop/b7ca2fc86f2a1d0c1c7328395657577a0ab47cd3/_freeze/site_libs/revealjs/dist/theme/fonts/source-sans-pro/source-sans-pro-italic.woff -------------------------------------------------------------------------------- /_freeze/site_libs/revealjs/dist/theme/fonts/source-sans-pro/source-sans-pro-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jadeynryan/parameterized-quarto-workshop/b7ca2fc86f2a1d0c1c7328395657577a0ab47cd3/_freeze/site_libs/revealjs/dist/theme/fonts/source-sans-pro/source-sans-pro-regular.eot -------------------------------------------------------------------------------- /_freeze/site_libs/revealjs/dist/theme/fonts/source-sans-pro/source-sans-pro-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jadeynryan/parameterized-quarto-workshop/b7ca2fc86f2a1d0c1c7328395657577a0ab47cd3/_freeze/site_libs/revealjs/dist/theme/fonts/source-sans-pro/source-sans-pro-regular.ttf -------------------------------------------------------------------------------- /_freeze/site_libs/revealjs/dist/theme/fonts/source-sans-pro/source-sans-pro-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jadeynryan/parameterized-quarto-workshop/b7ca2fc86f2a1d0c1c7328395657577a0ab47cd3/_freeze/site_libs/revealjs/dist/theme/fonts/source-sans-pro/source-sans-pro-regular.woff -------------------------------------------------------------------------------- /_freeze/site_libs/revealjs/dist/theme/fonts/source-sans-pro/source-sans-pro-semibold.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jadeynryan/parameterized-quarto-workshop/b7ca2fc86f2a1d0c1c7328395657577a0ab47cd3/_freeze/site_libs/revealjs/dist/theme/fonts/source-sans-pro/source-sans-pro-semibold.eot -------------------------------------------------------------------------------- /_freeze/site_libs/revealjs/dist/theme/fonts/source-sans-pro/source-sans-pro-semibold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jadeynryan/parameterized-quarto-workshop/b7ca2fc86f2a1d0c1c7328395657577a0ab47cd3/_freeze/site_libs/revealjs/dist/theme/fonts/source-sans-pro/source-sans-pro-semibold.ttf -------------------------------------------------------------------------------- /_freeze/site_libs/revealjs/dist/theme/fonts/source-sans-pro/source-sans-pro-semibold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jadeynryan/parameterized-quarto-workshop/b7ca2fc86f2a1d0c1c7328395657577a0ab47cd3/_freeze/site_libs/revealjs/dist/theme/fonts/source-sans-pro/source-sans-pro-semibold.woff -------------------------------------------------------------------------------- /_freeze/site_libs/revealjs/dist/theme/fonts/source-sans-pro/source-sans-pro-semibolditalic.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jadeynryan/parameterized-quarto-workshop/b7ca2fc86f2a1d0c1c7328395657577a0ab47cd3/_freeze/site_libs/revealjs/dist/theme/fonts/source-sans-pro/source-sans-pro-semibolditalic.eot -------------------------------------------------------------------------------- /_freeze/site_libs/revealjs/dist/theme/fonts/source-sans-pro/source-sans-pro-semibolditalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jadeynryan/parameterized-quarto-workshop/b7ca2fc86f2a1d0c1c7328395657577a0ab47cd3/_freeze/site_libs/revealjs/dist/theme/fonts/source-sans-pro/source-sans-pro-semibolditalic.ttf -------------------------------------------------------------------------------- /_freeze/site_libs/revealjs/dist/theme/fonts/source-sans-pro/source-sans-pro-semibolditalic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jadeynryan/parameterized-quarto-workshop/b7ca2fc86f2a1d0c1c7328395657577a0ab47cd3/_freeze/site_libs/revealjs/dist/theme/fonts/source-sans-pro/source-sans-pro-semibolditalic.woff -------------------------------------------------------------------------------- /_freeze/site_libs/revealjs/dist/theme/fonts/source-sans-pro/source-sans-pro.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: 'Source Sans Pro'; 3 | src: url('./source-sans-pro-regular.eot'); 4 | src: url('./source-sans-pro-regular.eot?#iefix') format('embedded-opentype'), 5 | url('./source-sans-pro-regular.woff') format('woff'), 6 | url('./source-sans-pro-regular.ttf') format('truetype'); 7 | font-weight: normal; 8 | font-style: normal; 9 | } 10 | 11 | @font-face { 12 | font-family: 'Source Sans Pro'; 13 | src: url('./source-sans-pro-italic.eot'); 14 | src: url('./source-sans-pro-italic.eot?#iefix') format('embedded-opentype'), 15 | url('./source-sans-pro-italic.woff') format('woff'), 16 | url('./source-sans-pro-italic.ttf') format('truetype'); 17 | font-weight: normal; 18 | font-style: italic; 19 | } 20 | 21 | @font-face { 22 | font-family: 'Source Sans Pro'; 23 | src: url('./source-sans-pro-semibold.eot'); 24 | src: url('./source-sans-pro-semibold.eot?#iefix') format('embedded-opentype'), 25 | url('./source-sans-pro-semibold.woff') format('woff'), 26 | url('./source-sans-pro-semibold.ttf') format('truetype'); 27 | font-weight: 600; 28 | font-style: normal; 29 | } 30 | 31 | @font-face { 32 | font-family: 'Source Sans Pro'; 33 | src: url('./source-sans-pro-semibolditalic.eot'); 34 | src: url('./source-sans-pro-semibolditalic.eot?#iefix') format('embedded-opentype'), 35 | url('./source-sans-pro-semibolditalic.woff') format('woff'), 36 | url('./source-sans-pro-semibolditalic.ttf') format('truetype'); 37 | font-weight: 600; 38 | font-style: italic; 39 | } 40 | -------------------------------------------------------------------------------- /_freeze/site_libs/revealjs/plugin/highlight/monokai.css: -------------------------------------------------------------------------------- 1 | /* 2 | Monokai style - ported by Luigi Maselli - http://grigio.org 3 | */ 4 | 5 | .hljs { 6 | display: block; 7 | overflow-x: auto; 8 | padding: 0.5em; 9 | background: #272822; 10 | color: #ddd; 11 | } 12 | 13 | .hljs-tag, 14 | .hljs-keyword, 15 | .hljs-selector-tag, 16 | .hljs-literal, 17 | .hljs-strong, 18 | .hljs-name { 19 | color: #f92672; 20 | } 21 | 22 | .hljs-code { 23 | color: #66d9ef; 24 | } 25 | 26 | .hljs-class .hljs-title { 27 | color: white; 28 | } 29 | 30 | .hljs-attribute, 31 | .hljs-symbol, 32 | .hljs-regexp, 33 | .hljs-link { 34 | color: #bf79db; 35 | } 36 | 37 | .hljs-string, 38 | .hljs-bullet, 39 | .hljs-subst, 40 | .hljs-title, 41 | .hljs-section, 42 | .hljs-emphasis, 43 | .hljs-type, 44 | .hljs-built_in, 45 | .hljs-builtin-name, 46 | .hljs-selector-attr, 47 | .hljs-selector-pseudo, 48 | .hljs-addition, 49 | .hljs-variable, 50 | .hljs-template-tag, 51 | .hljs-template-variable { 52 | color: #a6e22e; 53 | } 54 | 55 | .hljs-comment, 56 | .hljs-quote, 57 | .hljs-deletion, 58 | .hljs-meta { 59 | color: #75715e; 60 | } 61 | 62 | .hljs-keyword, 63 | .hljs-selector-tag, 64 | .hljs-literal, 65 | .hljs-doctag, 66 | .hljs-title, 67 | .hljs-section, 68 | .hljs-type, 69 | .hljs-selector-id { 70 | font-weight: bold; 71 | } 72 | -------------------------------------------------------------------------------- /_freeze/site_libs/revealjs/plugin/highlight/zenburn.css: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Zenburn style from voldmar.ru (c) Vladimir Epifanov 4 | based on dark.css by Ivan Sagalaev 5 | 6 | */ 7 | 8 | .hljs { 9 | display: block; 10 | overflow-x: auto; 11 | padding: 0.5em; 12 | background: #3f3f3f; 13 | color: #dcdcdc; 14 | } 15 | 16 | .hljs-keyword, 17 | .hljs-selector-tag, 18 | .hljs-tag { 19 | color: #e3ceab; 20 | } 21 | 22 | .hljs-template-tag { 23 | color: #dcdcdc; 24 | } 25 | 26 | .hljs-number { 27 | color: #8cd0d3; 28 | } 29 | 30 | .hljs-variable, 31 | .hljs-template-variable, 32 | .hljs-attribute { 33 | color: #efdcbc; 34 | } 35 | 36 | .hljs-literal { 37 | color: #efefaf; 38 | } 39 | 40 | .hljs-subst { 41 | color: #8f8f8f; 42 | } 43 | 44 | .hljs-title, 45 | .hljs-name, 46 | .hljs-selector-id, 47 | .hljs-selector-class, 48 | .hljs-section, 49 | .hljs-type { 50 | color: #efef8f; 51 | } 52 | 53 | .hljs-symbol, 54 | .hljs-bullet, 55 | .hljs-link { 56 | color: #dca3a3; 57 | } 58 | 59 | .hljs-deletion, 60 | .hljs-string, 61 | .hljs-built_in, 62 | .hljs-builtin-name { 63 | color: #cc9393; 64 | } 65 | 66 | .hljs-addition, 67 | .hljs-comment, 68 | .hljs-quote, 69 | .hljs-meta { 70 | color: #7f9f7f; 71 | } 72 | 73 | 74 | .hljs-emphasis { 75 | font-style: italic; 76 | } 77 | 78 | .hljs-strong { 79 | font-weight: bold; 80 | } 81 | -------------------------------------------------------------------------------- /_freeze/site_libs/revealjs/plugin/math/katex.js: -------------------------------------------------------------------------------- 1 | /** 2 | * A plugin which enables rendering of math equations inside 3 | * of reveal.js slides. Essentially a thin wrapper for KaTeX. 4 | * 5 | * @author Hakim El Hattab 6 | * @author Gerhard Burger 7 | */ 8 | export const KaTeX = () => { 9 | let deck; 10 | 11 | let defaultOptions = { 12 | version: 'latest', 13 | delimiters: [ 14 | {left: '$$', right: '$$', display: true}, // Note: $$ has to come before $ 15 | {left: '$', right: '$', display: false}, 16 | {left: '\\(', right: '\\)', display: false}, 17 | {left: '\\[', right: '\\]', display: true} 18 | ], 19 | ignoredTags: ['script', 'noscript', 'style', 'textarea', 'pre'] 20 | } 21 | 22 | const loadCss = src => { 23 | let link = document.createElement('link'); 24 | link.rel = 'stylesheet'; 25 | link.href = src; 26 | document.head.appendChild(link); 27 | }; 28 | 29 | /** 30 | * Loads a JavaScript file and returns a Promise for when it is loaded 31 | * Credits: https://aaronsmith.online/easily-load-an-external-script-using-javascript/ 32 | */ 33 | const loadScript = src => { 34 | return new Promise((resolve, reject) => { 35 | const script = document.createElement('script') 36 | script.type = 'text/javascript' 37 | script.onload = resolve 38 | script.onerror = reject 39 | script.src = src 40 | document.head.append(script) 41 | }) 42 | }; 43 | 44 | async function loadScripts(urls) { 45 | for(const url of urls) { 46 | await loadScript(url); 47 | } 48 | } 49 | 50 | return { 51 | id: 'katex', 52 | 53 | init: function (reveal) { 54 | 55 | deck = reveal; 56 | 57 | let revealOptions = deck.getConfig().katex || {}; 58 | 59 | let options = {...defaultOptions, ...revealOptions}; 60 | const {local, version, extensions, ...katexOptions} = options; 61 | 62 | let baseUrl = options.local || 'https://cdn.jsdelivr.net/npm/katex'; 63 | let versionString = options.local ? '' : '@' + options.version; 64 | 65 | let cssUrl = baseUrl + versionString + '/dist/katex.min.css'; 66 | let katexUrl = baseUrl + versionString + '/dist/katex.min.js'; 67 | let mhchemUrl = baseUrl + versionString + '/dist/contrib/mhchem.min.js' 68 | let karUrl = baseUrl + versionString + '/dist/contrib/auto-render.min.js'; 69 | 70 | let katexScripts = [katexUrl]; 71 | if(options.extensions && options.extensions.includes("mhchem")) { 72 | katexScripts.push(mhchemUrl); 73 | } 74 | katexScripts.push(karUrl); 75 | 76 | const renderMath = () => { 77 | renderMathInElement(reveal.getSlidesElement(), katexOptions); 78 | deck.layout(); 79 | } 80 | 81 | loadCss(cssUrl); 82 | 83 | // For some reason dynamically loading with defer attribute doesn't result in the expected behavior, the below code does 84 | loadScripts(katexScripts).then(() => { 85 | if( deck.isReady() ) { 86 | renderMath(); 87 | } 88 | else { 89 | deck.on( 'ready', renderMath.bind( this ) ); 90 | } 91 | }); 92 | 93 | } 94 | } 95 | 96 | }; 97 | -------------------------------------------------------------------------------- /_freeze/site_libs/revealjs/plugin/math/mathjax2.js: -------------------------------------------------------------------------------- 1 | /** 2 | * A plugin which enables rendering of math equations inside 3 | * of reveal.js slides. Essentially a thin wrapper for MathJax. 4 | * 5 | * @author Hakim El Hattab 6 | */ 7 | export const MathJax2 = () => { 8 | 9 | // The reveal.js instance this plugin is attached to 10 | let deck; 11 | 12 | let defaultOptions = { 13 | messageStyle: 'none', 14 | tex2jax: { 15 | inlineMath: [ [ '$', '$' ], [ '\\(', '\\)' ] ], 16 | skipTags: [ 'script', 'noscript', 'style', 'textarea', 'pre' ] 17 | }, 18 | skipStartupTypeset: true 19 | }; 20 | 21 | function loadScript( url, callback ) { 22 | 23 | let head = document.querySelector( 'head' ); 24 | let script = document.createElement( 'script' ); 25 | script.type = 'text/javascript'; 26 | script.src = url; 27 | 28 | // Wrapper for callback to make sure it only fires once 29 | let finish = () => { 30 | if( typeof callback === 'function' ) { 31 | callback.call(); 32 | callback = null; 33 | } 34 | } 35 | 36 | script.onload = finish; 37 | 38 | // IE 39 | script.onreadystatechange = () => { 40 | if ( this.readyState === 'loaded' ) { 41 | finish(); 42 | } 43 | } 44 | 45 | // Normal browsers 46 | head.appendChild( script ); 47 | 48 | } 49 | 50 | return { 51 | id: 'mathjax2', 52 | 53 | init: function( reveal ) { 54 | 55 | deck = reveal; 56 | 57 | let revealOptions = deck.getConfig().mathjax2 || deck.getConfig().math || {}; 58 | 59 | let options = { ...defaultOptions, ...revealOptions }; 60 | let mathjax = options.mathjax || 'https://cdn.jsdelivr.net/npm/mathjax@2/MathJax.js'; 61 | let config = options.config || 'TeX-AMS_HTML-full'; 62 | let url = mathjax + '?config=' + config; 63 | 64 | options.tex2jax = { ...defaultOptions.tex2jax, ...revealOptions.tex2jax }; 65 | 66 | options.mathjax = options.config = null; 67 | 68 | loadScript( url, function() { 69 | 70 | MathJax.Hub.Config( options ); 71 | 72 | // Typeset followed by an immediate reveal.js layout since 73 | // the typesetting process could affect slide height 74 | MathJax.Hub.Queue( [ 'Typeset', MathJax.Hub, deck.getRevealElement() ] ); 75 | MathJax.Hub.Queue( deck.layout ); 76 | 77 | // Reprocess equations in slides when they turn visible 78 | deck.on( 'slidechanged', function( event ) { 79 | 80 | MathJax.Hub.Queue( [ 'Typeset', MathJax.Hub, event.currentSlide ] ); 81 | 82 | } ); 83 | 84 | } ); 85 | 86 | } 87 | } 88 | 89 | }; 90 | -------------------------------------------------------------------------------- /_freeze/site_libs/revealjs/plugin/math/mathjax3.js: -------------------------------------------------------------------------------- 1 | /** 2 | * A plugin which enables rendering of math equations inside 3 | * of reveal.js slides. Essentially a thin wrapper for MathJax 3 4 | * 5 | * @author Hakim El Hattab 6 | * @author Gerhard Burger 7 | */ 8 | export const MathJax3 = () => { 9 | 10 | // The reveal.js instance this plugin is attached to 11 | let deck; 12 | 13 | let defaultOptions = { 14 | tex: { 15 | inlineMath: [ [ '$', '$' ], [ '\\(', '\\)' ] ] 16 | }, 17 | options: { 18 | skipHtmlTags: [ 'script', 'noscript', 'style', 'textarea', 'pre' ] 19 | }, 20 | startup: { 21 | ready: () => { 22 | MathJax.startup.defaultReady(); 23 | MathJax.startup.promise.then(() => { 24 | Reveal.layout(); 25 | }); 26 | } 27 | } 28 | }; 29 | 30 | function loadScript( url, callback ) { 31 | 32 | let script = document.createElement( 'script' ); 33 | script.type = "text/javascript" 34 | script.id = "MathJax-script" 35 | script.src = url; 36 | script.async = true 37 | 38 | // Wrapper for callback to make sure it only fires once 39 | script.onload = () => { 40 | if (typeof callback === 'function') { 41 | callback.call(); 42 | callback = null; 43 | } 44 | }; 45 | 46 | document.head.appendChild( script ); 47 | 48 | } 49 | 50 | return { 51 | id: 'mathjax3', 52 | init: function(reveal) { 53 | 54 | deck = reveal; 55 | 56 | let revealOptions = deck.getConfig().mathjax3 || {}; 57 | let options = {...defaultOptions, ...revealOptions}; 58 | options.tex = {...defaultOptions.tex, ...revealOptions.tex} 59 | options.options = {...defaultOptions.options, ...revealOptions.options} 60 | options.startup = {...defaultOptions.startup, ...revealOptions.startup} 61 | 62 | let url = options.mathjax || 'https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js'; 63 | options.mathjax = null; 64 | 65 | window.MathJax = options; 66 | 67 | loadScript( url, function() { 68 | // Reprocess equations in slides when they turn visible 69 | Reveal.addEventListener( 'slidechanged', function( event ) { 70 | MathJax.typeset(); 71 | } ); 72 | } ); 73 | 74 | } 75 | } 76 | 77 | }; 78 | -------------------------------------------------------------------------------- /_freeze/site_libs/revealjs/plugin/math/plugin.js: -------------------------------------------------------------------------------- 1 | import {KaTeX} from "./katex"; 2 | import {MathJax2} from "./mathjax2"; 3 | import {MathJax3} from "./mathjax3"; 4 | 5 | const defaultTypesetter = MathJax2; 6 | 7 | /*! 8 | * This plugin is a wrapper for the MathJax2, 9 | * MathJax3 and KaTeX typesetter plugins. 10 | */ 11 | export default Plugin = Object.assign( defaultTypesetter(), { 12 | KaTeX, 13 | MathJax2, 14 | MathJax3 15 | } ); -------------------------------------------------------------------------------- /_freeze/site_libs/revealjs/plugin/notes/plugin.js: -------------------------------------------------------------------------------- 1 | import speakerViewHTML from './speaker-view.html'; 2 | 3 | import { marked } from 'marked'; 4 | 5 | /** 6 | * Handles opening of and synchronization with the reveal.js 7 | * notes window. 8 | * 9 | * Handshake process: 10 | * 1. This window posts 'connect' to notes window 11 | * - Includes URL of presentation to show 12 | * 2. Notes window responds with 'connected' when it is available 13 | * 3. This window proceeds to send the current presentation state 14 | * to the notes window 15 | */ 16 | const Plugin = () => { 17 | 18 | let connectInterval; 19 | let speakerWindow = null; 20 | let deck; 21 | 22 | /** 23 | * Opens a new speaker view window. 24 | */ 25 | function openSpeakerWindow() { 26 | 27 | // If a window is already open, focus it 28 | if( speakerWindow && !speakerWindow.closed ) { 29 | speakerWindow.focus(); 30 | } 31 | else { 32 | speakerWindow = window.open( 'about:blank', 'reveal.js - Notes', 'width=1100,height=700' ); 33 | speakerWindow.marked = marked; 34 | speakerWindow.document.write( speakerViewHTML ); 35 | 36 | if( !speakerWindow ) { 37 | alert( 'Speaker view popup failed to open. Please make sure popups are allowed and reopen the speaker view.' ); 38 | return; 39 | } 40 | 41 | connect(); 42 | } 43 | 44 | } 45 | 46 | /** 47 | * Reconnect with an existing speaker view window. 48 | */ 49 | function reconnectSpeakerWindow( reconnectWindow ) { 50 | 51 | if( speakerWindow && !speakerWindow.closed ) { 52 | speakerWindow.focus(); 53 | } 54 | else { 55 | speakerWindow = reconnectWindow; 56 | window.addEventListener( 'message', onPostMessage ); 57 | onConnected(); 58 | } 59 | 60 | } 61 | 62 | /** 63 | * Connect to the notes window through a postmessage handshake. 64 | * Using postmessage enables us to work in situations where the 65 | * origins differ, such as a presentation being opened from the 66 | * file system. 67 | */ 68 | function connect() { 69 | 70 | const presentationURL = deck.getConfig().url; 71 | 72 | const url = typeof presentationURL === 'string' ? presentationURL : 73 | window.location.protocol + '//' + window.location.host + window.location.pathname + window.location.search; 74 | 75 | // Keep trying to connect until we get a 'connected' message back 76 | connectInterval = setInterval( function() { 77 | speakerWindow.postMessage( JSON.stringify( { 78 | namespace: 'reveal-notes', 79 | type: 'connect', 80 | state: deck.getState(), 81 | url 82 | } ), '*' ); 83 | }, 500 ); 84 | 85 | window.addEventListener( 'message', onPostMessage ); 86 | 87 | } 88 | 89 | /** 90 | * Calls the specified Reveal.js method with the provided argument 91 | * and then pushes the result to the notes frame. 92 | */ 93 | function callRevealApi( methodName, methodArguments, callId ) { 94 | 95 | let result = deck[methodName].apply( deck, methodArguments ); 96 | speakerWindow.postMessage( JSON.stringify( { 97 | namespace: 'reveal-notes', 98 | type: 'return', 99 | result, 100 | callId 101 | } ), '*' ); 102 | 103 | } 104 | 105 | /** 106 | * Posts the current slide data to the notes window. 107 | */ 108 | function post( event ) { 109 | 110 | let slideElement = deck.getCurrentSlide(), 111 | notesElement = slideElement.querySelector( 'aside.notes' ), 112 | fragmentElement = slideElement.querySelector( '.current-fragment' ); 113 | 114 | let messageData = { 115 | namespace: 'reveal-notes', 116 | type: 'state', 117 | notes: '', 118 | markdown: false, 119 | whitespace: 'normal', 120 | state: deck.getState() 121 | }; 122 | 123 | // Look for notes defined in a slide attribute 124 | if( slideElement.hasAttribute( 'data-notes' ) ) { 125 | messageData.notes = slideElement.getAttribute( 'data-notes' ); 126 | messageData.whitespace = 'pre-wrap'; 127 | } 128 | 129 | // Look for notes defined in a fragment 130 | if( fragmentElement ) { 131 | let fragmentNotes = fragmentElement.querySelector( 'aside.notes' ); 132 | if( fragmentNotes ) { 133 | notesElement = fragmentNotes; 134 | } 135 | else if( fragmentElement.hasAttribute( 'data-notes' ) ) { 136 | messageData.notes = fragmentElement.getAttribute( 'data-notes' ); 137 | messageData.whitespace = 'pre-wrap'; 138 | 139 | // In case there are slide notes 140 | notesElement = null; 141 | } 142 | } 143 | 144 | // Look for notes defined in an aside element 145 | if( notesElement ) { 146 | messageData.notes = notesElement.innerHTML; 147 | messageData.markdown = typeof notesElement.getAttribute( 'data-markdown' ) === 'string'; 148 | } 149 | 150 | speakerWindow.postMessage( JSON.stringify( messageData ), '*' ); 151 | 152 | } 153 | 154 | function onPostMessage( event ) { 155 | 156 | let data = JSON.parse( event.data ); 157 | if( data && data.namespace === 'reveal-notes' && data.type === 'connected' ) { 158 | clearInterval( connectInterval ); 159 | onConnected(); 160 | } 161 | else if( data && data.namespace === 'reveal-notes' && data.type === 'call' ) { 162 | callRevealApi( data.methodName, data.arguments, data.callId ); 163 | } 164 | 165 | } 166 | 167 | /** 168 | * Called once we have established a connection to the notes 169 | * window. 170 | */ 171 | function onConnected() { 172 | 173 | // Monitor events that trigger a change in state 174 | deck.on( 'slidechanged', post ); 175 | deck.on( 'fragmentshown', post ); 176 | deck.on( 'fragmenthidden', post ); 177 | deck.on( 'overviewhidden', post ); 178 | deck.on( 'overviewshown', post ); 179 | deck.on( 'paused', post ); 180 | deck.on( 'resumed', post ); 181 | 182 | // Post the initial state 183 | post(); 184 | 185 | } 186 | 187 | return { 188 | id: 'notes', 189 | 190 | init: function( reveal ) { 191 | 192 | deck = reveal; 193 | 194 | if( !/receiver/i.test( window.location.search ) ) { 195 | 196 | // If the there's a 'notes' query set, open directly 197 | if( window.location.search.match( /(\?|\&)notes/gi ) !== null ) { 198 | openSpeakerWindow(); 199 | } 200 | else { 201 | // Keep listening for speaker view hearbeats. If we receive a 202 | // heartbeat from an orphaned window, reconnect it. This ensures 203 | // that we remain connected to the notes even if the presentation 204 | // is reloaded. 205 | window.addEventListener( 'message', event => { 206 | 207 | if( !speakerWindow && typeof event.data === 'string' ) { 208 | let data; 209 | 210 | try { 211 | data = JSON.parse( event.data ); 212 | } 213 | catch( error ) {} 214 | 215 | if( data && data.namespace === 'reveal-notes' && data.type === 'heartbeat' ) { 216 | reconnectSpeakerWindow( event.source ); 217 | } 218 | } 219 | }); 220 | } 221 | 222 | // Open the notes when the 's' key is hit 223 | deck.addKeyBinding({keyCode: 83, key: 'S', description: 'Speaker notes view'}, function() { 224 | openSpeakerWindow(); 225 | } ); 226 | 227 | } 228 | 229 | }, 230 | 231 | open: openSpeakerWindow 232 | }; 233 | 234 | }; 235 | 236 | export default Plugin; 237 | -------------------------------------------------------------------------------- /_freeze/site_libs/revealjs/plugin/pdf-export/pdfexport.js: -------------------------------------------------------------------------------- 1 | var PdfExport = ( function( _Reveal ){ 2 | 3 | var Reveal = _Reveal; 4 | var setStylesheet = null; 5 | var installAltKeyBindings = null; 6 | 7 | function getRevealJsPath(){ 8 | var regex = /\b[^/]+\/reveal.css$/i; 9 | var script = Array.from( document.querySelectorAll( 'link' ) ).find( function( e ){ 10 | return e.attributes.href && e.attributes.href.value.search( regex ) >= 0; 11 | }); 12 | if( !script ){ 13 | console.error( 'reveal.css could not be found in included elements. Did you rename this file?' ); 14 | return ''; 15 | } 16 | return script.attributes.href.value.replace( regex, '' ); 17 | } 18 | 19 | function setStylesheet3( pdfExport ){ 20 | var link = document.querySelector( '#print' ); 21 | if( !link ){ 22 | link = document.createElement( 'link' ); 23 | link.rel = 'stylesheet'; 24 | link.id = 'print'; 25 | document.querySelector( 'head' ).appendChild( link ); 26 | } 27 | var style = 'paper'; 28 | if( pdfExport ){ 29 | style = 'pdf'; 30 | } 31 | link.href = getRevealJsPath() + 'css/print/' + style + '.css'; 32 | } 33 | 34 | function setStylesheet4( pdfExport ){ 35 | } 36 | 37 | function installAltKeyBindings3(){ 38 | } 39 | 40 | function installAltKeyBindings4(){ 41 | if( isPrintingPDF() ){ 42 | var config = Reveal.getConfig(); 43 | var shortcut = config.pdfExportShortcut || 'E'; 44 | window.addEventListener( 'keydown', function( e ){ 45 | if( e.target.nodeName.toUpperCase() == 'BODY' 46 | && ( e.key.toUpperCase() == shortcut.toUpperCase() || e.keyCode == shortcut.toUpperCase().charCodeAt( 0 ) ) ){ 47 | e.preventDefault(); 48 | togglePdfExport(); 49 | return false; 50 | } 51 | }, true ); 52 | } 53 | } 54 | 55 | function isPrintingPDF(){ 56 | return ( /print-pdf/gi ).test( window.location.search ); 57 | } 58 | 59 | function togglePdfExport(){ 60 | var url_doc = new URL( document.URL ); 61 | var query_doc = new URLSearchParams( url_doc.searchParams ); 62 | if( isPrintingPDF() ){ 63 | query_doc.delete( 'print-pdf' ); 64 | }else{ 65 | query_doc.set( 'print-pdf', '' ); 66 | } 67 | url_doc.search = ( query_doc.toString() ? '?' + query_doc.toString() : '' ); 68 | window.location.href = url_doc.toString(); 69 | } 70 | 71 | function installKeyBindings(){ 72 | var config = Reveal.getConfig(); 73 | var shortcut = config.pdfExportShortcut || 'E'; 74 | Reveal.addKeyBinding({ 75 | keyCode: shortcut.toUpperCase().charCodeAt( 0 ), 76 | key: shortcut.toUpperCase(), 77 | description: 'PDF export mode' 78 | }, togglePdfExport ); 79 | installAltKeyBindings(); 80 | } 81 | 82 | function install(){ 83 | installKeyBindings(); 84 | setStylesheet( isPrintingPDF() ); 85 | } 86 | 87 | var Plugin = { 88 | } 89 | 90 | if( Reveal && Reveal.VERSION && Reveal.VERSION.length && Reveal.VERSION[ 0 ] == '3' ){ 91 | // reveal 3.x 92 | setStylesheet = setStylesheet3; 93 | installAltKeyBindings = installAltKeyBindings3; 94 | install(); 95 | }else{ 96 | // must be reveal 4.x 97 | setStylesheet = setStylesheet4; 98 | installAltKeyBindings = installAltKeyBindings4; 99 | Plugin.id = 'pdf-export'; 100 | Plugin.init = function( _Reveal ){ 101 | Reveal = _Reveal; 102 | install(); 103 | }; 104 | Plugin.togglePdfExport = function () { 105 | togglePdfExport(); 106 | }; 107 | } 108 | 109 | return Plugin; 110 | 111 | })( Reveal ); 112 | -------------------------------------------------------------------------------- /_freeze/site_libs/revealjs/plugin/pdf-export/plugin.yml: -------------------------------------------------------------------------------- 1 | name: PdfExport 2 | script: pdfexport.js 3 | -------------------------------------------------------------------------------- /_freeze/site_libs/revealjs/plugin/quarto-line-highlight/line-highlight.css: -------------------------------------------------------------------------------- 1 | .reveal 2 | div.sourceCode 3 | pre 4 | code.has-line-highlights 5 | > span:not(.highlight-line) { 6 | opacity: 0.4; 7 | } 8 | 9 | .reveal pre.numberSource { 10 | padding-left: 0; 11 | } 12 | 13 | .reveal pre.numberSource code > span { 14 | left: -2.1em; 15 | } 16 | 17 | pre.numberSource code > span > a:first-child::before { 18 | left: -0.7em; 19 | } 20 | 21 | .reveal pre > code:not(:first-child).fragment { 22 | position: absolute; 23 | top: 0; 24 | left: 0; 25 | width: 100%; 26 | box-sizing: border-box; 27 | } 28 | 29 | .reveal div.sourceCode pre code { 30 | min-height: 100%; 31 | } 32 | -------------------------------------------------------------------------------- /_freeze/site_libs/revealjs/plugin/quarto-line-highlight/plugin.yml: -------------------------------------------------------------------------------- 1 | # adapted from https://github.com/hakimel/reveal.js/tree/master/plugin/highlight 2 | name: QuartoLineHighlight 3 | script: line-highlight.js 4 | stylesheet: line-highlight.css 5 | -------------------------------------------------------------------------------- /_freeze/site_libs/revealjs/plugin/quarto-support/footer.css: -------------------------------------------------------------------------------- 1 | .reveal .slide-logo { 2 | display: block; 3 | position: fixed; 4 | bottom: 0; 5 | right: 12px; 6 | max-height: 2.2rem; 7 | height: 100%; 8 | width: auto; 9 | z-index: 2; 10 | } 11 | 12 | .reveal .footer { 13 | display: block; 14 | position: fixed; 15 | bottom: 18px; 16 | width: 100%; 17 | margin: 0 auto; 18 | text-align: center; 19 | font-size: 18px; 20 | z-index: 2; 21 | } 22 | 23 | .reveal .footer > * { 24 | margin-top: 0; 25 | margin-bottom: 0; 26 | } 27 | 28 | .reveal .slide .footer { 29 | display: none; 30 | } 31 | 32 | .reveal .slide-number { 33 | bottom: 10px; 34 | right: 10px; 35 | font-size: 16px; 36 | background-color: transparent; 37 | } 38 | 39 | .reveal.has-logo .slide-number { 40 | bottom: initial; 41 | top: 8px; 42 | right: 8px; 43 | } 44 | 45 | .reveal .slide-number .slide-number-delimiter { 46 | margin: 0; 47 | } 48 | 49 | .reveal .slide-menu-button { 50 | left: 8px; 51 | bottom: 8px; 52 | } 53 | 54 | .reveal .slide-chalkboard-buttons { 55 | position: fixed; 56 | left: 12px; 57 | bottom: 8px; 58 | z-index: 30; 59 | font-size: 24px; 60 | } 61 | 62 | .reveal .slide-chalkboard-buttons.slide-menu-offset { 63 | left: 54px; 64 | } 65 | 66 | .reveal .slide-chalkboard-buttons > span { 67 | margin-right: 14px; 68 | cursor: pointer; 69 | } 70 | 71 | @media screen and (max-width: 800px) { 72 | .reveal .slide-logo { 73 | max-height: 1.1rem; 74 | bottom: -2px; 75 | right: 10px; 76 | } 77 | .reveal .footer { 78 | font-size: 14px; 79 | bottom: 12px; 80 | } 81 | .reveal .slide-number { 82 | font-size: 12px; 83 | bottom: 7px; 84 | } 85 | .reveal .slide-menu-button .fas::before { 86 | height: 1.3rem; 87 | width: 1.3rem; 88 | vertical-align: -0.125em; 89 | background-size: 1.3rem 1.3rem; 90 | } 91 | 92 | .reveal .slide-chalkboard-buttons .fas::before { 93 | height: 0.95rem; 94 | width: 0.95rem; 95 | background-size: 0.95rem 0.95rem; 96 | vertical-align: -0em; 97 | } 98 | 99 | .reveal .slide-chalkboard-buttons.slide-menu-offset { 100 | left: 36px; 101 | } 102 | .reveal .slide-chalkboard-buttons > span { 103 | margin-right: 9px; 104 | } 105 | } 106 | 107 | html.print-pdf .reveal .slide-menu-button, 108 | html.print-pdf .reveal .slide-chalkboard-buttons { 109 | display: none; 110 | } 111 | -------------------------------------------------------------------------------- /_freeze/site_libs/revealjs/plugin/quarto-support/plugin.yml: -------------------------------------------------------------------------------- 1 | name: QuartoSupport 2 | script: support.js 3 | stylesheet: footer.css 4 | config: 5 | smaller: false 6 | -------------------------------------------------------------------------------- /_freeze/site_libs/revealjs/plugin/reveal-menu/menu.css: -------------------------------------------------------------------------------- 1 | .slide-menu-wrapper { 2 | font-family: 'Source Sans Pro', Helvetica, sans-serif; 3 | } 4 | 5 | .slide-menu-wrapper .slide-menu { 6 | background-color: #333; 7 | z-index: 200; 8 | position: fixed; 9 | top: 0; 10 | width: 300px; 11 | height: 100%; 12 | /*overflow-y: scroll;*/ 13 | transition: transform 0.3s; 14 | font-size: 16px; 15 | font-weight: normal; 16 | } 17 | 18 | .slide-menu-wrapper .slide-menu.slide-menu--wide { 19 | width: 500px; 20 | } 21 | 22 | .slide-menu-wrapper .slide-menu.slide-menu--third { 23 | width: 33%; 24 | } 25 | 26 | .slide-menu-wrapper .slide-menu.slide-menu--half { 27 | width: 50%; 28 | } 29 | 30 | .slide-menu-wrapper .slide-menu.slide-menu--full { 31 | width: 95%; 32 | } 33 | 34 | /* 35 | * Slides menu 36 | */ 37 | 38 | .slide-menu-wrapper .slide-menu-items { 39 | margin: 0; 40 | padding: 0; 41 | width: 100%; 42 | border-bottom: solid 1px #555; 43 | } 44 | 45 | .slide-menu-wrapper .slide-menu-item, 46 | .slide-menu-wrapper .slide-menu-item-vertical { 47 | display: block; 48 | text-align: left; 49 | padding: 10px 18px; 50 | color: #aaa; 51 | cursor: pointer; 52 | } 53 | 54 | .slide-menu-wrapper .slide-menu-item-vertical { 55 | padding-left: 30px; 56 | } 57 | 58 | .slide-menu-wrapper .slide-menu--wide .slide-menu-item-vertical, 59 | .slide-menu-wrapper .slide-menu--third .slide-menu-item-vertical, 60 | .slide-menu-wrapper .slide-menu--half .slide-menu-item-vertical, 61 | .slide-menu-wrapper .slide-menu--full .slide-menu-item-vertical, 62 | .slide-menu-wrapper .slide-menu--custom .slide-menu-item-vertical { 63 | padding-left: 50px; 64 | } 65 | 66 | .slide-menu-wrapper .slide-menu-item { 67 | border-top: solid 1px #555; 68 | } 69 | 70 | .slide-menu-wrapper .active-menu-panel li.selected { 71 | background-color: #222; 72 | color: white; 73 | } 74 | 75 | .slide-menu-wrapper .active-menu-panel li.active { 76 | color: #eee; 77 | } 78 | 79 | .slide-menu-wrapper .slide-menu-item.no-title .slide-menu-item-title, 80 | .slide-menu-wrapper .slide-menu-item-vertical.no-title .slide-menu-item-title { 81 | font-style: italic; 82 | } 83 | 84 | .slide-menu-wrapper .slide-menu-item-number { 85 | color: #999; 86 | padding-right: 6px; 87 | } 88 | 89 | .slide-menu-wrapper .slide-menu-item i.far, 90 | .slide-menu-wrapper .slide-menu-item i.fas, 91 | .slide-menu-wrapper .slide-menu-item-vertical i.far, 92 | .slide-menu-wrapper .slide-menu-item-vertical i.fas, 93 | .slide-menu-wrapper .slide-menu-item svg.svg-inline--fa, 94 | .slide-menu-wrapper .slide-menu-item-vertical svg.svg-inline--fa { 95 | padding-right: 12px; 96 | display: none; 97 | } 98 | 99 | .slide-menu-wrapper .slide-menu-item.past i.fas.past, 100 | .slide-menu-wrapper .slide-menu-item-vertical.past i.fas.past, 101 | .slide-menu-wrapper .slide-menu-item.active i.fas.active, 102 | .slide-menu-wrapper .slide-menu-item-vertical.active i.fas.active, 103 | .slide-menu-wrapper .slide-menu-item.future i.far.future, 104 | .slide-menu-wrapper .slide-menu-item-vertical.future i.far.future, 105 | .slide-menu-wrapper .slide-menu-item.past svg.svg-inline--fa.past, 106 | .slide-menu-wrapper .slide-menu-item-vertical.past svg.svg-inline--fa.past, 107 | .slide-menu-wrapper .slide-menu-item.active svg.svg-inline--fa.active, 108 | .slide-menu-wrapper .slide-menu-item-vertical.active svg.svg-inline--fa.active, 109 | .slide-menu-wrapper .slide-menu-item.future svg.svg-inline--fa.future, 110 | .slide-menu-wrapper .slide-menu-item-vertical.future svg.svg-inline--fa.future { 111 | display: inline-block; 112 | } 113 | 114 | .slide-menu-wrapper .slide-menu-item.past i.fas.past, 115 | .slide-menu-wrapper .slide-menu-item-vertical.past i.fas.past, 116 | .slide-menu-wrapper .slide-menu-item.future i.far.future, 117 | .slide-menu-wrapper .slide-menu-item-vertical.future i.far.future, 118 | .slide-menu-wrapper .slide-menu-item.past svg.svg-inline--fa.past, 119 | .slide-menu-wrapper .slide-menu-item-vertical.past svg.svg-inline--fa.past, 120 | .slide-menu-wrapper .slide-menu-item.future svg.svg-inline--fa.future, 121 | .slide-menu-wrapper .slide-menu-item-vertical.future svg.svg-inline--fa.future { 122 | opacity: 0.4; 123 | } 124 | 125 | .slide-menu-wrapper .slide-menu-item.active i.fas.active, 126 | .slide-menu-wrapper .slide-menu-item-vertical.active i.fas.active, 127 | .slide-menu-wrapper .slide-menu-item.active svg.svg-inline--fa.active, 128 | .slide-menu-wrapper .slide-menu-item-vertical.active svg.svg-inline--fa.active { 129 | opacity: 0.8; 130 | } 131 | 132 | .slide-menu-wrapper .slide-menu--left { 133 | left: 0; 134 | -webkit-transform: translateX(-100%); 135 | -ms-transform: translateX(-100%); 136 | transform: translateX(-100%); 137 | } 138 | 139 | .slide-menu-wrapper .slide-menu--left.active { 140 | -webkit-transform: translateX(0); 141 | -ms-transform: translateX(0); 142 | transform: translateX(0); 143 | } 144 | 145 | .slide-menu-wrapper .slide-menu--right { 146 | right: 0; 147 | -webkit-transform: translateX(100%); 148 | -ms-transform: translateX(100%); 149 | transform: translateX(100%); 150 | } 151 | 152 | .slide-menu-wrapper .slide-menu--right.active { 153 | -webkit-transform: translateX(0); 154 | -ms-transform: translateX(0); 155 | transform: translateX(0); 156 | } 157 | 158 | .slide-menu-wrapper { 159 | transition: transform 0.3s; 160 | } 161 | 162 | /* 163 | * Toolbar 164 | */ 165 | .slide-menu-wrapper .slide-menu-toolbar { 166 | height: 60px; 167 | width: 100%; 168 | font-size: 12px; 169 | display: table; 170 | table-layout: fixed; /* ensures equal width */ 171 | margin: 0; 172 | padding: 0; 173 | border-bottom: solid 2px #666; 174 | } 175 | 176 | .slide-menu-wrapper .slide-menu-toolbar > li { 177 | display: table-cell; 178 | line-height: 150%; 179 | text-align: center; 180 | vertical-align: middle; 181 | cursor: pointer; 182 | color: #aaa; 183 | border-radius: 3px; 184 | } 185 | 186 | .slide-menu-wrapper .slide-menu-toolbar > li.toolbar-panel-button i, 187 | .slide-menu-wrapper 188 | .slide-menu-toolbar 189 | > li.toolbar-panel-button 190 | svg.svg-inline--fa { 191 | font-size: 1.7em; 192 | } 193 | 194 | .slide-menu-wrapper .slide-menu-toolbar > li.active-toolbar-button { 195 | color: white; 196 | text-shadow: 0 1px black; 197 | text-decoration: underline; 198 | } 199 | 200 | .slide-menu-toolbar > li.toolbar-panel-button:hover { 201 | color: white; 202 | } 203 | 204 | .slide-menu-toolbar 205 | > li.toolbar-panel-button:hover 206 | span.slide-menu-toolbar-label, 207 | .slide-menu-wrapper 208 | .slide-menu-toolbar 209 | > li.active-toolbar-button 210 | span.slide-menu-toolbar-label { 211 | visibility: visible; 212 | } 213 | 214 | /* 215 | * Panels 216 | */ 217 | .slide-menu-wrapper .slide-menu-panel { 218 | position: absolute; 219 | width: 100%; 220 | visibility: hidden; 221 | height: calc(100% - 60px); 222 | overflow-x: hidden; 223 | overflow-y: auto; 224 | color: #aaa; 225 | } 226 | 227 | .slide-menu-wrapper .slide-menu-panel.active-menu-panel { 228 | visibility: visible; 229 | } 230 | 231 | .slide-menu-wrapper .slide-menu-panel h1, 232 | .slide-menu-wrapper .slide-menu-panel h2, 233 | .slide-menu-wrapper .slide-menu-panel h3, 234 | .slide-menu-wrapper .slide-menu-panel h4, 235 | .slide-menu-wrapper .slide-menu-panel h5, 236 | .slide-menu-wrapper .slide-menu-panel h6 { 237 | margin: 20px 0 10px 0; 238 | color: #fff; 239 | line-height: 1.2; 240 | letter-spacing: normal; 241 | text-shadow: none; 242 | } 243 | 244 | .slide-menu-wrapper .slide-menu-panel h1 { 245 | font-size: 1.6em; 246 | } 247 | .slide-menu-wrapper .slide-menu-panel h2 { 248 | font-size: 1.4em; 249 | } 250 | .slide-menu-wrapper .slide-menu-panel h3 { 251 | font-size: 1.3em; 252 | } 253 | .slide-menu-wrapper .slide-menu-panel h4 { 254 | font-size: 1.1em; 255 | } 256 | .slide-menu-wrapper .slide-menu-panel h5 { 257 | font-size: 1em; 258 | } 259 | .slide-menu-wrapper .slide-menu-panel h6 { 260 | font-size: 0.9em; 261 | } 262 | 263 | .slide-menu-wrapper .slide-menu-panel p { 264 | margin: 10px 0 5px 0; 265 | } 266 | 267 | .slide-menu-wrapper .slide-menu-panel a { 268 | color: #ccc; 269 | text-decoration: underline; 270 | } 271 | 272 | .slide-menu-wrapper .slide-menu-panel a:hover { 273 | color: white; 274 | } 275 | 276 | .slide-menu-wrapper .slide-menu-item a { 277 | text-decoration: none; 278 | } 279 | 280 | .slide-menu-wrapper .slide-menu-custom-panel { 281 | width: calc(100% - 20px); 282 | padding-left: 10px; 283 | padding-right: 10px; 284 | } 285 | 286 | .slide-menu-wrapper .slide-menu-custom-panel .slide-menu-items { 287 | width: calc(100% + 20px); 288 | margin-left: -10px; 289 | margin-right: 10px; 290 | } 291 | 292 | /* 293 | * Theme and Transitions buttons 294 | */ 295 | 296 | .slide-menu-wrapper div[data-panel='Themes'] li, 297 | .slide-menu-wrapper div[data-panel='Transitions'] li { 298 | display: block; 299 | text-align: left; 300 | cursor: pointer; 301 | color: #848484; 302 | } 303 | 304 | /* 305 | * Menu controls 306 | */ 307 | .reveal .slide-menu-button { 308 | position: fixed; 309 | left: 30px; 310 | bottom: 30px; 311 | z-index: 30; 312 | font-size: 24px; 313 | } 314 | 315 | /* 316 | * Menu overlay 317 | */ 318 | 319 | .slide-menu-wrapper .slide-menu-overlay { 320 | position: fixed; 321 | z-index: 199; 322 | top: 0; 323 | left: 0; 324 | overflow: hidden; 325 | width: 0; 326 | height: 0; 327 | background-color: #000; 328 | opacity: 0; 329 | transition: opacity 0.3s, width 0s 0.3s, height 0s 0.3s; 330 | } 331 | 332 | .slide-menu-wrapper .slide-menu-overlay.active { 333 | width: 100%; 334 | height: 100%; 335 | opacity: 0.7; 336 | transition: opacity 0.3s; 337 | } 338 | 339 | /* 340 | * Hide menu for pdf printing 341 | */ 342 | body.print-pdf .slide-menu-wrapper .slide-menu, 343 | body.print-pdf .reveal .slide-menu-button, 344 | body.print-pdf .slide-menu-wrapper .slide-menu-overlay { 345 | display: none; 346 | } 347 | -------------------------------------------------------------------------------- /_freeze/site_libs/revealjs/plugin/reveal-menu/plugin.yml: -------------------------------------------------------------------------------- 1 | name: RevealMenu 2 | script: [menu.js, quarto-menu.js] 3 | stylesheet: [menu.css, quarto-menu.css] 4 | config: 5 | menu: 6 | side: "left" 7 | useTextContentForMissingTitles: true 8 | markers: false 9 | loadIcons: false 10 | -------------------------------------------------------------------------------- /_freeze/site_libs/revealjs/plugin/reveal-menu/quarto-menu.css: -------------------------------------------------------------------------------- 1 | .slide-menu-wrapper .slide-tool-item { 2 | display: block; 3 | text-align: left; 4 | padding: 10px 18px; 5 | color: #aaa; 6 | cursor: pointer; 7 | border-top: solid 1px #555; 8 | } 9 | 10 | .slide-menu-wrapper .slide-tool-item a { 11 | text-decoration: none; 12 | } 13 | 14 | .slide-menu-wrapper .slide-tool-item kbd { 15 | font-family: monospace; 16 | margin-right: 10px; 17 | padding: 3px 8px; 18 | color: inherit; 19 | border: 1px solid; 20 | border-radius: 5px; 21 | border-color: #555; 22 | } 23 | 24 | .slide-menu-wrapper .slide-menu-toolbar > li.active-toolbar-button { 25 | text-decoration: none; 26 | } 27 | 28 | .reveal .slide-menu-button { 29 | left: 8px; 30 | bottom: 8px; 31 | } 32 | 33 | .reveal .slide-menu-button .fas::before, 34 | .reveal .slide-chalkboard-buttons .fas::before, 35 | .slide-menu-wrapper .slide-menu-toolbar .fas::before { 36 | display: inline-block; 37 | height: 2.2rem; 38 | width: 2.2rem; 39 | content: ""; 40 | vertical-align: -0.125em; 41 | background-repeat: no-repeat; 42 | background-size: 2.2rem 2.2rem; 43 | } 44 | 45 | .reveal .slide-chalkboard-buttons .fas::before { 46 | height: 1.45rem; 47 | width: 1.45rem; 48 | background-size: 1.45rem 1.45rem; 49 | vertical-align: 0.1em; 50 | } 51 | 52 | .slide-menu-wrapper .slide-menu-toolbar .fas::before { 53 | height: 1.8rem; 54 | width: 1.8rem; 55 | background-size: 1.8rem 1.8rem; 56 | } 57 | 58 | .slide-menu-wrapper .slide-menu-toolbar .fa-images::before { 59 | background-image: url('data:image/svg+xml,'); 60 | } 61 | 62 | .slide-menu-wrapper .slide-menu-toolbar .fa-gear::before { 63 | background-image: url('data:image/svg+xml,'); 64 | } 65 | 66 | .slide-menu-wrapper .slide-menu-toolbar .fa-times::before { 67 | background-image: url('data:image/svg+xml,'); 68 | } 69 | -------------------------------------------------------------------------------- /_freeze/site_libs/revealjs/plugin/reveal-menu/quarto-menu.js: -------------------------------------------------------------------------------- 1 | window.revealMenuToolHandler = function (handler) { 2 | return function (event) { 3 | event.preventDefault(); 4 | handler(); 5 | Reveal.getPlugin("menu").closeMenu(); 6 | }; 7 | }; 8 | 9 | window.RevealMenuToolHandlers = { 10 | fullscreen: revealMenuToolHandler(function () { 11 | const element = document.documentElement; 12 | const requestMethod = 13 | element.requestFullscreen || 14 | element.webkitRequestFullscreen || 15 | element.webkitRequestFullScreen || 16 | element.mozRequestFullScreen || 17 | element.msRequestFullscreen; 18 | if (requestMethod) { 19 | requestMethod.apply(element); 20 | } 21 | }), 22 | speakerMode: revealMenuToolHandler(function () { 23 | Reveal.getPlugin("notes").open(); 24 | }), 25 | keyboardHelp: revealMenuToolHandler(function () { 26 | Reveal.toggleHelp(true); 27 | }), 28 | overview: revealMenuToolHandler(function () { 29 | Reveal.toggleOverview(true); 30 | }), 31 | toggleChalkboard: revealMenuToolHandler(function () { 32 | RevealChalkboard.toggleChalkboard(); 33 | }), 34 | toggleNotesCanvas: revealMenuToolHandler(function () { 35 | RevealChalkboard.toggleNotesCanvas(); 36 | }), 37 | downloadDrawings: revealMenuToolHandler(function () { 38 | RevealChalkboard.download(); 39 | }), 40 | togglePdfExport: revealMenuToolHandler(function () { 41 | PdfExport.togglePdfExport(); 42 | }), 43 | }; 44 | -------------------------------------------------------------------------------- /_freeze/site_libs/revealjs/plugin/search/plugin.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Handles finding a text string anywhere in the slides and showing the next occurrence to the user 3 | * by navigatating to that slide and highlighting it. 4 | * 5 | * @author Jon Snyder , February 2013 6 | */ 7 | 8 | const Plugin = () => { 9 | 10 | // The reveal.js instance this plugin is attached to 11 | let deck; 12 | 13 | let searchElement; 14 | let searchButton; 15 | let searchInput; 16 | 17 | let matchedSlides; 18 | let currentMatchedIndex; 19 | let searchboxDirty; 20 | let hilitor; 21 | 22 | function render() { 23 | 24 | searchElement = document.createElement( 'div' ); 25 | searchElement.classList.add( 'searchbox' ); 26 | searchElement.style.position = 'absolute'; 27 | searchElement.style.top = '10px'; 28 | searchElement.style.right = '10px'; 29 | searchElement.style.zIndex = 10; 30 | 31 | //embedded base64 search icon Designed by Sketchdock - http://www.sketchdock.com/: 32 | searchElement.innerHTML = ` 33 | `; 34 | 35 | searchInput = searchElement.querySelector( '.searchinput' ); 36 | searchInput.style.width = '240px'; 37 | searchInput.style.fontSize = '14px'; 38 | searchInput.style.padding = '4px 6px'; 39 | searchInput.style.color = '#000'; 40 | searchInput.style.background = '#fff'; 41 | searchInput.style.borderRadius = '2px'; 42 | searchInput.style.border = '0'; 43 | searchInput.style.outline = '0'; 44 | searchInput.style.boxShadow = '0 2px 18px rgba(0, 0, 0, 0.2)'; 45 | searchInput.style['-webkit-appearance'] = 'none'; 46 | 47 | deck.getRevealElement().appendChild( searchElement ); 48 | 49 | // searchButton.addEventListener( 'click', function(event) { 50 | // doSearch(); 51 | // }, false ); 52 | 53 | searchInput.addEventListener( 'keyup', function( event ) { 54 | switch (event.keyCode) { 55 | case 13: 56 | event.preventDefault(); 57 | doSearch(); 58 | searchboxDirty = false; 59 | break; 60 | default: 61 | searchboxDirty = true; 62 | } 63 | }, false ); 64 | 65 | closeSearch(); 66 | 67 | } 68 | 69 | function openSearch() { 70 | if( !searchElement ) render(); 71 | 72 | searchElement.style.display = 'inline'; 73 | searchInput.focus(); 74 | searchInput.select(); 75 | } 76 | 77 | function closeSearch() { 78 | if( !searchElement ) render(); 79 | 80 | searchElement.style.display = 'none'; 81 | if(hilitor) hilitor.remove(); 82 | } 83 | 84 | function toggleSearch() { 85 | if( !searchElement ) render(); 86 | 87 | if (searchElement.style.display !== 'inline') { 88 | openSearch(); 89 | } 90 | else { 91 | closeSearch(); 92 | } 93 | } 94 | 95 | function doSearch() { 96 | //if there's been a change in the search term, perform a new search: 97 | if (searchboxDirty) { 98 | var searchstring = searchInput.value; 99 | 100 | if (searchstring === '') { 101 | if(hilitor) hilitor.remove(); 102 | matchedSlides = null; 103 | } 104 | else { 105 | //find the keyword amongst the slides 106 | hilitor = new Hilitor("slidecontent"); 107 | matchedSlides = hilitor.apply(searchstring); 108 | currentMatchedIndex = 0; 109 | } 110 | } 111 | 112 | if (matchedSlides) { 113 | //navigate to the next slide that has the keyword, wrapping to the first if necessary 114 | if (matchedSlides.length && (matchedSlides.length <= currentMatchedIndex)) { 115 | currentMatchedIndex = 0; 116 | } 117 | if (matchedSlides.length > currentMatchedIndex) { 118 | deck.slide(matchedSlides[currentMatchedIndex].h, matchedSlides[currentMatchedIndex].v); 119 | currentMatchedIndex++; 120 | } 121 | } 122 | } 123 | 124 | // Original JavaScript code by Chirp Internet: www.chirp.com.au 125 | // Please acknowledge use of this code by including this header. 126 | // 2/2013 jon: modified regex to display any match, not restricted to word boundaries. 127 | function Hilitor(id, tag) { 128 | 129 | var targetNode = document.getElementById(id) || document.body; 130 | var hiliteTag = tag || "EM"; 131 | var skipTags = new RegExp("^(?:" + hiliteTag + "|SCRIPT|FORM)$"); 132 | var colors = ["#ff6", "#a0ffff", "#9f9", "#f99", "#f6f"]; 133 | var wordColor = []; 134 | var colorIdx = 0; 135 | var matchRegex = ""; 136 | var matchingSlides = []; 137 | 138 | this.setRegex = function(input) 139 | { 140 | input = input.replace(/^[^\w]+|[^\w]+$/g, "").replace(/[^\w'-]+/g, "|"); 141 | matchRegex = new RegExp("(" + input + ")","i"); 142 | } 143 | 144 | this.getRegex = function() 145 | { 146 | return matchRegex.toString().replace(/^\/\\b\(|\)\\b\/i$/g, "").replace(/\|/g, " "); 147 | } 148 | 149 | // recursively apply word highlighting 150 | this.hiliteWords = function(node) 151 | { 152 | if(node == undefined || !node) return; 153 | if(!matchRegex) return; 154 | if(skipTags.test(node.nodeName)) return; 155 | 156 | if(node.hasChildNodes()) { 157 | for(var i=0; i < node.childNodes.length; i++) 158 | this.hiliteWords(node.childNodes[i]); 159 | } 160 | if(node.nodeType == 3) { // NODE_TEXT 161 | var nv, regs; 162 | if((nv = node.nodeValue) && (regs = matchRegex.exec(nv))) { 163 | //find the slide's section element and save it in our list of matching slides 164 | var secnode = node; 165 | while (secnode != null && secnode.nodeName != 'SECTION') { 166 | secnode = secnode.parentNode; 167 | } 168 | 169 | var slideIndex = deck.getIndices(secnode); 170 | var slidelen = matchingSlides.length; 171 | var alreadyAdded = false; 172 | for (var i=0; i < slidelen; i++) { 173 | if ( (matchingSlides[i].h === slideIndex.h) && (matchingSlides[i].v === slideIndex.v) ) { 174 | alreadyAdded = true; 175 | } 176 | } 177 | if (! alreadyAdded) { 178 | matchingSlides.push(slideIndex); 179 | } 180 | 181 | if(!wordColor[regs[0].toLowerCase()]) { 182 | wordColor[regs[0].toLowerCase()] = colors[colorIdx++ % colors.length]; 183 | } 184 | 185 | var match = document.createElement(hiliteTag); 186 | match.appendChild(document.createTextNode(regs[0])); 187 | match.style.backgroundColor = wordColor[regs[0].toLowerCase()]; 188 | match.style.fontStyle = "inherit"; 189 | match.style.color = "#000"; 190 | 191 | var after = node.splitText(regs.index); 192 | after.nodeValue = after.nodeValue.substring(regs[0].length); 193 | node.parentNode.insertBefore(match, after); 194 | } 195 | } 196 | }; 197 | 198 | // remove highlighting 199 | this.remove = function() 200 | { 201 | var arr = document.getElementsByTagName(hiliteTag); 202 | var el; 203 | while(arr.length && (el = arr[0])) { 204 | el.parentNode.replaceChild(el.firstChild, el); 205 | } 206 | }; 207 | 208 | // start highlighting at target node 209 | this.apply = function(input) 210 | { 211 | if(input == undefined || !input) return; 212 | this.remove(); 213 | this.setRegex(input); 214 | this.hiliteWords(targetNode); 215 | return matchingSlides; 216 | }; 217 | 218 | } 219 | 220 | return { 221 | 222 | id: 'search', 223 | 224 | init: reveal => { 225 | 226 | deck = reveal; 227 | deck.registerKeyboardShortcut( 'CTRL + Shift + F', 'Search' ); 228 | 229 | document.addEventListener( 'keydown', function( event ) { 230 | if( event.key == "F" && (event.ctrlKey || event.metaKey) ) { //Control+Shift+f 231 | event.preventDefault(); 232 | toggleSearch(); 233 | } 234 | }, false ); 235 | 236 | }, 237 | 238 | open: openSearch 239 | 240 | } 241 | }; 242 | 243 | export default Plugin; -------------------------------------------------------------------------------- /_freeze/site_libs/revealjs/plugin/zoom/plugin.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * reveal.js Zoom plugin 3 | */ 4 | const Plugin = { 5 | 6 | id: 'zoom', 7 | 8 | init: function( reveal ) { 9 | 10 | reveal.getRevealElement().addEventListener( 'mousedown', function( event ) { 11 | var defaultModifier = /Linux/.test( window.navigator.platform ) ? 'ctrl' : 'alt'; 12 | 13 | var modifier = ( reveal.getConfig().zoomKey ? reveal.getConfig().zoomKey : defaultModifier ) + 'Key'; 14 | var zoomLevel = ( reveal.getConfig().zoomLevel ? reveal.getConfig().zoomLevel : 2 ); 15 | 16 | if( event[ modifier ] && !reveal.isOverview() ) { 17 | event.preventDefault(); 18 | 19 | zoom.to({ 20 | x: event.clientX, 21 | y: event.clientY, 22 | scale: zoomLevel, 23 | pan: false 24 | }); 25 | } 26 | } ); 27 | 28 | }, 29 | 30 | destroy: () => { 31 | 32 | zoom.reset(); 33 | 34 | } 35 | 36 | }; 37 | 38 | export default () => Plugin; 39 | 40 | /*! 41 | * zoom.js 0.3 (modified for use with reveal.js) 42 | * http://lab.hakim.se/zoom-js 43 | * MIT licensed 44 | * 45 | * Copyright (C) 2011-2014 Hakim El Hattab, http://hakim.se 46 | */ 47 | var zoom = (function(){ 48 | 49 | // The current zoom level (scale) 50 | var level = 1; 51 | 52 | // The current mouse position, used for panning 53 | var mouseX = 0, 54 | mouseY = 0; 55 | 56 | // Timeout before pan is activated 57 | var panEngageTimeout = -1, 58 | panUpdateInterval = -1; 59 | 60 | // Check for transform support so that we can fallback otherwise 61 | var supportsTransforms = 'transform' in document.body.style; 62 | 63 | if( supportsTransforms ) { 64 | // The easing that will be applied when we zoom in/out 65 | document.body.style.transition = 'transform 0.8s ease'; 66 | } 67 | 68 | // Zoom out if the user hits escape 69 | document.addEventListener( 'keyup', function( event ) { 70 | if( level !== 1 && event.keyCode === 27 ) { 71 | zoom.out(); 72 | } 73 | } ); 74 | 75 | // Monitor mouse movement for panning 76 | document.addEventListener( 'mousemove', function( event ) { 77 | if( level !== 1 ) { 78 | mouseX = event.clientX; 79 | mouseY = event.clientY; 80 | } 81 | } ); 82 | 83 | /** 84 | * Applies the CSS required to zoom in, prefers the use of CSS3 85 | * transforms but falls back on zoom for IE. 86 | * 87 | * @param {Object} rect 88 | * @param {Number} scale 89 | */ 90 | function magnify( rect, scale ) { 91 | 92 | var scrollOffset = getScrollOffset(); 93 | 94 | // Ensure a width/height is set 95 | rect.width = rect.width || 1; 96 | rect.height = rect.height || 1; 97 | 98 | // Center the rect within the zoomed viewport 99 | rect.x -= ( window.innerWidth - ( rect.width * scale ) ) / 2; 100 | rect.y -= ( window.innerHeight - ( rect.height * scale ) ) / 2; 101 | 102 | if( supportsTransforms ) { 103 | // Reset 104 | if( scale === 1 ) { 105 | document.body.style.transform = ''; 106 | } 107 | // Scale 108 | else { 109 | var origin = scrollOffset.x +'px '+ scrollOffset.y +'px', 110 | transform = 'translate('+ -rect.x +'px,'+ -rect.y +'px) scale('+ scale +')'; 111 | 112 | document.body.style.transformOrigin = origin; 113 | document.body.style.transform = transform; 114 | } 115 | } 116 | else { 117 | // Reset 118 | if( scale === 1 ) { 119 | document.body.style.position = ''; 120 | document.body.style.left = ''; 121 | document.body.style.top = ''; 122 | document.body.style.width = ''; 123 | document.body.style.height = ''; 124 | document.body.style.zoom = ''; 125 | } 126 | // Scale 127 | else { 128 | document.body.style.position = 'relative'; 129 | document.body.style.left = ( - ( scrollOffset.x + rect.x ) / scale ) + 'px'; 130 | document.body.style.top = ( - ( scrollOffset.y + rect.y ) / scale ) + 'px'; 131 | document.body.style.width = ( scale * 100 ) + '%'; 132 | document.body.style.height = ( scale * 100 ) + '%'; 133 | document.body.style.zoom = scale; 134 | } 135 | } 136 | 137 | level = scale; 138 | 139 | if( document.documentElement.classList ) { 140 | if( level !== 1 ) { 141 | document.documentElement.classList.add( 'zoomed' ); 142 | } 143 | else { 144 | document.documentElement.classList.remove( 'zoomed' ); 145 | } 146 | } 147 | } 148 | 149 | /** 150 | * Pan the document when the mosue cursor approaches the edges 151 | * of the window. 152 | */ 153 | function pan() { 154 | var range = 0.12, 155 | rangeX = window.innerWidth * range, 156 | rangeY = window.innerHeight * range, 157 | scrollOffset = getScrollOffset(); 158 | 159 | // Up 160 | if( mouseY < rangeY ) { 161 | window.scroll( scrollOffset.x, scrollOffset.y - ( 1 - ( mouseY / rangeY ) ) * ( 14 / level ) ); 162 | } 163 | // Down 164 | else if( mouseY > window.innerHeight - rangeY ) { 165 | window.scroll( scrollOffset.x, scrollOffset.y + ( 1 - ( window.innerHeight - mouseY ) / rangeY ) * ( 14 / level ) ); 166 | } 167 | 168 | // Left 169 | if( mouseX < rangeX ) { 170 | window.scroll( scrollOffset.x - ( 1 - ( mouseX / rangeX ) ) * ( 14 / level ), scrollOffset.y ); 171 | } 172 | // Right 173 | else if( mouseX > window.innerWidth - rangeX ) { 174 | window.scroll( scrollOffset.x + ( 1 - ( window.innerWidth - mouseX ) / rangeX ) * ( 14 / level ), scrollOffset.y ); 175 | } 176 | } 177 | 178 | function getScrollOffset() { 179 | return { 180 | x: window.scrollX !== undefined ? window.scrollX : window.pageXOffset, 181 | y: window.scrollY !== undefined ? window.scrollY : window.pageYOffset 182 | } 183 | } 184 | 185 | return { 186 | /** 187 | * Zooms in on either a rectangle or HTML element. 188 | * 189 | * @param {Object} options 190 | * - element: HTML element to zoom in on 191 | * OR 192 | * - x/y: coordinates in non-transformed space to zoom in on 193 | * - width/height: the portion of the screen to zoom in on 194 | * - scale: can be used instead of width/height to explicitly set scale 195 | */ 196 | to: function( options ) { 197 | 198 | // Due to an implementation limitation we can't zoom in 199 | // to another element without zooming out first 200 | if( level !== 1 ) { 201 | zoom.out(); 202 | } 203 | else { 204 | options.x = options.x || 0; 205 | options.y = options.y || 0; 206 | 207 | // If an element is set, that takes precedence 208 | if( !!options.element ) { 209 | // Space around the zoomed in element to leave on screen 210 | var padding = 20; 211 | var bounds = options.element.getBoundingClientRect(); 212 | 213 | options.x = bounds.left - padding; 214 | options.y = bounds.top - padding; 215 | options.width = bounds.width + ( padding * 2 ); 216 | options.height = bounds.height + ( padding * 2 ); 217 | } 218 | 219 | // If width/height values are set, calculate scale from those values 220 | if( options.width !== undefined && options.height !== undefined ) { 221 | options.scale = Math.max( Math.min( window.innerWidth / options.width, window.innerHeight / options.height ), 1 ); 222 | } 223 | 224 | if( options.scale > 1 ) { 225 | options.x *= options.scale; 226 | options.y *= options.scale; 227 | 228 | magnify( options, options.scale ); 229 | 230 | if( options.pan !== false ) { 231 | 232 | // Wait with engaging panning as it may conflict with the 233 | // zoom transition 234 | panEngageTimeout = setTimeout( function() { 235 | panUpdateInterval = setInterval( pan, 1000 / 60 ); 236 | }, 800 ); 237 | 238 | } 239 | } 240 | } 241 | }, 242 | 243 | /** 244 | * Resets the document zoom state to its default. 245 | */ 246 | out: function() { 247 | clearTimeout( panEngageTimeout ); 248 | clearInterval( panUpdateInterval ); 249 | 250 | magnify( { x: 0, y: 0 }, 1 ); 251 | 252 | level = 1; 253 | }, 254 | 255 | // Alias 256 | magnify: function( options ) { this.to( options ) }, 257 | reset: function() { this.out() }, 258 | 259 | zoomLevel: function() { 260 | return level; 261 | } 262 | } 263 | 264 | })(); 265 | -------------------------------------------------------------------------------- /_freeze/site_libs/revealjs/plugin/zoom/zoom.esm.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * reveal.js Zoom plugin 3 | */ 4 | var e={id:"zoom",init:function(e){e.getRevealElement().addEventListener("mousedown",(function(n){var o=/Linux/.test(window.navigator.platform)?"ctrl":"alt",i=(e.getConfig().zoomKey?e.getConfig().zoomKey:o)+"Key",d=e.getConfig().zoomLevel?e.getConfig().zoomLevel:2;n[i]&&!e.isOverview()&&(n.preventDefault(),t.to({x:n.clientX,y:n.clientY,scale:d,pan:!1}))}))},destroy:function(){t.reset()}},t=function(){var e=1,n=0,o=0,i=-1,d=-1,l="transform"in document.body.style;function s(t,n){var o=r();if(t.width=t.width||1,t.height=t.height||1,t.x-=(window.innerWidth-t.width*n)/2,t.y-=(window.innerHeight-t.height*n)/2,l)if(1===n)document.body.style.transform="";else{var i=o.x+"px "+o.y+"px",d="translate("+-t.x+"px,"+-t.y+"px) scale("+n+")";document.body.style.transformOrigin=i,document.body.style.transform=d}else 1===n?(document.body.style.position="",document.body.style.left="",document.body.style.top="",document.body.style.width="",document.body.style.height="",document.body.style.zoom=""):(document.body.style.position="relative",document.body.style.left=-(o.x+t.x)/n+"px",document.body.style.top=-(o.y+t.y)/n+"px",document.body.style.width=100*n+"%",document.body.style.height=100*n+"%",document.body.style.zoom=n);e=n,document.documentElement.classList&&(1!==e?document.documentElement.classList.add("zoomed"):document.documentElement.classList.remove("zoomed"))}function c(){var t=.12*window.innerWidth,i=.12*window.innerHeight,d=r();owindow.innerHeight-i&&window.scroll(d.x,d.y+(1-(window.innerHeight-o)/i)*(14/e)),nwindow.innerWidth-t&&window.scroll(d.x+(1-(window.innerWidth-n)/t)*(14/e),d.y)}function r(){return{x:void 0!==window.scrollX?window.scrollX:window.pageXOffset,y:void 0!==window.scrollY?window.scrollY:window.pageYOffset}}return l&&(document.body.style.transition="transform 0.8s ease"),document.addEventListener("keyup",(function(n){1!==e&&27===n.keyCode&&t.out()})),document.addEventListener("mousemove",(function(t){1!==e&&(n=t.clientX,o=t.clientY)})),{to:function(n){if(1!==e)t.out();else{if(n.x=n.x||0,n.y=n.y||0,n.element){var o=n.element.getBoundingClientRect();n.x=o.left-20,n.y=o.top-20,n.width=o.width+40,n.height=o.height+40}void 0!==n.width&&void 0!==n.height&&(n.scale=Math.max(Math.min(window.innerWidth/n.width,window.innerHeight/n.height),1)),n.scale>1&&(n.x*=n.scale,n.y*=n.scale,s(n,n.scale),!1!==n.pan&&(i=setTimeout((function(){d=setInterval(c,1e3/60)}),800)))}},out:function(){clearTimeout(i),clearInterval(d),s({x:0,y:0},1),e=1},magnify:function(e){this.to(e)},reset:function(){this.out()},zoomLevel:function(){return e}}}();export default function(){return e} 5 | -------------------------------------------------------------------------------- /_freeze/site_libs/revealjs/plugin/zoom/zoom.js: -------------------------------------------------------------------------------- 1 | !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(e="undefined"!=typeof globalThis?globalThis:e||self).RevealZoom=t()}(this,(function(){"use strict"; 2 | /*! 3 | * reveal.js Zoom plugin 4 | */var e={id:"zoom",init:function(e){e.getRevealElement().addEventListener("mousedown",(function(o){var n=/Linux/.test(window.navigator.platform)?"ctrl":"alt",i=(e.getConfig().zoomKey?e.getConfig().zoomKey:n)+"Key",d=e.getConfig().zoomLevel?e.getConfig().zoomLevel:2;o[i]&&!e.isOverview()&&(o.preventDefault(),t.to({x:o.clientX,y:o.clientY,scale:d,pan:!1}))}))},destroy:function(){t.reset()}},t=function(){var e=1,o=0,n=0,i=-1,d=-1,l="transform"in document.body.style;function s(t,o){var n=r();if(t.width=t.width||1,t.height=t.height||1,t.x-=(window.innerWidth-t.width*o)/2,t.y-=(window.innerHeight-t.height*o)/2,l)if(1===o)document.body.style.transform="";else{var i=n.x+"px "+n.y+"px",d="translate("+-t.x+"px,"+-t.y+"px) scale("+o+")";document.body.style.transformOrigin=i,document.body.style.transform=d}else 1===o?(document.body.style.position="",document.body.style.left="",document.body.style.top="",document.body.style.width="",document.body.style.height="",document.body.style.zoom=""):(document.body.style.position="relative",document.body.style.left=-(n.x+t.x)/o+"px",document.body.style.top=-(n.y+t.y)/o+"px",document.body.style.width=100*o+"%",document.body.style.height=100*o+"%",document.body.style.zoom=o);e=o,document.documentElement.classList&&(1!==e?document.documentElement.classList.add("zoomed"):document.documentElement.classList.remove("zoomed"))}function c(){var t=.12*window.innerWidth,i=.12*window.innerHeight,d=r();nwindow.innerHeight-i&&window.scroll(d.x,d.y+(1-(window.innerHeight-n)/i)*(14/e)),owindow.innerWidth-t&&window.scroll(d.x+(1-(window.innerWidth-o)/t)*(14/e),d.y)}function r(){return{x:void 0!==window.scrollX?window.scrollX:window.pageXOffset,y:void 0!==window.scrollY?window.scrollY:window.pageYOffset}}return l&&(document.body.style.transition="transform 0.8s ease"),document.addEventListener("keyup",(function(o){1!==e&&27===o.keyCode&&t.out()})),document.addEventListener("mousemove",(function(t){1!==e&&(o=t.clientX,n=t.clientY)})),{to:function(o){if(1!==e)t.out();else{if(o.x=o.x||0,o.y=o.y||0,o.element){var n=o.element.getBoundingClientRect();o.x=n.left-20,o.y=n.top-20,o.width=n.width+40,o.height=n.height+40}void 0!==o.width&&void 0!==o.height&&(o.scale=Math.max(Math.min(window.innerWidth/o.width,window.innerHeight/o.height),1)),o.scale>1&&(o.x*=o.scale,o.y*=o.scale,s(o,o.scale),!1!==o.pan&&(i=setTimeout((function(){d=setInterval(c,1e3/60)}),800)))}},out:function(){clearTimeout(i),clearInterval(d),s({x:0,y:0},1),e=1},magnify:function(e){this.to(e)},reset:function(){this.out()},zoomLevel:function(){return e}}}();return function(){return e}})); 5 | -------------------------------------------------------------------------------- /_publish.yml: -------------------------------------------------------------------------------- 1 | - source: project 2 | quarto-pub: 3 | - id: 5971dfa1-fe76-41ff-b5db-37b30ae5067d 4 | url: 'https://jadeyryan.quarto.pub/rladies-dc-quarto-params' 5 | - id: e8e72e21-1cb0-4150-93f8-e18789c66879 6 | url: 'https://jadeyryan.quarto.pub/rladies-abuja-quarto-params' 7 | - id: 4af5c0aa-9209-4234-83f3-2bb32e3dc7c1 8 | url: 'https://jadeyryan.quarto.pub/ceds-quarto-workshop' 9 | - id: e02b40a8-6565-4611-ae24-629c6bbd4d7d 10 | url: 'https://jadeyryan.quarto.pub/cascadia-quarto' 11 | -------------------------------------------------------------------------------- /_quarto.yml: -------------------------------------------------------------------------------- 1 | project: 2 | type: website 3 | render: 4 | - "*.qmd" 5 | preview: 6 | port: 3434 7 | browser: true 8 | watch-inputs: true 9 | timeout: 3600 10 | 11 | execute: 12 | freeze: auto 13 | echo: true 14 | 15 | website: 16 | page-navigation: true 17 | title: "Intermediate Quarto: Parameterized Reports Workshop" 18 | description: "Homepage for the Intermediate Quarto: Parameterized Reports Workshop at Cascadia R Conf 2024" 19 | site-url: "https://jadeyryan.quarto.pub/cascadia-quarto/" 20 | favicon: "images/cascadia-jr-quarto.webp" 21 | 22 | repo-url: https://github.com/jadeynryan/parameterized-quarto-workshop 23 | repo-actions: [source, edit, issue] 24 | 25 | page-footer: 26 | left: "© 2024 Jadey Ryan" 27 | center: "Website built with ❤︎️ and [Quarto](https://quarto.org/)." 28 | right: "Inspired by [R/Medicine 2023 workshop](https://github.com/shannonpileggi/rmedicine-data-cleaning-2023) and [ASA Traveling Courses on Quarto](https://quarto.org/docs/blog/posts/2023-12-05-asa-traveling-courses/)." 29 | background: "#07483f" 30 | 31 | sidebar: 32 | background: "#07483f" 33 | logo: "images/cascadia-jr-quarto.webp" 34 | logo-alt: "Cascadia R Conf hex sticker on top of JR's cat hex logo joined by a heart with the Quarto hex sticker." 35 | pinned: true 36 | align: center 37 | style: docked 38 | type: dark 39 | tools: 40 | - icon: github 41 | href: https://github.com/jadeynryan/parameterized-quarto-workshop 42 | text: GitHub 43 | contents: 44 | - href: index.qmd 45 | text: Home 46 | - href: prework.qmd 47 | text: Pre-work 48 | - href: license.qmd 49 | text: License 50 | - text: "---" 51 | - section: Slides 52 | contents: 53 | - href: 1-welcome/index.qmd 54 | text: Welcome 55 | - href: 2-parameters/index.qmd 56 | text: Parameters 57 | - href: 3-render/index.qmd 58 | text: Render 59 | - href: 4-conditionals/index.qmd 60 | text: Conditionals 61 | - href: 5-style/index.qmd 62 | text: Style 63 | - href: 6-summary/index.qmd 64 | text: Summary 65 | 66 | format: 67 | html: 68 | theme: [flatly, theme.scss] 69 | toc: true 70 | smooth-scroll: true 71 | link-external-newwindow: true 72 | code-link: true 73 | code-copy: true 74 | code-overflow: scroll 75 | mainfont: Atkinson Hyperlegible 76 | highlight-style: a11y 77 | 78 | revealjs: 79 | author: "Jadey Ryan // June 21, 2024
Intermediate Quarto // Cascadia R Conf" 80 | footer: "[jadeyryan.quarto.pub/cascadia-quarto/](https://jadeyryan.quarto.pub/cascadia-quarto/)" 81 | logo: "images/cascadia-jr-quarto.webp" 82 | logo-alt: "Cascadia R Conf hex sticker on top of JR's cat hex logo joined by a heart with the Quarto hex sticker." 83 | width: 1600 84 | height: 900 85 | theme: slides.scss 86 | highlight-style: a11y 87 | transition: fade 88 | slide-number: true 89 | df-print: kable 90 | 91 | # lightbox extension 92 | # https://github.com/quarto-ext/lightbox 93 | lightbox: 94 | desc-position: right # position of description 95 | # match: auto # give all images lightbox treatment 96 | effect: zoom # how lightbox opens/closes 97 | loop: false # doesn't loop to first image in gallery 98 | -------------------------------------------------------------------------------- /data/data.R: -------------------------------------------------------------------------------- 1 | ## Header ====================================================================== 2 | ## 3 | ## Script name: data.R 4 | ## 5 | ## Purpose: Get fun data about cats and dogs registered in Switzerland for 6 | ## R-Ladies workshop. 7 | ## 8 | ## Data source: Identitas AG, Animal statistics 9 | ## https://tierstatistik.identitas.ch/en/cats.html 10 | ## 11 | ## Author: Jadey Ryan 12 | ## 13 | ## Date created: 2024-01-14 14 | ## 15 | ## Notes: 16 | ## 17 | 18 | # Attach packages ============================================================== 19 | 20 | library(readr) 21 | library(janitor) 22 | library(lubridate) 23 | library(purrr) 24 | library(tidyr) 25 | library(dplyr) 26 | library(stringr) 27 | 28 | # Load data ==================================================================== 29 | 30 | read_data <- function(pet) { 31 | read_delim( 32 | paste0("https://tierstatistik.identitas.ch/data/", pet, "-breeds.csv"), 33 | delim = ";", 34 | skip = 1, 35 | show_col_types = FALSE 36 | ) |> 37 | clean_names() |> 38 | mutate( 39 | pet_type = pet, 40 | date = make_date(year, month), 41 | .before = year 42 | ) 43 | } 44 | 45 | pets <- c("cats", "dogs") |> 46 | set_names() |> 47 | map(read_data) 48 | 49 | # Tidy data ==================================================================== 50 | 51 | tidy_breeds <- function(data) { 52 | data |> 53 | pivot_longer( 54 | cols = !matches(c("pet_type|date|year|month")), 55 | names_to = "breed", 56 | values_to = "count" 57 | ) |> 58 | mutate( 59 | breed = str_to_title(str_replace_all(breed, "_", " ")) 60 | ) 61 | } 62 | 63 | pets <- pets |> 64 | map(tidy_breeds) |> 65 | list_rbind() 66 | 67 | # Save data ==================================================================== 68 | 69 | saveRDS(pets, "data/pets.RDS") 70 | -------------------------------------------------------------------------------- /data/pet-reports.RDS: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jadeynryan/parameterized-quarto-workshop/b7ca2fc86f2a1d0c1c7328395657577a0ab47cd3/data/pet-reports.RDS -------------------------------------------------------------------------------- /data/pets.RDS: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jadeynryan/parameterized-quarto-workshop/b7ca2fc86f2a1d0c1c7328395657577a0ab47cd3/data/pets.RDS -------------------------------------------------------------------------------- /exercises/intermediate-exercises.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jadeynryan/parameterized-quarto-workshop/b7ca2fc86f2a1d0c1c7328395657577a0ab47cd3/exercises/intermediate-exercises.zip -------------------------------------------------------------------------------- /images/cascadia-jr-quarto.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jadeynryan/parameterized-quarto-workshop/b7ca2fc86f2a1d0c1c7328395657577a0ab47cd3/images/cascadia-jr-quarto.webp -------------------------------------------------------------------------------- /images/jadey.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jadeynryan/parameterized-quarto-workshop/b7ca2fc86f2a1d0c1c7328395657577a0ab47cd3/images/jadey.jpg -------------------------------------------------------------------------------- /images/jr-logo-circle.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jadeynryan/parameterized-quarto-workshop/b7ca2fc86f2a1d0c1c7328395657577a0ab47cd3/images/jr-logo-circle.webp -------------------------------------------------------------------------------- /images/jr-logo-quarto.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jadeynryan/parameterized-quarto-workshop/b7ca2fc86f2a1d0c1c7328395657577a0ab47cd3/images/jr-logo-quarto.webp -------------------------------------------------------------------------------- /index.qmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Intermediate Quarto: Parameterized Reports Workshop" 3 | format: html 4 | --- 5 | 6 | [**Cascadia R Conference 2024**](https://cascadiarconf.com/) 7 | 8 | 📆 June 21, 2024 // 1:30 pm - 4:30 pm PT\ 9 | 🏫 University of Washington, South Lake Union, Building C\ 10 | 🏡 [Workshop website](https://jadeyryan.quarto.pub/cascadia-quarto)\ 11 | 🐙 [GitHub 12 | release](https://github.com/jadeynryan/parameterized-quarto-workshop/tree/cascadia)\ 13 | 14 | 15 | 16 | 17 | 18 | 19 | ## Description 20 | 21 | The [Intro to Quarto workshop](https://charlotte.quarto.pub/cascadia/) 22 | takes you through the basics of authoring a reproducible report using 23 | Quarto. This workshop builds on those concepts and teaches you how to 24 | level up your reproducible reports by using parameters, conditional 25 | content, conditional code execution, and custom styling sheets for HTML 26 | and Microsoft Word formats. Additionally, you will learn how to render 27 | all variations of a parameterized report at once using `quarto` and 28 | `purrr`. 29 | 30 | ## Learning objectives 31 | 32 | - Understand what parameterized reporting is and when it is useful. 33 | - Convert a Quarto document into a parameterized template and render 34 | all variations. 35 | - Generate multiple format outputs from the same template using 36 | conditional content, conditional code execution, and custom styling. 37 | 38 | ## Schedule 39 | 40 | | Time | Topic | 41 | |-------------|-----------------------------------------------------------------| 42 | | 1:30 - 2:00 | [Welcome](1-welcome/) & [Parameterizing reports](2-parameters/) | 43 | | 2:00 - 3:00 | [Rendering reports](3-render/) | 44 | | 3:00 - 3:30 | Break | 45 | | 3:30 - 4:00 | [Conditional content & code](4-conditionals/) | 46 | | 4:00 - 4:20 | [Styling reports](5-style/) | 47 | | 4:20 - 4:30 | [Summary](6-summary/) | 48 | 49 | ## Speaker 50 | 51 | ![](images/jadey.jpg){style="float:left;padding: 0 10px 0 0; border-radius:50%" 52 | fig-alt="Headshot of Jadey Ryan" width="150"} [**Jadey 53 | Ryan**](https://jadeyryan.com) is a data scientist in [Natural Resources 54 | and Agricultural Sciences](https://agr.wa.gov/AgScience) at the 55 | Washington State Department of Agriculture. She is an R enthusiast 56 | obsessed with efficiently creating beautiful data products and 57 | decision-support tools, especially with Quarto. Away from the computer, 58 | find her trying new foods with her husband, snuggling with her three 59 | cats, or taking leisurely strolls in the great outdoors. 60 | 61 | Learn more at [jadeyryan.com](https://jadeyryan.com). 62 | 63 | [{{< fa link size=xl >}}](https://jadeyryan.com) 64 | [{{< fa brands mastodon size=xl >}}](https://fosstodon.org/@jadeynryan) 65 | [{{< fa brands linkedin size=xl >}}](https://www.linkedin.com/in/jadey-ryan) 66 | [{{< fa brands github size=xl >}}](https://github.com/jadeynryan/) 67 | [{{< fa brands etsy size=xl >}}](https://thecodingcats.etsy.com/) 68 | -------------------------------------------------------------------------------- /license.qmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Disclaimer and license" 3 | format: html 4 | --- 5 | 6 | © 2024 Jadey Ryan 7 | 8 | Opinions expressed are solely my own and do not express the views of my employer or any organizations I am associated with. 9 | 10 | This work is released under the [Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International (CC BY-NC-SA) license](https://creativecommons.org/licenses/by-nc-sa/4.0/). 11 | 12 | {{< fa brands creative-commons size=2x >}} {{< fa brands creative-commons-by size=2x >}} {{< fa brands creative-commons-nc size=2x >}} {{< fa brands creative-commons-sa size=2x >}} 13 | 14 | In short, you may share and adapt this content with appropriate credit and notation of any changes. You may not use this material for any commercial purposes. 15 | -------------------------------------------------------------------------------- /parameterized-quarto-workshop.Rproj: -------------------------------------------------------------------------------- 1 | Version: 1.0 2 | 3 | RestoreWorkspace: No 4 | SaveWorkspace: No 5 | AlwaysSaveHistory: Default 6 | 7 | EnableCodeIndexing: Yes 8 | UseSpacesForTab: Yes 9 | NumSpacesForTab: 2 10 | Encoding: UTF-8 11 | 12 | RnwWeave: Sweave 13 | LaTeX: pdfLaTeX 14 | 15 | AutoAppendNewline: Yes 16 | StripTrailingWhitespace: Yes 17 | LineEndingConversion: Posix 18 | 19 | MarkdownWrap: Column 20 | MarkdownWrapAtColumn: 72 21 | -------------------------------------------------------------------------------- /prework.qmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Pre-workshop instructions" 3 | format: html 4 | execute: 5 | eval: false 6 | --- 7 | 8 | Prior to the workshop, please complete the following steps: 9 | 10 | *If you have trouble with these steps, or you'd prefer not to mess with your current set up, you can also work in Posit Cloud. See [Backup option](#backup-option-posit-cloud) below.* 11 | 12 | 13 | #### 1. Software 14 | 15 | Download and install the latest versions of R, RStudio, and Quarto: 16 | 17 | - R 4.2.3 or above: 18 | - RStudio 2024.04.0 or above: 19 | - Quarto 1.4 or above: 20 | 21 | #### 2. R Packages 22 | 23 | Install the following packages by copying and pasting the following into the console in RStudio: 24 | 25 | ```{r} 26 | install.packages(c("dplyr", "fs", "ggplot2", "here", "janitor", "knitr", 27 | "lubridate", "plotly", "purrr", "quarto", "readr", 28 | "rmarkdown", "stringr", "tidyr")) 29 | ``` 30 | 31 | #### 3. Exercises 32 | 33 | Download and open the exercises for this workshop by copying and pasting the following into the console in RStudio: 34 | 35 | This will download the RStudio project onto your Desktop. To choose a different destination, uncomment and use the `destdir` argument. 36 | 37 | ```{r} 38 | # install.packages("usethis") 39 | usethis::use_course( 40 | "https://github.com/jadeynryan/parameterized-quarto-workshop/raw/cascadia/exercises/intermediate-exercises.zip" 41 | # , destdir = "C:/Users/jryan/Documents/R/projects" 42 | ) 43 | ``` 44 | 45 | If `use_course()` didn't work, manually download the zip file from GitHub: [github.com/jadeynryan/parameterized-quarto-workshop/tree/cascadia/exercises](https://github.com/jadeynryan/parameterized-quarto-workshop/tree/cascadia/exercises). 46 | 47 | ## Backup option: Posit Cloud 48 | 49 | You can choose to use Posit Cloud instead which has the required tools already installed, along with a copy of the exercise files. Join the Posit Cloud [Quarto Cascadia workspace](https://posit.cloud/spaces/519059/join?access_code=ckv18LHEcSw3QtQ904uvpzLVlh4L2rk6RhKdpwyp) and open the [Intermediate Exercises project](https://posit.cloud/spaces/519059/content/8351107). 50 | 51 | ## `purrr` {#sec-purrr} 52 | 53 | If you're new to `purrr`, we recommend watching this R-Ladies Baltimore 54 | presentation [*Make your R Code purr with 55 | `purrr`*](https://www.youtube.com/watch?v=IewsPpjKElc) and reviewing Jenny 56 | Bryan's [tutorial](https://jennybc.github.io/purrr-tutorial/) and 57 | [workshop](https://github.com/jennybc/row-oriented-workflows). 58 | -------------------------------------------------------------------------------- /slides.scss: -------------------------------------------------------------------------------- 1 | // Include theme-specific fonts 2 | @import url('https://fonts.googleapis.com/css2?family=Atkinson+Hyperlegible:wght@400;700&family=Fira+Mono&family=Domine:wght@400;700;900&family=Nunito:wght@400;900&display=swap'); 3 | 4 | /*-- scss:defaults --*/ 5 | 6 | // colors 7 | $teal-light1: #f6fafa; 8 | $teal-light2: #A2F6EC; 9 | $teal: #0b7366; 10 | $teal-dark1: #07483f; 11 | $teal-dark2: #042521; 12 | $orange: #a98069; 13 | $orange-light:#e9dfd9; 14 | $orange-dark: #6a2d0a; 15 | 16 | // Background of the presentation 17 | $backgroundColor: $teal-light1; 18 | 19 | // Primary/body text 20 | $mainFont: 'Atkinson Hyperlegible', 'Nunito', sans-serif; 21 | $mainFontSize: 35px; 22 | 23 | // Vertical spacing between blocks of text 24 | $blockMargin: 0.3em; 25 | 26 | // Headings 27 | $headingMargin: 0 0 $blockMargin 0; 28 | $headingFont: 'Domine', serif; 29 | $headingColor: $teal; 30 | $headingLineHeight: 1em; 31 | $headingLetterSpacing: 2px; 32 | $headingTextTransform: none; 33 | $headingTextShadow: none; 34 | $headingFontWeight: bold; 35 | 36 | $heading1Size: 2.5em; 37 | $heading2Size: 2.2em; 38 | $heading3Size: 1.5em; 39 | $heading4Size: 1.0em; 40 | 41 | $codeFont: 'Fira Mono', monospace; 42 | $code-block-font-size: 0.8em; 43 | 44 | // Links and actions 45 | $linkColor: $orange-dark; 46 | $linkColorHover: darken( $linkColor, 20% ); 47 | 48 | section.has-dark-background a, section.has-dark-background a:hover { 49 | color: #bfe4fe !important; 50 | } 51 | 52 | // Text selection 53 | $selectionBackgroundColor: $teal; 54 | $selectionColor: #fff; 55 | 56 | // Contrast 57 | $min-contrast-ratio: 7; 58 | 59 | // Start using css here // 60 | 61 | // Font classes 62 | 63 | .warning { 64 | color: #990000; 65 | } 66 | 67 | .reveal a:not([role="tab"]) { 68 | text-decoration: underline !important; 69 | } 70 | 71 | .h2 { 72 | padding-bottom: 3em !important; 73 | } 74 | 75 | .subtitle { 76 | font-size: $heading3Size; 77 | font-weight: bold; 78 | } 79 | 80 | #title-slide .subtitle { 81 | margin-top: 6rem; 82 | } 83 | 84 | .quarto-title-author-name { 85 | font-size: $heading3Size; 86 | } 87 | 88 | .reveal div.sourceCode { 89 | margin-bottom: 15px !important; 90 | } 91 | 92 | // Table 93 | 94 | .reveal table { 95 | margin-left: 0!important; 96 | } 97 | 98 | // Footer 99 | 100 | .reveal .footer { 101 | font-size: 0.8em; 102 | color: $orange-dark; 103 | } 104 | 105 | // Containers 106 | 107 | .reveal .slide .v-center-container { 108 | display: flex; 109 | justify-content: center; 110 | align-items: center; 111 | height: 90%; 112 | flex-direction: column; 113 | } 114 | 115 | // Alignment 116 | 117 | .center { 118 | display: block; 119 | margin-left: auto !important; 120 | margin-right: auto !important; 121 | } 122 | 123 | // Background color sections 124 | 125 | .section { 126 | &:is(.slide-background) { 127 | background: $orange-dark; 128 | } 129 | } 130 | 131 | .demo { 132 | &:is(.slide-background) { 133 | background: $teal-dark2; 134 | } 135 | } 136 | 137 | .exercise { 138 | &:is(.slide-background) { 139 | background: $teal-dark1; 140 | } 141 | } 142 | 143 | // Fontawesome 144 | 145 | .fab, .fa-brands { 146 | font-family: 'Font Awesome 6 Brands'; 147 | font-weight: 400; 148 | color: $teal; 149 | line-height: 1.5; 150 | } 151 | 152 | .fas, .fa-solid { 153 | font-family: 'Font Awesome 6 Free'; 154 | font-weight: 900; 155 | color: $teal; 156 | line-height: 1.2; 157 | } 158 | 159 | // Slide iframes 160 | 161 | .slides { 162 | width: 100%; 163 | height: auto; 164 | aspect-ratio: 16 / 9; 165 | } 166 | -------------------------------------------------------------------------------- /theme.scss: -------------------------------------------------------------------------------- 1 | /*-- scss:defaults --*/ 2 | 3 | // Fonts 4 | @import url('https://fonts.googleapis.com/css2?family=Atkinson+Hyperlegible:wght@400;700&family=Fira+Mono&family=Domine:wght@400;700;900&family=Nunito:wght@400;900&display=swap'); 5 | 6 | // Colors 7 | 8 | // Most colors adopted from https://palx.jxnblk.com/ 9 | // with base color #0b7366 10 | 11 | $white: #fff; 12 | $gray-0: #f9f9f9; 13 | $gray-1: #eceeee; 14 | $gray-2: #dfe1e1; 15 | $gray-3: #d0d4d3; 16 | $gray-4: #c0c5c4; 17 | $gray-5: #aeb4b3; 18 | $gray-6: #99a1a0; 19 | $gray-7: #818b8a; 20 | $gray-8: #626f6d; 21 | $gray-9: #334240; 22 | $black: #000; 23 | 24 | // colors 25 | $teal-light1: #f6fafa; 26 | $teal-light2: #A2F6EC; 27 | $teal: #0b7366; 28 | $teal-dark1: #07483f; 29 | $teal-dark2: #042521; 30 | $orange: #a98069; 31 | $orange-light:#e9dfd9; 32 | $orange-dark: #6a2d0a; 33 | 34 | $primary: $teal; 35 | $secondary: $orange-dark; 36 | $light: $teal-light1; 37 | $light-accent: $gray-3; 38 | $dark: $gray-9; 39 | 40 | // Base document colors 41 | $navbar-bg: $primary; // navbar 42 | $navbar-fg: $teal-light1; // navbar foreground elements 43 | $navbar-hl: $white; // highlight color when hovering over navbar links 44 | $link-color: $orange-dark; // hyperlinks 45 | 46 | // Inline code 47 | $code-bg: $orange-light; // inline code background color 48 | $code-color: $orange-dark; // inline code text color 49 | 50 | // Contrast 51 | $min-contrast-ratio: 7; 52 | 53 | /*-- scss:rules --*/ 54 | 55 | .navbar-title { 56 | font-family: Domine, serif; 57 | font-weight: 700; 58 | } 59 | 60 | h1, h2 { 61 | font-weight: 900; 62 | font-family: Domine, serif; 63 | color: $teal-dark1; 64 | } 65 | 66 | h1 { 67 | font-size: 1.4em; 68 | } 69 | 70 | h2 { 71 | font-size: 1.2em; 72 | } 73 | 74 | h3, h4 { 75 | font-weight: 700; 76 | font-family: Domine, serif; 77 | color: $teal; 78 | } 79 | 80 | h3 { 81 | font-size: 1.15em; 82 | } 83 | 84 | h4 { 85 | font-size: 1.1em; 86 | } 87 | 88 | h5, h6 { 89 | font-size: 1.05em; 90 | font-weight: 600; 91 | font-family: Domine, serif; 92 | color: $secondary; 93 | } 94 | 95 | .fa-solid, .fab, .fa-brands { 96 | font-family: 'Font Awesome 6 Brands'; 97 | font-weight: 400; 98 | color: $teal; 99 | } 100 | 101 | p.subtitle.lead { 102 | font-size: 1em; 103 | text-align: center; 104 | } 105 | 106 | #title-block-header.quarto-title-block.default .description { 107 | margin-top: 1em; 108 | } 109 | 110 | body { 111 | font-family: Atkinson Hyperlegible, Nunito, sans-serif; 112 | background-color: $light; 113 | } 114 | 115 | // code 116 | 117 | code { 118 | font-family: Fira Mono, monospace; 119 | } 120 | 121 | .sourceCode pre code { 122 | padding: 1.2em; 123 | } 124 | 125 | // images 126 | 127 | .figure-img { 128 | margin-bottom: 0; 129 | line-height: 0; 130 | border-radius: 5px; 131 | } 132 | 133 | // specific classes 134 | 135 | .center-text { 136 | text-align: center; 137 | } 138 | 139 | .img-va-bottom { 140 | vertical-align: bottom; 141 | } 142 | 143 | .img-center { 144 | display: block; 145 | margin-left: auto; 146 | margin-right: auto; 147 | } 148 | 149 | .img-float-right { 150 | float: right; 151 | padding-left: 1em; 152 | } 153 | 154 | @media screen and (max-width: 991.98px) { 155 | .img-float-right { 156 | float: right; 157 | width: 100%; 158 | padding-bottom: 1em; 159 | padding-left: 0em; 160 | } 161 | } 162 | 163 | .sidebar-navigation .sidebar-divider { 164 | color: white; 165 | } 166 | 167 | // github icon button 168 | 169 | .bi-github { 170 | color: white; 171 | } 172 | // page buttons 173 | 174 | .page-link { 175 | background-color: $orange-light; 176 | color: $secondary; 177 | } 178 | 179 | .page-link:hover { 180 | background-color: $secondary; 181 | color: white; 182 | } 183 | 184 | .page-item.active .page-link { 185 | background-color: $secondary; 186 | } 187 | 188 | // slide iframes 189 | 190 | .slides { 191 | width: 100%; 192 | height: auto; 193 | aspect-ratio: 16 / 9; 194 | border-style: solid; 195 | border-width: 1px; 196 | } 197 | 198 | --------------------------------------------------------------------------------