{"id":13736712,"url":"https://github.com/devOpifex/component","last_synced_at":"2025-05-08T13:30:34.293Z","repository":{"id":200621091,"uuid":"705646610","full_name":"devOpifex/component","owner":"devOpifex","description":"📄 Components for Shiny","archived":false,"fork":false,"pushed_at":"2023-11-05T13:02:17.000Z","size":68,"stargazers_count":27,"open_issues_count":0,"forks_count":0,"subscribers_count":6,"default_branch":"master","last_synced_at":"2024-08-03T03:06:44.845Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"R","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/devOpifex.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null}},"created_at":"2023-10-16T12:27:20.000Z","updated_at":"2024-07-16T19:57:05.000Z","dependencies_parsed_at":null,"dependency_job_id":"810de56d-36a5-4483-9f37-ee6c8bacfed8","html_url":"https://github.com/devOpifex/component","commit_stats":null,"previous_names":["devopifex/component"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/devOpifex%2Fcomponent","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/devOpifex%2Fcomponent/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/devOpifex%2Fcomponent/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/devOpifex%2Fcomponent/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/devOpifex","download_url":"https://codeload.github.com/devOpifex/component/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":224732129,"owners_count":17360416,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-08-03T03:01:27.076Z","updated_at":"2024-11-15T04:32:24.621Z","avatar_url":"https://github.com/devOpifex.png","language":"R","funding_links":[],"categories":["Developer Tools","R"],"sub_categories":["Modularization"],"readme":"\u003cdiv align=\"center\"\u003e\n\n\u003c!-- badges: start --\u003e\n\u003c!-- badges: end --\u003e\n\n# component\n\nCreating components for [shiny](https://shiny.rstudio.com) inspired by [Vue](https://vuejs.org/)\n\n\u003c/div\u003e\n\nIt tackles three \"issues\" with Shiny modules:\n\n1. Generating scoped (namespaced) CSS and JavaScript code.\n2. Places all the code for a module in a single file.\n3. Binds CSS, and JavaScript of a module to it\n\nThis is inspired from Vue where one defines a component in a single file.\n\n```vue\n\u003cscript setup\u003e\nimport { ref } from 'vue'\nconst count = ref(0)\n\u003c/script\u003e\n\n\u003ctemplate\u003e\n  \u003cbutton @click=\"count++\"\u003eCount is: {{ count }}\u003c/button\u003e\n\u003c/template\u003e\n\n\u003cstyle scoped\u003e\nbutton {\n  font-weight: bold;\n}\n\u003c/style\u003e\n```\n\nWhere the file includes the style, script, and template.\nWe reproduce the same with R with the addition of the server component.\n\n## Installation\n\nYou can install the development version of component from [GitHub](https://github.com/) with:\n\n``` r\n# install.packages(\"remotes\")\nremotes::install_github(\"devOpifex/component\")\n```\n\n## Create\n\nFrom a package (see [lerprechaun](https://leprechaun.opifex.org) or \n[golem](https://thinkr-open.github.io/golem/)), create a component.\n\n``` r\n# create a component\n# give it a name\ncomponent::create(\"test\")\n```\n\nThis creates a new file in `R/` named `component-test.R`.\n\nComponents generate code via `devtools::document()` (more on that later)\nadd the roxygen2 roclet to your `DESCRIPTION`, e.g.:\n\n```\nRoxygen: list(markdown = TRUE, roclets = c(\"collate\", \"namespace\", \"rd\", \"component::roclet_component\"))\n```\n\nSo `create()` creates the component file but `devtools::document()` generates \nthe usable version of the component.\n\n```r\ncomponent::create() =\u003e `component-\u003cname\u003e.R` =\u003e devtools::document() =\u003e `component-generated-\u003cname\u003e.R`\n```\n\n## Component Anatomy\n\nThe default component as created by `component::create()` looks something like the code below\n(`R/component-test.R`).\n\n```r\n#' @component test\nNULL\n\n#' test javascript\n#' \n#' test javascript component.\n#' \n#' @param ... Any other argument.\n#' \n#' @keywords internal\n.test_javascript \u003c- \\(...) {\n    \"$('{{class red}}').on('mouseenter', (e) =\u003e {\n        $(e.target).toggleClass('{{ns red}}');\n    })\"\n}\n\n#' test css\n#' \n#' test css component.\n#' \n#' @param ... Any other argument.\n#' \n#' @keywords internal\n.test_css \u003c- \\(...) {\n  c(\n    \"{{class red}}{color:red;}\",\n    \"h1{font-weight: bold;}\"\n  )\n}\n\n#' test ui\n#' \n#' test UI component.\n#' \n#' @param ns Shiny's namespace function.\n#' @param ... Any other argument.\n#' \n#' @keywords internal\n.test_ui \u003c- \\(ns, ...) {\n  shiny::div(\n    shiny::h1(\"Component\", class = ns(\"red\")),\n    plotOutput(ns(\"plot\"))\n  )\n}\n\n#' test server\n#' \n#' test server component.\n#' \n#' @param input,output,session Arguments passed from mdoule's server.\n#' @param ... Any other argument.\n#' \n#' @keywords internal\n.test_server \u003c- \\(input, output, session, ...) {\n  output$plot \u003c- shiny::renderPlot(plot(stats::runif(200)))\n}\n```\n\n### Component tag\n\nThe `@component` tag indicates the name of the component,\nin this example `test`.\nThis tells component which functions to scan for:\n\n- `.\u003ccomponent\u003e_javascript`\n- `.\u003ccomponent\u003e_css`\n- `.\u003ccomponent\u003e_server`\n- `.\u003ccomponent\u003e_ui`\n\n### JavaScript and CSS\n\nThe `.test_javascript` and `.test_css` functions are used to generate\nscoped code.\n\n- `class`: transforms `{{ class red }}` into `.namespace-red`\n- `id`: transforms `{{ id red }}` into `#namespace-red`\n- `ns`: transforms `{{ ns red }}` into `namespace-red`\n- `json`: transforms `{{ json letters[1:2] }}` into `['a', 'b']`\n- You may still use `{{ x }}` in which case it will evaluate `x` \n(passed `*ui` or `*server` function).\n\nThese functions should return a character vector of length one or more.\n\nThe CSS is minified using [sass](https://github.com/rstudio/sass/), if you have \n[uglifyjs](https://www.npmjs.com/package/uglify-js) installed\nit is used to minify the JavaScript otherwise it is not\n(install it with `npm install uglify-js -g`).\n\n### UI and Server\n\nAlmost identical to the UI and server functions of a shiny module\n\n### How to use\n\nThe created template __cannot__ be used as-is, by documenting the code with \n`devtools::document()` we create  a new file (`R/component-generated-\u003ccomponent\u003e.R`)\nwhich contains:\n\n- `\u003ccomponent\u003e_ui`\n- `\u003ccomponent\u003e_server`\n\nIn this example, `test_ui`, and `server_ui`.\n\nThese are the functions that one should use in the application.\nAlways work on/edit the functions starting with a dot (e.g.: `.test_ui`),\nas the generated files get regenerated at every `devtools::document()` call.\n\nExample based on the abose \"test\" component, after running `devtools::document()`.\n\n```r\nlibrary(shiny)\n\nui \u003c- fluidPage(\n    title = \"Test\",\n    test_ui(\"test\")\n)\n\nserver \u003c- function(...){\n    test_server(\"test\")\n}\n\nshinyApp(ui, server)\n```\n\n## Example\n\nHere is the classic counter example but scoped and using JavaScript only.\n\n```r\n#' @component counter\nNULL\n\n#' counter javascript\n#' \n#' counter javascript component.\n#' \n#' @param ... Any other argument.\n#' \n#' @keywords internal\n.counter_javascript \u003c- \\(...) {\n  \"$(() =\u003e {\n    let index = 0;\n    $('{{id button}}').on('click', (e) =\u003e {\n      index++;\n      $('{{id output}}').html(index);\n      $('{{id output}}').toggleClass('{{ns criminal}}');\n    })\n  })\"\n}\n\n#' counter css\n#' \n#' counter css component.\n#' \n#' @param ... Any other argument.\n#' \n#' @keywords internal\n.counter_css \u003c- \\(...) {\n  \"{{class criminal}}{\n    color: {{color}};\n    font-weight: bold;\n  }\"\n}\n\n#' counter ui\n#' \n#' counter UI component.\n#' \n#' @param ns Shiny's namespace function.\n#' @param ... Any other argument.\n#' \n#' @keywords internal\n.counter_ui \u003c- \\(ns, ...) {\n  shiny::div(\n    shiny::h1(\"Counter\"),\n    shiny::tags$button(\n      class = \"btn btn-sm btn-primary\",\n      id = ns(\"button\"),\n      \"Counter\"\n    ),\n    shiny::p(\n      id = ns(\"output\"), \n      0L\n    )\n  )\n}\n\n#' counter server\n#' \n#' counter server component.\n#' \n#' @param input,output,session Arguments passed from mdoule's server.\n#' @param ... Any other argument.\n#' \n#' @keywords internal\n.counter_server \u003c- \\(input, output, session, ...) {}\n```\n\nThen, after documenting, it can be used as.\n\n```r\nlibrary(shiny)\n\nui \u003c- fluidPage(\n    title = \"Test\",\n    counter_ui(\"c1\", color = \"red\"),\n    counter_ui(\"c2\", color = \"blue\"),\n)\n\nserver \u003c- function(...){\n    counter_server(\"c1\")\n    counter_server(\"c2\")\n}\n\nshinyApp(ui, server)\n```\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FdevOpifex%2Fcomponent","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FdevOpifex%2Fcomponent","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FdevOpifex%2Fcomponent/lists"}