Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: countdown as a Quarto shortcode extension! #35

Merged
merged 36 commits into from
Feb 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
72ec0b8
Add a dev container
coatless Jan 18, 2024
f7838fc
Re-org directories into lib/, R/, and quarto/
coatless Jan 19, 2024
7bdb1be
Pass new working directory to setup-r-dependencies
coatless Jan 19, 2024
1a54ac7
Add a workflow dispatch
coatless Jan 19, 2024
4ac0544
Roll a dev version for the quarto extension
coatless Jan 20, 2024
ee5752e
Try a different empty value parsing approach for `kwargs`
coatless Jan 20, 2024
8400c4d
Merge branch 'main' into quarto-extension
coatless Jan 22, 2024
f2570ed
Remove capital R directory :(
coatless Jan 22, 2024
4036e55
Unblock _extension for Quarto
coatless Jan 22, 2024
1aad3b7
Figure out the best approach for checking if an option is set in kwar…
coatless Jan 22, 2024
687d41a
Update assets for Quarto
coatless Jan 22, 2024
f0c015e
First pass at revamping document-level CSS settings
coatless Jan 22, 2024
998fcc1
Revamp the main shortcode logic to use getOption()
coatless Jan 22, 2024
6434745
Remove whisker-like substitution
coatless Jan 22, 2024
e9a6ef7
Clean up default option registeration
coatless Jan 24, 2024
cce2792
Update default colors
coatless Jan 24, 2024
36ad034
Add tryPlaySound
coatless Jan 24, 2024
8db8c68
Handle inline styling options
coatless Jan 24, 2024
df21147
Document options that are supported in the README
coatless Jan 26, 2024
d4d7978
Improve string concatenation speed by using `table.concat()` instead …
coatless Jan 26, 2024
3639040
Add a few more countdown exmaples
coatless Jan 26, 2024
71d83af
Enable setting global options with the `countdown` meta key.
coatless Jan 27, 2024
a116aca
Apply suggestions from code review
coatless Jan 28, 2024
381b94e
Fix headers throughout example doc
coatless Jan 28, 2024
2dd2da9
Add timestring example & omit warning if minutes/seconds were in kwargs.
coatless Jan 28, 2024
e737fea
Update empty timer position defaults
coatless Jan 28, 2024
e0ac982
Remove spurious whitespace addition post merge commit.
coatless Jan 28, 2024
ac89497
Make sure to force an error and touch up the error
coatless Jan 28, 2024
ef9f405
Apply suggestions from code review
coatless Jan 29, 2024
69df9f5
Restructure defaults to only be a table of CSS variable names.
coatless Feb 2, 2024
369f018
Fix detection of `false` is supplied as a string by specifically chec…
coatless Feb 2, 2024
706af32
First pass on improving the README
coatless Feb 2, 2024
4951038
One more set of readme tweaks.
coatless Feb 2, 2024
98a26bb
Update countdown.lua
coatless Feb 3, 2024
1e72c36
Update countdown.lua
coatless Feb 3, 2024
34ec998
Update quarto/_extensions/countdown/countdown.lua
coatless Feb 3, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// Config options: https://github.com/rocker-org/devcontainer-templates/tree/main/src/r-ver
{
"name": "R (rocker/r-ver base)",
"image": "ghcr.io/rocker-org/devcontainer/r-ver:4.3",
// Add software
"features": {
// Required to test with knitr
// R package config: https://github.com/rocker-org/devcontainer-features/blob/main/src/r-rig/README.md
"ghcr.io/rocker-org/devcontainer-features/r-rig:1": {
"version": "none",
"installRMarkdown": true,
"installJupyterlab": true,
"installRadian": true
},
// You may wish to switch prerelease to latest for stable development
// Quarto configuration : https://github.com/rocker-org/devcontainer-features/blob/main/src/quarto-cli/README.md
"ghcr.io/rocker-org/devcontainer-features/quarto-cli:1": {
"version": "prerelease"
}
},
"customizations": {
"vscode": {
"settings": {
"r.rterm.linux": "/usr/local/bin/radian",
"r.bracketedPaste": true,
"r.plot.useHttpgd": true,
"[r]": {
"editor.wordSeparators": "`~!@#%$^&*()-=+[{]}\\|;:'\",<>/?"
}
},
// Enable a development set of extensions for Lua and Quarto
"extensions": ["quarto.quarto", "sumneko.lua", "GitHub.copilot"]
}
}
}
2 changes: 2 additions & 0 deletions .github/workflows/R-CMD-check.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ on:
branches: [main, master]
pull_request:
branches: [main, master]
workflow_dispatch: {}

name: R-CMD-check

Expand Down Expand Up @@ -45,6 +46,7 @@ jobs:
needs: check
working-directory: r


- uses: r-lib/actions/check-r-package@v2
with:
upload-snapshots: true
Expand Down
6 changes: 4 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,7 @@
.DS_Store

# Directories that start with _
_*/
!_snaps/
_dev/

## https://github.com/github/gitignore/blob/master/R.gitignore
# History files
Expand Down Expand Up @@ -74,3 +73,6 @@ Icon
Network Trash Folder
Temporary Items
.apdisk

/.luarc.json
*.luarc.json
2 changes: 0 additions & 2 deletions LICENSE

This file was deleted.

2 changes: 1 addition & 1 deletion lib/countdown.js
Original file line number Diff line number Diff line change
Expand Up @@ -377,7 +377,7 @@ class CountdownTimer {

playSound () {
let url = this.play_sound
if (!url) return
if (!url || url === "false") return
if (typeof url === 'boolean') {
const src = this.src_location
? this.src_location.replace('/countdown.js', '')
Expand Down
3 changes: 3 additions & 0 deletions quarto/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
*.html
*.pdf
*_files/
95 changes: 91 additions & 4 deletions quarto/README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# quarto-countdown: A Quarto Extension for Countdown
# quarto-countdown: Countdown Timers for Quarto RevealJS slides

The `countdown` extension allows you to incorporate countdown like timers on Quarto HTML Documents and RevealJS slides.
The `quarto-countdown` extension for [Quarto](https://quarto.org) allows you to incorporate countdown timers on [Quarto RevealJS slides](https://quarto.org/docs/presentations/revealjs/).

This extension can be used without installing R or the `{countdown}` R Package.
This extension doesn't require the installation of _R_ or the `{countdown}` _R_ Package.

## Installation

Expand All @@ -20,13 +20,100 @@ This command will download and install the extension under the `_extensions` sub

## Usage

To embed a countdown clock, use the `{{< countdown >}}` shortcode. For example:
To embed a countdown clock, use the `{{< countdown >}}` shortcode. For example, a countdown clock can be created by writing anywhere:

```default
{{< countdown >}}
```

For a longer or shorter countdown, specify the `minutes` and `seconds` options:

coatless marked this conversation as resolved.
Show resolved Hide resolved
```default
{{< countdown minutes=5 seconds=30 >}}
```

Or use a time string formatted in `"MM:SS"`

```default
{{< countdown "5:30" >}}
```

There are many more customizations to choose from. See the next section for more details.

### Customizations

The extension offers extensive customization options, akin to the features provided by the R package version of `countdown`. These customizations span both functionality and style. They can be configured either at the document level or for each individual timer.

#### In-line options

The `countdown` timer shortcode has a variety of customizations that can be set. The customizations can be split between functionality and style.

The functionality options are:

| Option | Default Value | Description |
| ------------------- | ------------------------------- | ------------------------------------------------------------------------- |
| `minutes` | `1` | Number of minutes with a total cap of 100 minutes |
| `seconds` | `0` | Number of seconds |
| `id` | A generated, unique ID | ID attribute of the HTML element. |
| `class` | "countdown" | Class attribute of the HTML element. |
| `warn_when` | `0` | Number of seconds before the countdown displays a warning. |
| `update_every` | `1` | Frequency at which the countdown should be updated, in seconds. |
| `play_sound` | `"false"` | Boolean indicating whether to play a sound during the countdown. |
| `blink_colon` | `"false"` | Boolean indicating whether the colon in the countdown should blink. |
| `start_immediately` | `"false"` | Boolean indicating whether the countdown should start immediately. |

The style options are:

| Style Option | Default Value | Description |
| ------------- | ---------------------------- | ------------------------------------------------------------------------- |
| `top` | `""` (empty) | Top position of the HTML element. |
| `right` | `"0"` | Right position of the HTML element. |
| `bottom` | `"0"` | Bottom position of the HTML element. |
| `left` | `""` (empty) | Left position of the HTML element. |
| `margin` | `"0.6em"` | Margin around the HTML element. |
gadenbuie marked this conversation as resolved.
Show resolved Hide resolved
| `padding` | `"10px 15px"` | Padding within the HTML element. |
| `font-size` | `"3rem"` | Font size of the HTML element. |
| `line-height` | `"1"` | Line height of the HTML element. |
| `style` | Computed based on attributes | String constructed based on style-related attributes of the HTML element. |

#### Document-level Options

Document-level options can be specified in the document's header using a YAML key-value format:

```yaml
---
title: "Example document-level settings"
countdown:
option: value
---
```

The following options are implemented:

| Option | Default Value | Description |
| --------------------------- | ------------------------------------------ | ------------------------------------------------- |
| `font_size` | `"3rem"` | Font size for the countdown element |
| `margin` | `"0.6em"` | Margin around the countdown element |
| `padding` | `"10px 15px"` | Padding within the countdown element |
| `box_shadow` | `"0px 4px 10px 0px rgba(50, 50, 50, 0.4)"` | Shadow applied to the countdown element |
| `border_width` | `"0.1875rem"` | Border width of the countdown element |
| `border_radius` | `"0.9rem"` | Border radius of the countdown element |
| `line_height` | `"1"` | Line height of the countdown element |
| `color_border` | `"#ddd"` | Border color of the countdown element |
| `color_background` | `"inherit"` | Background color of the countdown element |
| `color_text` | `"inherit"` | Text color of the countdown element |
| `color_running_background` | `"#43AC6A"` | Background color when the countdown is running |
| `color_running_border` | `"#2A9B59FF"` | Border color when the countdown is running |
| `color_running_text` | `"inherit"` | Text color when the countdown is running |
| `color_finished_background` | `"#F04124"` | Background color when the countdown is finished |
| `color_finished_border` | `"#DE3000FF"` | Border color when the countdown is finished |
| `color_finished_text` | `"inherit"` | Text color when the countdown is finished |
| `color_warning_background` | `"#E6C229"` | Background color when the countdown has a warning |
| `color_warning_border` | `"#CEAC04FF"` | Border color when the countdown has a warning |
| `color_warning_text` | `"inherit"` | Text color when the countdown has a warning |
| `selector` | `"root"` | Selector for the countdown element |


## Example

You can see a minimal example of the extension in action here: [example.qmd](example.qmd).
Expand Down
8 changes: 8 additions & 0 deletions quarto/_extensions/countdown/_extension.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
title: countdown
author: Garrick Aden-Buie and James Joseph Balamuta
version: 0.0.0-dev.1
quarto-required: ">=1.4.0"
contributes:
shortcodes:
- countdown.lua

175 changes: 175 additions & 0 deletions quarto/_extensions/countdown/assets/countdown.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
.countdown {
--_margin: 0.6em;
--_running-color: var(--countdown-color-running-text, rgba(0, 0, 0, 0.8));
--_running-background: var(--countdown-color-running-background, #43AC6A);
--_running-border-color: var(--countdown-color-running-border, rgba(0, 0, 0, 0.1));
--_finished-color: var(--countdown-color-finished-text, rgba(0, 0, 0, 0.7));
Comment on lines +5 to +6
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I started thinking about how we'd replace prismatic and these lines kind of hint at where my head is at.

First, we definitely do not need a prismatic replacement in the lua filter for this PR; we can certainly iterate after merging this one.

The set up with these lines is that we could tint or shade the countdown border and text color based on the background color. I went with shading (darkening) in the CSS, but we could have tinted (lightend) as well with rgba(255, 255, 255, 0.1) or similar.

The question is how we'd know whether we should lighten or darken. But I'm okay waiting on that as well and working it out in follow up PRs.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thinking out loud, there would be a Python port coming next. So, would either of these options be acceptable to avoid moving it into the Lua filter?

  1. Write the lighting and darkening logic in JavaScript (preferred)
  2. Custom CSS properties using calc alongside a flag on the inline element? light-color/dark-color?
:root {
      --main-background-color: #3498db;
      --text-color: calc(1 - var(--main-background-color) * 0.7); /* Default text color */
    }

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I'd really like to handle it in CSS, because that will be the easiest in the long term. Hopefully sometime soon we'll get color-contrast().

For now, I'm okay with the current approach and we can work it out in future iterations.

--_finished-background: var(--countdown-color-finished-background, #F04124);
--_finished-border-color: var(--countdown-color-finished-border, rgba(0, 0, 0, 0.1));

position: absolute;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
background: var(--countdown-color-background, inherit);
font-size: var(--countdown-font-size, 3rem);
line-height: var(--countdown-line-height, 1);
border-color: var(--countdown-color-border, #ddd);
border-width: var(--countdown-border-width, 0.1875rem);
border-style: solid;
border-radius: var(--countdown-border-radius, 0.9rem);
box-shadow: var(--countdown-box-shadow, 0px 4px 10px 0px rgba(50, 50, 50, 0.4));
margin: var(--countdown-margin, var(--_margin, 0.6em));
padding: var(--countdown-padding, 0.625rem 0.9rem);
text-align: center;
z-index: 10;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}

.countdown.inline {
position: relative;
width: max-content;
max-width: 100%;
}

.countdown .countdown-time {
background: none;
font-size: 100%;
padding: 0;
color: currentColor;
}

.countdown-digits {
color: var(--countdown-color-text);
}

.countdown.running {
border-color: var(--_running-border-color);
background-color: var(--_running-background);
}

.countdown.running .countdown-digits {
color: var(--_running-color);
}

.countdown.finished {
border-color: var(--_finished-border-color);
background-color: var(--_finished-background);
}

.countdown.finished .countdown-digits {
color: var(--_finished-color);
}

.countdown.running.warning {
border-color: var(--countdown-color-warning-border, rgba(0, 0, 0, 0.1));
background-color: var(--countdown-color-warning-background, #E6C229);
}

.countdown.running.warning .countdown-digits {
color: var(--countdown-color-warning-text, rgba(0, 0, 0, 0.7));
}

.countdown.running.blink-colon .countdown-digits.colon {
opacity: 0.1;
}

/* ------ Controls ------ */
.countdown:not(.running) .countdown-controls,
.countdown.no-controls .countdown-controls {
display: none;
}

.countdown-controls {
position: absolute;
top: -0.5rem;
right: -0.5rem;
left: -0.5rem;
display: flex;
justify-content: space-between;
margin: 0;
padding: 0;
}

.countdown-controls>button {
position: relative;
font-size: 1.5rem;
width: 1rem;
height: 1rem;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
font-family: monospace;
padding: 10px;
margin: 0;
background: inherit;
border: 2px solid;
border-radius: 100%;
transition: 50ms transform ease-in-out, 150ms opacity ease-in;
box-shadow: 0px 2px 5px 0px rgba(50, 50, 50, 0.4);
-webkit-box-shadow: 0px 2px 5px 0px rgba(50, 50, 50, 0.4);
--_button-bump: 0;
opacity: var(--_opacity, 0);
transform: translate(0, var(--_button-bump));
}

/* increase hit area of the +/- buttons */
.countdown .countdown-controls > button::after {
content: "";
height: 200%;
width: 200%;
position: absolute;
border-radius: 50%;
}

.countdown .countdown-controls>button:last-child {
color: var(--_running-color);
background-color: var(--_running-background);
border-color: var(--_running-border-color);
}

.countdown .countdown-controls>button:first-child {
color: var(--_finished-color);
background-color: var(--_finished-background);
border-color: var(--_finished-border-color);
}

.countdown.running:hover, .countdown.running:focus-within {
--_opacity: 1;
}

.countdown.running:hover .countdown-controls>button,
.countdown.running:focus-within .countdown-controls>button {
--_button-bump: -3px;
}

.countdown.running:hover .countdown-controls>button:active,
.countdown.running:focus-within .countdown-controls>button:active {
--_button-bump: 0;
}

coatless marked this conversation as resolved.
Show resolved Hide resolved
/* ---- Quarto Reveal.js ---- */
.reveal .countdown {
--_margin: 0;
}

/* ----- Fullscreen ----- */
.countdown.countdown-fullscreen {
z-index: 0;
}

.countdown-fullscreen.running .countdown-controls {
top: 1rem;
left: 0;
right: 0;
justify-content: center;
}

.countdown-fullscreen.running .countdown-controls>button+button {
margin-left: 1rem;
}
Loading
Loading