mirror of
https://github.com/rstudio/shiny.git
synced 2026-01-11 16:08:19 -05:00
Compare commits
67 Commits
bootstrapL
...
v1.7.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0fc861afb4 | ||
|
|
2300dae10b | ||
|
|
dfbb98abfd | ||
|
|
9670839235 | ||
|
|
1e2326c2b6 | ||
|
|
6f46b847e2 | ||
|
|
8c44559a1f | ||
|
|
d245a972ee | ||
|
|
c153d0591f | ||
|
|
2ce18ef324 | ||
|
|
2792d65e40 | ||
|
|
7b00f605aa | ||
|
|
4cb3f05e8e | ||
|
|
8e40c815eb | ||
|
|
6dfd8bc0ff | ||
|
|
2ef397f024 | ||
|
|
94749f6114 | ||
|
|
4a39588d00 | ||
|
|
f5d5832149 | ||
|
|
68deab9b0e | ||
|
|
96efac2bd7 | ||
|
|
a67059f9f9 | ||
|
|
cdc51c09c7 | ||
|
|
a6f02cf214 | ||
|
|
7600770a6e | ||
|
|
1b3ed88bd1 | ||
|
|
f01dc9f0fb | ||
|
|
9a65890e92 | ||
|
|
ffef0c2eb1 | ||
|
|
8b74338b0f | ||
|
|
ed3c676548 | ||
|
|
30c0a2bd29 | ||
|
|
997e5e5ce5 | ||
|
|
aba6b2e4db | ||
|
|
3f48e3b0af | ||
|
|
b4879a342c | ||
|
|
5070146061 | ||
|
|
d28c3e15ad | ||
|
|
4b496be520 | ||
|
|
979288a590 | ||
|
|
9365d4f3c4 | ||
|
|
e1daf8aae7 | ||
|
|
8a57dbf608 | ||
|
|
ac9b76c651 | ||
|
|
139526ef2d | ||
|
|
d1e7e6c63a | ||
|
|
29b574bf94 | ||
|
|
7e4248bbca | ||
|
|
fee267dc2e | ||
|
|
9864130435 | ||
|
|
c9770cbd03 | ||
|
|
ed6a40ba41 | ||
|
|
3c22cdf90c | ||
|
|
e55749b897 | ||
|
|
88cd87a5f7 | ||
|
|
244fdc72bc | ||
|
|
b9d163a71d | ||
|
|
61ee467dee | ||
|
|
7c0829d553 | ||
|
|
68eb4c6965 | ||
|
|
6d4015f61b | ||
|
|
d89513b7e0 | ||
|
|
a159594a45 | ||
|
|
78c62ad819 | ||
|
|
b3247d5a3b | ||
|
|
91f920e14c | ||
|
|
ce90d5cd0a |
@@ -21,3 +21,19 @@
|
||||
^TODO-promises.md$
|
||||
^manualtests$
|
||||
^\.github$
|
||||
|
||||
^\.yarn$
|
||||
^\.vscode$
|
||||
^\.madgerc$
|
||||
^\.prettierrc\.yml$
|
||||
^babel\.config\.json$
|
||||
^jest\.config\.js$
|
||||
^package\.json$
|
||||
^tsconfig\.json$
|
||||
^yarn\.lock$
|
||||
^node_modules$
|
||||
^coverage$
|
||||
^.ignore$
|
||||
^\.browserslistrc$
|
||||
^\.eslintrc\.yml$
|
||||
^\.yarnrc\.yml$
|
||||
|
||||
108
.eslintrc.yml
Normal file
108
.eslintrc.yml
Normal file
@@ -0,0 +1,108 @@
|
||||
root: true
|
||||
env:
|
||||
browser: true
|
||||
es6: true
|
||||
extends:
|
||||
- 'eslint:recommended'
|
||||
- 'plugin:@typescript-eslint/recommended'
|
||||
- 'plugin:jest/recommended'
|
||||
- 'prettier/@typescript-eslint'
|
||||
- 'plugin:prettier/recommended'
|
||||
- 'plugin:jest-dom/recommended'
|
||||
globals:
|
||||
Atomics: readonly
|
||||
SharedArrayBuffer: readonly
|
||||
parser: '@typescript-eslint/parser'
|
||||
parserOptions:
|
||||
ecmaVersion: 2018
|
||||
sourceType: module
|
||||
plugins:
|
||||
- '@typescript-eslint'
|
||||
- prettier
|
||||
- jest-dom
|
||||
- unicorn
|
||||
rules:
|
||||
"@typescript-eslint/explicit-function-return-type":
|
||||
- off
|
||||
"@typescript-eslint/no-explicit-any":
|
||||
- off
|
||||
"@typescript-eslint/explicit-module-boundary-types":
|
||||
- error
|
||||
|
||||
default-case:
|
||||
- error
|
||||
indent:
|
||||
- error
|
||||
- 2
|
||||
- SwitchCase: 1
|
||||
linebreak-style:
|
||||
- error
|
||||
- unix
|
||||
quotes:
|
||||
- error
|
||||
- double
|
||||
- avoid-escape
|
||||
semi:
|
||||
- error
|
||||
- always
|
||||
newline-after-var:
|
||||
- error
|
||||
- always
|
||||
dot-location:
|
||||
- error
|
||||
- property
|
||||
|
||||
camelcase:
|
||||
# - error
|
||||
- "off"
|
||||
|
||||
unicorn/filename-case:
|
||||
- error
|
||||
- case: camelCase
|
||||
|
||||
"@typescript-eslint/array-type":
|
||||
- error
|
||||
- default: array-simple
|
||||
readonly: array-simple
|
||||
"@typescript-eslint/consistent-indexed-object-style":
|
||||
- error
|
||||
- index-signature
|
||||
|
||||
"@typescript-eslint/sort-type-union-intersection-members":
|
||||
- error
|
||||
|
||||
"@typescript-eslint/consistent-type-imports":
|
||||
- error
|
||||
"@typescript-eslint/naming-convention":
|
||||
- error
|
||||
|
||||
- selector: default
|
||||
format: [camelCase]
|
||||
|
||||
- selector: method
|
||||
modifiers: [private]
|
||||
format: [camelCase]
|
||||
leadingUnderscore: require
|
||||
- selector: method
|
||||
modifiers: [protected]
|
||||
format: [camelCase]
|
||||
leadingUnderscore: require
|
||||
|
||||
- selector: variable
|
||||
format: [camelCase]
|
||||
trailingUnderscore: forbid
|
||||
leadingUnderscore: forbid
|
||||
|
||||
- selector: parameter
|
||||
format: [camelCase]
|
||||
trailingUnderscore: allow
|
||||
leadingUnderscore: forbid
|
||||
|
||||
- selector: [enum, enumMember]
|
||||
format: [PascalCase]
|
||||
|
||||
- selector: typeLike
|
||||
format: [PascalCase]
|
||||
custom:
|
||||
regex: "(t|T)ype$"
|
||||
match: false
|
||||
2
.gitattributes
vendored
2
.gitattributes
vendored
@@ -1,4 +1,6 @@
|
||||
/NEWS merge=union
|
||||
/inst/www/shared/shiny.js -merge -diff
|
||||
/inst/www/shared/shiny-*.js -merge -diff
|
||||
/inst/www/shared/shiny*.css -merge -diff
|
||||
*.min.js -merge -diff
|
||||
*.js.map -merge -diff
|
||||
|
||||
127
.github/workflows/R-CMD-check.yaml
vendored
127
.github/workflows/R-CMD-check.yaml
vendored
@@ -1,37 +1,73 @@
|
||||
name: R-CMD-check
|
||||
# NOTE: This workflow is overkill for most R packages
|
||||
# check-standard.yaml is likely a better choice
|
||||
# usethis::use_github_action("check-standard") will install it.
|
||||
#
|
||||
# For help debugging build failures open an issue on the RStudio community with the 'github-actions' tag.
|
||||
# https://community.rstudio.com/new-topic?category=Package%20development&tags=github-actions
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- master
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
- master
|
||||
- rc-v**
|
||||
|
||||
name: R-CMD-check
|
||||
|
||||
jobs:
|
||||
|
||||
rversions:
|
||||
name: R Versions
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
devel: ${{ steps.devel.outputs.installed-r-version }}
|
||||
release: ${{ steps.release.outputs.installed-r-version }}
|
||||
oldrel1: ${{ steps.oldrel1.outputs.installed-r-version }}
|
||||
oldrel2: ${{ steps.oldrel2.outputs.installed-r-version }}
|
||||
oldrel3: ${{ steps.oldrel3.outputs.installed-r-version }}
|
||||
oldrel4: ${{ steps.oldrel4.outputs.installed-r-version }}
|
||||
steps:
|
||||
- { name: devel, uses: r-lib/actions/setup-r@master, id: devel, with: { r-version: devel, install-r: false }}
|
||||
- { name: release, uses: r-lib/actions/setup-r@master, id: release, with: { r-version: release, install-r: false }}
|
||||
- { name: oldrel/1, uses: r-lib/actions/setup-r@master, id: oldrel1, with: { r-version: oldrel/1, install-r: false }}
|
||||
- { name: oldrel/2, uses: r-lib/actions/setup-r@master, id: oldrel2, with: { r-version: oldrel/2, install-r: false }}
|
||||
- { name: oldrel/3, uses: r-lib/actions/setup-r@master, id: oldrel3, with: { r-version: oldrel/3, install-r: false }}
|
||||
- { name: oldrel/4, uses: r-lib/actions/setup-r@master, id: oldrel4, with: { r-version: oldrel/4, install-r: false }}
|
||||
|
||||
|
||||
R-CMD-check:
|
||||
runs-on: ${{ matrix.config.os }}
|
||||
|
||||
name: ${{ matrix.config.os }} (${{ matrix.config.r }})
|
||||
|
||||
needs:
|
||||
- rversions
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
config:
|
||||
- {os: macOS-latest, r: 'devel'}
|
||||
- {os: macOS-latest, r: '4.0'}
|
||||
- {os: windows-latest, r: '4.0'}
|
||||
- {os: ubuntu-16.04, r: '4.0', rspm: "https://packagemanager.rstudio.com/cran/__linux__/xenial/latest"}
|
||||
- {os: ubuntu-16.04, r: '3.6', rspm: "https://packagemanager.rstudio.com/cran/__linux__/xenial/latest"}
|
||||
- {os: ubuntu-16.04, r: '3.5', rspm: "https://packagemanager.rstudio.com/cran/__linux__/xenial/latest"}
|
||||
- {os: ubuntu-16.04, r: '3.4', rspm: "https://packagemanager.rstudio.com/cran/__linux__/xenial/latest"}
|
||||
- {os: ubuntu-16.04, r: '3.3', rspm: "https://packagemanager.rstudio.com/cran/__linux__/xenial/latest"}
|
||||
- {os: macOS-latest, r: '${{ needs.rversions.outputs.release }}'}
|
||||
- {os: windows-latest, r: '${{ needs.rversions.outputs.release }}'}
|
||||
- {os: windows-latest, r: '3.6'}
|
||||
|
||||
- {os: ubuntu-20.04, r: '${{ needs.rversions.outputs.devel }}', http-user-agent: "release" }
|
||||
- {os: ubuntu-20.04, r: '${{ needs.rversions.outputs.release }}'}
|
||||
- {os: ubuntu-20.04, r: '${{ needs.rversions.outputs.oldrel1 }}'}
|
||||
- {os: ubuntu-20.04, r: '${{ needs.rversions.outputs.oldrel2 }}'}
|
||||
- {os: ubuntu-20.04, r: '${{ needs.rversions.outputs.oldrel3 }}'}
|
||||
- {os: ubuntu-20.04, r: '${{ needs.rversions.outputs.oldrel4 }}'}
|
||||
|
||||
- {os: ubuntu-18.04, r: '${{ needs.rversions.outputs.devel }}', http-user-agent: "release" }
|
||||
- {os: ubuntu-18.04, r: '${{ needs.rversions.outputs.release }}'}
|
||||
- {os: ubuntu-18.04, r: '${{ needs.rversions.outputs.oldrel1 }}'}
|
||||
- {os: ubuntu-18.04, r: '${{ needs.rversions.outputs.oldrel2 }}'}
|
||||
- {os: ubuntu-18.04, r: '${{ needs.rversions.outputs.oldrel3 }}'}
|
||||
- {os: ubuntu-18.04, r: '${{ needs.rversions.outputs.oldrel4 }}'}
|
||||
|
||||
env:
|
||||
_R_CHECK_FORCE_SUGGESTS_: false
|
||||
R_REMOTES_NO_ERRORS_FROM_WARNINGS: true
|
||||
RSPM: ${{ matrix.config.rspm }}
|
||||
GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
steps:
|
||||
@@ -44,38 +80,18 @@ jobs:
|
||||
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- uses: r-lib/actions/setup-r@master
|
||||
- uses: r-lib/actions/setup-r@v1
|
||||
id: install-r
|
||||
with:
|
||||
r-version: ${{ matrix.config.r }}
|
||||
http-user-agent: ${{ matrix.config.http-user-agent }}
|
||||
use-public-rspm: true
|
||||
|
||||
- uses: r-lib/actions/setup-pandoc@master
|
||||
- uses: r-lib/actions/setup-pandoc@v1
|
||||
|
||||
- name: Install pak and query dependencies
|
||||
shell: Rscript {0}
|
||||
run: |
|
||||
install.packages("pak", repos = "https://r-lib.github.io/p/pak/dev/")
|
||||
saveRDS(pak::pkg_deps_tree("local::.", dependencies = TRUE), ".github/r-depends.rds")
|
||||
|
||||
- name: Cache R packages
|
||||
uses: actions/cache@v2
|
||||
- uses: r-lib/actions/setup-r-dependencies@v1
|
||||
with:
|
||||
path: ${{ env.R_LIBS_USER }}
|
||||
key: ${{ matrix.config.os }}-${{ steps.install-r.outputs.installed-r-version }}-1-${{ hashFiles('.github/r-depends.rds') }}
|
||||
restore-keys: ${{ matrix.config.os }}-${{ steps.install-r.outputs.installed-r-version }}-1-
|
||||
|
||||
- name: Install system dependencies
|
||||
if: runner.os == 'Linux'
|
||||
shell: Rscript {0}
|
||||
run: |
|
||||
pak::local_system_requirements(execute = TRUE)
|
||||
|
||||
# https://stackoverflow.com/a/66568545/591574
|
||||
#> fatal error: 'X11/Intrinsic.h' file not found
|
||||
- name: Install Cairo macOS R devel dependency
|
||||
if: runner.os == 'macOS' && matrix.config.r == 'devel'
|
||||
run: |
|
||||
brew install libxt
|
||||
extra-packages: rcmdcheck
|
||||
|
||||
# xquartz and cairo are needed for Cairo package.
|
||||
# harfbuzz and fribidi are needed for textshaping package.
|
||||
@@ -86,20 +102,6 @@ jobs:
|
||||
brew install cairo
|
||||
brew install harfbuzz fribidi
|
||||
|
||||
# Use a shorter temp directory for pak installations, due to filename
|
||||
# length issues on Windows. https://github.com/r-lib/pak/issues/252
|
||||
- name: Windows temp dir
|
||||
if: runner.os == 'Windows'
|
||||
run: |
|
||||
New-Item -Path "C:\" -Name "tmp" -ItemType Directory
|
||||
echo "TMPDIR=c:\tmp" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
pak::local_install_dev_deps(upgrade = TRUE)
|
||||
pak::pkg_install("rcmdcheck")
|
||||
shell: Rscript {0}
|
||||
|
||||
- name: Find PhantomJS path
|
||||
id: phantomjs
|
||||
run: |
|
||||
@@ -115,18 +117,13 @@ jobs:
|
||||
Rscript
|
||||
-e "if (!shinytest::dependenciesInstalled()) shinytest::installDependencies()"
|
||||
|
||||
- name: Session info
|
||||
run: |
|
||||
options(width = 100)
|
||||
pkgs <- installed.packages()[, "Package"]
|
||||
sessioninfo::session_info(pkgs, include_base = TRUE)
|
||||
shell: Rscript {0}
|
||||
|
||||
- name: Check
|
||||
env:
|
||||
_R_CHECK_CRAN_INCOMING_: false
|
||||
_R_CHECK_FORCE_SUGGESTS_: ${{ matrix.config.r != 'devel' }}
|
||||
run: rcmdcheck::rcmdcheck(args = c("--no-manual", "--as-cran"), error_on = "warning", check_dir = "check")
|
||||
run: |
|
||||
options(crayon.enabled = TRUE)
|
||||
rcmdcheck::rcmdcheck(args = c("--no-manual", "--as-cran"), error_on = "warning", check_dir = "check")
|
||||
shell: Rscript {0}
|
||||
|
||||
- name: Show testthat output
|
||||
@@ -136,15 +133,7 @@ jobs:
|
||||
|
||||
- name: Upload check results
|
||||
if: failure()
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@main
|
||||
with:
|
||||
name: ${{ matrix.config.os }}-r${{ matrix.config.r }}-results
|
||||
path: check
|
||||
|
||||
- name: Fix path for Windows caching
|
||||
if: runner.os == 'Windows'
|
||||
# This is needed because if you use the default tar at this stage,
|
||||
# C:/Rtools/bin/tar.exe, it will say that it can't find gzip.exe. So
|
||||
# we'll just set the path so that the original tar that would be
|
||||
# found, will be found.
|
||||
run: echo "C:/Program Files/Git/usr/bin" >> $GITHUB_PATH
|
||||
|
||||
40
.github/workflows/rituals.yaml
vendored
40
.github/workflows/rituals.yaml
vendored
@@ -12,15 +12,14 @@ name: Rituals
|
||||
jobs:
|
||||
rituals:
|
||||
name: Rituals
|
||||
# if: false
|
||||
runs-on: ${{ matrix.config.os }}
|
||||
|
||||
runs-on: ${{ matrix.config.os }}
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
config:
|
||||
- { os: ubuntu-16.04, r: '4.0', node: "14.x", rspm: "https://packagemanager.rstudio.com/all/__linux__/xenial/latest"}
|
||||
- { os: ubuntu-20.04, r: 'release', node: "14.x", rspm: "https://packagemanager.rstudio.com/all/__linux__/focal/latest"}
|
||||
|
||||
env:
|
||||
R_REMOTES_NO_ERRORS_FROM_WARNINGS: true
|
||||
@@ -71,13 +70,12 @@ jobs:
|
||||
shell: Rscript {0}
|
||||
run: |
|
||||
pak::local_install_dev_deps(upgrade = TRUE)
|
||||
pak::pkg_install("sessioninfo")
|
||||
pak::pkg_install("devtools")
|
||||
|
||||
- name: Session info
|
||||
shell: Rscript {0}
|
||||
run: |
|
||||
options(width = 100)
|
||||
pak::pkg_install("sessioninfo")
|
||||
pkgs <- installed.packages()[, "Package"]
|
||||
sessioninfo::session_info(pkgs, include_base = TRUE)
|
||||
|
||||
@@ -100,6 +98,7 @@ jobs:
|
||||
|
||||
- name: Document
|
||||
run: |
|
||||
Rscript -e 'pak::pkg_install("devtools")'
|
||||
Rscript -e 'devtools::document()'
|
||||
git add man/\* NAMESPACE
|
||||
git commit -m 'Document (GitHub Actions)' || echo "No documentation changes to commit"
|
||||
@@ -123,14 +122,24 @@ jobs:
|
||||
key: ${{ matrix.config.os }}-${{ matrix.config.node }}-yarn-${{ hashFiles('**/yarn.lock') }}
|
||||
restore-keys: |
|
||||
${{ matrix.config.os }}-${{ matrix.config.node }}-yarn-
|
||||
|
||||
- name: Sync DESCRIPTION and package.json versions
|
||||
run: |
|
||||
Rscript -e 'pak::pkg_install("jsonlite")'
|
||||
Rscript -e 'pkg <- jsonlite::read_json("package.json", simplifyVector = TRUE)' \
|
||||
-e 'version <- as.list(read.dcf("DESCRIPTION")[1,])$Version' \
|
||||
-e 'pkg$version <- gsub("^(\\d+).(\\d+).(\\d+).(.+)$", "\\1.\\2.\\3-alpha.\\4", version)' \
|
||||
-e 'pkg$files <- as.list(pkg$files)' \
|
||||
-e 'jsonlite::write_json(pkg, path = "package.json", pretty = TRUE, auto_unbox = TRUE)'
|
||||
git add package.json && git commit -m 'sync package version (GitHub Actions)' || echo "No version changes to commit"
|
||||
- name: Build JS
|
||||
run: |
|
||||
cd srcts
|
||||
tree src
|
||||
tree srcts
|
||||
rm -r srcts/types
|
||||
yarn install --immutable && yarn build
|
||||
git add ./src && git commit -m 'yarn lint (GitHub Actions)' || echo "No yarn lint changes to commit"
|
||||
git add ./src_d && git commit -m 'yarn tsc (GitHub Actions)' || echo "No type definition changes to commit"
|
||||
git add ../inst && git commit -m 'yarn build (GitHub Actions)' || echo "No yarn build changes to commit"
|
||||
git add ./srcts/src && git commit -m 'yarn lint (GitHub Actions)' || echo "No yarn lint changes to commit"
|
||||
git add ./srcts/types && git commit -m 'yarn tsc (GitHub Actions)' || echo "No type definition changes to commit"
|
||||
git add ./inst && git commit -m 'yarn build (GitHub Actions)' || echo "No yarn build changes to commit"
|
||||
if [ -n "$(git status --porcelain)" ]
|
||||
then
|
||||
git status --porcelain
|
||||
@@ -145,13 +154,18 @@ jobs:
|
||||
if: github.event_name == 'pull_request'
|
||||
with:
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Git Push (MASTER)
|
||||
- name: Verify no un-pushed commits (MASTER)
|
||||
if: github.event_name == 'push'
|
||||
run: |
|
||||
git push https://${{github.actor}}:${{secrets.GITHUB_TOKEN}}@github.com/${{github.repository}}.git HEAD:${{ github.ref }} || echo "No changes to push"
|
||||
# Can't push to a protected branch
|
||||
if [ -n "`git cherry origin/master`" ]; then
|
||||
echo "Un-pushed commits:"
|
||||
git cherry -v origin/master
|
||||
echo "\nCan not push to a protected branch. Exiting"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Execute after pushing, as no updated files will be produced
|
||||
- name: Test TypeScript code
|
||||
run: |
|
||||
cd srcts
|
||||
yarn test
|
||||
|
||||
13
.gitignore
vendored
13
.gitignore
vendored
@@ -11,5 +11,18 @@ README.html
|
||||
.*.Rnb.cached
|
||||
tools/yarn-error.log
|
||||
|
||||
# TypeScript / yarn
|
||||
/node_modules/
|
||||
.cache
|
||||
.yarn/*
|
||||
!.yarn/releases
|
||||
!.yarn/plugins
|
||||
!.yarn/sdks
|
||||
!.yarn/versions
|
||||
.pnp.*
|
||||
coverage/
|
||||
madge.svg
|
||||
|
||||
|
||||
# GHA remotes installation
|
||||
.github/r-depends.rds
|
||||
|
||||
12
DESCRIPTION
12
DESCRIPTION
@@ -1,7 +1,7 @@
|
||||
Package: shiny
|
||||
Type: Package
|
||||
Title: Web Application Framework for R
|
||||
Version: 1.6.0.9022
|
||||
Version: 1.7.0
|
||||
Authors@R: c(
|
||||
person("Winston", "Chang", role = c("aut", "cre"), email = "winston@rstudio.com", comment = c(ORCID = "0000-0002-1576-2126")),
|
||||
person("Joe", "Cheng", role = "aut", email = "joe@rstudio.com"),
|
||||
@@ -79,7 +79,7 @@ Imports:
|
||||
jsonlite (>= 0.9.16),
|
||||
xtable,
|
||||
fontawesome (>= 0.2.1),
|
||||
htmltools (>= 0.5.1.9006),
|
||||
htmltools (>= 0.5.2),
|
||||
R6 (>= 2.0),
|
||||
sourcetools,
|
||||
later (>= 1.0.0),
|
||||
@@ -91,7 +91,7 @@ Imports:
|
||||
withr,
|
||||
commonmark (>= 1.7),
|
||||
glue (>= 1.3.2),
|
||||
bslib (>= 0.2.5.9002),
|
||||
bslib (>= 0.3.0),
|
||||
cachem,
|
||||
ellipsis,
|
||||
lifecycle (>= 0.2.0)
|
||||
@@ -112,10 +112,6 @@ Suggests:
|
||||
ragg,
|
||||
showtext,
|
||||
sass
|
||||
Remotes:
|
||||
r-lib/rlang,
|
||||
rstudio/bslib,
|
||||
rstudio/htmltools
|
||||
URL: https://shiny.rstudio.com/
|
||||
BugReports: https://github.com/rstudio/shiny/issues
|
||||
Collate:
|
||||
@@ -205,7 +201,7 @@ Collate:
|
||||
'version_selectize.R'
|
||||
'version_strftime.R'
|
||||
'viewer.R'
|
||||
RoxygenNote: 7.1.1
|
||||
RoxygenNote: 7.1.2
|
||||
Encoding: UTF-8
|
||||
Roxygen: list(markdown = TRUE)
|
||||
RdMacros: lifecycle
|
||||
|
||||
@@ -378,6 +378,7 @@ importFrom(htmltools,tags)
|
||||
importFrom(htmltools,validateCssUnit)
|
||||
importFrom(htmltools,withTags)
|
||||
importFrom(lifecycle,deprecated)
|
||||
importFrom(lifecycle,is_present)
|
||||
importFrom(promises,"%...!%")
|
||||
importFrom(promises,"%...>%")
|
||||
importFrom(promises,as.promise)
|
||||
@@ -386,14 +387,18 @@ importFrom(promises,promise)
|
||||
importFrom(promises,promise_reject)
|
||||
importFrom(promises,promise_resolve)
|
||||
importFrom(rlang,"%||%")
|
||||
importFrom(rlang,"fn_body<-")
|
||||
importFrom(rlang,"fn_fmls<-")
|
||||
importFrom(rlang,as_function)
|
||||
importFrom(rlang,as_quosure)
|
||||
importFrom(rlang,enexpr)
|
||||
importFrom(rlang,enquo)
|
||||
importFrom(rlang,enquo0)
|
||||
importFrom(rlang,enquos)
|
||||
importFrom(rlang,enquos0)
|
||||
importFrom(rlang,eval_tidy)
|
||||
importFrom(rlang,expr)
|
||||
importFrom(rlang,fn_body)
|
||||
importFrom(rlang,get_env)
|
||||
importFrom(rlang,get_expr)
|
||||
importFrom(rlang,inject)
|
||||
@@ -408,4 +413,8 @@ importFrom(rlang,new_function)
|
||||
importFrom(rlang,new_quosure)
|
||||
importFrom(rlang,pairlist2)
|
||||
importFrom(rlang,quo)
|
||||
importFrom(rlang,quo_get_expr)
|
||||
importFrom(rlang,quo_is_missing)
|
||||
importFrom(rlang,quo_set_env)
|
||||
importFrom(rlang,quo_set_expr)
|
||||
importFrom(rlang,zap_srcref)
|
||||
|
||||
20
NEWS.md
20
NEWS.md
@@ -1,4 +1,4 @@
|
||||
shiny 1.6.0.9000
|
||||
shiny 1.7.0
|
||||
================
|
||||
|
||||
## Full changelog
|
||||
@@ -7,6 +7,8 @@ shiny 1.6.0.9000
|
||||
|
||||
* The `format` and `locale` arguments to `sliderInput()` have been removed. They have been deprecated since 0.10.2.2 (released on 2014-12-08).
|
||||
|
||||
* Closed #3403: `insertTab()`'s `position` parameter now defaults to `"after"` instead of `"before"`. This has the benefit of allowing us to fix a bug in positioning when `target = NULL`, but has the drawback of changing the default behavior when `target` is not `NULL`. (#3404)
|
||||
|
||||
### New features and improvements
|
||||
|
||||
* Bootstrap 5 support. (#3410 and rstudio/bslib#304)
|
||||
@@ -15,13 +17,17 @@ shiny 1.6.0.9000
|
||||
* Closed #3322, #3313, #1823, #3321, #3320, #1928, and #2310: Various improvements to `navbarPage()`, `tabsetPanel()`, `tabPanel()`, `navbarMenu()`, etc. Also, these functions are now powered by the `{bslib}` package's new `nav()` API (consider using `{bslib}`'s API to create better looking and more fully featured navs). (#3388)
|
||||
|
||||
* All uses of `list(...)` have been replaced with `rlang::list2(...)`. This means that you can use trailing `,` without error and use rlang's `!!!` operator to "splice" a list of argument values into `...`. We think this'll be particularly useful for passing a list of `tabPanel()` to their consumers (i.e., `tabsetPanel()`, `navbarPage()`, etc). For example, `tabs <- list(tabPanel("A", "a"), tabPanel("B", "b")); navbarPage(!!!tabs)`. (#3315 and #3328)
|
||||
|
||||
|
||||
* `installExprFunction()` and `exprToFunction()` are now able to handle quosures when `quoted = TRUE`. So `render`-functions which call these functions (such as with `htmlwidgets`) can now understand quosures. Users can also use `rlang::inject()` to unquote a quosure for evaluation. This also means that `render` function no longer need `env` and `quoted` parameters; that information can be embedded into a quosure which is then passed to the `render` function. Better documentation was added for how to create `render` functions. (#3472)
|
||||
|
||||
* `icon(lib="fontawesome")` is now powered by the `{fontawesome}` package, which will make it easier to use the latest FA icons in the future (by updating the `{fontawesome}` package). (#3302)
|
||||
|
||||
* Closed #3397: `renderPlot()` new uses `ggplot2::get_alt_text()` to inform an `alt` text default (for `{ggplot2}` plots). (#3398)
|
||||
|
||||
* `modalDialog()` gains support for `size = "xl"`. (#3410)
|
||||
|
||||
* Addressed #2521: Updated the list of TCP ports that will be rejected by default in runapp.R, adding 5060, 5061 and 6566. Added documentation describing the port range (3000:8000) and which ports are rejected. (#3456)
|
||||
|
||||
### Other improvements
|
||||
|
||||
* Shiny's core JavaScript code was converted to TypeScript. For the latest development information, please see the [README.md in `./srcts`](https://github.com/rstudio/shiny/tree/master/srcts). (#3296)
|
||||
@@ -36,10 +42,14 @@ shiny 1.6.0.9000
|
||||
|
||||
* Closed #3345: Shiny now correctly renders `htmltools::htmlDependency()`(s) with a `list()` of `script` attributes when used in a dynamic UI context. This fairly new `htmlDependency()` feature was added in `{htmltools}` v0.5.1. (#3395)
|
||||
|
||||
* Closed #3374: `quoToFunction()` now works correctly with nested quosures; and as a result, quasi-quotation with rendering function (e.g., `renderPrint()`, `renderPlot()`, etc) now works as expected with nested quosures. (#3373)
|
||||
* Fixed [#2666](https://github.com/rstudio/shiny/issues/2666) and [#2670](https://github.com/rstudio/shiny/issues/2670): `nearPoints()` and `brushedPoints()` weren't properly account for missing values (#2666 was introduced in v1.4.0). ([#2668](https://github.com/rstudio/shiny/pull/2668))
|
||||
|
||||
* Closed #3374: `quoToFunction()` now works correctly with nested quosures; and as a result, quasi-quotation with rendering function (e.g., `renderPrint()`, `renderPlot()`, etc) now works as expected with nested quosures. (#3373)
|
||||
|
||||
* Exported `register_devmode_option()`. This method was described in the documentation for `devmode()` but was never exported. See `?devmode()` for more details on how to register Shiny Developer options using `register_devmode_option()`. (#3364)
|
||||
|
||||
* Closed #3484: In the RStudio IDE on Mac 11.5, selected checkboxes and radio buttons were not visible. (#3485)
|
||||
|
||||
### Library updates
|
||||
|
||||
* Closed #3286: Updated to Font-Awesome 5.15.2. (#3288)
|
||||
@@ -53,7 +63,7 @@ This release focuses on improvements in three main areas:
|
||||
|
||||
1. Better theming (and Bootstrap 4) support:
|
||||
* The `theme` argument of `fluidPage()`, `navbarPage()`, and `bootstrapPage()` all now understand `bslib::bs_theme()` objects, which can be used to opt-into Bootstrap 4, use any Bootswatch theme, and/or implement custom themes without writing any CSS.
|
||||
* The `session` object now includes `$setCurrentTheme()` and `$getCurrentTheme()` methods to dynamically update (or obtain) the page's `theme` after initial load, which is useful for things such as [adding a dark mode switch to an app](https://rstudio.github.io/bslib/articles/theming.html#dynamic-shiny) or some other "real-time" theming tool like `bslib::bs_themer()`.
|
||||
* The `session` object now includes `$setCurrentTheme()` and `$getCurrentTheme()` methods to dynamically update (or obtain) the page's `theme` after initial load, which is useful for things such as [adding a dark mode switch to an app](https://rstudio.github.io/bslib/articles/bslib.html#dynamic) or some other "real-time" theming tool like `bslib::bs_themer()`.
|
||||
* For more details, see [`{bslib}`'s website](https://rstudio.github.io/bslib/)
|
||||
|
||||
2. Caching of `reactive()` and `render*()` (e.g. `renderText()`, `renderTable()`, etc) expressions.
|
||||
@@ -175,7 +185,7 @@ shiny 1.5.0
|
||||
|
||||
* The new `moduleServer` function provides a simpler interface for creating and using modules. (#2773)
|
||||
|
||||
* Resolved #2732: `markdown()` is a new function for writing Markdown with Github extensions directly in Shiny UIs. Markdown rendering is performed by the [commonmark](https://github.com/jeroen/commonmark) package. (#2737)
|
||||
* Resolved #2732: `markdown()` is a new function for writing Markdown with Github extensions directly in Shiny UIs. Markdown rendering is performed by the [commonmark](https://github.com/r-lib/commonmark) package. (#2737)
|
||||
|
||||
* The `getCurrentOutputInfo()` function can now return the background color (`bg`), foreground color (`fg`), `accent` (i.e., hyperlink) color, and `font` information of the output's HTML container. This information is reported by `plotOutput()`, `imageOutput()`, and any other output bindings containing a class of `.shiny-report-theme`. This feature allows developers to style an output's contents based on the container's CSS styling. (#2740)
|
||||
|
||||
|
||||
@@ -255,7 +255,7 @@ utils::globalVariables(".GenericCallEnv", add = TRUE)
|
||||
#' the cache.
|
||||
#'
|
||||
#' You may need to provide a `cacheHint` to [createRenderFunction()] (or
|
||||
#' [htmlwidgets::shinyRenderWidget()], if you've authored an htmlwidget) in
|
||||
#' `htmlwidgets::shinyRenderWidget()`, if you've authored an htmlwidget) in
|
||||
#' order for `bindCache()` to correctly compute a cache key.
|
||||
#'
|
||||
#' The potential problem is a cache collision. Consider the following:
|
||||
@@ -292,11 +292,11 @@ utils::globalVariables(".GenericCallEnv", add = TRUE)
|
||||
#' In some cases, however, the automatic cache hint inference is not
|
||||
#' sufficient, and it is necessary to provide a cache hint. This is true
|
||||
#' for `renderPrint()`. Unlike `renderText()`, it wraps the user-provided
|
||||
#' expression in another function, before passing it to [markRenderFunction()]
|
||||
#' expression in another function, before passing it to [createRenderFunction()]
|
||||
#' (instead of [createRenderFunction()]). Because the user code is wrapped in
|
||||
#' another function, `markRenderFunction()` is not able to automatically
|
||||
#' another function, `createRenderFunction()` is not able to automatically
|
||||
#' extract the user-provided code and use it in the cache key. Instead,
|
||||
#' `renderPrint` calls `markRenderFunction()`, it explicitly passes along a
|
||||
#' `renderPrint` calls `createRenderFunction()`, it explicitly passes along a
|
||||
#' `cacheHint`, which includes a label and the original user expression.
|
||||
#'
|
||||
#' In general, if you need to provide a `cacheHint`, it is best practice to
|
||||
@@ -310,19 +310,19 @@ utils::globalVariables(".GenericCallEnv", add = TRUE)
|
||||
#'
|
||||
#' ```
|
||||
#' renderMyWidget <- function(expr) {
|
||||
#' expr <- substitute(expr)
|
||||
#' q <- rlang::enquo0(expr)
|
||||
#'
|
||||
#' htmlwidgets::shinyRenderWidget(expr,
|
||||
#' htmlwidgets::shinyRenderWidget(
|
||||
#' q,
|
||||
#' myWidgetOutput,
|
||||
#' quoted = TRUE,
|
||||
#' env = parent.frame(),
|
||||
#' cacheHint = list(label = "myWidget", userExpr = expr)
|
||||
#' cacheHint = list(label = "myWidget", userQuo = q)
|
||||
#' )
|
||||
#' }
|
||||
#' ```
|
||||
#'
|
||||
#' If your `render` function sets any internal state, you may find it useful
|
||||
#' in your call to [createRenderFunction()] or [markRenderFunction()] to use
|
||||
#' in your call to [createRenderFunction()] to use
|
||||
#' the `cacheWriteHook` and/or `cacheReadHook` parameters. These hooks are
|
||||
#' functions that run just before the object is stored in the cache, and just
|
||||
#' after the object is retrieved from the cache. They can modify the data
|
||||
@@ -339,8 +339,8 @@ utils::globalVariables(".GenericCallEnv", add = TRUE)
|
||||
#' effects or modify some external state, and they must re-execute each time
|
||||
#' in order to work properly.
|
||||
#'
|
||||
#' For developers of such code, they should call [createRenderFunction()] or
|
||||
#' [markRenderFunction()] with `cacheHint = FALSE`.
|
||||
#' For developers of such code, they should call [createRenderFunction()] (or
|
||||
#' [markRenderFunction()]) with `cacheHint = FALSE`.
|
||||
#'
|
||||
#'
|
||||
#' @section Caching with `renderPlot()`:
|
||||
|
||||
@@ -50,7 +50,7 @@ bootstrapPage <- function(..., title = NULL, theme = NULL, lang = NULL) {
|
||||
# the tagList() contents to avoid breaking user code that makes assumptions
|
||||
# about the return value https://github.com/rstudio/shiny/issues/3235
|
||||
if (is_bs_theme(theme)) {
|
||||
args <- list(bootstrapLib(theme), args)
|
||||
args <- c(bootstrapLib(theme), args)
|
||||
ui <- do.call(tagList, args)
|
||||
} else {
|
||||
ui <- do.call(tagList, args)
|
||||
@@ -82,30 +82,53 @@ getLang <- function(ui) {
|
||||
#' @inheritParams bootstrapPage
|
||||
#' @export
|
||||
bootstrapLib <- function(theme = NULL) {
|
||||
# In the non-bslib case, return static HTML dependencies
|
||||
if (!is_bs_theme(theme)) {
|
||||
return(bootstrapDependency(theme))
|
||||
}
|
||||
# To support static rendering of Bootstrap version dependent markup (e.g.,
|
||||
# tabsetPanel()), setCurrentTheme() at the start of the render (since
|
||||
# bootstrapLib() comes first in bootstrapPage(), all other UI should then know
|
||||
# what version of Bootstrap is being used). Then restore state after render as
|
||||
# long as shiny isn't running (in that case, since setCurrentTheme() uses
|
||||
# shinyOptions(), state will be automatically be restored when the app exits)
|
||||
oldTheme <- NULL
|
||||
tagList(
|
||||
.renderHook = function(x) {
|
||||
oldTheme <<- getCurrentTheme()
|
||||
tagFunction(function() {
|
||||
if (isRunning()) {
|
||||
setCurrentTheme(theme)
|
||||
# For refreshing Bootstrap CSS when session$setCurrentTheme() happens
|
||||
if (isRunning()) registerThemeDependency(bs_theme_deps)
|
||||
attachDependencies(x, bslib::bs_theme_dependencies(theme))
|
||||
},
|
||||
.postRenderHook = function() {
|
||||
if (!isRunning()) setCurrentTheme(oldTheme)
|
||||
NULL
|
||||
}
|
||||
)
|
||||
|
||||
# If we're not compiling Bootstrap Sass (from bslib), return the
|
||||
# static Bootstrap build.
|
||||
if (!is_bs_theme(theme)) {
|
||||
# We'll enter here if `theme` is the path to a .css file, like that
|
||||
# provided by `shinythemes::shinytheme("darkly")`.
|
||||
return(bootstrapDependency(theme))
|
||||
}
|
||||
|
||||
# Make bootstrap Sass available so other tagFunction()s (e.g.,
|
||||
# sliderInput() et al) can resolve their HTML dependencies at render time
|
||||
# using getCurrentTheme(). Note that we're making an implicit assumption
|
||||
# that this tagFunction() executes *before* all other tagFunction()s; but
|
||||
# that should be fine considering that, DOM tree order is preorder,
|
||||
# depth-first traversal, and at least in the bootstrapPage(theme) case, we
|
||||
# have control over the relative ordering.
|
||||
# https://dom.spec.whatwg.org/#concept-tree
|
||||
# https://stackoverflow.com/a/16113998/1583084
|
||||
#
|
||||
# Note also that since this is shinyOptions() (and not options()), the
|
||||
# option is automatically reset when the app (or session) exits
|
||||
if (isRunning()) {
|
||||
registerThemeDependency(bs_theme_deps)
|
||||
|
||||
} else {
|
||||
# Technically, this a potential issue (someone trying to execute/render
|
||||
# bootstrapLib outside of a Shiny app), but it seems that, in that case,
|
||||
# you likely have other problems, since sliderInput() et al. already assume
|
||||
# that Shiny is the one doing the rendering
|
||||
#warning(
|
||||
# "It appears `shiny::bootstrapLib()` was rendered outside of an Shiny ",
|
||||
# "application context, likely by calling `as.tags()`, `as.character()`, ",
|
||||
# "or `print()` directly on `bootstrapLib()` or UI components that may ",
|
||||
# "depend on it (e.g., `fluidPage()`, etc). For 'themable' UI components ",
|
||||
# "(e.g., `sliderInput()`, `selectInput()`, `dateInput()`, etc) to style ",
|
||||
# "themselves based on the Bootstrap theme, make sure `bootstrapLib()` is ",
|
||||
# "provided directly to the UI and that the UI is provided direction to ",
|
||||
# "`shinyApp()` (or `runApp()`)", call. = FALSE
|
||||
#)
|
||||
}
|
||||
|
||||
bslib::bs_theme_dependencies(theme)
|
||||
})
|
||||
}
|
||||
|
||||
# This is defined outside of bootstrapLib() because registerThemeDependency()
|
||||
|
||||
@@ -9,13 +9,19 @@
|
||||
#' @param details Additional information to be added after a new line to the displayed message
|
||||
#' @keywords internal
|
||||
shinyDeprecated <- function(
|
||||
version, what, with = NULL, details = NULL
|
||||
version,
|
||||
what,
|
||||
with = NULL,
|
||||
details = NULL,
|
||||
type = c("deprecated", "superseded")
|
||||
) {
|
||||
if (is_false(getOption("shiny.deprecation.messages"))) {
|
||||
return(invisible())
|
||||
}
|
||||
|
||||
msg <- paste0("`", what, "` is deprecated as of shiny ", version, ".")
|
||||
type <- match.arg(type)
|
||||
|
||||
msg <- paste0("`", what, "` is ", type, " as of shiny ", version, ".")
|
||||
if (!is.null(with)) {
|
||||
msg <- paste0(msg, "\n", "Please use `", with, "` instead.")
|
||||
}
|
||||
@@ -32,13 +38,20 @@ deprecatedEnvQuotedMessage <- function() {
|
||||
if (!in_devmode()) return(invisible())
|
||||
if (is_false(getOption("shiny.deprecation.messages"))) return(invisible())
|
||||
|
||||
# manually
|
||||
# Capture calling function
|
||||
grandparent_call <- sys.call(-2)
|
||||
# Turn language into user friendly string
|
||||
grandparent_txt <- paste0(utils::capture.output({grandparent_call}), collapse = "\n")
|
||||
|
||||
msg <- paste0(
|
||||
"The `env` and `quoted` arguments are deprecated as of shiny 1.6.0.",
|
||||
"The `env` and `quoted` arguments are deprecated as of shiny 1.7.0.",
|
||||
" Please use quosures from `rlang` instead.\n",
|
||||
"See <https://github.com/rstudio/shiny/issues/3108> for more information."
|
||||
"See <https://github.com/rstudio/shiny/issues/3108> for more information.\n",
|
||||
"Function call:\n",
|
||||
grandparent_txt
|
||||
)
|
||||
rlang::inform(message = msg, .frequency = "always", .frequency_id = msg, .file = stderr())
|
||||
# Call less often as users do not have much control over this warning
|
||||
rlang::inform(message = msg, .frequency = "regularly", .frequency_id = msg, .file = stderr())
|
||||
}
|
||||
|
||||
|
||||
@@ -60,7 +73,7 @@ diskCache <- function(
|
||||
logfile = NULL
|
||||
) {
|
||||
shinyDeprecated("1.6.0", "diskCache()", "cachem::cache_disk()")
|
||||
if (lifecycle::is_present(exec_missing)) {
|
||||
if (is_present(exec_missing)) {
|
||||
shinyDeprecated("1.6.0", "diskCache(exec_missing =)")
|
||||
}
|
||||
|
||||
@@ -93,7 +106,7 @@ memoryCache <- function(
|
||||
logfile = NULL)
|
||||
{
|
||||
shinyDeprecated("1.6.0", "diskCache()", "cachem::cache_mem()")
|
||||
if (lifecycle::is_present(exec_missing)) {
|
||||
if (is_present(exec_missing)) {
|
||||
shinyDeprecated("1.6.0", "diskCache(exec_missing =)")
|
||||
}
|
||||
|
||||
|
||||
@@ -512,7 +512,7 @@ MessageLogger = R6Class(
|
||||
return(txt)
|
||||
},
|
||||
singleLine = function(txt) {
|
||||
gsub("[^\\]\\n", "\\\\n", txt)
|
||||
gsub("([^\\])\\n", "\\1\\\\n", txt)
|
||||
},
|
||||
valueStr = function(valueStr) {
|
||||
paste0(
|
||||
|
||||
@@ -20,7 +20,6 @@
|
||||
#' `delay` milliseconds before sending an event.
|
||||
#' @seealso [brushOpts()] for brushing events.
|
||||
#' @export
|
||||
#' @keywords internal
|
||||
clickOpts <- function(id, clip = TRUE) {
|
||||
if (is.null(id))
|
||||
stop("id must not be NULL")
|
||||
|
||||
@@ -267,6 +267,7 @@ nearPoints <- function(df, coordinfo, xvar = NULL, yvar = NULL,
|
||||
stop("nearPoints: `yvar` ('", yvar ,"') not in names of input")
|
||||
|
||||
# Extract data values from the data frame
|
||||
coordinfo <- fortifyDiscreteLimits(coordinfo)
|
||||
x <- asNumber(df[[xvar]], coordinfo$domain$discrete_limits$x)
|
||||
y <- asNumber(df[[yvar]], coordinfo$domain$discrete_limits$y)
|
||||
|
||||
@@ -392,6 +393,7 @@ nearPoints <- function(df, coordinfo, xvar = NULL, yvar = NULL,
|
||||
# an input brush
|
||||
within_brush <- function(vals, brush, var = "x") {
|
||||
var <- match.arg(var, c("x", "y"))
|
||||
brush <- fortifyDiscreteLimits(brush)
|
||||
vals <- asNumber(vals, brush$domain$discrete_limits[[var]])
|
||||
# It's possible for a non-missing data values to not
|
||||
# map to the axis limits, for example:
|
||||
@@ -414,11 +416,43 @@ asNumber <- function(x, levels = NULL) {
|
||||
as.numeric(x)
|
||||
}
|
||||
|
||||
# Ensure the discrete limits/levels of a coordmap received
|
||||
# from the client matches the data structure sent the client.
|
||||
#
|
||||
# When we construct the coordmap (in getGgplotCoordmap()),
|
||||
# we save a character vector which may contain missing values
|
||||
# (e.g., c("a", "b", NA)). When that same character is received
|
||||
# from the client, it runs through decodeMessage() which sets
|
||||
# simplifyVector=FALSE, which means NA are replaced by NULL
|
||||
# (because jsonlite::fromJSON('["a", "b", null]') -> list("a", "b", NULL))
|
||||
#
|
||||
# Thankfully, it doesn't seem like it's meaningful for limits to
|
||||
# contains a NULL in the 1st place, so we simply treat NULL like NA.
|
||||
# For more context, https://github.com/rstudio/shiny/issues/2666
|
||||
fortifyDiscreteLimits <- function(coord) {
|
||||
# Note that discrete_limits$x/y are populated iff
|
||||
# x/y are discrete mappings
|
||||
coord$domain$discrete_limits <- lapply(
|
||||
coord$domain$discrete_limits,
|
||||
function(var) {
|
||||
# if there is an 'explicit' NULL, then the limits are NA
|
||||
if (is.null(var)) return(NA)
|
||||
vapply(var, function(x) {
|
||||
if (is.null(x) || isTRUE(is.na(x))) NA_character_ else x
|
||||
}, character(1))
|
||||
}
|
||||
)
|
||||
coord
|
||||
}
|
||||
|
||||
|
||||
|
||||
# Given a panelvar value and a vector x, return logical vector indicating which
|
||||
# items match the panelvar value. Because the panelvar value is always a
|
||||
# string but the vector could be numeric, it might be necessary to coerce the
|
||||
# panelvar to a number before comparing to the vector.
|
||||
panelMatch <- function(search_value, x) {
|
||||
if (is.null(search_value)) return(is.na(x))
|
||||
if (is.numeric(x)) search_value <- as.numeric(search_value)
|
||||
x == search_value
|
||||
}
|
||||
|
||||
@@ -229,8 +229,11 @@ ionRangeSliderDependencyCSS <- function(theme) {
|
||||
}
|
||||
|
||||
bslib::bs_dependency(
|
||||
input = sass::sass_file(
|
||||
system.file(package = "shiny", "www/shared/ionrangeslider/scss/shiny.scss")
|
||||
input = list(
|
||||
list(accent = "$component-active-bg"),
|
||||
sass::sass_file(
|
||||
system.file(package = "shiny", "www/shared/ionrangeslider/scss/shiny.scss")
|
||||
)
|
||||
),
|
||||
theme = theme,
|
||||
name = "ionRangeSlider",
|
||||
|
||||
@@ -113,7 +113,7 @@
|
||||
#' }
|
||||
#' @export
|
||||
insertTab <- function(inputId, tab, target = NULL,
|
||||
position = c("before", "after"), select = FALSE,
|
||||
position = c("after", "before"), select = FALSE,
|
||||
session = getDefaultReactiveDomain()) {
|
||||
bslib::nav_insert(
|
||||
inputId, tab, target,
|
||||
@@ -137,14 +137,14 @@ insertTab <- function(inputId, tab, target = NULL,
|
||||
#' @export
|
||||
prependTab <- function(inputId, tab, select = FALSE, menuName = NULL,
|
||||
session = getDefaultReactiveDomain()) {
|
||||
bslib::tab_prepend(inputId, tab, menu_title = menuName, select = select, session = session)
|
||||
bslib::nav_prepend(inputId, tab, menu_title = menuName, select = select, session = session)
|
||||
}
|
||||
|
||||
#' @rdname insertTab
|
||||
#' @export
|
||||
appendTab <- function(inputId, tab, select = FALSE, menuName = NULL,
|
||||
session = getDefaultReactiveDomain()) {
|
||||
bslib::tab_append(inputId, tab, menu_title = menuName, select = select, session = session)
|
||||
bslib::nav_append(inputId, tab, menu_title = menuName, select = select, session = session)
|
||||
}
|
||||
|
||||
#' @rdname insertTab
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#' themselves in knitr/rmarkdown documents.
|
||||
#'
|
||||
#' @name knitr_methods
|
||||
#' @keywords internal
|
||||
#' @param x Object to knit_print
|
||||
#' @param ... Additional knit_print arguments
|
||||
NULL
|
||||
|
||||
1
R/map.R
1
R/map.R
@@ -1,4 +1,3 @@
|
||||
#' @importFrom fastmap fastmap
|
||||
Map <- R6Class(
|
||||
'Map',
|
||||
portable = FALSE,
|
||||
|
||||
@@ -178,14 +178,15 @@ modalDialog <- function(..., title = NULL, footer = modalButton("Dismiss"),
|
||||
if (!is.null(footer)) div(class = "modal-footer", footer)
|
||||
)
|
||||
),
|
||||
tags$script(
|
||||
"if (window.bootstrap) {
|
||||
# jQuery plugin doesn't work in Bootstrap 5, but vanilla JS doesn't work in Bootstrap 4 :sob:
|
||||
tags$script(HTML(
|
||||
"if (window.bootstrap && !window.bootstrap.Modal.VERSION.match(/^4\\./)) {
|
||||
var modal = new bootstrap.Modal(document.getElementById('shiny-modal'));
|
||||
modal.show();
|
||||
} else {
|
||||
$('#shiny-modal').modal().focus();
|
||||
}"
|
||||
)
|
||||
))
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
170
R/reactives.R
170
R/reactives.R
@@ -875,8 +875,7 @@ Observable <- R6Class(
|
||||
invisible(.value)
|
||||
},
|
||||
format = function() {
|
||||
label <- sprintf('reactive(%s)', paste(deparse(body(.origFunc)), collapse='\n'))
|
||||
strsplit(label, "\n")[[1]]
|
||||
simpleExprToFunction(fn_body(.origFunc), "reactive")
|
||||
},
|
||||
.updateValue = function() {
|
||||
ctx <- Context$new(.domain, .label, type = 'observable',
|
||||
@@ -945,14 +944,15 @@ Observable <- R6Class(
|
||||
#' See the [Shiny tutorial](https://shiny.rstudio.com/tutorial/) for
|
||||
#' more information about reactive expressions.
|
||||
#'
|
||||
#' @param x For `reactive`, an expression (quoted or unquoted). For
|
||||
#' `is.reactive`, an object to test.
|
||||
#' @param env The parent environment for the reactive expression. By default,
|
||||
#' this is the calling environment, the same as when defining an ordinary
|
||||
#' non-reactive expression.
|
||||
#' @param quoted Is the expression quoted? By default, this is `FALSE`.
|
||||
#' This is useful when you want to use an expression that is stored in a
|
||||
#' variable; to do so, it must be quoted with `quote()`.
|
||||
#' @param x For `is.reactive()`, an object to test. For `reactive()`, an expression. When passing in a [`quo()`]sure with `reactive()`, remember to use [`rlang::inject()`] to distinguish that you are passing in the content of your quosure, not the expression of the quosure.
|
||||
#' @template param-env
|
||||
#' @templateVar x x
|
||||
#' @templateVar env env
|
||||
#' @templateVar quoted quoted
|
||||
#' @template param-quoted
|
||||
#' @templateVar x x
|
||||
#' @templateVar quoted quoted
|
||||
|
||||
#' @param label A label for the reactive expression, useful for debugging.
|
||||
#' @param domain See [domains].
|
||||
#' @param ..stacktraceon Advanced use only. For stack manipulation purposes; see
|
||||
@@ -961,46 +961,56 @@ Observable <- R6Class(
|
||||
#' @return a function, wrapped in a S3 class "reactive"
|
||||
#'
|
||||
#' @examples
|
||||
#' library(rlang)
|
||||
#' values <- reactiveValues(A=1)
|
||||
#'
|
||||
#' reactiveB <- reactive({
|
||||
#' values$A + 1
|
||||
#' })
|
||||
#'
|
||||
#' # Can use quoted expressions
|
||||
#' reactiveC <- reactive(quote({ values$A + 2 }), quoted = TRUE)
|
||||
#'
|
||||
#' # To store expressions for later conversion to reactive, use quote()
|
||||
#' expr_q <- quote({ values$A + 3 })
|
||||
#' reactiveD <- reactive(expr_q, quoted = TRUE)
|
||||
#'
|
||||
#' # View the values from the R console with isolate()
|
||||
#' isolate(reactiveB())
|
||||
#' # 2
|
||||
#'
|
||||
#' # To store expressions for later conversion to reactive, use quote()
|
||||
#' myquo <- rlang::quo(values$A + 2)
|
||||
#' # Unexpected value! Sending a quosure directly will not work as expected.
|
||||
#' reactiveC <- reactive(myquo)
|
||||
#' # We'd hope for `3`, but instead we get the quosure that was supplied.
|
||||
#' isolate(reactiveC())
|
||||
#'
|
||||
#' # Instead, the quosure should be `rlang::inject()`ed
|
||||
#' reactiveD <- rlang::inject(reactive(!!myquo))
|
||||
#' isolate(reactiveD())
|
||||
#' # 3
|
||||
#'
|
||||
#' # (Legacy) Can use quoted expressions
|
||||
#' expr <- quote({ values$A + 3 })
|
||||
#' reactiveE <- reactive(expr, quoted = TRUE)
|
||||
#' isolate(reactiveE())
|
||||
#' # 4
|
||||
#'
|
||||
#' @export
|
||||
reactive <- function(x, env = parent.frame(), quoted = FALSE,
|
||||
reactive <- function(
|
||||
x,
|
||||
env = parent.frame(),
|
||||
quoted = FALSE,
|
||||
...,
|
||||
label = NULL,
|
||||
domain = getDefaultReactiveDomain(),
|
||||
..stacktraceon = TRUE)
|
||||
{
|
||||
..stacktraceon = TRUE
|
||||
) {
|
||||
check_dots_empty()
|
||||
|
||||
x <- get_quosure(x, env, quoted)
|
||||
fun <- as_function(x)
|
||||
# as_function returns a function that takes `...`. We need one that takes no
|
||||
# args.
|
||||
formals(fun) <- list()
|
||||
|
||||
func <- installExprFunction(x, "func", env, quoted, wrappedWithLabel = FALSE)
|
||||
# Attach a label and a reference to the original user source for debugging
|
||||
label <- exprToLabel(get_expr(x), "reactive", label)
|
||||
userExpr <- fn_body(func)
|
||||
label <- exprToLabel(userExpr, "reactive", label)
|
||||
|
||||
o <- Observable$new(fun, label, domain, ..stacktraceon = ..stacktraceon)
|
||||
o <- Observable$new(func, label, domain, ..stacktraceon = ..stacktraceon)
|
||||
structure(
|
||||
o$getValue,
|
||||
observable = o,
|
||||
cacheHint = list(userExpr = zap_srcref(get_expr(x))),
|
||||
cacheHint = list(userExpr = zap_srcref(userExpr)),
|
||||
class = c("reactiveExpr", "reactive", "function")
|
||||
)
|
||||
}
|
||||
@@ -1325,12 +1335,7 @@ Observer <- R6Class(
|
||||
#'
|
||||
#' @param x An expression (quoted or unquoted). Any return value will be
|
||||
#' ignored.
|
||||
#' @param env The parent environment for the reactive expression. By default,
|
||||
#' this is the calling environment, the same as when defining an ordinary
|
||||
#' non-reactive expression.
|
||||
#' @param quoted Is the expression quoted? By default, this is `FALSE`.
|
||||
#' This is useful when you want to use an expression that is stored in a
|
||||
#' variable; to do so, it must be quoted with `quote()`.
|
||||
#' @inheritParams reactive
|
||||
#' @param label A label for the observer, useful for debugging.
|
||||
#' @param suspended If `TRUE`, start the observer in a suspended state. If
|
||||
#' `FALSE` (the default), start in a non-suspended state.
|
||||
@@ -1389,18 +1394,21 @@ Observer <- R6Class(
|
||||
#' print(values$A + 1)
|
||||
#' })
|
||||
#'
|
||||
#' # Can use quoted expressions
|
||||
#' obsC <- observe(quote({ print(values$A + 2) }), quoted = TRUE)
|
||||
#' # To store expressions for later conversion to observe, use rlang::quo()
|
||||
#' myquo <- rlang::quo({ print(values$A + 3) })
|
||||
#' obsC <- rlang::inject(observe(!!myquo))
|
||||
#'
|
||||
#' # To store expressions for later conversion to observe, use quote()
|
||||
#' expr_q <- quote({ print(values$A + 3) })
|
||||
#' obsD <- observe(expr_q, quoted = TRUE)
|
||||
#' # (Legacy) Can use quoted expressions
|
||||
#' obsD <- observe(quote({ print(values$A + 2) }), quoted = TRUE)
|
||||
#'
|
||||
#' # In a normal Shiny app, the web client will trigger flush events. If you
|
||||
#' # are at the console, you can force a flush with flushReact()
|
||||
#' shiny:::flushReact()
|
||||
#' @export
|
||||
observe <- function(x, env = parent.frame(), quoted = FALSE,
|
||||
observe <- function(
|
||||
x,
|
||||
env = parent.frame(),
|
||||
quoted = FALSE,
|
||||
...,
|
||||
label = NULL,
|
||||
suspended = FALSE,
|
||||
@@ -1411,18 +1419,11 @@ observe <- function(x, env = parent.frame(), quoted = FALSE,
|
||||
{
|
||||
check_dots_empty()
|
||||
|
||||
x <- get_quosure(x, env, quoted)
|
||||
fun <- as_function(x)
|
||||
# as_function returns a function that takes `...`. We need one that takes no
|
||||
# args.
|
||||
formals(fun) <- list()
|
||||
|
||||
if (is.null(label)) {
|
||||
label <- sprintf('observe(%s)', paste(deparse(get_expr(x)), collapse='\n'))
|
||||
}
|
||||
func <- installExprFunction(x, "func", env, quoted)
|
||||
label <- funcToLabel(func, "observe", label)
|
||||
|
||||
o <- Observer$new(
|
||||
fun,
|
||||
func,
|
||||
label = label,
|
||||
suspended = suspended,
|
||||
priority = priority,
|
||||
@@ -2144,23 +2145,30 @@ maskReactiveContext <- function(expr) {
|
||||
#' @param valueExpr The expression that produces the return value of the
|
||||
#' `eventReactive`. It will be executed within an [isolate()]
|
||||
#' scope.
|
||||
#' @param event.env The parent environment for `eventExpr`. By default,
|
||||
#' this is the calling environment.
|
||||
#' @param event.quoted Is the `eventExpr` expression quoted? By default,
|
||||
#' this is `FALSE`. This is useful when you want to use an expression
|
||||
#' that is stored in a variable; to do so, it must be quoted with
|
||||
#' `quote()`.
|
||||
#' @param handler.env The parent environment for `handlerExpr`. By default,
|
||||
#' this is the calling environment.
|
||||
#' @param handler.quoted Is the `handlerExpr` expression quoted? By
|
||||
#' default, this is `FALSE`. This is useful when you want to use an
|
||||
#' expression that is stored in a variable; to do so, it must be quoted with
|
||||
#' `quote()`.
|
||||
#' @param value.env The parent environment for `valueExpr`. By default,
|
||||
#' this is the calling environment.
|
||||
#' @param value.quoted Is the `valueExpr` expression quoted? By default,
|
||||
#' this is `FALSE`. This is useful when you want to use an expression
|
||||
#' that is stored in a variable; to do so, it must be quoted with `quote()`.
|
||||
#' @param event.env The parent environment for the reactive expression. By default,
|
||||
#' this is the calling environment, the same as when defining an ordinary
|
||||
#' non-reactive expression. If `eventExpr` is a quosure and `event.quoted` is `TRUE`,
|
||||
#' then `event.env` is ignored.
|
||||
#' @param event.quoted If it is `TRUE`, then the [`quote()`]ed value of `eventExpr`
|
||||
#' will be used when `eventExpr` is evaluated. If `eventExpr` is a quosure and you
|
||||
#' would like to use its expression as a value for `eventExpr`, then you must set
|
||||
#' `event.quoted` to `TRUE`.
|
||||
#' @param handler.env The parent environment for the reactive expression. By default,
|
||||
#' this is the calling environment, the same as when defining an ordinary
|
||||
#' non-reactive expression. If `handlerExpr` is a quosure and `handler.quoted` is `TRUE`,
|
||||
#' then `handler.env` is ignored.
|
||||
#' @param handler.quoted If it is `TRUE`, then the [`quote()`]ed value of `handlerExpr`
|
||||
#' will be used when `handlerExpr` is evaluated. If `handlerExpr` is a quosure and you
|
||||
#' would like to use its expression as a value for `handlerExpr`, then you must set
|
||||
#' `handler.quoted` to `TRUE`.
|
||||
#' @param value.env The parent environment for the reactive expression. By default,
|
||||
#' this is the calling environment, the same as when defining an ordinary
|
||||
#' non-reactive expression. If `valueExpr` is a quosure and `value.quoted` is `TRUE`,
|
||||
#' then `value.env` is ignored.
|
||||
#' @param value.quoted If it is `TRUE`, then the [`quote()`]ed value of `valueExpr`
|
||||
#' will be used when `valueExpr` is evaluated. If `valueExpr` is a quosure and you
|
||||
#' would like to use its expression as a value for `valueExpr`, then you must set
|
||||
#' `value.quoted` to `TRUE`.
|
||||
#' @param label A label for the observer or reactive, useful for debugging.
|
||||
#' @param suspended If `TRUE`, start the observer in a suspended state. If
|
||||
#' `FALSE` (the default), start in a non-suspended state.
|
||||
@@ -2274,15 +2282,13 @@ observeEvent <- function(eventExpr, handlerExpr,
|
||||
{
|
||||
check_dots_empty()
|
||||
|
||||
eventExpr <- get_quosure(eventExpr, event.env, event.quoted)
|
||||
handlerExpr <- get_quosure(handlerExpr, handler.env, handler.quoted)
|
||||
eventQ <- exprToQuo(eventExpr, event.env, event.quoted)
|
||||
handlerQ <- exprToQuo(handlerExpr, handler.env, handler.quoted)
|
||||
|
||||
if (is.null(label)) {
|
||||
label <- sprintf('observeEvent(%s)', paste(deparse(get_expr(eventExpr)), collapse='\n'))
|
||||
}
|
||||
label <- quoToLabel(eventQ, "observeEvent", label)
|
||||
|
||||
handler <- inject(observe(
|
||||
!!handlerExpr,
|
||||
!!handlerQ,
|
||||
label = label,
|
||||
suspended = suspended,
|
||||
priority = priority,
|
||||
@@ -2296,7 +2302,7 @@ observeEvent <- function(eventExpr, handlerExpr,
|
||||
ignoreInit = ignoreInit,
|
||||
once = once,
|
||||
label = label,
|
||||
!!eventExpr,
|
||||
!!eventQ,
|
||||
x = handler
|
||||
))
|
||||
|
||||
@@ -2314,19 +2320,17 @@ eventReactive <- function(eventExpr, valueExpr,
|
||||
{
|
||||
check_dots_empty()
|
||||
|
||||
eventExpr <- get_quosure(eventExpr, event.env, event.quoted)
|
||||
valueExpr <- get_quosure(valueExpr, value.env, value.quoted)
|
||||
eventQ <- exprToQuo(eventExpr, event.env, event.quoted)
|
||||
valueQ <- exprToQuo(valueExpr, value.env, value.quoted)
|
||||
|
||||
if (is.null(label)) {
|
||||
label <- sprintf('eventReactive(%s)', paste(deparse(get_expr(eventExpr)), collapse='\n'))
|
||||
}
|
||||
label <- quoToLabel(eventQ, "eventReactive", label)
|
||||
|
||||
invisible(inject(bindEvent(
|
||||
ignoreNULL = ignoreNULL,
|
||||
ignoreInit = ignoreInit,
|
||||
label = label,
|
||||
!!eventExpr,
|
||||
x = reactive(!!valueExpr, domain = domain, label = label)
|
||||
!!eventQ,
|
||||
x = reactive(!!valueQ, domain = domain, label = label)
|
||||
)))
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
####
|
||||
# Generated by `./tools/updateReexports.R`: do not edit by hand
|
||||
# Please call `source('tools/updateReexports.R') from the root folder to update`
|
||||
# Generated by `./tools/documentation/updateReexports.R`: do not edit by hand
|
||||
# Please call `source('tools/documentation/updateReexports.R')` from the root folder to update`
|
||||
####
|
||||
|
||||
|
||||
@@ -90,17 +90,20 @@ htmltools::em
|
||||
#' @export
|
||||
htmltools::hr
|
||||
|
||||
|
||||
# htmltools tag.Rd -------------------------------------------------------------
|
||||
|
||||
#' @importFrom htmltools tag
|
||||
#' @export
|
||||
htmltools::tag
|
||||
|
||||
|
||||
# htmltools tagList.Rd ---------------------------------------------------------
|
||||
|
||||
#' @importFrom htmltools tagList
|
||||
#' @export
|
||||
htmltools::tagList
|
||||
|
||||
|
||||
# htmltools tagAppendAttributes.Rd ---------------------------------------------
|
||||
|
||||
#' @importFrom htmltools tagAppendAttributes
|
||||
#' @export
|
||||
htmltools::tagAppendAttributes
|
||||
@@ -113,6 +116,9 @@ htmltools::tagHasAttribute
|
||||
#' @export
|
||||
htmltools::tagGetAttribute
|
||||
|
||||
|
||||
# htmltools tagAppendChild.Rd --------------------------------------------------
|
||||
|
||||
#' @importFrom htmltools tagAppendChild
|
||||
#' @export
|
||||
htmltools::tagAppendChild
|
||||
|
||||
@@ -181,7 +181,7 @@
|
||||
#' # At the top of app.R, this set the application-scoped cache to be a disk
|
||||
#' # cache that can be shared among multiple concurrent R processes, and is
|
||||
#' # deleted when the system reboots.
|
||||
#' shinyOptions(cache = cachem::cache_disk(file.path(dirname(tempdir()), "myapp-cache"))
|
||||
#' shinyOptions(cache = cachem::cache_disk(file.path(dirname(tempdir()), "myapp-cache")))
|
||||
#'
|
||||
#' # At the top of app.R, this set the application-scoped cache to be a disk
|
||||
#' # cache that can be shared among multiple concurrent R processes, and
|
||||
|
||||
@@ -46,9 +46,7 @@
|
||||
#' decorative images.
|
||||
#' @param ... Arguments to be passed through to [grDevices::png()].
|
||||
#' These can be used to set the width, height, background color, etc.
|
||||
#' @param env The environment in which to evaluate `expr`.
|
||||
#' @param quoted Is `expr` a quoted expression (with `quote()`)? This
|
||||
#' is useful if you want to save an expression in a variable.
|
||||
#' @inheritParams renderUI
|
||||
#' @param execOnResize If `FALSE` (the default), then when a plot is
|
||||
#' resized, Shiny will *replay* the plot drawing commands with
|
||||
#' [grDevices::replayPlot()] instead of re-executing `expr`.
|
||||
@@ -65,10 +63,13 @@ renderPlot <- function(expr, width = 'auto', height = 'auto', res = 72, ...,
|
||||
execOnResize = FALSE, outputArgs = list()
|
||||
) {
|
||||
|
||||
expr <- get_quosure(expr, env, quoted)
|
||||
# This ..stacktraceon is matched by a ..stacktraceoff.. when plotFunc
|
||||
# is called
|
||||
func <- quoToFunction(expr, "renderPlot", ..stacktraceon = TRUE)
|
||||
func <- installExprFunction(
|
||||
expr, "func", env, quoted,
|
||||
label = "renderPlot",
|
||||
# This ..stacktraceon is matched by a ..stacktraceoff.. when plotFunc
|
||||
# is called
|
||||
..stacktraceon = TRUE
|
||||
)
|
||||
|
||||
args <- list(...)
|
||||
|
||||
@@ -186,7 +187,7 @@ renderPlot <- function(expr, width = 'auto', height = 'auto', res = 72, ...,
|
||||
outputFunc,
|
||||
renderFunc,
|
||||
outputArgs,
|
||||
cacheHint = list(userExpr = get_expr(expr), res = res)
|
||||
cacheHint = list(userExpr = installedFuncExpr(func), res = res)
|
||||
)
|
||||
class(markedFunc) <- c("shiny.renderPlot", class(markedFunc))
|
||||
markedFunc
|
||||
@@ -344,7 +345,7 @@ custom_print.ggplot <- function(x) {
|
||||
|
||||
# Infer alt text description from renderPlot() value
|
||||
# (currently just ggplot2 is supported)
|
||||
getAltText <- function(x, default = "Plot Object") {
|
||||
getAltText <- function(x, default = "Plot object") {
|
||||
# Since, inside renderPlot(), custom_print.ggplot()
|
||||
# overrides print.ggplot, this class indicates a ggplot()
|
||||
if (!inherits(x, "ggplot_build_gtable")) {
|
||||
|
||||
@@ -42,9 +42,7 @@
|
||||
#' (i.e. they either evaluate to `NA` or `NaN`).
|
||||
#' @param ... Arguments to be passed through to [xtable::xtable()]
|
||||
#' and [xtable::print.xtable()].
|
||||
#' @param env The environment in which to evaluate `expr`.
|
||||
#' @param quoted Is `expr` a quoted expression (with `quote()`)?
|
||||
#' This is useful if you want to save an expression in a variable.
|
||||
#' @inheritParams renderUI
|
||||
#' @param outputArgs A list of arguments to be passed through to the
|
||||
#' implicit call to [tableOutput()] when `renderTable` is
|
||||
#' used in an interactive R Markdown document.
|
||||
@@ -74,8 +72,7 @@ renderTable <- function(expr, striped = FALSE, hover = FALSE,
|
||||
env = parent.frame(), quoted = FALSE,
|
||||
outputArgs=list())
|
||||
{
|
||||
expr <- get_quosure(expr, env, quoted)
|
||||
func <- quoToFunction(expr, "renderTable")
|
||||
func <- installExprFunction(expr, "func", env, quoted, label = "renderTable")
|
||||
|
||||
if (!is.function(spacing)) spacing <- match.arg(spacing)
|
||||
|
||||
|
||||
@@ -22,7 +22,10 @@
|
||||
#' @param port The TCP port that the application should listen on. If the
|
||||
#' `port` is not specified, and the `shiny.port` option is set (with
|
||||
#' `options(shiny.port = XX)`), then that port will be used. Otherwise,
|
||||
#' use a random port.
|
||||
#' use a random port between 3000:8000, excluding ports that are blocked
|
||||
#' by Google Chrome for being considered unsafe: 3659, 4045, 5060,
|
||||
#' 5061, 6000, 6566, 6665:6669 and 6697. Up to twenty random
|
||||
#' ports will be tried.
|
||||
#' @param launch.browser If true, the system's default web browser will be
|
||||
#' launched automatically after the app is started. Defaults to true in
|
||||
#' interactive sessions only. This value of this parameter can also be a
|
||||
@@ -301,7 +304,8 @@ runApp <- function(appDir=getwd(),
|
||||
# Reject ports in this range that are considered unsafe by Chrome
|
||||
# http://superuser.com/questions/188058/which-ports-are-considered-unsafe-on-chrome
|
||||
# https://github.com/rstudio/shiny/issues/1784
|
||||
if (!port %in% c(3659, 4045, 6000, 6665:6669, 6697)) {
|
||||
# https://chromium.googlesource.com/chromium/src.git/+/refs/heads/main/net/base/port_util.cc
|
||||
if (!port %in% c(3659, 4045, 5060, 5061, 6000, 6566, 6665:6669, 6697)) {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
#' value. The returned value will be used for the test snapshot.
|
||||
#' @param session A Shiny session object.
|
||||
#'
|
||||
#' @keywords internal
|
||||
#' @export
|
||||
setSerializer <- function(inputId, fun, session = getDefaultReactiveDomain()) {
|
||||
if (is.null(session)) {
|
||||
|
||||
@@ -41,12 +41,12 @@ inputHandlers <- Map$new()
|
||||
#' })
|
||||
#'
|
||||
#' ## On the Javascript side, the associated input binding must have a corresponding getType method:
|
||||
#' getType: function(el) {
|
||||
#' return "mypackage.validint";
|
||||
#' }
|
||||
#' # getType: function(el) {
|
||||
#' # return "mypackage.validint";
|
||||
#' # }
|
||||
#'
|
||||
#' }
|
||||
#' @seealso [removeInputHandler()]
|
||||
#' @seealso [removeInputHandler()] [applyInputHandlers()]
|
||||
#' @export
|
||||
registerInputHandler <- function(type, fun, force=FALSE){
|
||||
if (inputHandlers$containsKey(type) && !force){
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
## usethis namespace: start
|
||||
## usethis namespace: end
|
||||
#' @importFrom lifecycle deprecated
|
||||
#' @importFrom lifecycle deprecated is_present
|
||||
#' @importFrom grDevices dev.set dev.cur
|
||||
#' @importFrom fastmap fastmap
|
||||
#' @importFrom promises %...!%
|
||||
@@ -11,11 +11,13 @@
|
||||
#' promise promise_resolve promise_reject is.promising
|
||||
#' as.promise
|
||||
#' @importFrom rlang
|
||||
#' quo enquo as_function get_expr get_env new_function enquos
|
||||
#' quo enquo enquo0 as_function get_expr get_env new_function enquos
|
||||
#' eval_tidy expr pairlist2 new_quosure enexpr as_quosure is_quosure inject
|
||||
#' quo_set_env quo_set_expr quo_get_expr
|
||||
#' enquos0 zap_srcref %||% is_na
|
||||
#' is_false list2
|
||||
#' missing_arg is_missing maybe_missing
|
||||
#' quo_is_missing fn_fmls<- fn_body fn_body<-
|
||||
#' @importFrom ellipsis
|
||||
#' check_dots_empty check_dots_unnamed
|
||||
#' @import htmltools
|
||||
|
||||
@@ -2,12 +2,23 @@ utils::globalVariables('func', add = TRUE)
|
||||
|
||||
#' Mark a function as a render function
|
||||
#'
|
||||
#' `r lifecycle::badge("superseded")` Please use [`createRenderFunction()`] to
|
||||
#' support async execution. (Shiny 1.1.0)
|
||||
#'
|
||||
#' Should be called by implementers of `renderXXX` functions in order to mark
|
||||
#' their return values as Shiny render functions, and to provide a hint to Shiny
|
||||
#' regarding what UI function is most commonly used with this type of render
|
||||
#' function. This can be used in R Markdown documents to create complete output
|
||||
#' widgets out of just the render function.
|
||||
#'
|
||||
#' Note that it is generally preferable to use [createRenderFunction()] instead
|
||||
#' of `markRenderFunction()`. It essentially wraps up the user-provided
|
||||
#' expression in the `transform` function passed to it, then passes the resulting
|
||||
#' function to `markRenderFunction()`. It also provides a simpler calling
|
||||
#' interface. There may be cases where `markRenderFunction()` must be used instead of
|
||||
#' [createRenderFunction()] -- for example, when the `transform` parameter of
|
||||
#' [createRenderFunction()] is not flexible enough for your needs.
|
||||
#'
|
||||
#' @param uiFunc A function that renders Shiny UI. Must take a single argument:
|
||||
#' an output ID.
|
||||
#' @param renderFunc A function that is suitable for assigning to a Shiny output
|
||||
@@ -37,7 +48,7 @@ utils::globalVariables('func', add = TRUE)
|
||||
#' is able to serve JS and CSS resources.
|
||||
#' @return The `renderFunc` function, with annotations.
|
||||
#'
|
||||
#' @seealso [createRenderFunction()], [quoToFunction()]
|
||||
#' @seealso [createRenderFunction()]
|
||||
#' @export
|
||||
markRenderFunction <- function(
|
||||
uiFunc,
|
||||
@@ -47,6 +58,12 @@ markRenderFunction <- function(
|
||||
cacheWriteHook = NULL,
|
||||
cacheReadHook = NULL
|
||||
) {
|
||||
# (Do not emit warning for superseded code, "since there’s no risk if you keep using it")
|
||||
# # This method is called by the superseding function, createRenderFunction().
|
||||
# if (in_devmode()) {
|
||||
# shinyDeprecated("1.1.0", "markRenderFunction()", "createRenderFunction()")
|
||||
# }
|
||||
|
||||
force(renderFunc)
|
||||
|
||||
# a mutable object that keeps track of whether `useRenderFunction` has been
|
||||
@@ -94,6 +111,7 @@ markRenderFunction <- function(
|
||||
# For everything else, do nothing.
|
||||
cacheHint <- lapply(cacheHint, function(x) {
|
||||
if (is.function(x)) formalsAndBody(x)
|
||||
else if (is_quosure(x)) zap_srcref(quo_get_expr(x))
|
||||
else if (is.language(x)) zap_srcref(x)
|
||||
else x
|
||||
})
|
||||
@@ -133,10 +151,27 @@ print.shiny.render.function <- function(x, ...) {
|
||||
cat_line("<shiny.render.function>")
|
||||
}
|
||||
|
||||
#' Implement render functions
|
||||
#' Implement custom render functions
|
||||
#'
|
||||
#' This function is a wrapper for [markRenderFunction()] which provides support
|
||||
#' for async computation via promises.
|
||||
#' Developer-facing utilities for implementing a custom `renderXXX()` function.
|
||||
#' Before using these utilities directly, consider using the [`htmlwidgets`
|
||||
#' package](http://www.htmlwidgets.org/develop_intro.html) to implement custom
|
||||
#' outputs (i.e., custom `renderXXX()`/`xxxOutput()` functions). That said,
|
||||
#' these utilities can be used more directly if a full-blown htmlwidget isn't
|
||||
#' needed and/or the user-supplied reactive expression needs to be wrapped in
|
||||
#' additional call(s).
|
||||
#'
|
||||
#' To implement a custom `renderXXX()` function, essentially 2 things are needed:
|
||||
#' 1. Capture the user's reactive expression as a function.
|
||||
#' * New `renderXXX()` functions can use `quoToFunction()` for this, but
|
||||
#' already existing `renderXXX()` functions that contain `env` and `quoted`
|
||||
#' parameters may want to continue using `installExprFunction()` for better
|
||||
#' legacy support (see examples).
|
||||
#' 2. Flag the resulting function (from 1) as a Shiny rendering function and
|
||||
#' also provide a UI container for displaying the result of the rendering
|
||||
#' function.
|
||||
#' * `createRenderFunction()` is currently recommended (instead of
|
||||
#' [markRenderFunction()]) for this step (see examples).
|
||||
#'
|
||||
#' @param func A function without parameters, that returns user data. If the
|
||||
#' returned value is a promise, then the render function will proceed in async
|
||||
@@ -153,16 +188,24 @@ print.shiny.render.function <- function(x, ...) {
|
||||
#' @return An annotated render function, ready to be assigned to an
|
||||
#' `output` slot.
|
||||
#'
|
||||
#' @seealso [quoToFunction()], [markRenderFunction()].
|
||||
#'
|
||||
#' @examples
|
||||
#' # A very simple render function
|
||||
#' renderTriple <- function(x) {
|
||||
#' x <- substitute(x)
|
||||
#' if (!rlang::is_quosure(x)) {
|
||||
#' x <- rlang::new_quosure(x, env = parent.frame())
|
||||
#' }
|
||||
#' func <- quoToFunction(x, "renderTriple")
|
||||
#' # A custom render function that repeats the supplied value 3 times
|
||||
#' renderTriple <- function(expr) {
|
||||
#' # Wrap user-supplied reactive expression into a function
|
||||
#' func <- quoToFunction(rlang::enquo0(expr))
|
||||
#'
|
||||
#' createRenderFunction(
|
||||
#' func,
|
||||
#' transform = function(value, session, name, ...) {
|
||||
#' paste(rep(value, 3), collapse=", ")
|
||||
#' },
|
||||
#' outputFunc = textOutput
|
||||
#' )
|
||||
#' }
|
||||
#'
|
||||
#' # For better legacy support, consider using installExprFunction() over quoToFunction()
|
||||
#' renderTripleLegacy <- function(expr, env = parent.frame(), quoted = FALSE) {
|
||||
#' func <- installExprFunction(expr, "func", env, quoted)
|
||||
#'
|
||||
#' createRenderFunction(
|
||||
#' func,
|
||||
@@ -174,10 +217,38 @@ print.shiny.render.function <- function(x, ...) {
|
||||
#' }
|
||||
#'
|
||||
#' # Test render function from the console
|
||||
#' a <- 1
|
||||
#' r <- renderTriple({ a + 1 })
|
||||
#' a <- 2
|
||||
#' reactiveConsole(TRUE)
|
||||
#'
|
||||
#' v <- reactiveVal("basic")
|
||||
#' r <- renderTriple({ v() })
|
||||
#' r()
|
||||
#' #> [1] "basic, basic, basic"
|
||||
#'
|
||||
#' # User can supply quoted code via rlang::quo(). Note that evaluation of the
|
||||
#' # expression happens when r2() is invoked, not when r2 is created.
|
||||
#' q <- rlang::quo({ v() })
|
||||
#' r2 <- rlang::inject(renderTriple(!!q))
|
||||
#' v("rlang")
|
||||
#' r2()
|
||||
#' #> [1] "rlang, rlang, rlang"
|
||||
#'
|
||||
#' # Supplying quoted code without rlang::quo() requires installExprFunction()
|
||||
#' expr <- quote({ v() })
|
||||
#' r3 <- renderTripleLegacy(expr, quoted = TRUE)
|
||||
#' v("legacy")
|
||||
#' r3()
|
||||
#' #> [1] "legacy, legacy, legacy"
|
||||
#'
|
||||
#' # The legacy approach also supports with quosures (env is ignored in this case)
|
||||
#' q <- rlang::quo({ v() })
|
||||
#' r4 <- renderTripleLegacy(q, quoted = TRUE)
|
||||
#' v("legacy-rlang")
|
||||
#' r4()
|
||||
#' #> [1] "legacy-rlang, legacy-rlang, legacy-rlang"
|
||||
#'
|
||||
#' # Turn off reactivity in the console
|
||||
#' reactiveConsole(FALSE)
|
||||
#'
|
||||
#' @export
|
||||
createRenderFunction <- function(
|
||||
func,
|
||||
@@ -316,9 +387,7 @@ markOutputAttrs <- function(renderFunc, snapshotExclude = NULL,
|
||||
#' the output, see [plotPNG()].
|
||||
#'
|
||||
#' @param expr An expression that returns a list.
|
||||
#' @param env The environment in which to evaluate `expr`.
|
||||
#' @param quoted Is `expr` a quoted expression (with `quote()`)? This
|
||||
#' is useful if you want to save an expression in a variable.
|
||||
#' @inheritParams renderUI
|
||||
#' @param deleteFile Should the file in `func()$src` be deleted after
|
||||
#' it is sent to the client browser? Generally speaking, if the image is a
|
||||
#' temp file generated within `func`, then this should be `TRUE`;
|
||||
@@ -397,11 +466,10 @@ markOutputAttrs <- function(renderFunc, snapshotExclude = NULL,
|
||||
#'
|
||||
#' shinyApp(ui, server)
|
||||
#' }
|
||||
renderImage <- function(expr, env=parent.frame(), quoted=FALSE,
|
||||
renderImage <- function(expr, env = parent.frame(), quoted = FALSE,
|
||||
deleteFile, outputArgs=list())
|
||||
{
|
||||
expr <- get_quosure(expr, env, quoted)
|
||||
func <- quoToFunction(expr, "renderImage")
|
||||
func <- installExprFunction(expr, "func", env, quoted, label = "renderImage")
|
||||
|
||||
# missing() must be used directly within the function with the given arg
|
||||
if (missing(deleteFile)) {
|
||||
@@ -523,9 +591,7 @@ isTemp <- function(path, tempDir = tempdir(), mustExist) {
|
||||
#' function return [invisible()].
|
||||
#'
|
||||
#' @param expr An expression to evaluate.
|
||||
#' @param env The environment in which to evaluate `expr`. For expert use only.
|
||||
#' @param quoted Is `expr` a quoted expression (with `quote()`)? This
|
||||
#' is useful if you want to save an expression in a variable.
|
||||
#' @inheritParams renderUI
|
||||
#' @param width Width of printed output.
|
||||
#' @param outputArgs A list of arguments to be passed through to the implicit
|
||||
#' call to [verbatimTextOutput()] or [textOutput()] when the functions are
|
||||
@@ -536,8 +602,7 @@ isTemp <- function(path, tempDir = tempdir(), mustExist) {
|
||||
renderPrint <- function(expr, env = parent.frame(), quoted = FALSE,
|
||||
width = getOption('width'), outputArgs=list())
|
||||
{
|
||||
expr <- get_quosure(expr, env, quoted)
|
||||
func <- quoToFunction(expr, "renderPrint")
|
||||
func <- installExprFunction(expr, "func", env, quoted, label = "renderPrint")
|
||||
|
||||
# Set a promise domain that sets the console width
|
||||
# and captures output
|
||||
@@ -569,7 +634,7 @@ renderPrint <- function(expr, env = parent.frame(), quoted = FALSE,
|
||||
outputArgs,
|
||||
cacheHint = list(
|
||||
label = "renderPrint",
|
||||
origUserExpr = get_expr(expr)
|
||||
origUserExpr = installedFuncExpr(func)
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -619,11 +684,10 @@ createRenderPrintPromiseDomain <- function(width) {
|
||||
#' element.
|
||||
#' @export
|
||||
#' @rdname renderPrint
|
||||
renderText <- function(expr, env=parent.frame(), quoted=FALSE,
|
||||
renderText <- function(expr, env = parent.frame(), quoted = FALSE,
|
||||
outputArgs=list(), sep=" ") {
|
||||
|
||||
expr <- get_quosure(expr, env, quoted)
|
||||
func <- quoToFunction(expr, "renderText")
|
||||
func <- installExprFunction(expr, "func", env, quoted, label = "renderText")
|
||||
|
||||
createRenderFunction(
|
||||
func,
|
||||
@@ -644,9 +708,13 @@ renderText <- function(expr, env=parent.frame(), quoted=FALSE,
|
||||
#'
|
||||
#' @param expr An expression that returns a Shiny tag object, [HTML()],
|
||||
#' or a list of such objects.
|
||||
#' @param env The environment in which to evaluate `expr`.
|
||||
#' @param quoted Is `expr` a quoted expression (with `quote()`)? This
|
||||
#' is useful if you want to save an expression in a variable.
|
||||
#' @template param-env
|
||||
#' @templateVar x expr
|
||||
#' @templateVar env env
|
||||
#' @templateVar quoted quoted
|
||||
#' @template param-quoted
|
||||
#' @templateVar x expr
|
||||
#' @templateVar quoted quoted
|
||||
#' @param outputArgs A list of arguments to be passed through to the implicit
|
||||
#' call to [uiOutput()] when `renderUI` is used in an
|
||||
#' interactive R Markdown document.
|
||||
@@ -675,8 +743,7 @@ renderText <- function(expr, env=parent.frame(), quoted=FALSE,
|
||||
renderUI <- function(expr, env = parent.frame(), quoted = FALSE,
|
||||
outputArgs = list())
|
||||
{
|
||||
expr <- get_quosure(expr, env, quoted)
|
||||
func <- quoToFunction(expr, "renderUI")
|
||||
func <- installExprFunction(expr, "func", env, quoted, label = "renderUI")
|
||||
|
||||
createRenderFunction(
|
||||
func,
|
||||
@@ -755,6 +822,10 @@ downloadHandler <- function(filename, content, contentType=NA, outputArgs=list()
|
||||
#' Table output with the JavaScript DataTables library
|
||||
#'
|
||||
#' @description
|
||||
#' `r lifecycle::badge("superseded")` Please use
|
||||
#' \href{https://rstudio.github.io/DT/shiny.html}{\code{DT::renderDataTable()}}.
|
||||
#' (Shiny 0.11.1)
|
||||
#'
|
||||
#' Makes a reactive version of the given function that returns a data frame (or
|
||||
#' matrix), which will be rendered with the [DataTables](https://datatables.net)
|
||||
#' library. Paging, searching, filtering, and sorting can be done on the R side
|
||||
@@ -829,8 +900,7 @@ renderDataTable <- function(expr, options = NULL, searchDelay = 500,
|
||||
)
|
||||
}
|
||||
|
||||
expr <- get_quosure(expr, env, quoted)
|
||||
func <- quoToFunction(expr, "renderDataTable")
|
||||
func <- installExprFunction(expr, "func", env, quoted, label = "renderDataTable")
|
||||
|
||||
renderFunc <- function(shinysession, name, ...) {
|
||||
if (is.function(options)) options <- options()
|
||||
|
||||
243
R/utils-lang.R
243
R/utils-lang.R
@@ -51,60 +51,199 @@ formalsAndBody <- function(x) {
|
||||
}
|
||||
|
||||
|
||||
# This function is to be called from functions like `reactive()`, `observe()`,
|
||||
# and the various render functions. It handles the following cases:
|
||||
# - The typical case where x is an unquoted expression, and `env` and `quoted`
|
||||
# are not used.
|
||||
# - New-style metaprogramming cases, where rlang::inject() is used to inline a
|
||||
# quosure into the AST, as in `inject(reactive(!!x))`.
|
||||
# - Old-style metaprogramming cases, where `env` and/or `quoted` are used.
|
||||
#
|
||||
# Much of the complexity is handling old-style metaprogramming cases. The code
|
||||
# in this function is more complicated because it needs to look at unevaluated
|
||||
# expressions in the _calling_ function. If this code were put directly in the
|
||||
# calling function, it would look like this:
|
||||
#
|
||||
# if (!missing(env) || !missing(quoted)) {
|
||||
# deprecatedEnvQuotedMessage()
|
||||
# if (!quoted) x <- substitute(x)
|
||||
# x <- new_quosure(x, env)
|
||||
#
|
||||
# } else {
|
||||
# x <- substitute(x)
|
||||
# if (!is_quosure(x)) {
|
||||
# x <- new_quosure(x, env = parent.frame())
|
||||
# }
|
||||
# }
|
||||
#
|
||||
# In the future, the calling functions will not need to have the `env` and
|
||||
# `quoted` arguments -- `rlang::inject()` and quosures can be used instead.
|
||||
# Instead of using this function, `get_quosure()`, the caller can instead use
|
||||
# just the following code:
|
||||
#
|
||||
# x <- substitute(x)
|
||||
# if (!is_quosure(x)) {
|
||||
# x <- new_quosure(x, env = parent.frame())
|
||||
# }
|
||||
#
|
||||
get_quosure <- function(x, env, quoted) {
|
||||
if (!eval(substitute(missing(env)), parent.frame()) ||
|
||||
!eval(substitute(missing(quoted)), parent.frame()))
|
||||
{
|
||||
deprecatedEnvQuotedMessage()
|
||||
if (!quoted) {
|
||||
x <- eval(substitute(substitute(x)), parent.frame())
|
||||
}
|
||||
x <- new_quosure(x, env)
|
||||
#' @describeIn createRenderFunction convert a quosure to a function.
|
||||
#' @param q Quosure of the expression `x`. When capturing expressions to create
|
||||
#' your quosure, it is recommended to use [`enquo0()`] to not unquote the
|
||||
#' object too early. See [`enquo0()`] for more details.
|
||||
#' @inheritParams installExprFunction
|
||||
#' @export
|
||||
quoToFunction <- function(
|
||||
q,
|
||||
label = sys.call(-1)[[1]],
|
||||
..stacktraceon = FALSE
|
||||
) {
|
||||
func <- quoToSimpleFunction(as_quosure(q))
|
||||
wrapFunctionLabel(func, updateFunctionLabel(label), ..stacktraceon = ..stacktraceon, dots = FALSE)
|
||||
}
|
||||
|
||||
} else {
|
||||
x <- eval(substitute(substitute(x)), parent.frame())
|
||||
|
||||
# At this point, x can be a quosure if rlang::inject() is used, but the
|
||||
# typical case is that x is not a quosure.
|
||||
if (!is_quosure(x)) {
|
||||
x <- new_quosure(x, env = parent.frame(2L))
|
||||
updateFunctionLabel <- function(label) {
|
||||
badFnName <- "anonymous"
|
||||
if (all(is.language(label))) {
|
||||
# Prevent immediately invoked functions like as.language(a()())
|
||||
if (is.language(label) && length(label) > 1) {
|
||||
return(badFnName)
|
||||
}
|
||||
label <- deparse(label, width.cutoff = 500L)
|
||||
}
|
||||
label <- as.character(label)
|
||||
# Prevent function calls that are over one line; (Assignments are hard to perform)
|
||||
# Prevent immediately invoked functions like "a()()"
|
||||
if (length(label) > 1 || grepl("(", label, fixed = TRUE)) {
|
||||
return(badFnName)
|
||||
}
|
||||
if (label == "NULL") {
|
||||
return(badFnName)
|
||||
}
|
||||
label
|
||||
}
|
||||
|
||||
quoToSimpleFunction <- function(q) {
|
||||
# Should not use `new_function(list(), get_expr(q), get_env(q))` as extra logic
|
||||
# is done by rlang to convert the quosure to a function within `as_function(q)`
|
||||
fun <- as_function(q)
|
||||
|
||||
# If the quosure is empty, then the returned function can not be called.
|
||||
# https://github.com/r-lib/rlang/issues/1244
|
||||
if (quo_is_missing(q)) {
|
||||
fn_body(fun) <- quote({})
|
||||
}
|
||||
|
||||
x
|
||||
# `as_function()` returns a function that takes `...`. We need one that takes no
|
||||
# args.
|
||||
fn_fmls(fun) <- list()
|
||||
|
||||
fun
|
||||
}
|
||||
|
||||
|
||||
#' Convert an expression to a function
|
||||
#'
|
||||
#' `r lifecycle::badge("superseded")` Please use [`installExprFunction()`] for a better
|
||||
#' debugging experience (Shiny 0.8.0). If the `expr` and `quoted` parameters are not needed, please see
|
||||
#' [`quoToFunction()`] (Shiny 1.6.0).
|
||||
#'
|
||||
#' Similar to [installExprFunction()] but doesn't register debug hooks.
|
||||
#'
|
||||
#' @param expr A quoted or unquoted expression, or a quosure.
|
||||
#' @param env The desired environment for the function. Defaults to the
|
||||
#' calling environment two steps back.
|
||||
#' @param quoted Is the expression quoted?
|
||||
#' @seealso [`installExprFunction()`] for the modern approach to converting an expression to a function
|
||||
#' @export
|
||||
#' @keywords internal
|
||||
exprToFunction <- function(expr, env = parent.frame(), quoted = FALSE) {
|
||||
# If `expr` is a raw quosure, must say `quoted = TRUE`; (env is ignored)
|
||||
# If `inject()` a quosure, env is ignored, and quoted should be FALSE (aka ignored).
|
||||
# Make article of usage
|
||||
# * (by joe)
|
||||
|
||||
if (!quoted) {
|
||||
expr <- eval(substitute(substitute(expr)), parent.frame())
|
||||
}
|
||||
# MUST call with `quoted = TRUE` as exprToQuo() will not reach high enough
|
||||
q <- exprToQuo(expr, env, quoted = TRUE)
|
||||
|
||||
# MUST call `as_function()`. Can NOT call `new_function()`
|
||||
# rlang has custom logic for handling converting a quosure to a function
|
||||
quoToSimpleFunction(q)
|
||||
}
|
||||
# For internal use only; External users should be using `exprToFunction()` or `installExprFunction()`
|
||||
# MUST be the exact same logic as `exprToFunction()`, but without the `quoToSimpleFunction()` call
|
||||
exprToQuo <- function(expr, env = parent.frame(), quoted = FALSE) {
|
||||
if (!quoted) {
|
||||
expr <- eval(substitute(substitute(expr)), parent.frame())
|
||||
}
|
||||
q <-
|
||||
if (is_quosure(expr)) {
|
||||
# inject()ed quosure
|
||||
# do nothing
|
||||
expr
|
||||
} else if (is.language(expr) || rlang::is_atomic(expr) || is.null(expr)) {
|
||||
# Most common case...
|
||||
new_quosure(expr, env = env)
|
||||
} else {
|
||||
stop("Don't know how to convert '", class(expr)[1], "' to a function; a quosure or quoted expression was expected")
|
||||
}
|
||||
q
|
||||
}
|
||||
|
||||
#' @describeIn createRenderFunction converts a user's reactive `expr` into a
|
||||
#' function that's assigned to a `name` in the `assign.env`.
|
||||
#'
|
||||
#' @param name The name the function should be given
|
||||
#' @param eval.env The desired environment for the function. Defaults to the
|
||||
#' calling environment two steps back.
|
||||
#' @param assign.env The environment in which the function should be assigned.
|
||||
#' @param label A label for the object to be shown in the debugger. Defaults to
|
||||
#' the name of the calling function.
|
||||
#' @param wrappedWithLabel,..stacktraceon Advanced use only. For stack manipulation purposes; see
|
||||
#' [stacktrace()].
|
||||
#' @inheritParams exprToFunction
|
||||
#' @export
|
||||
installExprFunction <- function(expr, name, eval.env = parent.frame(2),
|
||||
quoted = FALSE,
|
||||
assign.env = parent.frame(1),
|
||||
label = sys.call(-1)[[1]],
|
||||
wrappedWithLabel = TRUE,
|
||||
..stacktraceon = FALSE) {
|
||||
if (!quoted) {
|
||||
quoted <- TRUE
|
||||
expr <- eval(substitute(substitute(expr)), parent.frame())
|
||||
}
|
||||
|
||||
func <- exprToFunction(expr, eval.env, quoted)
|
||||
if (length(label) > 1) {
|
||||
# Just in case the deparsed code is more complicated than we imagine. If we
|
||||
# have a label with length > 1 it causes warnings in wrapFunctionLabel.
|
||||
label <- paste0(label, collapse = "\n")
|
||||
}
|
||||
wrappedWithLabel <- isTRUE(wrappedWithLabel)
|
||||
if (wrappedWithLabel) {
|
||||
func <- wrapFunctionLabel(func, updateFunctionLabel(label), ..stacktraceon = ..stacktraceon, dots = FALSE)
|
||||
}
|
||||
assign(name, func, envir = assign.env)
|
||||
if (!wrappedWithLabel) {
|
||||
registerDebugHook(name, assign.env, label)
|
||||
}
|
||||
|
||||
invisible(func)
|
||||
}
|
||||
|
||||
# Utility function for creating a debugging label, given an expression.
|
||||
# `expr` is a quoted expression.
|
||||
# `function_name` is the name of the calling function.
|
||||
# `label` is an optional user-provided label. If NULL, it will be inferred.
|
||||
exprToLabel <- function(expr, function_name, label = NULL) {
|
||||
srcref <- attr(expr, "srcref", exact = TRUE)
|
||||
if (is.null(label)) {
|
||||
label <- rexprSrcrefToLabel(
|
||||
srcref[[1]],
|
||||
simpleExprToFunction(expr, function_name)
|
||||
)
|
||||
}
|
||||
if (length(srcref) >= 2) attr(label, "srcref") <- srcref[[2]]
|
||||
attr(label, "srcfile") <- srcFileOfRef(srcref[[1]])
|
||||
label
|
||||
}
|
||||
simpleExprToFunction <- function(expr, function_name) {
|
||||
sprintf('%s(%s)', function_name, paste(deparse(expr), collapse='\n'))
|
||||
}
|
||||
|
||||
installedFuncExpr <- function(func) {
|
||||
fn_body(attr(func, "wrappedFunc", exact = TRUE))
|
||||
}
|
||||
|
||||
funcToLabelBody <- function(func) {
|
||||
paste(deparse(installedFuncExpr(func)), collapse='\n')
|
||||
}
|
||||
funcToLabel <- function(func, functionLabel, label = NULL) {
|
||||
if (!is.null(label)) return(label)
|
||||
|
||||
sprintf(
|
||||
'%s(%s)',
|
||||
functionLabel,
|
||||
funcToLabelBody(func)
|
||||
)
|
||||
}
|
||||
quoToLabelBody <- function(q) {
|
||||
paste(deparse(quo_get_expr(q)), collapse='\n')
|
||||
}
|
||||
quoToLabel <- function(q, functionLabel, label = NULL) {
|
||||
if (!is.null(label)) return(label)
|
||||
|
||||
sprintf(
|
||||
'%s(%s)',
|
||||
functionLabel,
|
||||
quoToLabelBody(q)
|
||||
)
|
||||
}
|
||||
|
||||
185
R/utils.R
185
R/utils.R
@@ -404,165 +404,6 @@ getContentType <- function(file, defaultType = 'application/octet-stream') {
|
||||
mime::guess_type(file, unknown = defaultType, subtype = subtype)
|
||||
}
|
||||
|
||||
# Create a zero-arg function from a quoted expression and environment
|
||||
# @examples
|
||||
# makeFunction(body=quote(print(3)))
|
||||
makeFunction <- function(args = pairlist(), body, env = parent.frame()) {
|
||||
eval(call("function", args, body), env)
|
||||
}
|
||||
|
||||
#' Convert an expression to a function
|
||||
#'
|
||||
#' This is to be called from another function, because it will attempt to get
|
||||
#' an unquoted expression from two calls back. Note: as of Shiny 1.6.0, it is
|
||||
#' recommended to use [quoToFunction()] instead.
|
||||
#'
|
||||
#' If expr is a quoted expression, then this just converts it to a function.
|
||||
#' If expr is a function, then this simply returns expr (and prints a
|
||||
#' deprecation message).
|
||||
#' If expr was a non-quoted expression from two calls back, then this will
|
||||
#' quote the original expression and convert it to a function.
|
||||
#
|
||||
#' @param expr A quoted or unquoted expression, or a function.
|
||||
#' @param env The desired environment for the function. Defaults to the
|
||||
#' calling environment two steps back.
|
||||
#' @param quoted Is the expression quoted?
|
||||
#'
|
||||
#' @examples
|
||||
#' # Example of a new renderer, similar to renderText
|
||||
#' # This is something that toolkit authors will do
|
||||
#' renderTriple <- function(expr, env=parent.frame(), quoted=FALSE) {
|
||||
#' # Convert expr to a function
|
||||
#' func <- shiny::exprToFunction(expr, env, quoted)
|
||||
#'
|
||||
#' function() {
|
||||
#' value <- func()
|
||||
#' paste(rep(value, 3), collapse=", ")
|
||||
#' }
|
||||
#' }
|
||||
#'
|
||||
#'
|
||||
#' # Example of using the renderer.
|
||||
#' # This is something that app authors will do.
|
||||
#' values <- reactiveValues(A="text")
|
||||
#'
|
||||
#' \dontrun{
|
||||
#' # Create an output object
|
||||
#' output$tripleA <- renderTriple({
|
||||
#' values$A
|
||||
#' })
|
||||
#' }
|
||||
#'
|
||||
#' # At the R console, you can experiment with the renderer using isolate()
|
||||
#' tripleA <- renderTriple({
|
||||
#' values$A
|
||||
#' })
|
||||
#'
|
||||
#' isolate(tripleA())
|
||||
#' # "text, text, text"
|
||||
#' @export
|
||||
exprToFunction <- function(expr, env=parent.frame(), quoted=FALSE) {
|
||||
if (!quoted) {
|
||||
expr <- eval(substitute(substitute(expr)), parent.frame())
|
||||
}
|
||||
|
||||
# expr is a quoted expression
|
||||
makeFunction(body=expr, env=env)
|
||||
}
|
||||
|
||||
#' Install an expression as a function
|
||||
#'
|
||||
#' Installs an expression in the given environment as a function, and registers
|
||||
#' debug hooks so that breakpoints may be set in the function. Note: as of
|
||||
#' Shiny 1.6.0, it is recommended to use [quoToFunction()] instead.
|
||||
#'
|
||||
#' This function can replace `exprToFunction` as follows: we may use
|
||||
#' `func <- exprToFunction(expr)` if we do not want the debug hooks, or
|
||||
#' `installExprFunction(expr, "func")` if we do. Both approaches create a
|
||||
#' function named `func` in the current environment.
|
||||
#'
|
||||
#' @seealso Wraps [exprToFunction()]; see that method's documentation
|
||||
#' for more documentation and examples.
|
||||
#'
|
||||
#' @param expr A quoted or unquoted expression
|
||||
#' @param name The name the function should be given
|
||||
#' @param eval.env The desired environment for the function. Defaults to the
|
||||
#' calling environment two steps back.
|
||||
#' @param quoted Is the expression quoted?
|
||||
#' @param assign.env The environment in which the function should be assigned.
|
||||
#' @param label A label for the object to be shown in the debugger. Defaults to
|
||||
#' the name of the calling function.
|
||||
#' @param wrappedWithLabel,..stacktraceon Advanced use only. For stack manipulation purposes; see
|
||||
#' [stacktrace()].
|
||||
#' @export
|
||||
installExprFunction <- function(expr, name, eval.env = parent.frame(2),
|
||||
quoted = FALSE,
|
||||
assign.env = parent.frame(1),
|
||||
label = deparse(sys.call(-1)[[1]]),
|
||||
wrappedWithLabel = TRUE,
|
||||
..stacktraceon = FALSE) {
|
||||
if (!quoted) {
|
||||
quoted <- TRUE
|
||||
expr <- eval(substitute(substitute(expr)), parent.frame())
|
||||
}
|
||||
|
||||
func <- exprToFunction(expr, eval.env, quoted)
|
||||
if (length(label) > 1) {
|
||||
# Just in case the deparsed code is more complicated than we imagine. If we
|
||||
# have a label with length > 1 it causes warnings in wrapFunctionLabel.
|
||||
label <- paste0(label, collapse = "\n")
|
||||
}
|
||||
if (wrappedWithLabel) {
|
||||
func <- wrapFunctionLabel(func, label, ..stacktraceon = ..stacktraceon)
|
||||
} else {
|
||||
registerDebugHook(name, assign.env, label)
|
||||
}
|
||||
assign(name, func, envir = assign.env)
|
||||
}
|
||||
|
||||
#' Convert a quosure to a function for a Shiny render function
|
||||
#'
|
||||
#' This takes a quosure and label, and wraps them into a function that should be
|
||||
#' passed to [createRenderFunction()] or [markRenderFunction()].
|
||||
#'
|
||||
#' This function was added in Shiny 1.6.0. Previously, it was recommended to use
|
||||
#' [installExprFunction()] or [exprToFunction()] in render functions, but now we
|
||||
#' recommend using [quoToFunction()], because it does not require `env` and
|
||||
#' `quoted` arguments -- that information is captured by quosures provided by
|
||||
#' \pkg{rlang}.
|
||||
#'
|
||||
#' @param q A quosure.
|
||||
#' @inheritParams installExprFunction
|
||||
#' @seealso [createRenderFunction()] for example usage.
|
||||
#'
|
||||
#' @export
|
||||
quoToFunction <- function(q, label, ..stacktraceon = FALSE) {
|
||||
q <- as_quosure(q)
|
||||
func <- as_function(q)
|
||||
# as_function returns a function that takes `...`. We want one that takes no
|
||||
# args.
|
||||
formals(func) <- list()
|
||||
wrapFunctionLabel(func, label, ..stacktraceon = ..stacktraceon)
|
||||
}
|
||||
|
||||
|
||||
# Utility function for creating a debugging label, given an expression.
|
||||
# `expr` is a quoted expression.
|
||||
# `function_name` is the name of the calling function.
|
||||
# `label` is an optional user-provided label. If NULL, it will be inferred.
|
||||
exprToLabel <- function(expr, function_name, label = NULL) {
|
||||
srcref <- attr(expr, "srcref", exact = TRUE)
|
||||
if (is.null(label)) {
|
||||
label <- rexprSrcrefToLabel(
|
||||
srcref[[1]],
|
||||
sprintf('%s(%s)', function_name, paste(deparse(expr), collapse = '\n'))
|
||||
)
|
||||
}
|
||||
if (length(srcref) >= 2) attr(label, "srcref") <- srcref[[2]]
|
||||
attr(label, "srcfile") <- srcFileOfRef(srcref[[1]])
|
||||
label
|
||||
}
|
||||
|
||||
#' Parse a GET query string from a URL
|
||||
#'
|
||||
#' Returns a named list of key-value pairs.
|
||||
@@ -1159,7 +1000,7 @@ reactiveStop <- function(message = "", class = NULL) {
|
||||
#'
|
||||
#' ui <- fluidPage(
|
||||
#' checkboxGroupInput('in1', 'Check some letters', choices = head(LETTERS)),
|
||||
#' selectizeInput('in2', 'Select a state', choices = state.name),
|
||||
#' selectizeInput('in2', 'Select a state', choices = c("", state.name)),
|
||||
#' plotOutput('plot')
|
||||
#' )
|
||||
#'
|
||||
@@ -1597,21 +1438,31 @@ dateYMD <- function(date = NULL, argName = "value") {
|
||||
# function which calls the original function using the specified name. This can
|
||||
# be helpful for profiling, because the specified name will show up on the stack
|
||||
# trace.
|
||||
wrapFunctionLabel <- function(func, name, ..stacktraceon = FALSE) {
|
||||
wrapFunctionLabel <- function(func, name, ..stacktraceon = FALSE, dots = TRUE) {
|
||||
if (name == "name" || name == "func" || name == "relabelWrapper") {
|
||||
stop("Invalid name for wrapFunctionLabel: ", name)
|
||||
}
|
||||
assign(name, func, environment())
|
||||
registerDebugHook(name, environment(), name)
|
||||
|
||||
if (..stacktraceon) {
|
||||
# We need to wrap the `...` in `!!quote(...)` so that R CMD check won't
|
||||
# complain about "... may be used in an incorrect context"
|
||||
body <- expr({ ..stacktraceon..((!!name)(!!quote(...))) })
|
||||
if (isTRUE(dots)) {
|
||||
if (..stacktraceon) {
|
||||
# We need to wrap the `...` in `!!quote(...)` so that R CMD check won't
|
||||
# complain about "... may be used in an incorrect context"
|
||||
body <- expr({ ..stacktraceon..((!!name)(!!quote(...))) })
|
||||
} else {
|
||||
body <- expr({ (!!name)(!!quote(...)) })
|
||||
}
|
||||
relabelWrapper <- new_function(pairlist2(... =), body, environment())
|
||||
} else {
|
||||
body <- expr({ (!!name)(!!quote(...)) })
|
||||
# Same logic as when `dots = TRUE`, but without the `...`
|
||||
if (..stacktraceon) {
|
||||
body <- expr({ ..stacktraceon..((!!name)()) })
|
||||
} else {
|
||||
body <- expr({ (!!name)() })
|
||||
}
|
||||
relabelWrapper <- new_function(list(), body, environment())
|
||||
}
|
||||
relabelWrapper <- new_function(pairlist2(... =), body, environment())
|
||||
|
||||
# Preserve the original function that was passed in; is used for caching.
|
||||
attr(relabelWrapper, "wrappedFunc") <- func
|
||||
|
||||
@@ -58,3 +58,7 @@ We welcome contributions to the **shiny** package. Please see our [CONTRIBUTING.
|
||||
## License
|
||||
|
||||
The shiny package as a whole is licensed under the GPLv3. See the [LICENSE](LICENSE) file for more details.
|
||||
|
||||
## R version support
|
||||
|
||||
Shiny is supported on the latest release version of R, as well as the previous four minor release versions of R. For example, if the latest release R version is 4.1, then that version is supported, as well as 4.0, 3.6, 3.5, and 3.4.
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -13,6 +13,13 @@
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
/* https://github.com/rstudio/shiny/issues/3443 */
|
||||
/* https://css-tricks.com/inheriting-box-sizing-probably-slightly-better-best-practice/ */
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.irs *, .irs *:before, .irs *:after {
|
||||
box-sizing: inherit;
|
||||
}
|
||||
|
||||
.irs-line {
|
||||
@@ -172,9 +179,9 @@
|
||||
.irs--shiny .irs-bar {
|
||||
top: 25px;
|
||||
height: 8px;
|
||||
border-top: 1px solid #337ab7;
|
||||
border-bottom: 1px solid #337ab7;
|
||||
background: #337ab7;
|
||||
border-top: 1px solid #428bca;
|
||||
border-bottom: 1px solid #428bca;
|
||||
background: #428bca;
|
||||
}
|
||||
|
||||
.irs--shiny .irs-bar--single {
|
||||
@@ -228,7 +235,7 @@
|
||||
color: #fff;
|
||||
text-shadow: none;
|
||||
padding: 1px 3px;
|
||||
background-color: #337ab7;
|
||||
background-color: #428bca;
|
||||
border-radius: 3px;
|
||||
font-size: 11px;
|
||||
line-height: 1.333;
|
||||
|
||||
@@ -4,6 +4,12 @@
|
||||
@include pos-r();
|
||||
-webkit-touch-callout: none;
|
||||
@include no-click();
|
||||
/* https://github.com/rstudio/shiny/issues/3443 */
|
||||
/* https://css-tricks.com/inheriting-box-sizing-probably-slightly-better-best-practice/ */
|
||||
box-sizing: border-box;
|
||||
*, *:before, *:after {
|
||||
box-sizing: inherit;
|
||||
}
|
||||
|
||||
&-line {
|
||||
@include pos-r();
|
||||
|
||||
@@ -37,7 +37,7 @@ $font-family: $font-family-base !default;
|
||||
// "High-level" coloring
|
||||
$bg: $body-bg !default;
|
||||
$fg: color-contrast($body-bg) !default;
|
||||
$accent: $component-active-bg !default;
|
||||
$accent: #428bca !default;
|
||||
|
||||
// "Low-level" coloring, borders, and fonts
|
||||
$line_bg: linear-gradient(to bottom, mix($bg, $fg, 87%) -50%, $bg 150%) !default;
|
||||
|
||||
@@ -1,17 +1,3 @@
|
||||
(function() {
|
||||
var protocol = 'ws:';
|
||||
if (window.location.protocol === 'https:')
|
||||
protocol = 'wss:';
|
||||
|
||||
var defaultPath = window.location.pathname;
|
||||
if (!/\/$/.test(defaultPath))
|
||||
defaultPath += '/';
|
||||
defaultPath += 'autoreload/';
|
||||
|
||||
var ws = new WebSocket(protocol + '//' + window.location.host + defaultPath);
|
||||
ws.onmessage = function(event) {
|
||||
if (event.data === "autoreload") {
|
||||
window.location.reload()
|
||||
}
|
||||
}
|
||||
})();
|
||||
/*! shiny 1.7.0 | (c) 2012-2021 RStudio, PBC. | License: GPL-3 | file LICENSE */
|
||||
(function(){var t="ws:";window.location.protocol==="https:"&&(t="wss:");var o=window.location.pathname;/\/$/.test(o)||(o+="/");o+="autoreload/";var n=new WebSocket(t+"//"+window.location.host+o);n.onmessage=function(a){a.data==="autoreload"&&window.location.reload()};})();
|
||||
//# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsiLi4vLi4vLi4vc3JjdHMvZXh0cmFzL3NoaW55LWF1dG9yZWxvYWQudHMiXSwKICAic291cmNlc0NvbnRlbnQiOiBbIi8qIGVzbGludC1kaXNhYmxlIHVuaWNvcm4vZmlsZW5hbWUtY2FzZSAqL1xudmFyIHByb3RvY29sID0gXCJ3czpcIjtcbmlmICh3aW5kb3cubG9jYXRpb24ucHJvdG9jb2wgPT09IFwiaHR0cHM6XCIpIHByb3RvY29sID0gXCJ3c3M6XCI7XG52YXIgZGVmYXVsdFBhdGggPSB3aW5kb3cubG9jYXRpb24ucGF0aG5hbWU7XG5pZiAoIS9cXC8kLy50ZXN0KGRlZmF1bHRQYXRoKSkgZGVmYXVsdFBhdGggKz0gXCIvXCI7XG5kZWZhdWx0UGF0aCArPSBcImF1dG9yZWxvYWQvXCI7XG52YXIgd3MgPSBuZXcgV2ViU29ja2V0KHByb3RvY29sICsgXCIvL1wiICsgd2luZG93LmxvY2F0aW9uLmhvc3QgKyBkZWZhdWx0UGF0aCk7XG5cbndzLm9ubWVzc2FnZSA9IGZ1bmN0aW9uIChldmVudCkge1xuICBpZiAoZXZlbnQuZGF0YSA9PT0gXCJhdXRvcmVsb2FkXCIpIHtcbiAgICB3aW5kb3cubG9jYXRpb24ucmVsb2FkKCk7XG4gIH1cbn07XG5cbmV4cG9ydCB7fTsiXSwKICAibWFwcGluZ3MiOiAiO1lBQ0EsR0FBSSxHQUFXLE1BQ2YsQUFBSSxPQUFPLFNBQVMsV0FBYSxVQUFVLEdBQVcsUUFDdEQsR0FBSSxHQUFjLE9BQU8sU0FBUyxTQUNsQyxBQUFLLE1BQU0sS0FBSyxJQUFjLElBQWUsS0FDN0MsR0FBZSxjQUNmLEdBQUksR0FBSyxHQUFJLFdBQVUsRUFBVyxLQUFPLE9BQU8sU0FBUyxLQUFPLEdBRWhFLEVBQUcsVUFBWSxTQUFVLEVBQU8sQ0FDOUIsQUFBSSxFQUFNLE9BQVMsY0FDakIsT0FBTyxTQUFTIiwKICAibmFtZXMiOiBbXQp9Cg==
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
4
inst/www/shared/shiny-es5.min.js
vendored
4
inst/www/shared/shiny-es5.min.js
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1,87 +1,2 @@
|
||||
#showcase-well {
|
||||
border-radius: 0;
|
||||
-webkit-border-radius: 0;
|
||||
-moz-border-radius: 0;
|
||||
}
|
||||
|
||||
.shiny-code {
|
||||
background-color: white;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.shiny-code code {
|
||||
font-family: Menlo, Consolas, "Courier New", monospace;
|
||||
}
|
||||
|
||||
.shiny-code-container {
|
||||
margin-top: 20px;
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.shiny-code-container h3 {
|
||||
display: inline;
|
||||
margin-right: 15px;
|
||||
}
|
||||
|
||||
.showcase-header {
|
||||
font-size: 16px;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
.showcase-code-link {
|
||||
text-align: right;
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
#showcase-app-container {
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
#showcase-code-tabs pre {
|
||||
border: none;
|
||||
line-height: 1em;
|
||||
}
|
||||
|
||||
#showcase-code-tabs .nav,
|
||||
#showcase-code-tabs ul {
|
||||
margin-bottom: 0px;
|
||||
}
|
||||
|
||||
#showcase-app-code {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#showcase-code-tabs {
|
||||
margin-right: 15px;
|
||||
}
|
||||
|
||||
#showcase-code-tabs .tab-content {
|
||||
border-style: solid;
|
||||
border-color: #e5e5e5;
|
||||
border-width: 0px 1px 1px 1px;
|
||||
overflow:auto;
|
||||
-webkit-border-bottom-right-radius: 4px;
|
||||
-webkit-border-bottom-left-radius: 4px;
|
||||
-moz-border-radius-bottomright: 4px;
|
||||
-moz-border-radius-bottomleft: 4px;
|
||||
border-bottom-right-radius: 4px;
|
||||
border-bottom-left-radius: 4px;
|
||||
}
|
||||
|
||||
#showcase-code-position-toggle {
|
||||
float: right;
|
||||
}
|
||||
|
||||
#showcase-sxs-code {
|
||||
padding-top: 20px;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
.showcase-code-license {
|
||||
display: block;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
#showcase-code-content pre {
|
||||
background-color: white;
|
||||
}
|
||||
/*! shiny 1.7.0 | (c) 2012-2021 RStudio, PBC. | License: GPL-3 | file LICENSE */
|
||||
#showcase-well{border-radius:0}.shiny-code{background-color:#fff;margin-bottom:0}.shiny-code code{font-family:Menlo,Consolas,"Courier New",monospace}.shiny-code-container{margin-top:20px;clear:both}.shiny-code-container h3{display:inline;margin-right:15px}.showcase-header{font-size:16px;font-weight:normal}.showcase-code-link{text-align:right;padding:15px}#showcase-app-container{vertical-align:top}#showcase-code-tabs{margin-right:15px}#showcase-code-tabs pre{border:none;line-height:1em}#showcase-code-tabs .nav{margin-bottom:0}#showcase-code-tabs ul{margin-bottom:0}#showcase-code-tabs .tab-content{border-style:solid;border-color:#e5e5e5;border-width:0px 1px 1px 1px;overflow:auto;border-bottom-right-radius:4px;border-bottom-left-radius:4px}#showcase-app-code{width:100%}#showcase-code-position-toggle{float:right}#showcase-sxs-code{padding-top:20px;vertical-align:top}.showcase-code-license{display:block;text-align:right}#showcase-code-content pre{background-color:#fff}
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -1,8 +1,3 @@
|
||||
// Listen for messages from parent frame. This file is only added when the
|
||||
// shiny.testmode option is TRUE.
|
||||
window.addEventListener("message", function(e) {
|
||||
var message = e.data;
|
||||
|
||||
if (message.code)
|
||||
eval(message.code);
|
||||
});
|
||||
/*! shiny 1.7.0 | (c) 2012-2021 RStudio, PBC. | License: GPL-3 | file LICENSE */
|
||||
(function(){var a=eval;window.addEventListener("message",function(i){var e=i.data;e.code&&a(e.code)});})();
|
||||
//# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsiLi4vLi4vLi4vc3JjdHMvc3JjL3V0aWxzL2V2YWwudHMiLCAiLi4vLi4vLi4vc3JjdHMvZXh0cmFzL3NoaW55LXRlc3Rtb2RlLnRzIl0sCiAgInNvdXJjZXNDb250ZW50IjogWyIvL2VzYnVpbGQuZ2l0aHViLmlvL2NvbnRlbnQtdHlwZXMvI2RpcmVjdC1ldmFsXG4vL3RsL2RyO1xuLy8gKiBEaXJlY3QgdXNhZ2Ugb2YgYGV2YWwoXCJ4XCIpYCBpcyBiYWQgd2l0aCBidW5kbGVkIGNvZGUuXG4vLyAqIEluc3RlYWQsIHVzZSBpbmRpcmVjdCBjYWxscyB0byBgZXZhbGAgc3VjaCBhcyBgaW5kaXJlY3RFdmFsKFwieFwiKWBcbi8vICAgKiBFdmVuIGp1c3QgcmVuYW1pbmcgdGhlIGZ1bmN0aW9uIHdvcmtzIHdlbGwgZW5vdWdoLlxuLy8gPiBUaGlzIGlzIGtub3duIGFzIFwiaW5kaXJlY3QgZXZhbFwiIGJlY2F1c2UgZXZhbCBpcyBub3QgYmVpbmcgY2FsbGVkIGRpcmVjdGx5LCBhbmQgc28gZG9lcyBub3QgdHJpZ2dlciB0aGUgZ3JhbW1hdGljYWwgc3BlY2lhbCBjYXNlIGZvciBkaXJlY3QgZXZhbCBpbiB0aGUgSmF2YVNjcmlwdCBWTS4gWW91IGNhbiBjYWxsIGluZGlyZWN0IGV2YWwgdXNpbmcgYW55IHN5bnRheCBhdCBhbGwgZXhjZXB0IGZvciBhbiBleHByZXNzaW9uIG9mIHRoZSBleGFjdCBmb3JtIGV2YWwoJ3gnKS4gRm9yIGV4YW1wbGUsIHZhciBldmFsMiA9IGV2YWw7IGV2YWwyKCd4JykgYW5kIFtldmFsXVswXSgneCcpIGFuZCB3aW5kb3cuZXZhbCgneCcpIGFyZSBhbGwgaW5kaXJlY3QgZXZhbCBjYWxscy5cbi8vID4gV2hlbiB5b3UgdXNlIGluZGlyZWN0IGV2YWwsIHRoZSBjb2RlIGlzIGV2YWx1YXRlZCBpbiB0aGUgZ2xvYmFsIHNjb3BlIGluc3RlYWQgb2YgaW4gdGhlIGlubGluZSBzY29wZSBvZiB0aGUgY2FsbGVyLlxudmFyIGluZGlyZWN0RXZhbCA9IGV2YWw7XG5leHBvcnQgeyBpbmRpcmVjdEV2YWwgfTsiLCAiLyogZXNsaW50LWRpc2FibGUgdW5pY29ybi9maWxlbmFtZS1jYXNlICovXG5pbXBvcnQgeyBpbmRpcmVjdEV2YWwgfSBmcm9tIFwiLi4vc3JjL3V0aWxzL2V2YWxcIjsgLy8gTGlzdGVuIGZvciBtZXNzYWdlcyBmcm9tIHBhcmVudCBmcmFtZS4gVGhpcyBmaWxlIGlzIG9ubHkgYWRkZWQgd2hlbiB0aGVcbi8vIHNoaW55LnRlc3Rtb2RlIG9wdGlvbiBpcyBUUlVFLlxuXG53aW5kb3cuYWRkRXZlbnRMaXN0ZW5lcihcIm1lc3NhZ2VcIiwgZnVuY3Rpb24gKGUpIHtcbiAgdmFyIG1lc3NhZ2UgPSBlLmRhdGE7XG4gIGlmIChtZXNzYWdlLmNvZGUpIGluZGlyZWN0RXZhbChtZXNzYWdlLmNvZGUpO1xufSk7Il0sCiAgIm1hcHBpbmdzIjogIjtZQU9BLEdBQUksR0FBZSxLQ0huQixPQUFPLGlCQUFpQixVQUFXLFNBQVUsRUFBRyxDQUM5QyxHQUFJLEdBQVUsRUFBRSxLQUNoQixBQUFJLEVBQVEsTUFBTSxFQUFhLEVBQVEiLAogICJuYW1lcyI6IFtdCn0K
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
3
inst/www/shared/shiny.min.css
vendored
3
inst/www/shared/shiny.min.css
vendored
File diff suppressed because one or more lines are too long
3
inst/www/shared/shiny.min.js
vendored
3
inst/www/shared/shiny.min.js
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -295,6 +295,15 @@ pre.shiny-text-output {
|
||||
margin-top: 0px;
|
||||
}
|
||||
|
||||
/* Workaround for radio buttons and checkboxes not showing on Qt on Mac.
|
||||
This occurs in the RStudio IDE on macOS 11.5.
|
||||
https://github.com/rstudio/shiny/issues/3484
|
||||
*/
|
||||
.qtmac input[type="radio"],
|
||||
.qtmac input[type="checkbox"] {
|
||||
zoom: 1.0000001;
|
||||
}
|
||||
|
||||
/* consistency with bootstrap.css for selectize.js */
|
||||
.selectize-control {
|
||||
margin-bottom: 10px;
|
||||
|
||||
6
man-roxygen/param-env.R
Normal file
6
man-roxygen/param-env.R
Normal file
@@ -0,0 +1,6 @@
|
||||
# Also update observeEvent param descriptions!
|
||||
# https://github.com/r-lib/roxygen2/issues/1241
|
||||
#' @param <%= env %> The parent environment for the reactive expression. By default,
|
||||
#' this is the calling environment, the same as when defining an ordinary
|
||||
#' non-reactive expression. If `<%= x %>` is a quosure and `<%= quoted %>` is `TRUE`,
|
||||
#' then `<%= env %>` is ignored.
|
||||
6
man-roxygen/param-quoted.R
Normal file
6
man-roxygen/param-quoted.R
Normal file
@@ -0,0 +1,6 @@
|
||||
# Also update observeEvent param descriptions!
|
||||
# https://github.com/r-lib/roxygen2/issues/1241
|
||||
#' @param <%= quoted %> If it is `TRUE`, then the [`quote()`]ed value of `<%= x %>`
|
||||
#' will be used when `<%= x %>` is evaluated. If `<%= x %>` is a quosure and you
|
||||
#' would like to use its expression as a value for `<%= x %>`, then you must set
|
||||
#' `<%= quoted %>` to `TRUE`.
|
||||
@@ -40,7 +40,7 @@ sense, namely:
|
||||
|
||||
In the example here, the \code{bindCache()} key consists of \code{input$x} and
|
||||
\code{input$y} combined, and the value is \code{input$x * input$y}. In this simple
|
||||
example, for any given key, there is only one possible returned value.\if{html}{\out{<div class="NA">}}\preformatted{r <- reactive(\{ input$x * input$y \}) \%>\%
|
||||
example, for any given key, there is only one possible returned value.\if{html}{\out{<div class="sourceCode NA">}}\preformatted{r <- reactive(\{ input$x * input$y \}) \%>\%
|
||||
bindCache(input$x, input$y)
|
||||
}\if{html}{\out{</div>}}
|
||||
|
||||
@@ -63,7 +63,7 @@ to do some sort of reduction on the data that still captures information
|
||||
about whether a value can be retrieved from the cache. For example, if you
|
||||
have a large data set with timestamps, it might make sense to extract the
|
||||
most recent timestamp and return that. Then, instead of hashing the entire
|
||||
data object, the cached reactive only needs to hash the timestamp.\if{html}{\out{<div class="NA">}}\preformatted{r <- reactive(\{ compute(bigdata()) \} \%>\%
|
||||
data object, the cached reactive only needs to hash the timestamp.\if{html}{\out{<div class="sourceCode NA">}}\preformatted{r <- reactive(\{ compute(bigdata()) \} \%>\%
|
||||
bindCache(\{ extract_most_recent_time(bigdata()) \})
|
||||
}\if{html}{\out{</div>}}
|
||||
|
||||
@@ -105,7 +105,7 @@ cache key is not too expensive.
|
||||
Remember that the key is \emph{reactive}, so it is not re-executed every single
|
||||
time that someone accesses the cached reactive. It is only re-executed if
|
||||
it has been invalidated by one of the reactives it depends on. For
|
||||
example, suppose we have this cached reactive:\if{html}{\out{<div class="NA">}}\preformatted{r <- reactive(\{ input$x * input$y \}) \%>\%
|
||||
example, suppose we have this cached reactive:\if{html}{\out{<div class="sourceCode NA">}}\preformatted{r <- reactive(\{ input$x * input$y \}) \%>\%
|
||||
bindCache(input$x, input$y)
|
||||
}\if{html}{\out{</div>}}
|
||||
|
||||
@@ -244,7 +244,7 @@ cache collisions, dealing with internal state that may be set by the,
|
||||
the cache.
|
||||
|
||||
You may need to provide a \code{cacheHint} to \code{\link[=createRenderFunction]{createRenderFunction()}} (or
|
||||
\code{\link[htmlwidgets:htmlwidgets-shiny]{htmlwidgets::shinyRenderWidget()}}, if you've authored an htmlwidget) in
|
||||
\code{htmlwidgets::shinyRenderWidget()}, if you've authored an htmlwidget) in
|
||||
order for \code{bindCache()} to correctly compute a cache key.
|
||||
|
||||
The potential problem is a cache collision. Consider the following:\preformatted{output$x1 <- renderText(\{ input$x \}) \%>\% bindCache(input$x)
|
||||
@@ -258,7 +258,7 @@ this, a \emph{cache hint} is automatically added when \code{\link[=renderText]{r
|
||||
\code{\link[=createRenderFunction]{createRenderFunction()}}. The cache hint is used as part of the actual
|
||||
cache key, in addition to the one passed to \code{bindCache()} by the user. The
|
||||
cache hint can be viewed by calling the internal Shiny function
|
||||
\code{extractCacheHint()}:\if{html}{\out{<div class="NA">}}\preformatted{r <- renderText(\{ input$x \})
|
||||
\code{extractCacheHint()}:\if{html}{\out{<div class="sourceCode NA">}}\preformatted{r <- renderText(\{ input$x \})
|
||||
shiny:::extractCacheHint(r)
|
||||
}\if{html}{\out{</div>}}
|
||||
|
||||
@@ -275,11 +275,11 @@ render function for cache collisions in a real application.
|
||||
In some cases, however, the automatic cache hint inference is not
|
||||
sufficient, and it is necessary to provide a cache hint. This is true
|
||||
for \code{renderPrint()}. Unlike \code{renderText()}, it wraps the user-provided
|
||||
expression in another function, before passing it to \code{\link[=markRenderFunction]{markRenderFunction()}}
|
||||
expression in another function, before passing it to \code{\link[=createRenderFunction]{createRenderFunction()}}
|
||||
(instead of \code{\link[=createRenderFunction]{createRenderFunction()}}). Because the user code is wrapped in
|
||||
another function, \code{markRenderFunction()} is not able to automatically
|
||||
another function, \code{createRenderFunction()} is not able to automatically
|
||||
extract the user-provided code and use it in the cache key. Instead,
|
||||
\code{renderPrint} calls \code{markRenderFunction()}, it explicitly passes along a
|
||||
\code{renderPrint} calls \code{createRenderFunction()}, it explicitly passes along a
|
||||
\code{cacheHint}, which includes a label and the original user expression.
|
||||
|
||||
In general, if you need to provide a \code{cacheHint}, it is best practice to
|
||||
@@ -290,19 +290,19 @@ For \pkg{htmlwidgets}, it will try to automatically infer a cache hint;
|
||||
again, you can inspect the cache hint with \code{shiny:::extractCacheHint()} and
|
||||
also test it in an application. If you do need to explicitly provide a
|
||||
cache hint, pass it to \code{shinyRenderWidget}. For example:\preformatted{renderMyWidget <- function(expr) \{
|
||||
expr <- substitute(expr)
|
||||
q <- rlang::enquo0(expr)
|
||||
|
||||
htmlwidgets::shinyRenderWidget(expr,
|
||||
htmlwidgets::shinyRenderWidget(
|
||||
q,
|
||||
myWidgetOutput,
|
||||
quoted = TRUE,
|
||||
env = parent.frame(),
|
||||
cacheHint = list(label = "myWidget", userExpr = expr)
|
||||
cacheHint = list(label = "myWidget", userQuo = q)
|
||||
)
|
||||
\}
|
||||
}
|
||||
|
||||
If your \code{render} function sets any internal state, you may find it useful
|
||||
in your call to \code{\link[=createRenderFunction]{createRenderFunction()}} or \code{\link[=markRenderFunction]{markRenderFunction()}} to use
|
||||
in your call to \code{\link[=createRenderFunction]{createRenderFunction()}} to use
|
||||
the \code{cacheWriteHook} and/or \code{cacheReadHook} parameters. These hooks are
|
||||
functions that run just before the object is stored in the cache, and just
|
||||
after the object is retrieved from the cache. They can modify the data
|
||||
@@ -321,8 +321,8 @@ Some render functions cannot be cached, typically because they have side
|
||||
effects or modify some external state, and they must re-execute each time
|
||||
in order to work properly.
|
||||
|
||||
For developers of such code, they should call \code{\link[=createRenderFunction]{createRenderFunction()}} or
|
||||
\code{\link[=markRenderFunction]{markRenderFunction()}} with \code{cacheHint = FALSE}.
|
||||
For developers of such code, they should call \code{\link[=createRenderFunction]{createRenderFunction()}} (or
|
||||
\code{\link[=markRenderFunction]{markRenderFunction()}}) with \code{cacheHint = FALSE}.
|
||||
}
|
||||
|
||||
\section{Caching with \code{renderPlot()}}{
|
||||
|
||||
@@ -171,7 +171,7 @@ user sets both \code{x} and \code{y}, and then clicks on an \link{actionButton}
|
||||
\code{go}.
|
||||
|
||||
To use both caching and events, the object should first be passed to
|
||||
\code{bindCache()}, then \code{bindEvent()}. For example:\if{html}{\out{<div class="NA">}}\preformatted{r <- reactive(\{
|
||||
\code{bindCache()}, then \code{bindEvent()}. For example:\if{html}{\out{<div class="sourceCode NA">}}\preformatted{r <- reactive(\{
|
||||
Sys.sleep(2) # Pretend this is an expensive computation
|
||||
input$x * input$y
|
||||
\}) \%>\%
|
||||
|
||||
@@ -49,4 +49,3 @@ These functions give control over the \code{click}, \code{dblClick} and
|
||||
\seealso{
|
||||
\code{\link[=brushOpts]{brushOpts()}} for brushing events.
|
||||
}
|
||||
\keyword{internal}
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
% Generated by roxygen2: do not edit by hand
|
||||
% Please edit documentation in R/shinywrappers.R
|
||||
% Please edit documentation in R/shinywrappers.R, R/utils-lang.R
|
||||
\name{createRenderFunction}
|
||||
\alias{createRenderFunction}
|
||||
\title{Implement render functions}
|
||||
\alias{quoToFunction}
|
||||
\alias{installExprFunction}
|
||||
\title{Implement custom render functions}
|
||||
\usage{
|
||||
createRenderFunction(
|
||||
func,
|
||||
@@ -13,6 +15,19 @@ createRenderFunction(
|
||||
cacheWriteHook = NULL,
|
||||
cacheReadHook = NULL
|
||||
)
|
||||
|
||||
quoToFunction(q, label = sys.call(-1)[[1]], ..stacktraceon = FALSE)
|
||||
|
||||
installExprFunction(
|
||||
expr,
|
||||
name,
|
||||
eval.env = parent.frame(2),
|
||||
quoted = FALSE,
|
||||
assign.env = parent.frame(1),
|
||||
label = sys.call(-1)[[1]],
|
||||
wrappedWithLabel = TRUE,
|
||||
..stacktraceon = FALSE
|
||||
)
|
||||
}
|
||||
\arguments{
|
||||
\item{func}{A function without parameters, that returns user data. If the
|
||||
@@ -55,23 +70,85 @@ argument, the value retrieved from the cache. This can be useful when some
|
||||
side effect needs to occur for a render function to behave correctly. For
|
||||
example, some render functions call \code{\link[=createWebDependency]{createWebDependency()}} so that Shiny
|
||||
is able to serve JS and CSS resources.}
|
||||
|
||||
\item{q}{Quosure of the expression \code{x}. When capturing expressions to create
|
||||
your quosure, it is recommended to use \code{\link[=enquo0]{enquo0()}} to not unquote the
|
||||
object too early. See \code{\link[=enquo0]{enquo0()}} for more details.}
|
||||
|
||||
\item{label}{A label for the object to be shown in the debugger. Defaults to
|
||||
the name of the calling function.}
|
||||
|
||||
\item{expr}{A quoted or unquoted expression, or a quosure.}
|
||||
|
||||
\item{name}{The name the function should be given}
|
||||
|
||||
\item{eval.env}{The desired environment for the function. Defaults to the
|
||||
calling environment two steps back.}
|
||||
|
||||
\item{quoted}{Is the expression quoted?}
|
||||
|
||||
\item{assign.env}{The environment in which the function should be assigned.}
|
||||
|
||||
\item{wrappedWithLabel, ..stacktraceon}{Advanced use only. For stack manipulation purposes; see
|
||||
\code{\link[=stacktrace]{stacktrace()}}.}
|
||||
}
|
||||
\value{
|
||||
An annotated render function, ready to be assigned to an
|
||||
\code{output} slot.
|
||||
}
|
||||
\description{
|
||||
This function is a wrapper for \code{\link[=markRenderFunction]{markRenderFunction()}} which provides support
|
||||
for async computation via promises.
|
||||
Developer-facing utilities for implementing a custom \code{renderXXX()} function.
|
||||
Before using these utilities directly, consider using the \href{http://www.htmlwidgets.org/develop_intro.html}{\code{htmlwidgets} package} to implement custom
|
||||
outputs (i.e., custom \code{renderXXX()}/\code{xxxOutput()} functions). That said,
|
||||
these utilities can be used more directly if a full-blown htmlwidget isn't
|
||||
needed and/or the user-supplied reactive expression needs to be wrapped in
|
||||
additional call(s).
|
||||
}
|
||||
\details{
|
||||
To implement a custom \code{renderXXX()} function, essentially 2 things are needed:
|
||||
\enumerate{
|
||||
\item Capture the user's reactive expression as a function.
|
||||
\itemize{
|
||||
\item New \code{renderXXX()} functions can use \code{quoToFunction()} for this, but
|
||||
already existing \code{renderXXX()} functions that contain \code{env} and \code{quoted}
|
||||
parameters may want to continue using \code{installExprFunction()} for better
|
||||
legacy support (see examples).
|
||||
}
|
||||
\item Flag the resulting function (from 1) as a Shiny rendering function and
|
||||
also provide a UI container for displaying the result of the rendering
|
||||
function.
|
||||
\itemize{
|
||||
\item \code{createRenderFunction()} is currently recommended (instead of
|
||||
\code{\link[=markRenderFunction]{markRenderFunction()}}) for this step (see examples).
|
||||
}
|
||||
}
|
||||
}
|
||||
\section{Functions}{
|
||||
\itemize{
|
||||
\item \code{quoToFunction}: convert a quosure to a function.
|
||||
|
||||
\item \code{installExprFunction}: converts a user's reactive \code{expr} into a
|
||||
function that's assigned to a \code{name} in the \code{assign.env}.
|
||||
}}
|
||||
|
||||
\examples{
|
||||
# A very simple render function
|
||||
renderTriple <- function(x) {
|
||||
x <- substitute(x)
|
||||
if (!rlang::is_quosure(x)) {
|
||||
x <- rlang::new_quosure(x, env = parent.frame())
|
||||
}
|
||||
func <- quoToFunction(x, "renderTriple")
|
||||
# A custom render function that repeats the supplied value 3 times
|
||||
renderTriple <- function(expr) {
|
||||
# Wrap user-supplied reactive expression into a function
|
||||
func <- quoToFunction(rlang::enquo0(expr))
|
||||
|
||||
createRenderFunction(
|
||||
func,
|
||||
transform = function(value, session, name, ...) {
|
||||
paste(rep(value, 3), collapse=", ")
|
||||
},
|
||||
outputFunc = textOutput
|
||||
)
|
||||
}
|
||||
|
||||
# For better legacy support, consider using installExprFunction() over quoToFunction()
|
||||
renderTripleLegacy <- function(expr, env = parent.frame(), quoted = FALSE) {
|
||||
func <- installExprFunction(expr, "func", env, quoted)
|
||||
|
||||
createRenderFunction(
|
||||
func,
|
||||
@@ -83,11 +160,36 @@ renderTriple <- function(x) {
|
||||
}
|
||||
|
||||
# Test render function from the console
|
||||
a <- 1
|
||||
r <- renderTriple({ a + 1 })
|
||||
a <- 2
|
||||
reactiveConsole(TRUE)
|
||||
|
||||
v <- reactiveVal("basic")
|
||||
r <- renderTriple({ v() })
|
||||
r()
|
||||
}
|
||||
\seealso{
|
||||
\code{\link[=quoToFunction]{quoToFunction()}}, \code{\link[=markRenderFunction]{markRenderFunction()}}.
|
||||
#> [1] "basic, basic, basic"
|
||||
|
||||
# User can supply quoted code via rlang::quo(). Note that evaluation of the
|
||||
# expression happens when r2() is invoked, not when r2 is created.
|
||||
q <- rlang::quo({ v() })
|
||||
r2 <- rlang::inject(renderTriple(!!q))
|
||||
v("rlang")
|
||||
r2()
|
||||
#> [1] "rlang, rlang, rlang"
|
||||
|
||||
# Supplying quoted code without rlang::quo() requires installExprFunction()
|
||||
expr <- quote({ v() })
|
||||
r3 <- renderTripleLegacy(expr, quoted = TRUE)
|
||||
v("legacy")
|
||||
r3()
|
||||
#> [1] "legacy, legacy, legacy"
|
||||
|
||||
# The legacy approach also supports with quosures (env is ignored in this case)
|
||||
q <- rlang::quo({ v() })
|
||||
r4 <- renderTripleLegacy(q, quoted = TRUE)
|
||||
v("legacy-rlang")
|
||||
r4()
|
||||
#> [1] "legacy-rlang, legacy-rlang, legacy-rlang"
|
||||
|
||||
# Turn off reactivity in the console
|
||||
reactiveConsole(FALSE)
|
||||
|
||||
}
|
||||
|
||||
@@ -101,7 +101,7 @@ displays a message once every 8 hrs (by default)
|
||||
value and Developer message. This registration method allows package
|
||||
authors to write one message in a single location.
|
||||
|
||||
For example, the following Shiny Developer Mode options are registered:\if{html}{\out{<div class="r">}}\preformatted{# Reload the Shiny app when a sourced R file changes
|
||||
For example, the following Shiny Developer Mode options are registered:\if{html}{\out{<div class="sourceCode r">}}\preformatted{# Reload the Shiny app when a sourced R file changes
|
||||
register_devmode_option(
|
||||
"shiny.autoreload",
|
||||
"Turning on shiny autoreload. To disable, call `options(shiny.autoreload = FALSE)`",
|
||||
@@ -126,7 +126,7 @@ register_devmode_option(
|
||||
Other known, non-Shiny Developer Mode options:
|
||||
\itemize{
|
||||
\item Sass:
|
||||
}\if{html}{\out{<div class="r">}}\preformatted{# Display the full stack trace when errors occur during Shiny app execution
|
||||
}\if{html}{\out{<div class="sourceCode r">}}\preformatted{# Display the full stack trace when errors occur during Shiny app execution
|
||||
register_devmode_option(
|
||||
"sass.cache",
|
||||
"Turning off sass cache. To use default caching, call `options(sass.cache = TRUE)`",
|
||||
@@ -165,7 +165,7 @@ re-implementing these two functions:
|
||||
|
||||
This function should return \code{TRUE} if \code{getOption("shiny.devmode")} is set.
|
||||
In addition, we strongly recommend that it also checks to make sure
|
||||
\code{testthat} is not testing.\if{html}{\out{<div class="r">}}\preformatted{in_devmode <- function() \{
|
||||
\code{testthat} is not testing.\if{html}{\out{<div class="sourceCode r">}}\preformatted{in_devmode <- function() \{
|
||||
isTRUE(getOption("shiny.devmode", FALSE)) &&
|
||||
!identical(Sys.getenv("TESTTHAT"), "true")
|
||||
\}
|
||||
@@ -201,7 +201,7 @@ recommend displaying a message (\code{devmode_message}) to \code{stderr()} once
|
||||
hours using \code{\link[rlang:abort]{rlang::inform()}}. This will keep the author up to date as to
|
||||
which Dev Mode options are being altered. To allow developers a chance to
|
||||
disable Dev Mode messages, the message should be skipped if
|
||||
\code{getOption("shiny.devmode.verbose", TRUE)} is not \code{TRUE}.\if{html}{\out{<div class="r">}}\preformatted{get_devmode_option <- function(name, default = NULL, devmode_default, devmode_message) \{
|
||||
\code{getOption("shiny.devmode.verbose", TRUE)} is not \code{TRUE}.\if{html}{\out{<div class="sourceCode r">}}\preformatted{get_devmode_option <- function(name, default = NULL, devmode_default, devmode_message) \{
|
||||
if (!in_devmode()) \{
|
||||
# Dev Mode disabled, act like `getOption()`
|
||||
return(getOption(name, default = default))
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
% Generated by roxygen2: do not edit by hand
|
||||
% Please edit documentation in R/utils.R
|
||||
% Please edit documentation in R/utils-lang.R
|
||||
\name{exprToFunction}
|
||||
\alias{exprToFunction}
|
||||
\title{Convert an expression to a function}
|
||||
@@ -7,7 +7,7 @@
|
||||
exprToFunction(expr, env = parent.frame(), quoted = FALSE)
|
||||
}
|
||||
\arguments{
|
||||
\item{expr}{A quoted or unquoted expression, or a function.}
|
||||
\item{expr}{A quoted or unquoted expression, or a quosure.}
|
||||
|
||||
\item{env}{The desired environment for the function. Defaults to the
|
||||
calling environment two steps back.}
|
||||
@@ -15,47 +15,14 @@ calling environment two steps back.}
|
||||
\item{quoted}{Is the expression quoted?}
|
||||
}
|
||||
\description{
|
||||
This is to be called from another function, because it will attempt to get
|
||||
an unquoted expression from two calls back. Note: as of Shiny 1.6.0, it is
|
||||
recommended to use \code{\link[=quoToFunction]{quoToFunction()}} instead.
|
||||
\ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#superseded}{\figure{lifecycle-superseded.svg}{options: alt='[Superseded]'}}}{\strong{[Superseded]}} Please use \code{\link[=installExprFunction]{installExprFunction()}} for a better
|
||||
debugging experience (Shiny 0.8.0). If the \code{expr} and \code{quoted} parameters are not needed, please see
|
||||
\code{\link[=quoToFunction]{quoToFunction()}} (Shiny 1.6.0).
|
||||
}
|
||||
\details{
|
||||
If expr is a quoted expression, then this just converts it to a function.
|
||||
If expr is a function, then this simply returns expr (and prints a
|
||||
deprecation message).
|
||||
If expr was a non-quoted expression from two calls back, then this will
|
||||
quote the original expression and convert it to a function.
|
||||
Similar to \code{\link[=installExprFunction]{installExprFunction()}} but doesn't register debug hooks.
|
||||
}
|
||||
\examples{
|
||||
# Example of a new renderer, similar to renderText
|
||||
# This is something that toolkit authors will do
|
||||
renderTriple <- function(expr, env=parent.frame(), quoted=FALSE) {
|
||||
# Convert expr to a function
|
||||
func <- shiny::exprToFunction(expr, env, quoted)
|
||||
|
||||
function() {
|
||||
value <- func()
|
||||
paste(rep(value, 3), collapse=", ")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
# Example of using the renderer.
|
||||
# This is something that app authors will do.
|
||||
values <- reactiveValues(A="text")
|
||||
|
||||
\dontrun{
|
||||
# Create an output object
|
||||
output$tripleA <- renderTriple({
|
||||
values$A
|
||||
})
|
||||
}
|
||||
|
||||
# At the R console, you can experiment with the renderer using isolate()
|
||||
tripleA <- renderTriple({
|
||||
values$A
|
||||
})
|
||||
|
||||
isolate(tripleA())
|
||||
# "text, text, text"
|
||||
\seealso{
|
||||
\code{\link[=installExprFunction]{installExprFunction()}} for the modern approach to converting an expression to a function
|
||||
}
|
||||
\keyword{internal}
|
||||
|
||||
@@ -11,7 +11,7 @@ insertTab(
|
||||
inputId,
|
||||
tab,
|
||||
target = NULL,
|
||||
position = c("before", "after"),
|
||||
position = c("after", "before"),
|
||||
select = FALSE,
|
||||
session = getDefaultReactiveDomain()
|
||||
)
|
||||
|
||||
@@ -1,50 +0,0 @@
|
||||
% Generated by roxygen2: do not edit by hand
|
||||
% Please edit documentation in R/utils.R
|
||||
\name{installExprFunction}
|
||||
\alias{installExprFunction}
|
||||
\title{Install an expression as a function}
|
||||
\usage{
|
||||
installExprFunction(
|
||||
expr,
|
||||
name,
|
||||
eval.env = parent.frame(2),
|
||||
quoted = FALSE,
|
||||
assign.env = parent.frame(1),
|
||||
label = deparse(sys.call(-1)[[1]]),
|
||||
wrappedWithLabel = TRUE,
|
||||
..stacktraceon = FALSE
|
||||
)
|
||||
}
|
||||
\arguments{
|
||||
\item{expr}{A quoted or unquoted expression}
|
||||
|
||||
\item{name}{The name the function should be given}
|
||||
|
||||
\item{eval.env}{The desired environment for the function. Defaults to the
|
||||
calling environment two steps back.}
|
||||
|
||||
\item{quoted}{Is the expression quoted?}
|
||||
|
||||
\item{assign.env}{The environment in which the function should be assigned.}
|
||||
|
||||
\item{label}{A label for the object to be shown in the debugger. Defaults to
|
||||
the name of the calling function.}
|
||||
|
||||
\item{wrappedWithLabel, ..stacktraceon}{Advanced use only. For stack manipulation purposes; see
|
||||
\code{\link[=stacktrace]{stacktrace()}}.}
|
||||
}
|
||||
\description{
|
||||
Installs an expression in the given environment as a function, and registers
|
||||
debug hooks so that breakpoints may be set in the function. Note: as of
|
||||
Shiny 1.6.0, it is recommended to use \code{\link[=quoToFunction]{quoToFunction()}} instead.
|
||||
}
|
||||
\details{
|
||||
This function can replace \code{exprToFunction} as follows: we may use
|
||||
\code{func <- exprToFunction(expr)} if we do not want the debug hooks, or
|
||||
\code{installExprFunction(expr, "func")} if we do. Both approaches create a
|
||||
function named \code{func} in the current environment.
|
||||
}
|
||||
\seealso{
|
||||
Wraps \code{\link[=exprToFunction]{exprToFunction()}}; see that method's documentation
|
||||
for more documentation and examples.
|
||||
}
|
||||
@@ -24,3 +24,4 @@ knit_print.reactive(x, ..., inline = FALSE)
|
||||
These S3 methods are necessary to help Shiny applications and UI chunks embed
|
||||
themselves in knitr/rmarkdown documents.
|
||||
}
|
||||
\keyword{internal}
|
||||
|
||||
@@ -51,12 +51,24 @@ is able to serve JS and CSS resources.}
|
||||
The \code{renderFunc} function, with annotations.
|
||||
}
|
||||
\description{
|
||||
\ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#superseded}{\figure{lifecycle-superseded.svg}{options: alt='[Superseded]'}}}{\strong{[Superseded]}} Please use \code{\link[=createRenderFunction]{createRenderFunction()}} to
|
||||
support async execution. (Shiny 1.1.0)
|
||||
}
|
||||
\details{
|
||||
Should be called by implementers of \code{renderXXX} functions in order to mark
|
||||
their return values as Shiny render functions, and to provide a hint to Shiny
|
||||
regarding what UI function is most commonly used with this type of render
|
||||
function. This can be used in R Markdown documents to create complete output
|
||||
widgets out of just the render function.
|
||||
|
||||
Note that it is generally preferable to use \code{\link[=createRenderFunction]{createRenderFunction()}} instead
|
||||
of \code{markRenderFunction()}. It essentially wraps up the user-provided
|
||||
expression in the \code{transform} function passed to it, then passes the resulting
|
||||
function to \code{markRenderFunction()}. It also provides a simpler calling
|
||||
interface. There may be cases where \code{markRenderFunction()} must be used instead of
|
||||
\code{\link[=createRenderFunction]{createRenderFunction()}} -- for example, when the \code{transform} parameter of
|
||||
\code{\link[=createRenderFunction]{createRenderFunction()}} is not flexible enough for your needs.
|
||||
}
|
||||
\seealso{
|
||||
\code{\link[=createRenderFunction]{createRenderFunction()}}, \code{\link[=quoToFunction]{quoToFunction()}}
|
||||
\code{\link[=createRenderFunction]{createRenderFunction()}}
|
||||
}
|
||||
|
||||
@@ -23,11 +23,13 @@ ignored.}
|
||||
|
||||
\item{env}{The parent environment for the reactive expression. By default,
|
||||
this is the calling environment, the same as when defining an ordinary
|
||||
non-reactive expression.}
|
||||
non-reactive expression. If \code{x} is a quosure and \code{quoted} is \code{TRUE},
|
||||
then \code{env} is ignored.}
|
||||
|
||||
\item{quoted}{Is the expression quoted? By default, this is \code{FALSE}.
|
||||
This is useful when you want to use an expression that is stored in a
|
||||
variable; to do so, it must be quoted with \code{quote()}.}
|
||||
\item{quoted}{If it is \code{TRUE}, then the \code{\link[=quote]{quote()}}ed value of \code{x}
|
||||
will be used when \code{x} is evaluated. If \code{x} is a quosure and you
|
||||
would like to use its expression as a value for \code{x}, then you must set
|
||||
\code{quoted} to \code{TRUE}.}
|
||||
|
||||
\item{...}{Not used.}
|
||||
|
||||
@@ -116,12 +118,12 @@ obsB <- observe({
|
||||
print(values$A + 1)
|
||||
})
|
||||
|
||||
# Can use quoted expressions
|
||||
obsC <- observe(quote({ print(values$A + 2) }), quoted = TRUE)
|
||||
# To store expressions for later conversion to observe, use rlang::quo()
|
||||
myquo <- rlang::quo({ print(values$A + 3) })
|
||||
obsC <- rlang::inject(observe(!!myquo))
|
||||
|
||||
# To store expressions for later conversion to observe, use quote()
|
||||
expr_q <- quote({ print(values$A + 3) })
|
||||
obsD <- observe(expr_q, quoted = TRUE)
|
||||
# (Legacy) Can use quoted expressions
|
||||
obsD <- observe(quote({ print(values$A + 2) }), quoted = TRUE)
|
||||
|
||||
# In a normal Shiny app, the web client will trigger flush events. If you
|
||||
# are at the console, you can force a flush with flushReact()
|
||||
|
||||
@@ -48,21 +48,25 @@ invalidated. This should be a side-effect-producing action (the return
|
||||
value will be ignored). It will be executed within an \code{\link[=isolate]{isolate()}}
|
||||
scope.}
|
||||
|
||||
\item{event.env}{The parent environment for \code{eventExpr}. By default,
|
||||
this is the calling environment.}
|
||||
\item{event.env}{The parent environment for the reactive expression. By default,
|
||||
this is the calling environment, the same as when defining an ordinary
|
||||
non-reactive expression. If \code{eventExpr} is a quosure and \code{event.quoted} is \code{TRUE},
|
||||
then \code{event.env} is ignored.}
|
||||
|
||||
\item{event.quoted}{Is the \code{eventExpr} expression quoted? By default,
|
||||
this is \code{FALSE}. This is useful when you want to use an expression
|
||||
that is stored in a variable; to do so, it must be quoted with
|
||||
\code{quote()}.}
|
||||
\item{event.quoted}{If it is \code{TRUE}, then the \code{\link[=quote]{quote()}}ed value of \code{eventExpr}
|
||||
will be used when \code{eventExpr} is evaluated. If \code{eventExpr} is a quosure and you
|
||||
would like to use its expression as a value for \code{eventExpr}, then you must set
|
||||
\code{event.quoted} to \code{TRUE}.}
|
||||
|
||||
\item{handler.env}{The parent environment for \code{handlerExpr}. By default,
|
||||
this is the calling environment.}
|
||||
\item{handler.env}{The parent environment for the reactive expression. By default,
|
||||
this is the calling environment, the same as when defining an ordinary
|
||||
non-reactive expression. If \code{handlerExpr} is a quosure and \code{handler.quoted} is \code{TRUE},
|
||||
then \code{handler.env} is ignored.}
|
||||
|
||||
\item{handler.quoted}{Is the \code{handlerExpr} expression quoted? By
|
||||
default, this is \code{FALSE}. This is useful when you want to use an
|
||||
expression that is stored in a variable; to do so, it must be quoted with
|
||||
\code{quote()}.}
|
||||
\item{handler.quoted}{If it is \code{TRUE}, then the \code{\link[=quote]{quote()}}ed value of \code{handlerExpr}
|
||||
will be used when \code{handlerExpr} is evaluated. If \code{handlerExpr} is a quosure and you
|
||||
would like to use its expression as a value for \code{handlerExpr}, then you must set
|
||||
\code{handler.quoted} to \code{TRUE}.}
|
||||
|
||||
\item{...}{Currently not used.}
|
||||
|
||||
@@ -99,12 +103,15 @@ happen once.}
|
||||
\code{eventReactive}. It will be executed within an \code{\link[=isolate]{isolate()}}
|
||||
scope.}
|
||||
|
||||
\item{value.env}{The parent environment for \code{valueExpr}. By default,
|
||||
this is the calling environment.}
|
||||
\item{value.env}{The parent environment for the reactive expression. By default,
|
||||
this is the calling environment, the same as when defining an ordinary
|
||||
non-reactive expression. If \code{valueExpr} is a quosure and \code{value.quoted} is \code{TRUE},
|
||||
then \code{value.env} is ignored.}
|
||||
|
||||
\item{value.quoted}{Is the \code{valueExpr} expression quoted? By default,
|
||||
this is \code{FALSE}. This is useful when you want to use an expression
|
||||
that is stored in a variable; to do so, it must be quoted with \code{quote()}.}
|
||||
\item{value.quoted}{If it is \code{TRUE}, then the \code{\link[=quote]{quote()}}ed value of \code{valueExpr}
|
||||
will be used when \code{valueExpr} is evaluated. If \code{valueExpr} is a quosure and you
|
||||
would like to use its expression as a value for \code{valueExpr}, then you must set
|
||||
\code{value.quoted} to \code{TRUE}.}
|
||||
}
|
||||
\value{
|
||||
\code{observeEvent} returns an observer reference class object (see
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
% Generated by roxygen2: do not edit by hand
|
||||
% Please edit documentation in R/utils.R
|
||||
\name{quoToFunction}
|
||||
\alias{quoToFunction}
|
||||
\title{Convert a quosure to a function for a Shiny render function}
|
||||
\usage{
|
||||
quoToFunction(q, label, ..stacktraceon = FALSE)
|
||||
}
|
||||
\arguments{
|
||||
\item{q}{A quosure.}
|
||||
|
||||
\item{label}{A label for the object to be shown in the debugger. Defaults to
|
||||
the name of the calling function.}
|
||||
|
||||
\item{..stacktraceon}{Advanced use only. For stack manipulation purposes; see
|
||||
\code{\link[=stacktrace]{stacktrace()}}.}
|
||||
}
|
||||
\description{
|
||||
This takes a quosure and label, and wraps them into a function that should be
|
||||
passed to \code{\link[=createRenderFunction]{createRenderFunction()}} or \code{\link[=markRenderFunction]{markRenderFunction()}}.
|
||||
}
|
||||
\details{
|
||||
This function was added in Shiny 1.6.0. Previously, it was recommended to use
|
||||
\code{\link[=installExprFunction]{installExprFunction()}} or \code{\link[=exprToFunction]{exprToFunction()}} in render functions, but now we
|
||||
recommend using \code{\link[=quoToFunction]{quoToFunction()}}, because it does not require \code{env} and
|
||||
\code{quoted} arguments -- that information is captured by quosures provided by
|
||||
\pkg{rlang}.
|
||||
}
|
||||
\seealso{
|
||||
\code{\link[=createRenderFunction]{createRenderFunction()}} for example usage.
|
||||
}
|
||||
@@ -18,16 +18,17 @@ reactive(
|
||||
is.reactive(x)
|
||||
}
|
||||
\arguments{
|
||||
\item{x}{For \code{reactive}, an expression (quoted or unquoted). For
|
||||
\code{is.reactive}, an object to test.}
|
||||
\item{x}{For \code{is.reactive()}, an object to test. For \code{reactive()}, an expression. When passing in a \code{\link[=quo]{quo()}}sure with \code{reactive()}, remember to use \code{\link[rlang:inject]{rlang::inject()}} to distinguish that you are passing in the content of your quosure, not the expression of the quosure.}
|
||||
|
||||
\item{env}{The parent environment for the reactive expression. By default,
|
||||
this is the calling environment, the same as when defining an ordinary
|
||||
non-reactive expression.}
|
||||
non-reactive expression. If \code{x} is a quosure and \code{quoted} is \code{TRUE},
|
||||
then \code{env} is ignored.}
|
||||
|
||||
\item{quoted}{Is the expression quoted? By default, this is \code{FALSE}.
|
||||
This is useful when you want to use an expression that is stored in a
|
||||
variable; to do so, it must be quoted with \code{quote()}.}
|
||||
\item{quoted}{If it is \code{TRUE}, then the \code{\link[=quote]{quote()}}ed value of \code{x}
|
||||
will be used when \code{x} is evaluated. If \code{x} is a quosure and you
|
||||
would like to use its expression as a value for \code{x}, then you must set
|
||||
\code{quoted} to \code{TRUE}.}
|
||||
|
||||
\item{...}{Not used.}
|
||||
|
||||
@@ -58,21 +59,32 @@ See the \href{https://shiny.rstudio.com/tutorial/}{Shiny tutorial} for
|
||||
more information about reactive expressions.
|
||||
}
|
||||
\examples{
|
||||
library(rlang)
|
||||
values <- reactiveValues(A=1)
|
||||
|
||||
reactiveB <- reactive({
|
||||
values$A + 1
|
||||
})
|
||||
|
||||
# Can use quoted expressions
|
||||
reactiveC <- reactive(quote({ values$A + 2 }), quoted = TRUE)
|
||||
|
||||
# To store expressions for later conversion to reactive, use quote()
|
||||
expr_q <- quote({ values$A + 3 })
|
||||
reactiveD <- reactive(expr_q, quoted = TRUE)
|
||||
|
||||
# View the values from the R console with isolate()
|
||||
isolate(reactiveB())
|
||||
# 2
|
||||
|
||||
# To store expressions for later conversion to reactive, use quote()
|
||||
myquo <- rlang::quo(values$A + 2)
|
||||
# Unexpected value! Sending a quosure directly will not work as expected.
|
||||
reactiveC <- reactive(myquo)
|
||||
# We'd hope for `3`, but instead we get the quosure that was supplied.
|
||||
isolate(reactiveC())
|
||||
|
||||
# Instead, the quosure should be `rlang::inject()`ed
|
||||
reactiveD <- rlang::inject(reactive(!!myquo))
|
||||
isolate(reactiveD())
|
||||
# 3
|
||||
|
||||
# (Legacy) Can use quoted expressions
|
||||
expr <- quote({ values$A + 3 })
|
||||
reactiveE <- reactive(expr, quoted = TRUE)
|
||||
isolate(reactiveE())
|
||||
# 4
|
||||
|
||||
}
|
||||
|
||||
@@ -51,12 +51,12 @@ registerInputHandler("mypackage.validint", function(x, shinysession, name) {
|
||||
})
|
||||
|
||||
## On the Javascript side, the associated input binding must have a corresponding getType method:
|
||||
getType: function(el) {
|
||||
return "mypackage.validint";
|
||||
}
|
||||
# getType: function(el) {
|
||||
# return "mypackage.validint";
|
||||
# }
|
||||
|
||||
}
|
||||
}
|
||||
\seealso{
|
||||
\code{\link[=removeInputHandler]{removeInputHandler()}}
|
||||
\code{\link[=removeInputHandler]{removeInputHandler()}} \code{\link[=applyInputHandlers]{applyInputHandlers()}}
|
||||
}
|
||||
|
||||
@@ -219,7 +219,7 @@ shinyOptions(cache = cachem::cache_mem(max_size = 20e6, max_age = 3600))
|
||||
# At the top of app.R, this set the application-scoped cache to be a disk
|
||||
# cache that can be shared among multiple concurrent R processes, and is
|
||||
# deleted when the system reboots.
|
||||
shinyOptions(cache = cachem::cache_disk(file.path(dirname(tempdir()), "myapp-cache"))
|
||||
shinyOptions(cache = cachem::cache_disk(file.path(dirname(tempdir()), "myapp-cache")))
|
||||
|
||||
# At the top of app.R, this set the application-scoped cache to be a disk
|
||||
# cache that can be shared among multiple concurrent R processes, and
|
||||
|
||||
@@ -48,16 +48,25 @@ indicate which columns to escape, e.g. \code{1:5} (the first 5 columns),
|
||||
\code{c(1, 3, 4)}, or \code{c(-1, -3)} (all columns except the first and
|
||||
third), or \code{c('Species', 'Sepal.Length')}.}
|
||||
|
||||
\item{env}{The environment in which to evaluate \code{expr}.}
|
||||
\item{env}{The parent environment for the reactive expression. By default,
|
||||
this is the calling environment, the same as when defining an ordinary
|
||||
non-reactive expression. If \code{expr} is a quosure and \code{quoted} is \code{TRUE},
|
||||
then \code{env} is ignored.}
|
||||
|
||||
\item{quoted}{Is \code{expr} a quoted expression (with \code{quote()})?
|
||||
This is useful if you want to save an expression in a variable.}
|
||||
\item{quoted}{If it is \code{TRUE}, then the \code{\link[=quote]{quote()}}ed value of \code{expr}
|
||||
will be used when \code{expr} is evaluated. If \code{expr} is a quosure and you
|
||||
would like to use its expression as a value for \code{expr}, then you must set
|
||||
\code{quoted} to \code{TRUE}.}
|
||||
|
||||
\item{outputArgs}{A list of arguments to be passed through to the implicit
|
||||
call to \code{dataTableOutput()} when \code{renderDataTable()} is used
|
||||
in an interactive R Markdown document.}
|
||||
}
|
||||
\description{
|
||||
\ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#superseded}{\figure{lifecycle-superseded.svg}{options: alt='[Superseded]'}}}{\strong{[Superseded]}} Please use
|
||||
\href{https://rstudio.github.io/DT/shiny.html}{\code{DT::renderDataTable()}}.
|
||||
(Shiny 0.11.1)
|
||||
|
||||
Makes a reactive version of the given function that returns a data frame (or
|
||||
matrix), which will be rendered with the \href{https://datatables.net}{DataTables}
|
||||
library. Paging, searching, filtering, and sorting can be done on the R side
|
||||
|
||||
@@ -15,10 +15,15 @@ renderImage(
|
||||
\arguments{
|
||||
\item{expr}{An expression that returns a list.}
|
||||
|
||||
\item{env}{The environment in which to evaluate \code{expr}.}
|
||||
\item{env}{The parent environment for the reactive expression. By default,
|
||||
this is the calling environment, the same as when defining an ordinary
|
||||
non-reactive expression. If \code{expr} is a quosure and \code{quoted} is \code{TRUE},
|
||||
then \code{env} is ignored.}
|
||||
|
||||
\item{quoted}{Is \code{expr} a quoted expression (with \code{quote()})? This
|
||||
is useful if you want to save an expression in a variable.}
|
||||
\item{quoted}{If it is \code{TRUE}, then the \code{\link[=quote]{quote()}}ed value of \code{expr}
|
||||
will be used when \code{expr} is evaluated. If \code{expr} is a quosure and you
|
||||
would like to use its expression as a value for \code{expr}, then you must set
|
||||
\code{quoted} to \code{TRUE}.}
|
||||
|
||||
\item{deleteFile}{Should the file in \code{func()$src} be deleted after
|
||||
it is sent to the client browser? Generally speaking, if the image is a
|
||||
|
||||
@@ -50,10 +50,15 @@ ggplot objects; for other plots, \code{NA} results in alt text of "Plot object".
|
||||
\code{NULL} or \code{""} is not recommended because those should be limited to
|
||||
decorative images.}
|
||||
|
||||
\item{env}{The environment in which to evaluate \code{expr}.}
|
||||
\item{env}{The parent environment for the reactive expression. By default,
|
||||
this is the calling environment, the same as when defining an ordinary
|
||||
non-reactive expression. If \code{expr} is a quosure and \code{quoted} is \code{TRUE},
|
||||
then \code{env} is ignored.}
|
||||
|
||||
\item{quoted}{Is \code{expr} a quoted expression (with \code{quote()})? This
|
||||
is useful if you want to save an expression in a variable.}
|
||||
\item{quoted}{If it is \code{TRUE}, then the \code{\link[=quote]{quote()}}ed value of \code{expr}
|
||||
will be used when \code{expr} is evaluated. If \code{expr} is a quosure and you
|
||||
would like to use its expression as a value for \code{expr}, then you must set
|
||||
\code{quoted} to \code{TRUE}.}
|
||||
|
||||
\item{execOnResize}{If \code{FALSE} (the default), then when a plot is
|
||||
resized, Shiny will \emph{replay} the plot drawing commands with
|
||||
|
||||
@@ -24,10 +24,15 @@ renderText(
|
||||
\arguments{
|
||||
\item{expr}{An expression to evaluate.}
|
||||
|
||||
\item{env}{The environment in which to evaluate \code{expr}. For expert use only.}
|
||||
\item{env}{The parent environment for the reactive expression. By default,
|
||||
this is the calling environment, the same as when defining an ordinary
|
||||
non-reactive expression. If \code{expr} is a quosure and \code{quoted} is \code{TRUE},
|
||||
then \code{env} is ignored.}
|
||||
|
||||
\item{quoted}{Is \code{expr} a quoted expression (with \code{quote()})? This
|
||||
is useful if you want to save an expression in a variable.}
|
||||
\item{quoted}{If it is \code{TRUE}, then the \code{\link[=quote]{quote()}}ed value of \code{expr}
|
||||
will be used when \code{expr} is evaluated. If \code{expr} is a quosure and you
|
||||
would like to use its expression as a value for \code{expr}, then you must set
|
||||
\code{quoted} to \code{TRUE}.}
|
||||
|
||||
\item{width}{Width of printed output.}
|
||||
|
||||
|
||||
@@ -71,10 +71,15 @@ columns will be displayed in scientific format with a precision of
|
||||
\item{...}{Arguments to be passed through to \code{\link[xtable:xtable]{xtable::xtable()}}
|
||||
and \code{\link[xtable:print.xtable]{xtable::print.xtable()}}.}
|
||||
|
||||
\item{env}{The environment in which to evaluate \code{expr}.}
|
||||
\item{env}{The parent environment for the reactive expression. By default,
|
||||
this is the calling environment, the same as when defining an ordinary
|
||||
non-reactive expression. If \code{expr} is a quosure and \code{quoted} is \code{TRUE},
|
||||
then \code{env} is ignored.}
|
||||
|
||||
\item{quoted}{Is \code{expr} a quoted expression (with \code{quote()})?
|
||||
This is useful if you want to save an expression in a variable.}
|
||||
\item{quoted}{If it is \code{TRUE}, then the \code{\link[=quote]{quote()}}ed value of \code{expr}
|
||||
will be used when \code{expr} is evaluated. If \code{expr} is a quosure and you
|
||||
would like to use its expression as a value for \code{expr}, then you must set
|
||||
\code{quoted} to \code{TRUE}.}
|
||||
|
||||
\item{outputArgs}{A list of arguments to be passed through to the
|
||||
implicit call to \code{\link[=tableOutput]{tableOutput()}} when \code{renderTable} is
|
||||
|
||||
@@ -10,10 +10,15 @@ renderUI(expr, env = parent.frame(), quoted = FALSE, outputArgs = list())
|
||||
\item{expr}{An expression that returns a Shiny tag object, \code{\link[=HTML]{HTML()}},
|
||||
or a list of such objects.}
|
||||
|
||||
\item{env}{The environment in which to evaluate \code{expr}.}
|
||||
\item{env}{The parent environment for the reactive expression. By default,
|
||||
this is the calling environment, the same as when defining an ordinary
|
||||
non-reactive expression. If \code{expr} is a quosure and \code{quoted} is \code{TRUE},
|
||||
then \code{env} is ignored.}
|
||||
|
||||
\item{quoted}{Is \code{expr} a quoted expression (with \code{quote()})? This
|
||||
is useful if you want to save an expression in a variable.}
|
||||
\item{quoted}{If it is \code{TRUE}, then the \code{\link[=quote]{quote()}}ed value of \code{expr}
|
||||
will be used when \code{expr} is evaluated. If \code{expr} is a quosure and you
|
||||
would like to use its expression as a value for \code{expr}, then you must set
|
||||
\code{quoted} to \code{TRUE}.}
|
||||
|
||||
\item{outputArgs}{A list of arguments to be passed through to the implicit
|
||||
call to \code{\link[=uiOutput]{uiOutput()}} when \code{renderUI} is used in an
|
||||
|
||||
@@ -30,7 +30,10 @@ expression that produces a Shiny app object.
|
||||
\item{port}{The TCP port that the application should listen on. If the
|
||||
\code{port} is not specified, and the \code{shiny.port} option is set (with
|
||||
\code{options(shiny.port = XX)}), then that port will be used. Otherwise,
|
||||
use a random port.}
|
||||
use a random port between 3000:8000, excluding ports that are blocked
|
||||
by Google Chrome for being considered unsafe: 3659, 4045, 5060,
|
||||
5061, 6000, 6566, 6665:6669 and 6697. Up to twenty random
|
||||
ports will be tried.}
|
||||
|
||||
\item{launch.browser}{If true, the system's default web browser will be
|
||||
launched automatically after the app is started. Defaults to true in
|
||||
|
||||
@@ -19,7 +19,10 @@ list the available examples.}
|
||||
\item{port}{The TCP port that the application should listen on. If the
|
||||
\code{port} is not specified, and the \code{shiny.port} option is set (with
|
||||
\code{options(shiny.port = XX)}), then that port will be used. Otherwise,
|
||||
use a random port.}
|
||||
use a random port between 3000:8000, excluding ports that are blocked
|
||||
by Google Chrome for being considered unsafe: 3659, 4045, 5060,
|
||||
5061, 6000, 6566, 6665:6669 and 6697. Up to twenty random
|
||||
ports will be tried.}
|
||||
|
||||
\item{launch.browser}{If true, the system's default web browser will be
|
||||
launched automatically after the app is started. Defaults to true in
|
||||
|
||||
@@ -17,4 +17,3 @@ value. The returned value will be used for the test snapshot.}
|
||||
\description{
|
||||
Add a function for serializing an input before bookmarking application state
|
||||
}
|
||||
\keyword{internal}
|
||||
|
||||
@@ -4,7 +4,13 @@
|
||||
\alias{shinyDeprecated}
|
||||
\title{Print message for deprecated functions in Shiny}
|
||||
\usage{
|
||||
shinyDeprecated(version, what, with = NULL, details = NULL)
|
||||
shinyDeprecated(
|
||||
version,
|
||||
what,
|
||||
with = NULL,
|
||||
details = NULL,
|
||||
type = c("deprecated", "superseded")
|
||||
)
|
||||
}
|
||||
\arguments{
|
||||
\item{version}{Shiny version when the function was deprecated}
|
||||
|
||||
@@ -68,7 +68,7 @@ options(device.ask.default = FALSE)
|
||||
|
||||
ui <- fluidPage(
|
||||
checkboxGroupInput('in1', 'Check some letters', choices = head(LETTERS)),
|
||||
selectizeInput('in2', 'Select a state', choices = state.name),
|
||||
selectizeInput('in2', 'Select a state', choices = c("", state.name)),
|
||||
plotOutput('plot')
|
||||
)
|
||||
|
||||
|
||||
@@ -76,10 +76,10 @@ value when it is a single choice input and the empty string is not in the
|
||||
The resulting server \code{input} value will be returned as:
|
||||
\itemize{
|
||||
\item A symbol if \code{multiple = FALSE}. The \code{input} value should be
|
||||
used with rlang's \code{\link[rlang:bang-bang]{rlang::!!()}}. For example,
|
||||
used with rlang's \code{\link[rlang:nse-force]{rlang::!!()}}. For example,
|
||||
\code{ggplot2::aes(!!input$variable)}.
|
||||
\item A list of symbols if \code{multiple = TRUE}. The \code{input} value
|
||||
should be used with rlang's \code{\link[rlang:bang-bang]{rlang::!!!()}} to expand
|
||||
should be used with rlang's \code{\link[rlang:nse-force]{rlang::!!!()}} to expand
|
||||
the symbol list as individual arguments. For example,
|
||||
\code{dplyr::select(mtcars, !!!input$variabls)} which is
|
||||
equivalent to \code{dplyr::select(mtcars, !!input$variabls[[1]], !!input$variabls[[2]], ..., !!input$variabls[[length(input$variabls)]])}.
|
||||
|
||||
@@ -1,81 +1,98 @@
|
||||
{
|
||||
"name": "@types/shiny",
|
||||
"private": true,
|
||||
"version": "1.6.0",
|
||||
"homepage": "https://shiny.rstudio.com",
|
||||
"repository": "github:rstudio/shiny",
|
||||
"name": "@types/rstudio-shiny",
|
||||
"version": "1.7.0",
|
||||
"license": "GPL-3.0-only",
|
||||
"main": "",
|
||||
"types": "src_d/shiny/index.d.ts",
|
||||
"browser": "",
|
||||
"types": "srcts/types/extras/globalShiny.d.ts",
|
||||
"files": [
|
||||
"DESCRIPTION",
|
||||
"LICENSE",
|
||||
"NEWS.md",
|
||||
"srcts/types/**/*.d.ts"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 14",
|
||||
"yarn": ">= 1.22"
|
||||
},
|
||||
"dependencies": {
|
||||
"@types/bootstrap": "3.4.0",
|
||||
"@types/bootstrap-datepicker": "0.0.14",
|
||||
"@types/datatables.net": "^1.10.19",
|
||||
"@types/ion-rangeslider": "2.3.0",
|
||||
"@types/jquery": "patch:@types/jquery@3.5.5#./srcts/patch/types-jquery.patch",
|
||||
"@types/selectize": "0.12.34"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.14.3",
|
||||
"@babel/plugin-proposal-class-properties": "^7.13.0",
|
||||
"@babel/preset-env": "^7.14.2",
|
||||
"@babel/preset-typescript": "^7.13.0",
|
||||
"@babel/runtime": "^7.14.0",
|
||||
"@deanc/esbuild-plugin-postcss": "^1.0.1",
|
||||
"@testing-library/dom": "^7.31.0",
|
||||
"@testing-library/jest-dom": "^5.12.0",
|
||||
"@testing-library/user-event": "^13.1.9",
|
||||
"@types/bootstrap": "3.4.0",
|
||||
"@types/bootstrap-datepicker": "0.0.14",
|
||||
"@types/datatables.net": "^1.10.19",
|
||||
"@types/ion-rangeslider": "2.3.0",
|
||||
"@types/highlightjs": "^9.12.1",
|
||||
"@types/jest": "^26.0.23",
|
||||
"@types/jquery": "^3.5.5",
|
||||
"@types/jqueryui": "1.12.15",
|
||||
"@types/lodash": "^4.14.170",
|
||||
"@types/node": "^15.6.1",
|
||||
"@types/selectize": "0.12.34",
|
||||
"@types/showdown": "^1.9.3",
|
||||
"@typescript-eslint/eslint-plugin": "^4.25.0",
|
||||
"@typescript-eslint/parser": "^4.25.0",
|
||||
"autoprefixer": "^10.2.6",
|
||||
"bootstrap-datepicker": "1.9.0",
|
||||
"browserslist": "^4.16.6",
|
||||
"core-js": "^3.13.0",
|
||||
"esbuild": "^0.12.4",
|
||||
"esbuild-plugin-babel": "0.2.3",
|
||||
"esbuild-plugin-babel": "https://github.com/schloerke/esbuild-plugin-babel#patch-2",
|
||||
"esbuild-plugin-globals": "^0.1.1",
|
||||
"esbuild-plugin-sass": "^0.5.2",
|
||||
"eslint": "^7.27.0",
|
||||
"eslint-config-prettier": "^7.2.0",
|
||||
"eslint-plugin-jest": "^24.3.6",
|
||||
"eslint-plugin-jest-dom": "^3.9.0",
|
||||
"eslint-plugin-prettier": "^3.4.0",
|
||||
"eslint-plugin-unicorn": "^33.0.1",
|
||||
"ion-rangeslider": "2.3.1",
|
||||
"jest": "^26.6.3",
|
||||
"jquery": "3.6.0",
|
||||
"lodash": "^4.17.21",
|
||||
"madge": "^4.0.2",
|
||||
"node-gyp": "^8.1.0",
|
||||
"parcel": "^1.12.4",
|
||||
"parcel-bundler": "^1.12.5",
|
||||
"phantomjs-prebuilt": "^2.1.16",
|
||||
"postcss": "^8.3.5",
|
||||
"prettier": "2.3.0",
|
||||
"readcontrol": "^1.0.0",
|
||||
"replace": "^1.2.1",
|
||||
"selectize": "0.12.4",
|
||||
"strftime": "0.9.2",
|
||||
"ts-jest": "^26",
|
||||
"ts-node": "^10.0.0",
|
||||
"type-coverage": "^2.17.5",
|
||||
"typescript": "~4.1.5"
|
||||
"typescript": "~4.1.5",
|
||||
"util-inspect": "https://github.com/deecewan/browser-util-inspect#c0b4350df4378ffd743e8c36dd3898ce3992823e"
|
||||
},
|
||||
"scripts": {
|
||||
"prepare": "node --eval \"1+1; // help yarn users not install the whole repo; https://github.com/yarnpkg/yarn/issues/2822#issuecomment-847360267\"",
|
||||
"watch": "yarn run build_shiny --watch",
|
||||
"build": "yarn run build_shiny && yarn run bundle_external_libs",
|
||||
"build": "yarn run build_shiny && yarn run bundle_extras && yarn run bundle_external_libs",
|
||||
"build_shiny": "yarn run checks && yarn run bundle_shiny",
|
||||
"bundle_shiny": "node esbuild.config.mjs",
|
||||
"circular_dep_image": "madge --circular --extensions ts --image madge.svg src",
|
||||
"bundle_external_libs": "node esbuild.external_libs.mjs",
|
||||
"bundle_shiny_parcel2": "parcel build -d ../inst/www/shared --no-minify -o shiny.js src/index.ts",
|
||||
"watch_parcel2": "yarn run checks && parcel run -d ../inst/www/shared -o shiny.js srcjs/index.ts",
|
||||
"replace_shiny_version2": "replace --silent '\"[^\"]+\"; // @VERSION@' \"\\\"`node -e 'console.log(require(\"readcontrol\").readSync(\"../DESCRIPTION\").version)'`\\\"; // @VERSION@\" src/shiny.ts",
|
||||
"bundle_shiny": "ts-node srcts/build/shiny.ts",
|
||||
"bundle_external_libs": "ts-node srcts/build/external_libs.ts",
|
||||
"bundle_extras": "ts-node srcts/build/extras.ts",
|
||||
"test": "jest --coverage",
|
||||
"test_phantom": "echo '\n\t!! Must manually stop phantomjs test !!\n\n' && yarn bundle_shiny && phantomjs --debug=yes ../inst/www/shared/shiny.js",
|
||||
"lint": "eslint --fix --ext .ts src",
|
||||
"typescript-check": "tsc -p tsconfig.json",
|
||||
"type-check": "type-coverage -p tsconfig.json --detail --at-least 85",
|
||||
"import-check": "madge --circular --extensions ts src",
|
||||
"checks": "yarn run lint && yarn run typescript-check && yarn run type-check && yarn run import-check"
|
||||
},
|
||||
"dependencies": {
|
||||
"core-js": "^3.13.0",
|
||||
"lodash": "^4.17.21",
|
||||
"util-inspect": "https://github.com/deecewan/browser-util-inspect#c0b4350df4378ffd743e8c36dd3898ce3992823e"
|
||||
"checks": "yarn run lint && yarn run build_types && yarn run coverage && yarn run circular",
|
||||
"lint": "node --eval \"console.log('linting code...')\" && eslint --fix --ext .ts srcts/src",
|
||||
"build_types": "tsc -p tsconfig.json",
|
||||
"coverage_detailed": "yarn type-check --detail",
|
||||
"coverage": "type-coverage -p tsconfig.json --at-least 90",
|
||||
"circular": "madge --circular --extensions ts srcts/src",
|
||||
"circular_image": "madge --circular --extensions ts --image madge.svg srcts/src"
|
||||
}
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
root: true
|
||||
env:
|
||||
browser: true
|
||||
es6: true
|
||||
extends:
|
||||
- 'eslint:recommended'
|
||||
- 'plugin:@typescript-eslint/recommended'
|
||||
- 'plugin:jest/recommended'
|
||||
- 'prettier/@typescript-eslint'
|
||||
- 'plugin:prettier/recommended'
|
||||
- 'plugin:jest-dom/recommended'
|
||||
globals:
|
||||
Atomics: readonly
|
||||
SharedArrayBuffer: readonly
|
||||
parser: '@typescript-eslint/parser'
|
||||
parserOptions:
|
||||
ecmaVersion: 2018
|
||||
sourceType: module
|
||||
plugins:
|
||||
- '@typescript-eslint'
|
||||
- prettier
|
||||
- jest-dom
|
||||
rules:
|
||||
"@typescript-eslint/explicit-function-return-type":
|
||||
- off
|
||||
"@typescript-eslint/no-explicit-any":
|
||||
- off
|
||||
"@typescript-eslint/explicit-module-boundary-types":
|
||||
- error
|
||||
camelcase:
|
||||
- error
|
||||
default-case:
|
||||
- error
|
||||
indent:
|
||||
- error
|
||||
- 2
|
||||
- SwitchCase: 1
|
||||
linebreak-style:
|
||||
- error
|
||||
- unix
|
||||
quotes:
|
||||
- error
|
||||
- double
|
||||
- avoid-escape
|
||||
semi:
|
||||
- error
|
||||
- always
|
||||
newline-after-var:
|
||||
- error
|
||||
- always
|
||||
dot-location:
|
||||
- error
|
||||
- property
|
||||
10
srcts/.gitignore
vendored
10
srcts/.gitignore
vendored
@@ -1,10 +0,0 @@
|
||||
node_modules/
|
||||
.cache
|
||||
.yarn/*
|
||||
!.yarn/releases
|
||||
!.yarn/plugins
|
||||
!.yarn/sdks
|
||||
!.yarn/versions
|
||||
.pnp.*
|
||||
coverage/
|
||||
madge.svg
|
||||
118
srcts/README.md
118
srcts/README.md
@@ -1,4 +1,23 @@
|
||||
# TypeScript build tools
|
||||
# Using Shiny TypeScript Definitions
|
||||
|
||||
When developing TypeScript projects that use `window.Shiny`, we recommend installing the Shiny TypeScript definitions to your package. To install the latest stable definitions, call
|
||||
|
||||
```bash
|
||||
yarn add https://github.com/rstudio/shiny\#v1.7.0
|
||||
```
|
||||
|
||||
, matching the GitHub tag to your current the Shiny CRAN release (ex: `v1.7.0`). If you are asked to select a version of `@types/jquery`, please select the closest matching version.
|
||||
|
||||
This will provide a global type defintion of `Shiny`, let your IDE know that `window.Shiny` is of type `Shiny`, and declare a globally available variable `Shiny` within your project. You **should not** need to import anything. Similar to `jQuery`, it should _Just Work_<sup>TM</sup>.
|
||||
|
||||
When loading your compiled file, it should be loaded after `shiny.js` is loaded. If you are using an `htmlDependency()` to add your code to the page, your script will automatically be loaded after has been loaded.
|
||||
|
||||
|
||||
----------------------------------------------------
|
||||
|
||||
# TypeScript build tools (Shiny Developers)
|
||||
|
||||
All files will be described as if the working directory is the root folder of `rstudio/shiny`, not relative to this `README.md` file.
|
||||
|
||||
## First-time setup
|
||||
Shiny's TypeScript build tools use Node.js, along with [yarn](https://yarnpkg.com/) v2 to manage the JavaScript packages.
|
||||
@@ -14,9 +33,10 @@ node --version
|
||||
yarn --version
|
||||
```
|
||||
|
||||
Once both are installed, run the following in this directory (`srcts/`) to install the packages :
|
||||
Once both are installed, run the following in the root repo directory to install the packages :
|
||||
|
||||
```bash
|
||||
# Sitting in `rstudio/shiny` repo
|
||||
yarn install
|
||||
```
|
||||
|
||||
@@ -47,12 +67,12 @@ If in the future you want to upgrade or add a package, run:
|
||||
yarn add --dev [packagename]
|
||||
```
|
||||
|
||||
This will automatically add the package to the dependencies in `./package.json`, and it will also update the `./yarn.lock` to reflect that change. If someone other than yourself does this, simply run `yarn` to update your local packages to match the new `./package.json`.
|
||||
This will automatically add the package to the dependencies in `package.json`, and it will also update the `yarn.lock` to reflect that change. If someone other than yourself does this, simply run `yarn` to update your local packages to match the new `package.json`.
|
||||
|
||||
## Upgrading packages
|
||||
Periodically, it's good to upgrade the packages to a recent version. There's two ways of doing this, depending on your intention:
|
||||
|
||||
1. Use `yarn up` to upgrade all dependencies to their latest version based on the version range specified in the package.json file (the `./yarn.lock` file will be recreated as well. Yarn packages use [semantic versioning](https://yarnpkg.com/en/docs/dependency-versions), i.e. each version is writen with a maximum of 3 dot-separated numbers such that: `major.minor.patch`. For example in the version `3.1.4`, 3 is the major version number, 1 is the minor version number and 4 is the patch version number. Here are the most used operators (these appear before the version number):
|
||||
1. Use `yarn up` to upgrade all dependencies to their latest version based on the version range specified in the package.json file (the `yarn.lock` file will be recreated as well. Yarn packages use [semantic versioning](https://yarnpkg.com/en/docs/dependency-versions), i.e. each version is writen with a maximum of 3 dot-separated numbers such that: `major.minor.patch`. For example in the version `3.1.4`, 3 is the major version number, 1 is the minor version number and 4 is the patch version number. Here are the most used operators (these appear before the version number):
|
||||
|
||||
- `~` is for upgrades that keep the minor version the same (assuming that was specified);
|
||||
|
||||
@@ -62,16 +82,70 @@ Periodically, it's good to upgrade the packages to a recent version. There's two
|
||||
|
||||
3. To see all outdated packages, run `yarn outdated`
|
||||
|
||||
# Configure TypeScript
|
||||
# TypeScript
|
||||
|
||||
## Learn about TypeScript
|
||||
|
||||
The documentation by [TypeScript](https://www.typescriptlang.org/docs/) is a solid resource to know each and every bell and whistle. Most features have examples and convey the thoughts well.
|
||||
|
||||
[TypeScript Deep Dive](https://basarat.gitbook.io/typescript/) is an online `bookdown`-like approach to TypeScript by "Microsoft MVP for TypeScript", Basarat Ali Syed. In his book, he goes through many examples of what you "should do", not necessarily "what is possible" like the [TypeScript docs](https://www.typescriptlang.org/docs/).
|
||||
|
||||
## TypeScript StyleGuide
|
||||
|
||||
Using the style guid from [TypeScript Deep Dive / StyleGuide](https://basarat.gitbook.io/typescript/styleguide), we extend it to have the usage be more familiar to R developers and preexisting Shiny development. The goal is to produce consistent code that can be injested quickly.
|
||||
|
||||
|
||||
### StyleGuide
|
||||
|
||||
* `null` vs. `undefined`
|
||||
* Do not use `x === null` unless you truly mean it.
|
||||
* Safer to use _truthy_ or _falsey_ checks instead. Ex: `if (x) {}`
|
||||
* `type` vs `interface`
|
||||
* > Use `type` when you might need a union or intersection: `type Foo = number | { someProperty: number }`
|
||||
* > Use `interface` when you want extends or implements: `interface FooBar extends Foo { bar: string;}`
|
||||
* > Otherwise use whatever makes you happy that day.
|
||||
* Namespace
|
||||
* `PascalCase`
|
||||
* Ex: `Shiny`
|
||||
|
||||
### Enforced (by `eslint`) StyleGuide
|
||||
|
||||
* Variable
|
||||
* `camelCase`
|
||||
* Ex: `const hello = "world`
|
||||
* Class
|
||||
* `PascalCase`
|
||||
* Ex: `class InputBinding {}`
|
||||
* Type, Interface definitions:
|
||||
* `PascalCase`
|
||||
* Ex: `type BindingBase = {name: string}`
|
||||
* Ex: `interface ShinyEventMessage extends JQuery.Event {}`
|
||||
* Enum
|
||||
* `PascalCase`
|
||||
* (Currently unused)
|
||||
* Single vs. Double Quotes
|
||||
* While the JS community has decided on single quotes, R has decided on double quotes.
|
||||
* > When you can't use double quotes, try using back ticks (`).
|
||||
* Annotate Arrays as `Type[]`
|
||||
* Ex: `Foo[]` (vs `Array<Foo>`)
|
||||
* Annotate Records as `{[key: string]: valueType}`
|
||||
* Ex: `const x: {[key: string]: number} = {a: 4}`
|
||||
* Ex: Extend the unknown key definition with static keys: `const x: {known: string, [key: string]: number} = {known: "yes", a: 4}`
|
||||
* File Names
|
||||
* `camelCase` - Enforced by `eslint`
|
||||
|
||||
## Config files
|
||||
|
||||
The JavaScript community likes to build many small, effective packages that do minimal work. The unfortunate side effect is needing a config file for everything.
|
||||
|
||||
## Config files
|
||||
All config files are located in the root folder to avoid opening two separate VS Code projects.
|
||||
|
||||
* `.browserslistrc`
|
||||
* Used with `browserslist` and `core-js` to determine which polyfills should be incorporated.
|
||||
* `.eslintrc.yml`
|
||||
* Used with `eslint` and `prettier` to determine how the TypeScript files should be formatted and which lint failures should cause warnings, errors, or be ignored.
|
||||
* `.madgerc`
|
||||
* Package used to determine if circular dependencies are found. `type` only imports are ignored as they are not included in the final bundle.
|
||||
* `.prettierrc.yml`
|
||||
* Used by `prettier` to know how to adjust code when a file is saved in VSCode or within `eslint`'s linting process.
|
||||
* `yarnrc.yml`
|
||||
@@ -82,15 +156,13 @@ The JavaScript community likes to build many small, effective packages that do m
|
||||
* `"useBuiltIns": "usage"` - `core-js` polyfills are only added as they are _used_.
|
||||
* `"corejs": "3.9"` - This number should match the installed `core-js` number.
|
||||
* `"ignore":["node_modules/core-js"]` - The `core-js` library is directly ignored to [avoid being processed by `babel`](https://github.com/zloirock/core-js/issues/743#issuecomment-571983318).
|
||||
* `esbuild.config.mjs`
|
||||
* Script that will build `shiny.js` and `shiny.min.js` with their sourcemaps
|
||||
* `jest.config.js`
|
||||
* Used to configure [`jest` testing](https://jestjs.io/)
|
||||
* `package.json`
|
||||
* Contains useful scripts that can be run by `yarn` via `yarn run SCRIPTNAME`.
|
||||
* The scripts described below are inteded for developer use. All other scripts are means to an end.
|
||||
* `yarn run watch` - Watch `./src` for changes and rebuild the JavaScript files.
|
||||
* `yarn run build` - Build `shiny.js` and `shiny.min.js` in `../inst/www/shared`. Both files will have a corresponding sourcemap
|
||||
* `yarn run watch` - Watch `srcts/src` for changes and rebuild the JavaScript files.
|
||||
* `yarn run build` - Build `shiny.js` and `shiny.min.js` in `inst/www/shared`. Both files will have a corresponding sourcemap
|
||||
* `yarn run lint` - Fix all TypeScript lints using [`eslint`](https://eslint.org/) and [`prettier`](https://prettier.io/)
|
||||
* `yarn run test` - Run all TypeScript tests
|
||||
* `tsconfig.json` -
|
||||
@@ -100,13 +172,11 @@ The JavaScript community likes to build many small, effective packages that do m
|
||||
* `preserveConstEnums: false` - Do no preserve enum values into the final code. (If true, produces bloat / unused code)
|
||||
* `isolatedModules: true` & `esModuleInterop: true` - Requested by `esbuild`. This [allows for `esbuild`](https://esbuild.github.io/content-types/#typescript) to safely compile the files in parallel
|
||||
|
||||
|
||||
|
||||
## Bundle TypeScript
|
||||
|
||||
[esbuild](https://esbuild.github.io/) is a build tool that (for Shiny's purposes) compiles the TypeScript into a single JavaScript file.
|
||||
|
||||
To run all build tasks, from within the `./srcts` directory, run:
|
||||
To run all build tasks, run:
|
||||
|
||||
```bash
|
||||
yarn build
|
||||
@@ -120,6 +190,10 @@ yarn watch
|
||||
|
||||
Both JavaScript files will produce a sourcemap (`**.js.map`) that the browser will understand. This will help you debug Shiny's JavaScript code within the browser and point back to the original TypeScript files.
|
||||
|
||||
### Exported types
|
||||
|
||||
`./extras/globalShiny.ts` contains global declarations to define `window.Shiny`, a globally available `Shiny` variable, and a globally available `Shiny` type. This file is in a parallel folder to `./src` to avoid `Shiny` from being globally accessable within the source code. However, this file is the default type defintion when the Type definitions are installed by external developers.
|
||||
|
||||
### GitHub Actions
|
||||
|
||||
On push to the `master` branch or push to a Pull Request to the `master` branch, a GitHub Action will be run to make sure the bundled JavaScript code is up to date. If the source code does not compile to the exact same file, it will be committed an pushed back to the outdated branch. (This makes it so the full build tools are not necessary for small tweaks and comments. 🎉)
|
||||
@@ -141,13 +215,6 @@ For this to work you must first install `xdotool` using your distribution's pack
|
||||
```bash
|
||||
find ../srcts/ | entr bash -c './node_modules/grunt/bin/grunt && xdotool search --onlyvisible --class Chrome windowfocus key ctrl+r'
|
||||
``` -->
|
||||
|
||||
|
||||
|
||||
# Development in VSCode
|
||||
|
||||
VSCode does not like to develop TypeScript with the configuration files in a subfolder. To leverage full VSCode capabilities, it is recommended to open the `./srcts` folder as the root folder of a VSCode project. This will enable VSCode to readily find all of the configuration files.
|
||||
|
||||
# Updating dependencies
|
||||
### `@types/jquery`
|
||||
|
||||
@@ -165,3 +232,14 @@ To update the version of `core-js`:
|
||||
|
||||
* Check if there is a newer version available by running `yarn outdated core-js`. (If there's no output, then you have the latest version.)
|
||||
* Run `yarn add --dev core-js --exact`.
|
||||
|
||||
|
||||
### External libraries
|
||||
|
||||
Shiny already has a handful of html dependencies that should NOT be bundled within `shiny.js`. To update the dependencies below, see the directions in in [`tools/README.md`](../tools).
|
||||
* `jquery` / `@types/jquery`
|
||||
* `bootstrap` / `@types/bootstrap`
|
||||
* Bootstrap is not being updated anymore. Only bootstrap 3.4 will be utilized within shiny.js. To use the latest bootstrap, see [`rstudio/bslib`](https://github.com/rstudio/bslib)
|
||||
* `bootstrap-datepicker` / `@types/bootstrap-datepicker`
|
||||
* `ion-rangeslider` / `@types/ion-rangeslider`
|
||||
* `selectize` / `@types/selectize`
|
||||
|
||||
@@ -85,12 +85,12 @@
|
||||
* √ Each _file_ will be pulled out as possible into smaller files in separate PRs
|
||||
* √ Convert `FileProcessor` to a true class definition
|
||||
* Break up `./utils` into many files
|
||||
* Remove any `: any` types
|
||||
* Make `@typescript-eslint/explicit-module-boundary-types` an error
|
||||
* Fix all `// eslint-disable-next-line no-prototype-builtins` lines
|
||||
* √ Remove any `: any` types
|
||||
* √ Make `@typescript-eslint/explicit-module-boundary-types` an error
|
||||
* √ Fix all `// eslint-disable-next-line no-prototype-builtins` lines
|
||||
* TypeScript other shiny files (ex: showcasemode)
|
||||
* Completely remove `parcel` from `./package.json` and only use `esbuild`
|
||||
* Delete 'shiny-es5' files
|
||||
* √ Completely remove `parcel` from `./package.json` and only use `esbuild`
|
||||
* √ Delete 'shiny-es5' files
|
||||
* Delete 'old' folder
|
||||
* _Uglify_ js files (like in previous Gruntfile.js)
|
||||
* datepicker
|
||||
|
||||
80
srcts/build/_build.ts
Normal file
80
srcts/build/_build.ts
Normal file
@@ -0,0 +1,80 @@
|
||||
import {
|
||||
build as esbuildBuild,
|
||||
BuildIncremental,
|
||||
BuildOptions,
|
||||
BuildResult,
|
||||
} from "esbuild";
|
||||
import readcontrol from "readcontrol";
|
||||
import process from "process";
|
||||
import { basename } from "path";
|
||||
|
||||
const outDir = "./inst/www/shared/";
|
||||
|
||||
type ShinyDesc = { version: string; package: string; license: string };
|
||||
const shinyDesc = readcontrol.readSync("./DESCRIPTION") as ShinyDesc;
|
||||
|
||||
const bannerTxt = [
|
||||
`/*! ${shinyDesc.package} ${shinyDesc.version}`,
|
||||
`(c) 2012-${new Date().getFullYear()} RStudio, PBC.`,
|
||||
`License: ${shinyDesc.license} */`,
|
||||
].join(" | ");
|
||||
const banner = {
|
||||
js: bannerTxt,
|
||||
css: bannerTxt,
|
||||
};
|
||||
|
||||
async function build(
|
||||
opts: BuildOptions
|
||||
): Promise<BuildIncremental | BuildResult> {
|
||||
const outFileNames = opts.outfile
|
||||
? [basename(opts.outfile)]
|
||||
: (opts.entryPoints as string[]).map((entry) => basename(entry));
|
||||
|
||||
const strSizes = outFileNames.map((outFileName) => outFileName.length);
|
||||
|
||||
strSizes.push("shiny.min.js".length);
|
||||
const strSize = Math.max(...strSizes);
|
||||
const printNames = outFileNames;
|
||||
|
||||
for (let i = 0; i < printNames.length; i++) {
|
||||
while (printNames[i].length < strSize) {
|
||||
printNames[i] = printNames[i] + " ";
|
||||
}
|
||||
}
|
||||
|
||||
const onRebuild = function (error?: string) {
|
||||
if (error) {
|
||||
console.error(printNames.join(", "), "watch build failed:\n", error);
|
||||
} else {
|
||||
printNames.map((printName) => {
|
||||
console.log("√ -", printName, "-", new Date().toJSON());
|
||||
});
|
||||
}
|
||||
return;
|
||||
};
|
||||
|
||||
let incremental = false;
|
||||
let watch: false | { onRebuild: (error, result) => void } = false;
|
||||
|
||||
if (process.argv.length >= 3 && process.argv[2] == "--watch") {
|
||||
incremental = true;
|
||||
watch = {
|
||||
onRebuild: onRebuild,
|
||||
};
|
||||
}
|
||||
|
||||
outFileNames.map((outFileName) => {
|
||||
console.log("Building " + outFileName);
|
||||
});
|
||||
return esbuildBuild({
|
||||
incremental: incremental,
|
||||
watch: watch,
|
||||
target: "es5",
|
||||
...opts,
|
||||
}).then((x) => {
|
||||
onRebuild();
|
||||
return x;
|
||||
});
|
||||
}
|
||||
|
||||
export { outDir, build, shinyDesc, banner };
|
||||
57
srcts/build/external_libs.ts
Normal file
57
srcts/build/external_libs.ts
Normal file
@@ -0,0 +1,57 @@
|
||||
// This build script must be executed from the root repo directory via
|
||||
// ```
|
||||
// yarn build
|
||||
// ```
|
||||
|
||||
import { build, outDir } from "./_build";
|
||||
import { readdir, unlink, writeFile } from "fs/promises";
|
||||
import globalsPlugin from "esbuild-plugin-globals";
|
||||
|
||||
const opts = {
|
||||
bundle: false,
|
||||
sourcemap: false,
|
||||
};
|
||||
|
||||
readdir(outDir + "datepicker/js/locales/").then(async (localeFiles) => {
|
||||
const requireFiles = localeFiles
|
||||
.map(function (filename) {
|
||||
return `require("./locales/${filename}");`;
|
||||
})
|
||||
.join("\n");
|
||||
|
||||
const tmpFile = outDir + "datepicker/js/temp.js";
|
||||
|
||||
await writeFile(
|
||||
tmpFile,
|
||||
`require("./bootstrap-datepicker.js");\n${requireFiles}`
|
||||
);
|
||||
|
||||
await build({
|
||||
...opts,
|
||||
plugins: [
|
||||
globalsPlugin({
|
||||
jquery: "window.jQuery",
|
||||
}),
|
||||
],
|
||||
bundle: true,
|
||||
entryPoints: [tmpFile],
|
||||
outfile: outDir + "datepicker/js/bootstrap-datepicker.min.js",
|
||||
minify: true,
|
||||
});
|
||||
// Clean up
|
||||
unlink(tmpFile);
|
||||
});
|
||||
|
||||
build({
|
||||
...opts,
|
||||
entryPoints: [outDir + "ionrangeslider/js/ion.rangeSlider.js"],
|
||||
outfile: outDir + "ionrangeslider/js/ion.rangeSlider.min.js",
|
||||
minify: true,
|
||||
});
|
||||
|
||||
build({
|
||||
...opts,
|
||||
entryPoints: [outDir + "selectize/accessibility/js/selectize-plugin-a11y.js"],
|
||||
outfile: outDir + "selectize/accessibility/js/selectize-plugin-a11y.min.js",
|
||||
minify: true,
|
||||
});
|
||||
54
srcts/build/extras.ts
Normal file
54
srcts/build/extras.ts
Normal file
@@ -0,0 +1,54 @@
|
||||
// This build script must be executed from the root repo directory via
|
||||
// ```
|
||||
// yarn build
|
||||
// ```
|
||||
|
||||
// - TypeScript -----------------------------------------------------------
|
||||
|
||||
import { banner, build, outDir } from "./_build";
|
||||
import babelPlugin from "esbuild-plugin-babel";
|
||||
|
||||
build({
|
||||
bundle: true,
|
||||
sourcemap: "inline",
|
||||
minify: true,
|
||||
plugins: [babelPlugin()],
|
||||
banner: banner,
|
||||
entryPoints: [
|
||||
"srcts/extras/shiny-autoreload.ts",
|
||||
"srcts/extras/shiny-showcase.ts",
|
||||
"srcts/extras/shiny-testmode.ts",
|
||||
],
|
||||
outdir: outDir,
|
||||
});
|
||||
|
||||
// - Sass -----------------------------------------------------------
|
||||
|
||||
import autoprefixer from "autoprefixer";
|
||||
import postCssPlugin from "@deanc/esbuild-plugin-postcss";
|
||||
import sassPlugin from "esbuild-plugin-sass";
|
||||
|
||||
const sassOpts = {
|
||||
minify: true,
|
||||
banner: banner,
|
||||
plugins: [
|
||||
sassPlugin(),
|
||||
postCssPlugin({
|
||||
plugins: [autoprefixer],
|
||||
}),
|
||||
],
|
||||
};
|
||||
|
||||
build({
|
||||
...sassOpts,
|
||||
entryPoints: ["srcts/extras/shiny-showcase.scss"],
|
||||
outfile: outDir + "shiny-showcase.css",
|
||||
});
|
||||
build({
|
||||
...sassOpts,
|
||||
entryPoints: [
|
||||
// Must keep shiny.scss within `inst` to be able to use as htmldependency
|
||||
outDir + "shiny_scss/shiny.scss",
|
||||
],
|
||||
outfile: outDir + "shiny.min.css",
|
||||
});
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user