Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/devOpifex/component
📄 Components for Shiny
https://github.com/devOpifex/component
Last synced: 27 days ago
JSON representation
📄 Components for Shiny
- Host: GitHub
- URL: https://github.com/devOpifex/component
- Owner: devOpifex
- License: gpl-3.0
- Created: 2023-10-16T12:27:20.000Z (about 1 year ago)
- Default Branch: master
- Last Pushed: 2023-11-05T13:02:17.000Z (about 1 year ago)
- Last Synced: 2024-08-03T03:06:44.845Z (4 months ago)
- Language: R
- Homepage:
- Size: 66.4 KB
- Stars: 27
- Watchers: 6
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE.md
Awesome Lists containing this project
- awesome-shiny-extensions - component - Create components for Shiny, inspired by Vue.js. (Developer Tools / Modularization)
- jimsghstars - devOpifex/component - 📄 Components for Shiny (R)
README
# component
Creating components for [shiny](https://shiny.rstudio.com) inspired by [Vue](https://vuejs.org/)
It tackles three "issues" with Shiny modules:
1. Generating scoped (namespaced) CSS and JavaScript code.
2. Places all the code for a module in a single file.
3. Binds CSS, and JavaScript of a module to itThis is inspired from Vue where one defines a component in a single file.
```vue
import { ref } from 'vue'
const count = ref(0)Count is: {{ count }}
button {
font-weight: bold;
}```
Where the file includes the style, script, and template.
We reproduce the same with R with the addition of the server component.## Installation
You can install the development version of component from [GitHub](https://github.com/) with:
``` r
# install.packages("remotes")
remotes::install_github("devOpifex/component")
```## Create
From a package (see [lerprechaun](https://leprechaun.opifex.org) or
[golem](https://thinkr-open.github.io/golem/)), create a component.``` r
# create a component
# give it a name
component::create("test")
```This creates a new file in `R/` named `component-test.R`.
Components generate code via `devtools::document()` (more on that later)
add the roxygen2 roclet to your `DESCRIPTION`, e.g.:```
Roxygen: list(markdown = TRUE, roclets = c("collate", "namespace", "rd", "component::roclet_component"))
```So `create()` creates the component file but `devtools::document()` generates
the usable version of the component.```r
component::create() => `component-.R` => devtools::document() => `component-generated-.R`
```## Component Anatomy
The default component as created by `component::create()` looks something like the code below
(`R/component-test.R`).```r
#' @component test
NULL#' test javascript
#'
#' test javascript component.
#'
#' @param ... Any other argument.
#'
#' @keywords internal
.test_javascript <- \(...) {
"$('{{class red}}').on('mouseenter', (e) => {
$(e.target).toggleClass('{{ns red}}');
})"
}#' test css
#'
#' test css component.
#'
#' @param ... Any other argument.
#'
#' @keywords internal
.test_css <- \(...) {
c(
"{{class red}}{color:red;}",
"h1{font-weight: bold;}"
)
}#' test ui
#'
#' test UI component.
#'
#' @param ns Shiny's namespace function.
#' @param ... Any other argument.
#'
#' @keywords internal
.test_ui <- \(ns, ...) {
shiny::div(
shiny::h1("Component", class = ns("red")),
plotOutput(ns("plot"))
)
}#' test server
#'
#' test server component.
#'
#' @param input,output,session Arguments passed from mdoule's server.
#' @param ... Any other argument.
#'
#' @keywords internal
.test_server <- \(input, output, session, ...) {
output$plot <- shiny::renderPlot(plot(stats::runif(200)))
}
```### Component tag
The `@component` tag indicates the name of the component,
in this example `test`.
This tells component which functions to scan for:- `._javascript`
- `._css`
- `._server`
- `._ui`### JavaScript and CSS
The `.test_javascript` and `.test_css` functions are used to generate
scoped code.- `class`: transforms `{{ class red }}` into `.namespace-red`
- `id`: transforms `{{ id red }}` into `#namespace-red`
- `ns`: transforms `{{ ns red }}` into `namespace-red`
- `json`: transforms `{{ json letters[1:2] }}` into `['a', 'b']`
- You may still use `{{ x }}` in which case it will evaluate `x`
(passed `*ui` or `*server` function).These functions should return a character vector of length one or more.
The CSS is minified using [sass](https://github.com/rstudio/sass/), if you have
[uglifyjs](https://www.npmjs.com/package/uglify-js) installed
it is used to minify the JavaScript otherwise it is not
(install it with `npm install uglify-js -g`).### UI and Server
Almost identical to the UI and server functions of a shiny module
### How to use
The created template __cannot__ be used as-is, by documenting the code with
`devtools::document()` we create a new file (`R/component-generated-.R`)
which contains:- `_ui`
- `_server`In this example, `test_ui`, and `server_ui`.
These are the functions that one should use in the application.
Always work on/edit the functions starting with a dot (e.g.: `.test_ui`),
as the generated files get regenerated at every `devtools::document()` call.Example based on the abose "test" component, after running `devtools::document()`.
```r
library(shiny)ui <- fluidPage(
title = "Test",
test_ui("test")
)server <- function(...){
test_server("test")
}shinyApp(ui, server)
```## Example
Here is the classic counter example but scoped and using JavaScript only.
```r
#' @component counter
NULL#' counter javascript
#'
#' counter javascript component.
#'
#' @param ... Any other argument.
#'
#' @keywords internal
.counter_javascript <- \(...) {
"$(() => {
let index = 0;
$('{{id button}}').on('click', (e) => {
index++;
$('{{id output}}').html(index);
$('{{id output}}').toggleClass('{{ns criminal}}');
})
})"
}#' counter css
#'
#' counter css component.
#'
#' @param ... Any other argument.
#'
#' @keywords internal
.counter_css <- \(...) {
"{{class criminal}}{
color: {{color}};
font-weight: bold;
}"
}#' counter ui
#'
#' counter UI component.
#'
#' @param ns Shiny's namespace function.
#' @param ... Any other argument.
#'
#' @keywords internal
.counter_ui <- \(ns, ...) {
shiny::div(
shiny::h1("Counter"),
shiny::tags$button(
class = "btn btn-sm btn-primary",
id = ns("button"),
"Counter"
),
shiny::p(
id = ns("output"),
0L
)
)
}#' counter server
#'
#' counter server component.
#'
#' @param input,output,session Arguments passed from mdoule's server.
#' @param ... Any other argument.
#'
#' @keywords internal
.counter_server <- \(input, output, session, ...) {}
```Then, after documenting, it can be used as.
```r
library(shiny)ui <- fluidPage(
title = "Test",
counter_ui("c1", color = "red"),
counter_ui("c2", color = "blue"),
)server <- function(...){
counter_server("c1")
counter_server("c2")
}shinyApp(ui, server)
```