mirror of
https://github.com/rstudio/shiny.git
synced 2026-01-11 16:08:19 -05:00
Compare commits
4 Commits
v1.7.3
...
bootstrapL
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
923c1b2450 | ||
|
|
914dc71d67 | ||
|
|
a13ca9fd3a | ||
|
|
b8b34370da |
@@ -21,19 +21,3 @@
|
||||
^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
108
.eslintrc.yml
@@ -1,108 +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
|
||||
- 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,6 +1,4 @@
|
||||
/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
|
||||
|
||||
12
.github/shiny-workflows/routine.sh
vendored
12
.github/shiny-workflows/routine.sh
vendored
@@ -1,12 +0,0 @@
|
||||
#!/bin/bash -e
|
||||
. ./tools/documentation/checkDocsCurrent.sh
|
||||
|
||||
echo "Updating package.json version to match DESCRIPTION Version"
|
||||
Rscript ./tools/updatePackageJsonVersion.R
|
||||
if [ -n "$(git status --porcelain package.json)" ]
|
||||
then
|
||||
yarn build
|
||||
git add ./inst package.json && git commit -m 'Sync package version (GitHub Actions)' || echo "No package version to commit"
|
||||
else
|
||||
echo "No package version difference detected; package.json is current."
|
||||
fi
|
||||
159
.github/workflows/R-CMD-check.yaml
vendored
159
.github/workflows/R-CMD-check.yaml
vendored
@@ -1,23 +1,150 @@
|
||||
# Workflow derived from https://github.com/rstudio/shiny-workflows
|
||||
#
|
||||
# NOTE: This Shiny team GHA workflow is overkill for most R packages.
|
||||
# For most R packages it is better to use https://github.com/r-lib/actions
|
||||
name: R-CMD-check
|
||||
on:
|
||||
push:
|
||||
branches: [main, rc-**]
|
||||
branches:
|
||||
- master
|
||||
pull_request:
|
||||
branches: [main]
|
||||
schedule:
|
||||
- cron: '0 5 * * 1' # every monday
|
||||
branches:
|
||||
- master
|
||||
|
||||
name: Package checks
|
||||
|
||||
jobs:
|
||||
website:
|
||||
uses: rstudio/shiny-workflows/.github/workflows/website.yaml@v1
|
||||
routine:
|
||||
uses: rstudio/shiny-workflows/.github/workflows/routine.yaml@v1
|
||||
with:
|
||||
node-version: "14.x"
|
||||
|
||||
R-CMD-check:
|
||||
uses: rstudio/shiny-workflows/.github/workflows/R-CMD-check.yaml@v1
|
||||
runs-on: ${{ matrix.config.os }}
|
||||
|
||||
name: ${{ matrix.config.os }} (${{ matrix.config.r }})
|
||||
|
||||
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"}
|
||||
|
||||
env:
|
||||
_R_CHECK_FORCE_SUGGESTS_: false
|
||||
R_REMOTES_NO_ERRORS_FROM_WARNINGS: true
|
||||
RSPM: ${{ matrix.config.rspm }}
|
||||
GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
steps:
|
||||
# https://github.com/actions/checkout/issues/135
|
||||
- name: Set git to use LF
|
||||
if: runner.os == 'Windows'
|
||||
run: |
|
||||
git config --system core.autocrlf false
|
||||
git config --system core.eol lf
|
||||
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- uses: r-lib/actions/setup-r@master
|
||||
id: install-r
|
||||
with:
|
||||
r-version: ${{ matrix.config.r }}
|
||||
|
||||
- uses: r-lib/actions/setup-pandoc@master
|
||||
|
||||
- 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
|
||||
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
|
||||
|
||||
# xquartz and cairo are needed for Cairo package.
|
||||
# harfbuzz and fribidi are needed for textshaping package.
|
||||
- name: Mac systemdeps
|
||||
if: runner.os == 'macOS'
|
||||
run: |
|
||||
brew install --cask xquartz
|
||||
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: |
|
||||
echo "::set-output name=path::$(Rscript -e 'cat(shinytest:::phantom_paths()[[1]])')"
|
||||
- name: Cache PhantomJS
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: ${{ steps.phantomjs.outputs.path }}
|
||||
key: ${{ matrix.config.os }}-phantomjs
|
||||
restore-keys: ${{ matrix.config.os }}-phantomjs
|
||||
- name: Install PhantomJS
|
||||
run: >
|
||||
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")
|
||||
shell: Rscript {0}
|
||||
|
||||
- name: Show testthat output
|
||||
if: always()
|
||||
run: find check -name 'testthat.Rout*' -exec cat '{}' \; || true
|
||||
shell: bash
|
||||
|
||||
- name: Upload check results
|
||||
if: failure()
|
||||
uses: actions/upload-artifact@v2
|
||||
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
|
||||
|
||||
157
.github/workflows/rituals.yaml
vendored
Normal file
157
.github/workflows/rituals.yaml
vendored
Normal file
@@ -0,0 +1,157 @@
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- ghactions
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
|
||||
name: Rituals
|
||||
|
||||
jobs:
|
||||
rituals:
|
||||
name: Rituals
|
||||
# if: false
|
||||
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"}
|
||||
|
||||
env:
|
||||
R_REMOTES_NO_ERRORS_FROM_WARNINGS: true
|
||||
RSPM: ${{ matrix.config.rspm }}
|
||||
GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
|
||||
- uses: r-lib/actions/pr-fetch@master
|
||||
name: Git Pull (PR)
|
||||
if: github.event_name == 'pull_request'
|
||||
with:
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- uses: r-lib/actions/setup-r@master
|
||||
id: install-r
|
||||
with:
|
||||
r-version: ${{ matrix.config.r }}
|
||||
|
||||
- uses: r-lib/actions/setup-pandoc@master
|
||||
|
||||
- name: Git Config
|
||||
run: |
|
||||
git config user.name "${GITHUB_ACTOR}"
|
||||
git config user.email "${GITHUB_ACTOR}@users.noreply.github.com"
|
||||
|
||||
- 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
|
||||
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)
|
||||
|
||||
- name: Install dependencies
|
||||
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)
|
||||
pkgs <- installed.packages()[, "Package"]
|
||||
sessioninfo::session_info(pkgs, include_base = TRUE)
|
||||
|
||||
- name: Url redirects
|
||||
# only perform if in an RC branch (`rc-vX.Y.Z`)
|
||||
if: ${{ github.event_name == 'push' && contains(github.ref, '/rc-v') }}
|
||||
run: |
|
||||
Rscript -e 'pak::pkg_install("r-lib/urlchecker"); urlchecker::url_update()'
|
||||
# throw an error if man files were updated
|
||||
if [ -n "$(git status --porcelain man)" ]
|
||||
then
|
||||
git status --porcelain
|
||||
>&2 echo "Updated links found in files above"
|
||||
>&2 echo 'Run `urlchecker::url_update()` to fix links locally'
|
||||
exit 1
|
||||
fi
|
||||
# Add locally changed urls
|
||||
git add .
|
||||
git commit -m 'Update links (GitHub Actions)' || echo "No link changes to commit"
|
||||
|
||||
- name: Document
|
||||
run: |
|
||||
Rscript -e 'devtools::document()'
|
||||
git add man/\* NAMESPACE
|
||||
git commit -m 'Document (GitHub Actions)' || echo "No documentation changes to commit"
|
||||
|
||||
- name: Check documentation
|
||||
run: |
|
||||
./tools/documentation/checkDocsCurrent.sh
|
||||
|
||||
- uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: ${{ matrix.config.node }}
|
||||
# https://github.com/actions/cache/blame/ccf96194800dbb7b7094edcd5a7cf3ec3c270f10/examples.md#L185-L200
|
||||
- name: Get yarn cache directory path
|
||||
id: yarn-cache-dir-path
|
||||
run: echo "::set-output name=dir::$(yarn cache dir)"
|
||||
- name: yarn cache
|
||||
uses: actions/cache@v2
|
||||
id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`)
|
||||
with:
|
||||
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
|
||||
key: ${{ matrix.config.os }}-${{ matrix.config.node }}-yarn-${{ hashFiles('**/yarn.lock') }}
|
||||
restore-keys: |
|
||||
${{ matrix.config.os }}-${{ matrix.config.node }}-yarn-
|
||||
- name: Build JS
|
||||
run: |
|
||||
cd srcts
|
||||
tree src
|
||||
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"
|
||||
if [ -n "$(git status --porcelain)" ]
|
||||
then
|
||||
git status --porcelain
|
||||
>&2 echo "The above files changed when we built the JavaScript assets."
|
||||
exit 1
|
||||
else
|
||||
echo "No difference detected; TypeScript build is current."
|
||||
fi
|
||||
|
||||
- name: Git Push (PR)
|
||||
uses: r-lib/actions/pr-push@master
|
||||
if: github.event_name == 'pull_request'
|
||||
with:
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Git Push (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"
|
||||
|
||||
# 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,18 +11,5 @@ 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
|
||||
|
||||
18
.vscode/settings.json
vendored
18
.vscode/settings.json
vendored
@@ -1,18 +0,0 @@
|
||||
{
|
||||
"search.exclude": {
|
||||
"**/.yarn": true,
|
||||
"**/.pnp.*": true
|
||||
},
|
||||
"prettier.prettierPath": "./node_modules/prettier",
|
||||
"typescript.enablePromptUseWorkspaceTsdk": true,
|
||||
"[r]": {
|
||||
"files.trimTrailingWhitespace": true,
|
||||
"files.insertFinalNewline": true,
|
||||
},
|
||||
"[typescript]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||
"editor.formatOnSave": true,
|
||||
"files.trimTrailingWhitespace": true,
|
||||
"files.insertFinalNewline": true,
|
||||
},
|
||||
}
|
||||
18
DESCRIPTION
18
DESCRIPTION
@@ -1,7 +1,7 @@
|
||||
Package: shiny
|
||||
Type: Package
|
||||
Title: Web Application Framework for R
|
||||
Version: 1.7.3
|
||||
Version: 1.6.0.9022
|
||||
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"),
|
||||
@@ -78,8 +78,8 @@ Imports:
|
||||
mime (>= 0.3),
|
||||
jsonlite (>= 0.9.16),
|
||||
xtable,
|
||||
fontawesome (>= 0.4.0),
|
||||
htmltools (>= 0.5.2),
|
||||
fontawesome (>= 0.2.1),
|
||||
htmltools (>= 0.5.1.9006),
|
||||
R6 (>= 2.0),
|
||||
sourcetools,
|
||||
later (>= 1.0.0),
|
||||
@@ -91,7 +91,7 @@ Imports:
|
||||
withr,
|
||||
commonmark (>= 1.7),
|
||||
glue (>= 1.3.2),
|
||||
bslib (>= 0.3.0),
|
||||
bslib (>= 0.2.5.9002),
|
||||
cachem,
|
||||
ellipsis,
|
||||
lifecycle (>= 0.2.0)
|
||||
@@ -105,12 +105,17 @@ Suggests:
|
||||
ggplot2,
|
||||
reactlog (>= 1.0.0),
|
||||
magrittr,
|
||||
shinytest (>= 1.4.0.9003),
|
||||
yaml,
|
||||
future,
|
||||
dygraphs,
|
||||
ragg,
|
||||
showtext,
|
||||
sass
|
||||
Remotes:
|
||||
r-lib/rlang,
|
||||
rstudio/bslib,
|
||||
rstudio/htmltools
|
||||
URL: https://shiny.rstudio.com/
|
||||
BugReports: https://github.com/rstudio/shiny/issues
|
||||
Collate:
|
||||
@@ -188,7 +193,6 @@ Collate:
|
||||
'shinywrappers.R'
|
||||
'showcase.R'
|
||||
'snapshot.R'
|
||||
'staticimports.R'
|
||||
'tar.R'
|
||||
'test-export.R'
|
||||
'test-server.R'
|
||||
@@ -201,10 +205,8 @@ Collate:
|
||||
'version_selectize.R'
|
||||
'version_strftime.R'
|
||||
'viewer.R'
|
||||
RoxygenNote: 7.2.1
|
||||
RoxygenNote: 7.1.1
|
||||
Encoding: UTF-8
|
||||
Roxygen: list(markdown = TRUE)
|
||||
RdMacros: lifecycle
|
||||
Config/testthat/edition: 3
|
||||
Config/Needs/check:
|
||||
shinytest2
|
||||
|
||||
@@ -378,7 +378,6 @@ importFrom(htmltools,tags)
|
||||
importFrom(htmltools,validateCssUnit)
|
||||
importFrom(htmltools,withTags)
|
||||
importFrom(lifecycle,deprecated)
|
||||
importFrom(lifecycle,is_present)
|
||||
importFrom(promises,"%...!%")
|
||||
importFrom(promises,"%...>%")
|
||||
importFrom(promises,as.promise)
|
||||
@@ -387,18 +386,14 @@ 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)
|
||||
@@ -413,8 +408,4 @@ 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)
|
||||
|
||||
98
NEWS.md
98
NEWS.md
@@ -1,75 +1,5 @@
|
||||
shiny 1.7.3
|
||||
===========
|
||||
|
||||
### Bug fixes
|
||||
|
||||
* Shiny 1.7.0 changed the `icon(lib="fontawesome")` implementation from a bundled copy of fontawesome, to the {fontawesome} package. This led to issue #3688, where icons that were previously working, were now breaking. That's because {fontawesome} 0.3.0 and earlier did not have support for icon names used in Font Awesome 5 and earlier, only the newest icon names used in Font Awesome 6. Now, {fontawesome} 0.4.0 has restored support for those older icon names, and Shiny 1.7.2.1 has updated its {fontawesome} requirement to >=0.4.0.
|
||||
|
||||
|
||||
shiny 1.7.2
|
||||
===========
|
||||
|
||||
## Full changelog
|
||||
|
||||
### Breaking changes
|
||||
|
||||
* Closed #3626: `renderPlot()` (and `plotPNG()`) now uses `ragg::agg_png()` by default when the [`{ragg}` package](https://github.com/r-lib/ragg) is installed. To restore the previous behavior, set `options(shiny.useragg = FALSE)`. (#3654)
|
||||
|
||||
### New features and improvements
|
||||
|
||||
* Closed #1545: `insertUI()` now executes `<script>` tags. (#3630)
|
||||
|
||||
* `fileInput()` can set the `capture` attribute to facilitates user access to a device's media capture mechanism, such as a camera, or microphone, from within a file upload control ([W3C HTML Media Capture](https://www.w3.org/TR/html-media-capture/)). (Thanks to khaled-alshamaa, #3481)
|
||||
|
||||
* Closed tidyverse/dplyr#5552: Compatibility of dplyr 1.0 (and rlang chained errors in general) with `req()`, `validate()`, and friends.
|
||||
|
||||
* Closed tidyverse/dplyr#6154: Values from an `actionButton()` had S3 classes in the incorrect order.
|
||||
|
||||
* Closed #3346: Default for `ref` input in `runGithub()` changed from `"master"` to `"HEAD"`. (#3564)
|
||||
|
||||
* Closed #3619: In R 4.2, `splitLayout()` no longer raises warnings about incorrect length in an `if` statement. (Thanks to @dmenne, #3625)
|
||||
|
||||
### Bug fixes
|
||||
|
||||
* Closed #3250:`{rlang}`/`{tidyeval}` conditions (i.e., warnings and errors) are no longer filtered from stack traces. (#3602)
|
||||
|
||||
* Closed #3581: Errors in throttled/debounced reactive expressions no longer cause the session to exit. (#3624)
|
||||
|
||||
* Closed #3657: `throttle.ts` and the `Throttler` typescript objects it provides now function as intended. (Thanks gto @dvg-p4, #3659)
|
||||
|
||||
* The auto-reload feature (`options(shiny.autoreload=TRUE)`) was not being activated by `devmode(TRUE)`, despite a console message asserting that it was. (#3620)
|
||||
|
||||
* Closed #2297: If an error occurred in parsing a value in a bookmark query string, an error would be thrown and nothing would be restored. Now a message is displayed and that value is ignored. (Thanks to @daattali, #3385)
|
||||
|
||||
* Restored the previous behavior of automatically guessing the `Content-Type` header for `downloadHandler` functions when no explicit `contentType` argument is supplied. (#3393)
|
||||
|
||||
* Previously, updating an input value without a corresponding Input binding element did not trigger a JavaScript `shiny:inputchanged` event. Now, if no Input binding element is found, the `shiny:inputchanged` event is triggered on `window.document`. (#3584)
|
||||
|
||||
* Closed #2955: Input and output bindings previously attempted to use `el['data-input-id']`, but that never worked. They now use `el.getAttribute('data-input-id')` instead. (#3538)
|
||||
|
||||
### Minor improvements
|
||||
|
||||
* When taking a test snapshot, the sort order of the json keys of the `input`, `output`, and `export` fields is currently sorted using the locale of the machine. This can lead to inconsistent test snapshot results. To opt-in to a consistent ordering of snapshot fields with `{shinytest}`, please set the global option `options(shiny.snapshotsortc = TRUE)`. `{shinytest2}` users do not need to set this value. (#3515)
|
||||
|
||||
* Closed rstudio/shinytest2#222: When restoring a context (i.e., bookmarking) from a URL, Shiny now better handles a trailing `=` after `_inputs_` and `_values_`. (#3648)
|
||||
|
||||
* Shiny's internal HTML dependencies are now mounted dynamically instead of statically. (#3537)
|
||||
|
||||
* HTML dependencies that are sent to dynamic UI now have better type checking, and no longer require a `dep.src.href` field. (#3537)
|
||||
|
||||
|
||||
shiny 1.7.1
|
||||
===========
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
* Closed #3516: Fix regression in repeated calls to `appendTab()` when `navbarMenu()` is already present within a `tabsetPanel()`/`navbarPage()`. (#3518)
|
||||
|
||||
* Re-arranged conditions for testthat 1.0.0 compatibility. (#3512)
|
||||
|
||||
|
||||
shiny 1.7.0
|
||||
===========
|
||||
shiny 1.6.0.9000
|
||||
================
|
||||
|
||||
## Full changelog
|
||||
|
||||
@@ -77,8 +7,6 @@ shiny 1.7.0
|
||||
|
||||
* 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)
|
||||
@@ -87,20 +15,16 @@ shiny 1.7.0
|
||||
* 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/v1.7.0/srcts). (#3296)
|
||||
* 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)
|
||||
|
||||
* Switched from `digest::digest()` to `rlang::hash()` for hashing. (#3264)
|
||||
|
||||
@@ -112,14 +36,10 @@ shiny 1.7.0
|
||||
|
||||
* 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)
|
||||
|
||||
* 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)
|
||||
* 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)
|
||||
@@ -133,7 +53,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/bslib.html#dynamic) 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/theming.html#dynamic-shiny) 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.
|
||||
@@ -255,7 +175,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/r-lib/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/jeroen/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)
|
||||
|
||||
@@ -521,7 +441,7 @@ This is a significant release for Shiny, with a major new feature that was nearl
|
||||
|
||||
* Removed the (ridiculously outdated) "experimental feature" tag from the reference documentation for `renderUI`. (#2036)
|
||||
|
||||
* Addressed #1907: the `ignoreInit` argument was first added only to `observeEvent`. Later, we also added it to `eventReactive`, but forgot to update the documentation. Now done, thanks @flo12392! (#2036)
|
||||
* Addressed #1907: the `ignoreInit` argument was first added only to `observeEvent`. Later, we also added it to `eventReactive`, but forgot to update the documentation. Now done, thanks [@flo12392](https://github.com/flo12392)! (#2036)
|
||||
|
||||
### Bug fixes
|
||||
|
||||
@@ -535,7 +455,7 @@ This is a significant release for Shiny, with a major new feature that was nearl
|
||||
|
||||
* Fixed #1600: URL-encoded bookmarking did not work with sliders that had dates or date-times. (#1961)
|
||||
|
||||
* Fixed #1962: [File dragging and dropping](https://www.rstudio.com/blog/shiny-1-0-4/) broke in the presence of jQuery version 3.0 as introduced by the [rhandsontable](https://jrowen.github.io/rhandsontable/) [htmlwidget](https://www.htmlwidgets.org/). (#2005)
|
||||
* Fixed #1962: [File dragging and dropping](https://blog.rstudio.com/2017/08/15/shiny-1-0-4/) broke in the presence of jQuery version 3.0 as introduced by the [rhandsontable](https://jrowen.github.io/rhandsontable/) [htmlwidget](https://www.htmlwidgets.org/). (#2005)
|
||||
|
||||
* Improved the error handling inside the `addResourcePath()` function, to give end users more informative error messages when the `directoryPath` argument cannot be normalized. This is especially useful for `runtime: shiny_prerendered` Rmd documents, like `learnr` tutorials. (#1968)
|
||||
|
||||
|
||||
@@ -10,7 +10,8 @@
|
||||
#' 2: app.R : Main application file
|
||||
#' 3: R/example.R : Helper file with R code
|
||||
#' 4: R/example-module.R : Example module
|
||||
#' 5: tests/testthat/ : Tests using the testthat and shinytest2 package
|
||||
#' 5: tests/shinytest/ : Tests using the shinytest package
|
||||
#' 6: tests/testthat/ : Tests using the testthat package
|
||||
#' ```
|
||||
#'
|
||||
#' If option 1 is selected, the full example application including the
|
||||
@@ -23,12 +24,13 @@
|
||||
#' | |- example-module.R
|
||||
#' | `- example.R
|
||||
#' `- tests
|
||||
#' |- shinytest.R
|
||||
#' |- shinytest
|
||||
#' | `- mytest.R
|
||||
#' |- testthat.R
|
||||
#' `- testthat
|
||||
#' |- setup-shinytest2.R
|
||||
#' |- test-examplemodule.R
|
||||
#' |- test-server.R
|
||||
#' |- test-shinytest2.R
|
||||
#' `- test-sort.R
|
||||
#' ```
|
||||
#'
|
||||
@@ -43,21 +45,20 @@
|
||||
#' * `tests/` contains various tests for the application. You may
|
||||
#' choose to use or remove any of them. They can be executed by the
|
||||
#' [runTests()] function.
|
||||
#' * `tests/shinytest.R` is a test runner for test files in the
|
||||
#' `tests/shinytest/` directory.
|
||||
#' * `tests/shinytest/mytest.R` is a test that uses the
|
||||
#' [shinytest](https://rstudio.github.io/shinytest/) package to do
|
||||
#' snapshot-based testing.
|
||||
#' * `tests/testthat.R` is a test runner for test files in the
|
||||
#' `tests/testthat/` directory using the
|
||||
#' [shinytest2](https://rstudio.github.io/shinytest2/reference/test_app.html)
|
||||
#' package.
|
||||
#' * `tests/testthat/setup-shinytest2.R` is setup file to source your `./R` folder into the testing environment.
|
||||
#' `tests/testthat/` directory using the [testthat](https://testthat.r-lib.org/) package.
|
||||
#' * `tests/testthat/test-examplemodule.R` is a test for an application's module server function.
|
||||
#' * `tests/testthat/test-server.R` is a test for the application's server code
|
||||
#' * `tests/testthat/test-shinytest2.R` is a test that uses the
|
||||
#' [shinytest2](https://rstudio.github.io/shinytest2/) package to do
|
||||
#' snapshot-based testing.
|
||||
#' * `tests/testthat/test-sort.R` is a test for a supporting function in the `R/` directory.
|
||||
#'
|
||||
#' @param path Path to create new shiny application template.
|
||||
#' @param examples Either one of "default", "ask", "all", or any combination of
|
||||
#' "app", "rdir", "module", and "tests". In an
|
||||
#' "app", "rdir", "module", "shinytest", and "testthat". In an
|
||||
#' interactive session, "default" falls back to "ask"; in a non-interactive
|
||||
#' session, "default" falls back to "all". With "ask", this function will
|
||||
#' prompt the user to select which template items will be added to the new app
|
||||
@@ -78,19 +79,15 @@ shinyAppTemplate <- function(path = NULL, examples = "default", dryrun = FALSE)
|
||||
# =======================================================
|
||||
|
||||
choices <- c(
|
||||
app = "app.R : Main application file",
|
||||
rdir = "R/example.R : Helper file with R code",
|
||||
module = "R/example-module.R : Example module",
|
||||
tests = "tests/testthat/ : Tests using {testthat} and {shinytest2}"
|
||||
app = "app.R : Main application file",
|
||||
rdir = "R/example.R : Helper file with R code",
|
||||
module = "R/example-module.R : Example module",
|
||||
shinytest = "tests/shinytest/ : Tests using the shinytest package",
|
||||
testthat = "tests/testthat/ : Tests using the testthat package"
|
||||
)
|
||||
|
||||
# Support legacy value
|
||||
examples[examples == "shinytest"] <- "tests"
|
||||
examples[examples == "testthat"] <- "tests"
|
||||
examples <- unique(examples)
|
||||
|
||||
if (identical(examples, "default")) {
|
||||
if (rlang::is_interactive()) {
|
||||
if (interactive()) {
|
||||
examples <- "ask"
|
||||
} else {
|
||||
examples <- "all"
|
||||
@@ -127,8 +124,18 @@ shinyAppTemplate <- function(path = NULL, examples = "default", dryrun = FALSE)
|
||||
return(invisible())
|
||||
}
|
||||
|
||||
if ("tests" %in% examples) {
|
||||
rlang::check_installed("shinytest2", "for {testthat} tests to work as expected", version = "0.2.0")
|
||||
if ("shinytest" %in% examples) {
|
||||
if (!is_available("shinytest", "1.4.0"))
|
||||
{
|
||||
message(
|
||||
"The tests/shinytest directory needs shinytest 1.4.0 or later to work properly."
|
||||
)
|
||||
if (is_available("shinytest")) {
|
||||
message("You currently have shinytest ",
|
||||
utils::packageVersion("shinytest"), " installed.")
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
# =======================================================
|
||||
@@ -145,7 +152,7 @@ shinyAppTemplate <- function(path = NULL, examples = "default", dryrun = FALSE)
|
||||
|
||||
# Helper to resolve paths relative to our template
|
||||
template_path <- function(...) {
|
||||
system_file("app_template", ..., package = "shiny")
|
||||
system.file("app_template", ..., package = "shiny")
|
||||
}
|
||||
|
||||
# Resolve path relative to destination
|
||||
@@ -201,13 +208,16 @@ shinyAppTemplate <- function(path = NULL, examples = "default", dryrun = FALSE)
|
||||
}
|
||||
|
||||
# Copy the files for a tests/ subdirectory
|
||||
copy_test_dir <- function() {
|
||||
copy_test_dir <- function(name) {
|
||||
files <- dir(template_path("tests"), recursive = TRUE)
|
||||
# Note: This is not the same as using dir(pattern = "^shinytest"), since
|
||||
# that will not match files inside of shinytest/.
|
||||
files <- files[grepl(paste0("^", name), files)]
|
||||
|
||||
# Filter out files that are not module files in the R directory.
|
||||
if (! "rdir" %in% examples) {
|
||||
# find all files in the testthat folder that are not module or server files
|
||||
is_r_folder_file <- !grepl("module|server|shinytest2|testthat", basename(files))
|
||||
is_r_folder_file <- (!grepl("module|server", basename(files))) & (dirname(files) == "testthat")
|
||||
files <- files[!is_r_folder_file]
|
||||
}
|
||||
|
||||
@@ -272,10 +282,12 @@ shinyAppTemplate <- function(path = NULL, examples = "default", dryrun = FALSE)
|
||||
copy_file(file.path("R", module_files))
|
||||
}
|
||||
|
||||
# tests/testthat dir
|
||||
if ("tests" %in% examples) {
|
||||
copy_test_dir()
|
||||
# tests/ dir
|
||||
if ("shinytest" %in% examples) {
|
||||
copy_test_dir("shinytest")
|
||||
}
|
||||
if ("testthat" %in% examples) {
|
||||
copy_test_dir("testthat")
|
||||
}
|
||||
|
||||
invisible()
|
||||
}
|
||||
|
||||
@@ -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 [createRenderFunction()]
|
||||
#' expression in another function, before passing it to [markRenderFunction()]
|
||||
#' (instead of [createRenderFunction()]). Because the user code is wrapped in
|
||||
#' another function, `createRenderFunction()` is not able to automatically
|
||||
#' another function, `markRenderFunction()` is not able to automatically
|
||||
#' extract the user-provided code and use it in the cache key. Instead,
|
||||
#' `renderPrint` calls `createRenderFunction()`, it explicitly passes along a
|
||||
#' `renderPrint` calls `markRenderFunction()`, 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) {
|
||||
#' q <- rlang::enquo0(expr)
|
||||
#' expr <- substitute(expr)
|
||||
#'
|
||||
#' htmlwidgets::shinyRenderWidget(
|
||||
#' q,
|
||||
#' htmlwidgets::shinyRenderWidget(expr,
|
||||
#' myWidgetOutput,
|
||||
#' quoted = TRUE,
|
||||
#' cacheHint = list(label = "myWidget", userQuo = q)
|
||||
#' env = parent.frame(),
|
||||
#' cacheHint = list(label = "myWidget", userExpr = expr)
|
||||
#' )
|
||||
#' }
|
||||
#' ```
|
||||
#'
|
||||
#' If your `render` function sets any internal state, you may find it useful
|
||||
#' in your call to [createRenderFunction()] to use
|
||||
#' in your call to [createRenderFunction()] or [markRenderFunction()] 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()`:
|
||||
|
||||
@@ -321,38 +321,34 @@ RestoreContext <- R6Class("RestoreContext",
|
||||
if (substr(queryString, 1, 1) == '?')
|
||||
queryString <- substr(queryString, 2, nchar(queryString))
|
||||
|
||||
# The "=" after "_inputs_" is optional. Shiny doesn't generate URLs with
|
||||
# "=", but httr always adds "=".
|
||||
inputs_reg <- "(^|&)_inputs_=?(&|$)"
|
||||
values_reg <- "(^|&)_values_=?(&|$)"
|
||||
|
||||
# Error if multiple '_inputs_' or '_values_'. This is needed because
|
||||
# strsplit won't add an entry if the search pattern is at the end of a
|
||||
# string.
|
||||
if (length(gregexpr(inputs_reg, queryString)[[1]]) > 1)
|
||||
if (length(gregexpr("(^|&)_inputs_(&|$)", queryString)[[1]]) > 1)
|
||||
stop("Invalid state string: more than one '_inputs_' found")
|
||||
if (length(gregexpr(values_reg, queryString)[[1]]) > 1)
|
||||
if (length(gregexpr("(^|&)_values_(&|$)", queryString)[[1]]) > 1)
|
||||
stop("Invalid state string: more than one '_values_' found")
|
||||
|
||||
# Look for _inputs_ and store following content in inputStr
|
||||
splitStr <- strsplit(queryString, inputs_reg)[[1]]
|
||||
splitStr <- strsplit(queryString, "(^|&)_inputs_(&|$)")[[1]]
|
||||
if (length(splitStr) == 2) {
|
||||
inputStr <- splitStr[2]
|
||||
# Remove any _values_ (and content after _values_) that may come after
|
||||
# _inputs_
|
||||
inputStr <- strsplit(inputStr, values_reg)[[1]][1]
|
||||
inputStr <- strsplit(inputStr, "(^|&)_values_(&|$)")[[1]][1]
|
||||
|
||||
} else {
|
||||
inputStr <- ""
|
||||
}
|
||||
|
||||
# Look for _values_ and store following content in valueStr
|
||||
splitStr <- strsplit(queryString, values_reg)[[1]]
|
||||
splitStr <- strsplit(queryString, "(^|&)_values_(&|$)")[[1]]
|
||||
if (length(splitStr) == 2) {
|
||||
valueStr <- splitStr[2]
|
||||
# Remove any _inputs_ (and content after _inputs_) that may come after
|
||||
# _values_
|
||||
valueStr <- strsplit(valueStr, inputs_reg)[[1]][1]
|
||||
valueStr <- strsplit(valueStr, "(^|&)_inputs_(&|$)")[[1]][1]
|
||||
|
||||
} else {
|
||||
valueStr <- ""
|
||||
@@ -363,20 +359,16 @@ RestoreContext <- R6Class("RestoreContext",
|
||||
values <- parseQueryString(valueStr, nested = TRUE)
|
||||
|
||||
valuesFromJSON <- function(vals) {
|
||||
varsUnparsed <- c()
|
||||
valsParsed <- mapply(names(vals), vals, SIMPLIFY = FALSE,
|
||||
mapply(names(vals), vals, SIMPLIFY = FALSE,
|
||||
FUN = function(name, value) {
|
||||
tryCatch(
|
||||
safeFromJSON(value),
|
||||
error = function(e) {
|
||||
varsUnparsed <<- c(varsUnparsed, name)
|
||||
warning("Failed to parse URL parameter \"", name, "\"")
|
||||
stop("Failed to parse URL parameter \"", name, "\"")
|
||||
}
|
||||
)
|
||||
}
|
||||
)
|
||||
valsParsed[varsUnparsed] <- NULL
|
||||
valsParsed
|
||||
}
|
||||
|
||||
inputs <- valuesFromJSON(inputs)
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
#' @param sidebarPanel The [sidebarPanel] containing input controls
|
||||
#' @param mainPanel The [mainPanel] containing outputs
|
||||
#' @keywords internal
|
||||
#' @return A UI definition that can be passed to the [shinyUI] function
|
||||
#' @return A UI defintion that can be passed to the [shinyUI] function
|
||||
#' @export
|
||||
pageWithSidebar <- function(headerPanel,
|
||||
sidebarPanel,
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
#' Can also be set as a side effect of the [titlePanel()] function.
|
||||
#' @inheritParams bootstrapPage
|
||||
#'
|
||||
#' @return A UI definition that can be passed to the [shinyUI] function.
|
||||
#' @return A UI defintion that can be passed to the [shinyUI] function.
|
||||
#'
|
||||
#' @details To create a fluid page use the `fluidPage` function and include
|
||||
#' instances of `fluidRow` and [column()] within it. As an
|
||||
@@ -111,7 +111,7 @@ fluidRow <- function(...) {
|
||||
#' @param title The browser window title (defaults to the host URL of the page)
|
||||
#' @inheritParams bootstrapPage
|
||||
#'
|
||||
#' @return A UI definition that can be passed to the [shinyUI] function.
|
||||
#' @return A UI defintion that can be passed to the [shinyUI] function.
|
||||
#'
|
||||
#' @details To create a fixed page use the `fixedPage` function and include
|
||||
#' instances of `fixedRow` and [column()] within it. Note that
|
||||
@@ -516,7 +516,7 @@ splitLayout <- function(..., cellWidths = NULL, cellArgs = list()) {
|
||||
children <- children[childIdx]
|
||||
count <- length(children)
|
||||
|
||||
if (length(cellWidths) == 0 || isTRUE(is.na(cellWidths))) {
|
||||
if (length(cellWidths) == 0 || is.na(cellWidths)) {
|
||||
cellWidths <- sprintf("%.3f%%", 100 / count)
|
||||
}
|
||||
cellWidths <- rep(cellWidths, length.out = count)
|
||||
|
||||
@@ -24,7 +24,7 @@ NULL
|
||||
#' This will be used as the lang in the \code{<html>} tag, as in \code{<html lang="en">}.
|
||||
#' The default (NULL) results in an empty string.
|
||||
#'
|
||||
#' @return A UI definition that can be passed to the [shinyUI] function.
|
||||
#' @return A UI defintion that can be passed to the [shinyUI] function.
|
||||
#'
|
||||
#' @note The `basicPage` function is deprecated, you should use the
|
||||
#' [fluidPage()] function instead.
|
||||
@@ -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 <- c(bootstrapLib(theme), args)
|
||||
args <- list(bootstrapLib(theme), args)
|
||||
ui <- do.call(tagList, args)
|
||||
} else {
|
||||
ui <- do.call(tagList, args)
|
||||
@@ -82,53 +82,30 @@ getLang <- function(ui) {
|
||||
#' @inheritParams bootstrapPage
|
||||
#' @export
|
||||
bootstrapLib <- function(theme = NULL) {
|
||||
tagFunction(function() {
|
||||
if (isRunning()) {
|
||||
# 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()
|
||||
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()
|
||||
@@ -138,7 +115,8 @@ bs_theme_deps <- function(theme) {
|
||||
}
|
||||
|
||||
is_bs_theme <- function(x) {
|
||||
bslib::is_bs_theme(x)
|
||||
is_available("bslib", "0.2.0.9000") &&
|
||||
bslib::is_bs_theme(x)
|
||||
}
|
||||
|
||||
#' Obtain Shiny's Bootstrap Sass theme
|
||||
@@ -214,10 +192,11 @@ registerThemeDependency <- function(func) {
|
||||
|
||||
bootstrapDependency <- function(theme) {
|
||||
htmlDependency(
|
||||
"bootstrap",
|
||||
bootstrapVersion,
|
||||
src = "www/shared/bootstrap",
|
||||
package = "shiny",
|
||||
"bootstrap", bootstrapVersion,
|
||||
c(
|
||||
href = "shared/bootstrap",
|
||||
file = system.file("www/shared/bootstrap", package = "shiny")
|
||||
),
|
||||
script = c(
|
||||
"js/bootstrap.min.js",
|
||||
# Safely adding accessibility plugin for screen readers and keyboard users; no break for sighted aspects (see https://github.com/paypal/bootstrap-accessibility-plugin)
|
||||
@@ -385,7 +364,7 @@ collapseSizes <- function(padding) {
|
||||
#' @inheritParams bootstrapPage
|
||||
#' @param icon Optional icon to appear on a `navbarMenu` tab.
|
||||
#'
|
||||
#' @return A UI definition that can be passed to the [shinyUI] function.
|
||||
#' @return A UI defintion that can be passed to the [shinyUI] function.
|
||||
#'
|
||||
#' @details The `navbarMenu` function can be used to create an embedded
|
||||
#' menu within the navbar that in turns includes additional tabPanels (see
|
||||
@@ -1107,17 +1086,11 @@ tableOutput <- function(outputId) {
|
||||
|
||||
dataTableDependency <- list(
|
||||
htmlDependency(
|
||||
"datatables",
|
||||
"1.10.5",
|
||||
src = "www/shared/datatables",
|
||||
package = "shiny",
|
||||
"datatables", "1.10.5", c(href = "shared/datatables"),
|
||||
script = "js/jquery.dataTables.min.js"
|
||||
),
|
||||
htmlDependency(
|
||||
"datatables-bootstrap",
|
||||
"1.10.5",
|
||||
src = "www/shared/datatables",
|
||||
package = "shiny",
|
||||
"datatables-bootstrap", "1.10.5", c(href = "shared/datatables"),
|
||||
stylesheet = c("css/dataTables.bootstrap.css", "css/dataTables.extra.css"),
|
||||
script = "js/dataTables.bootstrap.js"
|
||||
)
|
||||
@@ -1157,7 +1130,7 @@ dataTableOutput <- function(outputId) {
|
||||
htmlOutput <- function(outputId, inline = FALSE,
|
||||
container = if (inline) span else div, ...)
|
||||
{
|
||||
if (any_unnamed(list(...))) {
|
||||
if (anyUnnamed(list(...))) {
|
||||
warning("Unnamed elements in ... will be replaced with dynamic UI.")
|
||||
}
|
||||
container(id = outputId, class="shiny-html-output", ...)
|
||||
|
||||
@@ -228,9 +228,7 @@ withLogErrors <- function(expr,
|
||||
if (promises::is.promise(result)) {
|
||||
result <- promises::catch(result, function(cond) {
|
||||
# Don't print shiny.silent.error (i.e. validation errors)
|
||||
if (cnd_inherits(cond, "shiny.silent.error")) {
|
||||
return()
|
||||
}
|
||||
if (inherits(cond, "shiny.silent.error")) return()
|
||||
if (isTRUE(getOption("show.error.messages"))) {
|
||||
printError(cond, full = full, offset = offset)
|
||||
}
|
||||
@@ -241,7 +239,7 @@ withLogErrors <- function(expr,
|
||||
},
|
||||
error = function(cond) {
|
||||
# Don't print shiny.silent.error (i.e. validation errors)
|
||||
if (cnd_inherits(cond, "shiny.silent.error")) return()
|
||||
if (inherits(cond, "shiny.silent.error")) return()
|
||||
if (isTRUE(getOption("show.error.messages"))) {
|
||||
printError(cond, full = full, offset = offset)
|
||||
}
|
||||
@@ -421,17 +419,8 @@ pruneStackTrace <- function(parents) {
|
||||
# Loop over the parent indices. Anything that is not parented by current_node
|
||||
# (a.k.a. last-known-good node), or is a dupe, can be discarded. Anything that
|
||||
# is kept becomes the new current_node.
|
||||
#
|
||||
# jcheng 2022-03-18: Two more reasons a node can be kept:
|
||||
# 1. parent is 0
|
||||
# 2. parent is i
|
||||
# Not sure why either of these situations happen, but they're common when
|
||||
# interacting with rlang/dplyr errors. See issue rstudio/shiny#3250 for repro
|
||||
# cases.
|
||||
include <- vapply(seq_along(parents), function(i) {
|
||||
if ((!is_dupe[[i]] && parents[[i]] == current_node) ||
|
||||
parents[[i]] == 0 ||
|
||||
parents[[i]] == i) {
|
||||
if (!is_dupe[[i]] && parents[[i]] == current_node) {
|
||||
current_node <<- i
|
||||
TRUE
|
||||
} else {
|
||||
|
||||
@@ -9,19 +9,13 @@
|
||||
#' @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,
|
||||
type = c("deprecated", "superseded")
|
||||
version, what, with = NULL, details = NULL
|
||||
) {
|
||||
if (is_false(getOption("shiny.deprecation.messages"))) {
|
||||
return(invisible())
|
||||
}
|
||||
|
||||
type <- match.arg(type)
|
||||
|
||||
msg <- paste0("`", what, "` is ", type, " as of shiny ", version, ".")
|
||||
msg <- paste0("`", what, "` is deprecated as of shiny ", version, ".")
|
||||
if (!is.null(with)) {
|
||||
msg <- paste0(msg, "\n", "Please use `", with, "` instead.")
|
||||
}
|
||||
@@ -38,20 +32,13 @@ deprecatedEnvQuotedMessage <- function() {
|
||||
if (!in_devmode()) return(invisible())
|
||||
if (is_false(getOption("shiny.deprecation.messages"))) return(invisible())
|
||||
|
||||
# Capture calling function
|
||||
grandparent_call <- sys.call(-2)
|
||||
# Turn language into user friendly string
|
||||
grandparent_txt <- paste0(utils::capture.output({grandparent_call}), collapse = "\n")
|
||||
|
||||
# manually
|
||||
msg <- paste0(
|
||||
"The `env` and `quoted` arguments are deprecated as of shiny 1.7.0.",
|
||||
"The `env` and `quoted` arguments are deprecated as of shiny 1.6.0.",
|
||||
" Please use quosures from `rlang` instead.\n",
|
||||
"See <https://github.com/rstudio/shiny/issues/3108> for more information.\n",
|
||||
"Function call:\n",
|
||||
grandparent_txt
|
||||
"See <https://github.com/rstudio/shiny/issues/3108> for more information."
|
||||
)
|
||||
# Call less often as users do not have much control over this warning
|
||||
rlang::inform(message = msg, .frequency = "regularly", .frequency_id = msg, .file = stderr())
|
||||
rlang::inform(message = msg, .frequency = "always", .frequency_id = msg, .file = stderr())
|
||||
}
|
||||
|
||||
|
||||
@@ -73,7 +60,7 @@ diskCache <- function(
|
||||
logfile = NULL
|
||||
) {
|
||||
shinyDeprecated("1.6.0", "diskCache()", "cachem::cache_disk()")
|
||||
if (is_present(exec_missing)) {
|
||||
if (lifecycle::is_present(exec_missing)) {
|
||||
shinyDeprecated("1.6.0", "diskCache(exec_missing =)")
|
||||
}
|
||||
|
||||
@@ -106,7 +93,7 @@ memoryCache <- function(
|
||||
logfile = NULL)
|
||||
{
|
||||
shinyDeprecated("1.6.0", "diskCache()", "cachem::cache_mem()")
|
||||
if (is_present(exec_missing)) {
|
||||
if (lifecycle::is_present(exec_missing)) {
|
||||
shinyDeprecated("1.6.0", "diskCache(exec_missing =)")
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#' Shiny Developer Mode
|
||||
#'
|
||||
#' @description `r lifecycle::badge("experimental")`
|
||||
#' @description \lifecycle{experimental}
|
||||
#'
|
||||
#' Developer Mode enables a number of [options()] to make a developer's life
|
||||
#' easier, like enabling non-minified JS and printing messages about
|
||||
|
||||
55
R/globals.R
55
R/globals.R
@@ -1,6 +1,55 @@
|
||||
# A scope where we can put mutable global state
|
||||
.globals <- new.env(parent = emptyenv())
|
||||
|
||||
register_s3_method <- function(pkg, generic, class, fun = NULL) {
|
||||
stopifnot(is.character(pkg), length(pkg) == 1)
|
||||
stopifnot(is.character(generic), length(generic) == 1)
|
||||
stopifnot(is.character(class), length(class) == 1)
|
||||
|
||||
if (is.null(fun)) {
|
||||
fun <- get(paste0(generic, ".", class), envir = parent.frame())
|
||||
} else {
|
||||
stopifnot(is.function(fun))
|
||||
}
|
||||
|
||||
if (pkg %in% loadedNamespaces()) {
|
||||
registerS3method(generic, class, fun, envir = asNamespace(pkg))
|
||||
}
|
||||
|
||||
# Always register hook in case pkg is loaded at some
|
||||
# point the future (or, potentially, but less commonly,
|
||||
# unloaded & reloaded)
|
||||
setHook(
|
||||
packageEvent(pkg, "onLoad"),
|
||||
function(...) {
|
||||
registerS3method(generic, class, fun, envir = asNamespace(pkg))
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
register_upgrade_message <- function(pkg, version) {
|
||||
|
||||
msg <- sprintf(
|
||||
"This version of Shiny is designed to work with '%s' >= %s.
|
||||
Please upgrade via install.packages('%s').",
|
||||
pkg, version, pkg
|
||||
)
|
||||
|
||||
if (pkg %in% loadedNamespaces() && !is_available(pkg, version)) {
|
||||
packageStartupMessage(msg)
|
||||
}
|
||||
|
||||
# Always register hook in case pkg is loaded at some
|
||||
# point the future (or, potentially, but less commonly,
|
||||
# unloaded & reloaded)
|
||||
setHook(
|
||||
packageEvent(pkg, "onLoad"),
|
||||
function(...) {
|
||||
if (!is_available(pkg, version)) packageStartupMessage(msg)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
.onLoad <- function(libname, pkgname) {
|
||||
# R's lazy-loading package scheme causes the private seed to be cached in the
|
||||
# package itself, making our PRNG completely deterministic. This line resets
|
||||
@@ -13,9 +62,9 @@
|
||||
|
||||
# Make sure these methods are available to knitr if shiny is loaded but not
|
||||
# attached.
|
||||
s3_register("knitr::knit_print", "reactive")
|
||||
s3_register("knitr::knit_print", "shiny.appobj")
|
||||
s3_register("knitr::knit_print", "shiny.render.function")
|
||||
register_s3_method("knitr", "knit_print", "reactive")
|
||||
register_s3_method("knitr", "knit_print", "shiny.appobj")
|
||||
register_s3_method("knitr", "knit_print", "shiny.render.function")
|
||||
|
||||
# Shiny 1.4.0 bumps jQuery 1.x to 3.x, which caused a problem
|
||||
# with static-rendering of htmlwidgets, and htmlwidgets 1.5
|
||||
|
||||
38
R/graph.R
38
R/graph.R
@@ -4,7 +4,7 @@
|
||||
# @param version The version of the package
|
||||
check_suggested <- function(package, version = NULL) {
|
||||
|
||||
if (is_installed(package, version)) {
|
||||
if (is_available(package, version)) {
|
||||
return()
|
||||
}
|
||||
|
||||
@@ -115,28 +115,22 @@ check_reactlog <- function() {
|
||||
}
|
||||
# read reactlog version from description file
|
||||
# prevents version mismatch in code and description file
|
||||
reactlog_version <- local({
|
||||
version <- NULL
|
||||
function() {
|
||||
if (!is.null(version)) return(version)
|
||||
reactlog_version <- function() {
|
||||
desc <- read.dcf(system.file("DESCRIPTION", package = "shiny", mustWork = TRUE))
|
||||
suggests <- desc[1,"Suggests"][[1]]
|
||||
suggests_pkgs <- strsplit(suggests, "\n")[[1]]
|
||||
|
||||
desc <- read.dcf(system_file("DESCRIPTION", package = "shiny"))
|
||||
suggests <- desc[1,"Suggests"][[1]]
|
||||
suggests_pkgs <- strsplit(suggests, "\n")[[1]]
|
||||
|
||||
reactlog_info <- suggests_pkgs[grepl("reactlog", suggests_pkgs)]
|
||||
if (length(reactlog_info) == 0) {
|
||||
stop("reactlog can not be found in shiny DESCRIPTION file")
|
||||
}
|
||||
|
||||
reactlog_info <- sub("^[^\\(]*\\(", "", reactlog_info)
|
||||
reactlog_info <- sub("\\)[^\\)]*$", "", reactlog_info)
|
||||
reactlog_info <- sub("^[>= ]*", "", reactlog_info)
|
||||
|
||||
version <<- package_version(reactlog_info)
|
||||
version
|
||||
reactlog_info <- suggests_pkgs[grepl("reactlog", suggests_pkgs)]
|
||||
if (length(reactlog_info) == 0) {
|
||||
stop("reactlog can not be found in shiny DESCRIPTION file")
|
||||
}
|
||||
})
|
||||
|
||||
reactlog_info <- sub("^[^\\(]*\\(", "", reactlog_info)
|
||||
reactlog_info <- sub("\\)[^\\)]*$", "", reactlog_info)
|
||||
reactlog_info <- sub("^[>= ]*", "", reactlog_info)
|
||||
|
||||
package_version(reactlog_info)
|
||||
}
|
||||
|
||||
|
||||
RLog <- R6Class(
|
||||
@@ -518,7 +512,7 @@ MessageLogger = R6Class(
|
||||
return(txt)
|
||||
},
|
||||
singleLine = function(txt) {
|
||||
gsub("([^\\])\\n", "\\1\\\\n", txt)
|
||||
gsub("[^\\]\\n", "\\\\n", txt)
|
||||
},
|
||||
valueStr = function(valueStr) {
|
||||
paste0(
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#' `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,7 +267,6 @@ 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)
|
||||
|
||||
@@ -393,7 +392,6 @@ 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:
|
||||
@@ -416,43 +414,11 @@ 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
|
||||
}
|
||||
|
||||
@@ -1,14 +1,19 @@
|
||||
startPNG <- function(filename, width, height, res, ...) {
|
||||
pngfun <- if ((getOption('shiny.useragg') %||% TRUE) && is_installed("ragg")) {
|
||||
ragg::agg_png
|
||||
# shiny.useragg is an experimental option that isn't officially supported or
|
||||
# documented. It's here in the off chance that someone really wants
|
||||
# to use ragg (say, instead of showtext, for custom font rendering).
|
||||
# In the next shiny release, this option will likely be superseded in
|
||||
# favor of a fully customizable graphics device option
|
||||
if ((getOption('shiny.useragg') %||% FALSE) && is_available("ragg")) {
|
||||
pngfun <- ragg::agg_png
|
||||
} else if (capabilities("aqua")) {
|
||||
# i.e., png(type = 'quartz')
|
||||
grDevices::png
|
||||
} else if ((getOption('shiny.usecairo') %||% TRUE) && is_installed("Cairo")) {
|
||||
Cairo::CairoPNG
|
||||
pngfun <- grDevices::png
|
||||
} else if ((getOption('shiny.usecairo') %||% TRUE) && is_available("Cairo")) {
|
||||
pngfun <- Cairo::CairoPNG
|
||||
} else {
|
||||
# i.e., png(type = 'cairo')
|
||||
grDevices::png
|
||||
pngfun <- grDevices::png
|
||||
}
|
||||
|
||||
args <- rlang::list2(filename=filename, width=width, height=height, res=res, ...)
|
||||
@@ -52,31 +57,33 @@ startPNG <- function(filename, width, height, res, ...) {
|
||||
grDevices::dev.cur()
|
||||
}
|
||||
|
||||
#' Capture a plot as a PNG file.
|
||||
#' Run a plotting function and save the output as a PNG
|
||||
#'
|
||||
#' The PNG graphics device used is determined in the following order:
|
||||
#' * If the ragg package is installed (and the `shiny.useragg` is not
|
||||
#' set to `FALSE`), then use [ragg::agg_png()].
|
||||
#' * If a quartz device is available (i.e., `capabilities("aqua")` is
|
||||
#' `TRUE`), then use `png(type = "quartz")`.
|
||||
#' * If the Cairo package is installed (and the `shiny.usecairo` option
|
||||
#' is not set to `FALSE`), then use [Cairo::CairoPNG()].
|
||||
#' * Otherwise, use [grDevices::png()]. In this case, Linux and Windows
|
||||
#' may not antialias some point shapes, resulting in poor quality output.
|
||||
#' This function returns the name of the PNG file that it generates. In
|
||||
#' essence, it calls `png()`, then `func()`, then `dev.off()`.
|
||||
#' So `func` must be a function that will generate a plot when used this
|
||||
#' way.
|
||||
#'
|
||||
#' For output, it will try to use the following devices, in this order:
|
||||
#' quartz (via [grDevices::png()]), then [Cairo::CairoPNG()],
|
||||
#' and finally [grDevices::png()]. This is in order of quality of
|
||||
#' output. Notably, plain `png` output on Linux and Windows may not
|
||||
#' antialias some point shapes, resulting in poor quality output.
|
||||
#'
|
||||
#' In some cases, `Cairo()` provides output that looks worse than
|
||||
#' `png()`. To disable Cairo output for an app, use
|
||||
#' `options(shiny.usecairo=FALSE)`.
|
||||
#'
|
||||
#' @param func A function that generates a plot.
|
||||
#' @param filename The name of the output file. Defaults to a temp file with
|
||||
#' extension `.png`.
|
||||
#' @param width Width in pixels.
|
||||
#' @param height Height in pixels.
|
||||
#' @param res Resolution in pixels per inch. This value is passed to the
|
||||
#' graphics device. Note that this affects the resolution of PNG rendering in
|
||||
#' @param res Resolution in pixels per inch. This value is passed to
|
||||
#' [grDevices::png()]. Note that this affects the resolution of PNG rendering in
|
||||
#' R; it won't change the actual ppi of the browser.
|
||||
#' @param ... Arguments to be passed through to the graphics device. These can
|
||||
#' be used to set the width, height, background color, etc.
|
||||
#'
|
||||
#' @return A path to the newly generated PNG file.
|
||||
#'
|
||||
#' @param ... Arguments to be passed through to [grDevices::png()].
|
||||
#' These can be used to set the width, height, background color, etc.
|
||||
#' @export
|
||||
plotPNG <- function(func, filename=tempfile(fileext='.png'),
|
||||
width=400, height=400, res=72, ...) {
|
||||
|
||||
@@ -138,8 +138,7 @@ datePickerDependency <- function(theme) {
|
||||
htmlDependency(
|
||||
name = "bootstrap-datepicker-js",
|
||||
version = version_bs_date_picker,
|
||||
src = "www/shared/datepicker",
|
||||
package = "shiny",
|
||||
src = c(href = "shared/datepicker"),
|
||||
script = if (getOption("shiny.minified", TRUE)) "js/bootstrap-datepicker.min.js"
|
||||
else "js/bootstrap-datepicker.js",
|
||||
# Need to enable noConflict mode. See #1346.
|
||||
@@ -158,19 +157,18 @@ datePickerCSS <- function(theme) {
|
||||
return(htmlDependency(
|
||||
name = "bootstrap-datepicker-css",
|
||||
version = version_bs_date_picker,
|
||||
src = "www/shared/datepicker",
|
||||
package = "shiny",
|
||||
src = c(href = "shared/datepicker"),
|
||||
stylesheet = "css/bootstrap-datepicker3.min.css"
|
||||
))
|
||||
}
|
||||
|
||||
scss_file <- system_file(package = "shiny", "www/shared/datepicker/scss/build3.scss")
|
||||
scss_file <- system.file(package = "shiny", "www/shared/datepicker/scss/build3.scss")
|
||||
|
||||
bslib::bs_dependency(
|
||||
input = sass::sass_file(scss_file),
|
||||
theme = theme,
|
||||
name = "bootstrap-datepicker",
|
||||
version = version_bs_date_picker,
|
||||
cache_key_extra = get_package_version("shiny")
|
||||
cache_key_extra = shinyPackageVersion()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -23,18 +23,7 @@
|
||||
#' @param buttonLabel The label used on the button. Can be text or an HTML tag
|
||||
#' object.
|
||||
#' @param placeholder The text to show before a file has been uploaded.
|
||||
#' @param capture What source to use for capturing image, audio or video data.
|
||||
#' This attribute facilitates user access to a device's media capture
|
||||
#' mechanism, such as a camera, or microphone, from within a file upload
|
||||
#' control.
|
||||
#'
|
||||
#' A value of `user` indicates that the user-facing camera and/or microphone
|
||||
#' should be used. A value of `environment` specifies that the outward-facing
|
||||
#' camera and/or microphone should be used.
|
||||
#'
|
||||
#' By default on most phones, this will accept still photos or video. For
|
||||
#' still photos only, also use `accept="image/*"`. For video only, use
|
||||
#' `accept="video/*"`.
|
||||
#' @examples
|
||||
#' ## Only run examples in interactive R sessions
|
||||
#' if (interactive()) {
|
||||
@@ -84,8 +73,7 @@
|
||||
#'
|
||||
#' @export
|
||||
fileInput <- function(inputId, label, multiple = FALSE, accept = NULL,
|
||||
width = NULL, buttonLabel = "Browse...", placeholder = "No file selected",
|
||||
capture = NULL) {
|
||||
width = NULL, buttonLabel = "Browse...", placeholder = "No file selected") {
|
||||
|
||||
restoredValue <- restoreInput(id = inputId, default = NULL)
|
||||
|
||||
@@ -113,9 +101,6 @@ fileInput <- function(inputId, label, multiple = FALSE, accept = NULL,
|
||||
if (length(accept) > 0)
|
||||
inputTag$attribs$accept <- paste(accept, collapse=',')
|
||||
|
||||
if (!is.null(capture)) {
|
||||
inputTag$attribs$capture <- capture
|
||||
}
|
||||
|
||||
div(class = "form-group shiny-input-container",
|
||||
style = css(width = validateCssUnit(width)),
|
||||
|
||||
@@ -213,7 +213,14 @@ selectizeIt <- function(inputId, select, options, nonempty = FALSE) {
|
||||
deps <- list(selectizeDependency())
|
||||
|
||||
if ('drag_drop' %in% options$plugins) {
|
||||
deps[[length(deps) + 1]] <- jqueryuiDependency()
|
||||
deps <- c(
|
||||
deps,
|
||||
list(htmlDependency(
|
||||
'jqueryui', '1.12.1',
|
||||
c(href = 'shared/jqueryui'),
|
||||
script = 'jquery-ui.min.js'
|
||||
))
|
||||
)
|
||||
}
|
||||
|
||||
# Insert script on same level as <select> tag
|
||||
@@ -240,7 +247,7 @@ selectizeDependencyFunc <- function(theme) {
|
||||
return(selectizeStaticDependency(version_selectize))
|
||||
}
|
||||
|
||||
selectizeDir <- system_file(package = "shiny", "www/shared/selectize/")
|
||||
selectizeDir <- system.file(package = "shiny", "www/shared/selectize/")
|
||||
bs_version <- bslib::theme_version(theme)
|
||||
stylesheet <- file.path(
|
||||
selectizeDir, "scss", paste0("selectize.bootstrap", bs_version, ".scss")
|
||||
@@ -260,17 +267,15 @@ selectizeDependencyFunc <- function(theme) {
|
||||
theme = theme,
|
||||
name = "selectize",
|
||||
version = version_selectize,
|
||||
cache_key_extra = get_package_version("shiny"),
|
||||
cache_key_extra = shinyPackageVersion(),
|
||||
.dep_args = list(script = script)
|
||||
)
|
||||
}
|
||||
|
||||
selectizeStaticDependency <- function(version) {
|
||||
htmlDependency(
|
||||
"selectize",
|
||||
version,
|
||||
src = "www/shared/selectize",
|
||||
package = "shiny",
|
||||
"selectize", version,
|
||||
src = c(href = "shared/selectize"),
|
||||
stylesheet = "css/selectize.bootstrap3.css",
|
||||
script = c(
|
||||
"js/selectize.min.js",
|
||||
|
||||
@@ -205,17 +205,13 @@ ionRangeSliderDependency <- function() {
|
||||
list(
|
||||
# ion.rangeSlider also needs normalize.css, which is already included in Bootstrap.
|
||||
htmlDependency(
|
||||
"ionrangeslider-javascript",
|
||||
version_ion_range_slider,
|
||||
src = "www/shared/ionrangeslider",
|
||||
package = "shiny",
|
||||
"ionrangeslider-javascript", version_ion_range_slider,
|
||||
src = c(href = "shared/ionrangeslider"),
|
||||
script = "js/ion.rangeSlider.min.js"
|
||||
),
|
||||
htmlDependency(
|
||||
"strftime",
|
||||
version_strftime,
|
||||
src = "www/shared/strftime",
|
||||
package = "shiny",
|
||||
"strftime", version_strftime,
|
||||
src = c(href = "shared/strftime"),
|
||||
script = "strftime-min.js"
|
||||
),
|
||||
bslib::bs_dependency_defer(ionRangeSliderDependencyCSS)
|
||||
@@ -227,23 +223,19 @@ ionRangeSliderDependencyCSS <- function(theme) {
|
||||
return(htmlDependency(
|
||||
"ionrangeslider-css",
|
||||
version_ion_range_slider,
|
||||
src = "www/shared/ionrangeslider",
|
||||
package = "shiny",
|
||||
src = c(href = "shared/ionrangeslider"),
|
||||
stylesheet = "css/ion.rangeSlider.css"
|
||||
))
|
||||
}
|
||||
|
||||
bslib::bs_dependency(
|
||||
input = list(
|
||||
list(accent = "$component-active-bg"),
|
||||
sass::sass_file(
|
||||
system_file(package = "shiny", "www/shared/ionrangeslider/scss/shiny.scss")
|
||||
)
|
||||
input = sass::sass_file(
|
||||
system.file(package = "shiny", "www/shared/ionrangeslider/scss/shiny.scss")
|
||||
),
|
||||
theme = theme,
|
||||
name = "ionRangeSlider",
|
||||
version = version_ion_range_slider,
|
||||
cache_key_extra = get_package_version("shiny")
|
||||
cache_key_extra = shinyPackageVersion()
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -41,7 +41,7 @@ normalizeChoicesArgs <- function(choices, choiceNames, choiceValues,
|
||||
if (length(choiceNames) != length(choiceValues)) {
|
||||
stop("`choiceNames` and `choiceValues` must have the same length.")
|
||||
}
|
||||
if (any_named(choiceNames) || any_named(choiceValues)) {
|
||||
if (anyNamed(choiceNames) || anyNamed(choiceValues)) {
|
||||
stop("`choiceNames` and `choiceValues` must not be named.")
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -113,7 +113,7 @@
|
||||
#' }
|
||||
#' @export
|
||||
insertTab <- function(inputId, tab, target = NULL,
|
||||
position = c("after", "before"), select = FALSE,
|
||||
position = c("before", "after"), 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::nav_prepend(inputId, tab, menu_title = menuName, select = select, session = session)
|
||||
bslib::tab_prepend(inputId, tab, menu_title = menuName, select = select, session = session)
|
||||
}
|
||||
|
||||
#' @rdname insertTab
|
||||
#' @export
|
||||
appendTab <- function(inputId, tab, select = FALSE, menuName = NULL,
|
||||
session = getDefaultReactiveDomain()) {
|
||||
bslib::nav_append(inputId, tab, menu_title = menuName, select = select, session = session)
|
||||
bslib::tab_append(inputId, tab, menu_title = menuName, select = select, session = session)
|
||||
}
|
||||
|
||||
#' @rdname insertTab
|
||||
|
||||
13
R/jqueryui.R
13
R/jqueryui.R
@@ -79,8 +79,8 @@ absolutePanel <- function(...,
|
||||
if (isTRUE(draggable)) {
|
||||
divTag <- tagAppendAttributes(divTag, class='draggable')
|
||||
return(tagList(
|
||||
singleton(tags$head(tags$script(src='shared/jqueryui/jquery-ui.min.js'))),
|
||||
divTag,
|
||||
jqueryuiDependency(),
|
||||
tags$script('$(".draggable").draggable();')
|
||||
))
|
||||
} else {
|
||||
@@ -99,14 +99,3 @@ fixedPanel <- function(...,
|
||||
width=width, height=height, draggable=draggable, cursor=match.arg(cursor),
|
||||
fixed=TRUE)
|
||||
}
|
||||
|
||||
|
||||
jqueryuiDependency <- function() {
|
||||
htmlDependency(
|
||||
'jqueryui',
|
||||
'1.12.1',
|
||||
src = 'www/shared/jqueryui',
|
||||
package = 'shiny',
|
||||
script = 'jquery-ui.min.js'
|
||||
)
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
#' themselves in knitr/rmarkdown documents.
|
||||
#'
|
||||
#' @name knitr_methods
|
||||
#' @keywords internal
|
||||
#' @param x Object to knit_print
|
||||
#' @param ... Additional knit_print arguments
|
||||
NULL
|
||||
@@ -63,7 +62,7 @@ knit_print.shiny.appobj <- function(x, ...) {
|
||||
#' @param inline Whether the object is printed inline.
|
||||
knit_print.shiny.render.function <- function(x, ..., inline = FALSE) {
|
||||
x <- htmltools::as.tags(x, inline = inline)
|
||||
output <- knitr::knit_print(tagList(x), ..., inline = inline)
|
||||
output <- knitr::knit_print(tagList(x))
|
||||
attr(output, "knit_cacheable") <- FALSE
|
||||
attr(output, "knit_meta") <- append(attr(output, "knit_meta"),
|
||||
shiny_rmd_warning())
|
||||
@@ -77,5 +76,5 @@ knit_print.reactive <- function(x, ..., inline = FALSE) {
|
||||
renderFunc <- if (inline) renderText else renderPrint
|
||||
knitr::knit_print(renderFunc({
|
||||
x()
|
||||
}), ..., inline = inline)
|
||||
}), inline = inline)
|
||||
}
|
||||
|
||||
1
R/map.R
1
R/map.R
@@ -1,3 +1,4 @@
|
||||
#' @importFrom fastmap fastmap
|
||||
Map <- R6Class(
|
||||
'Map',
|
||||
portable = FALSE,
|
||||
|
||||
@@ -348,7 +348,7 @@ HandlerManager <- R6Class("HandlerManager",
|
||||
httpResponse(status = 500L,
|
||||
content_type = "text/html; charset=UTF-8",
|
||||
content = as.character(htmltools::htmlTemplate(
|
||||
system_file("template", "error.html", package = "shiny"),
|
||||
system.file("template", "error.html", package = "shiny"),
|
||||
message = conditionMessage(err)
|
||||
))
|
||||
)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# Promise helpers taken from:
|
||||
# https://github.com/rstudio/promises/blob/main/tests/testthat/common.R
|
||||
# https://github.com/rstudio/promises/blob/master/tests/testthat/common.R
|
||||
# Block until all pending later tasks have executed
|
||||
wait_for_it <- function() {
|
||||
while (!later::loop_empty()) {
|
||||
|
||||
@@ -178,15 +178,14 @@ modalDialog <- function(..., title = NULL, footer = modalButton("Dismiss"),
|
||||
if (!is.null(footer)) div(class = "modal-footer", footer)
|
||||
)
|
||||
),
|
||||
# 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\\./)) {
|
||||
tags$script(
|
||||
"if (window.bootstrap) {
|
||||
var modal = new bootstrap.Modal(document.getElementById('shiny-modal'));
|
||||
modal.show();
|
||||
} else {
|
||||
$('#shiny-modal').modal().focus();
|
||||
}"
|
||||
))
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
182
R/reactives.R
182
R/reactives.R
@@ -875,7 +875,8 @@ Observable <- R6Class(
|
||||
invisible(.value)
|
||||
},
|
||||
format = function() {
|
||||
simpleExprToFunction(fn_body(.origFunc), "reactive")
|
||||
label <- sprintf('reactive(%s)', paste(deparse(body(.origFunc)), collapse='\n'))
|
||||
strsplit(label, "\n")[[1]]
|
||||
},
|
||||
.updateValue = function() {
|
||||
ctx <- Context$new(.domain, .label, type = 'observable',
|
||||
@@ -944,15 +945,14 @@ Observable <- R6Class(
|
||||
#' See the [Shiny tutorial](https://shiny.rstudio.com/tutorial/) for
|
||||
#' more information about reactive expressions.
|
||||
#'
|
||||
#' @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 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 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,56 +961,46 @@ Observable <- R6Class(
|
||||
#' @return a function, wrapped in a S3 class "reactive"
|
||||
#'
|
||||
#' @examples
|
||||
#' library(rlang)
|
||||
#' values <- reactiveValues(A=1)
|
||||
#'
|
||||
#' reactiveB <- reactive({
|
||||
#' values$A + 1
|
||||
#' })
|
||||
#' # View the values from the R console with isolate()
|
||||
#' isolate(reactiveB())
|
||||
#' # 2
|
||||
#'
|
||||
#' # Can use quoted expressions
|
||||
#' reactiveC <- reactive(quote({ values$A + 2 }), quoted = TRUE)
|
||||
#'
|
||||
#' # 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.
|
||||
#' expr_q <- quote({ values$A + 3 })
|
||||
#' reactiveD <- reactive(expr_q, quoted = TRUE)
|
||||
#'
|
||||
#' # View the values from the R console with isolate()
|
||||
#' isolate(reactiveB())
|
||||
#' 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()
|
||||
|
||||
func <- installExprFunction(x, "func", env, quoted, wrappedWithLabel = FALSE)
|
||||
# Attach a label and a reference to the original user source for debugging
|
||||
userExpr <- fn_body(func)
|
||||
label <- exprToLabel(userExpr, "reactive", label)
|
||||
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()
|
||||
|
||||
o <- Observable$new(func, label, domain, ..stacktraceon = ..stacktraceon)
|
||||
# Attach a label and a reference to the original user source for debugging
|
||||
label <- exprToLabel(get_expr(x), "reactive", label)
|
||||
|
||||
o <- Observable$new(fun, label, domain, ..stacktraceon = ..stacktraceon)
|
||||
structure(
|
||||
o$getValue,
|
||||
observable = o,
|
||||
cacheHint = list(userExpr = zap_srcref(userExpr)),
|
||||
cacheHint = list(userExpr = zap_srcref(get_expr(x))),
|
||||
class = c("reactiveExpr", "reactive", "function")
|
||||
)
|
||||
}
|
||||
@@ -1203,7 +1193,7 @@ Observer <- R6Class(
|
||||
# validation = function(e) NULL,
|
||||
# shiny.output.cancel = function(e) NULL
|
||||
|
||||
if (cnd_inherits(e, "shiny.silent.error")) {
|
||||
if (inherits(e, "shiny.silent.error")) {
|
||||
return()
|
||||
}
|
||||
|
||||
@@ -1335,7 +1325,12 @@ Observer <- R6Class(
|
||||
#'
|
||||
#' @param x An expression (quoted or unquoted). Any return value will be
|
||||
#' ignored.
|
||||
#' @inheritParams reactive
|
||||
#' @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 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.
|
||||
@@ -1394,21 +1389,18 @@ Observer <- R6Class(
|
||||
#' print(values$A + 1)
|
||||
#' })
|
||||
#'
|
||||
#' # To store expressions for later conversion to observe, use rlang::quo()
|
||||
#' myquo <- rlang::quo({ print(values$A + 3) })
|
||||
#' obsC <- rlang::inject(observe(!!myquo))
|
||||
#' # Can use quoted expressions
|
||||
#' obsC <- observe(quote({ print(values$A + 2) }), quoted = TRUE)
|
||||
#'
|
||||
#' # (Legacy) Can use quoted expressions
|
||||
#' obsD <- observe(quote({ print(values$A + 2) }), quoted = TRUE)
|
||||
#' # To store expressions for later conversion to observe, use quote()
|
||||
#' expr_q <- quote({ print(values$A + 3) })
|
||||
#' obsD <- observe(expr_q, 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,
|
||||
@@ -1419,11 +1411,18 @@ observe <- function(
|
||||
{
|
||||
check_dots_empty()
|
||||
|
||||
func <- installExprFunction(x, "func", env, quoted)
|
||||
label <- funcToLabel(func, "observe", label)
|
||||
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'))
|
||||
}
|
||||
|
||||
o <- Observer$new(
|
||||
func,
|
||||
fun,
|
||||
label = label,
|
||||
suspended = suspended,
|
||||
priority = priority,
|
||||
@@ -2145,30 +2144,23 @@ 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 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 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 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.
|
||||
@@ -2282,13 +2274,15 @@ observeEvent <- function(eventExpr, handlerExpr,
|
||||
{
|
||||
check_dots_empty()
|
||||
|
||||
eventQ <- exprToQuo(eventExpr, event.env, event.quoted)
|
||||
handlerQ <- exprToQuo(handlerExpr, handler.env, handler.quoted)
|
||||
eventExpr <- get_quosure(eventExpr, event.env, event.quoted)
|
||||
handlerExpr <- get_quosure(handlerExpr, handler.env, handler.quoted)
|
||||
|
||||
label <- quoToLabel(eventQ, "observeEvent", label)
|
||||
if (is.null(label)) {
|
||||
label <- sprintf('observeEvent(%s)', paste(deparse(get_expr(eventExpr)), collapse='\n'))
|
||||
}
|
||||
|
||||
handler <- inject(observe(
|
||||
!!handlerQ,
|
||||
!!handlerExpr,
|
||||
label = label,
|
||||
suspended = suspended,
|
||||
priority = priority,
|
||||
@@ -2302,7 +2296,7 @@ observeEvent <- function(eventExpr, handlerExpr,
|
||||
ignoreInit = ignoreInit,
|
||||
once = once,
|
||||
label = label,
|
||||
!!eventQ,
|
||||
!!eventExpr,
|
||||
x = handler
|
||||
))
|
||||
|
||||
@@ -2320,17 +2314,19 @@ eventReactive <- function(eventExpr, valueExpr,
|
||||
{
|
||||
check_dots_empty()
|
||||
|
||||
eventQ <- exprToQuo(eventExpr, event.env, event.quoted)
|
||||
valueQ <- exprToQuo(valueExpr, value.env, value.quoted)
|
||||
eventExpr <- get_quosure(eventExpr, event.env, event.quoted)
|
||||
valueExpr <- get_quosure(valueExpr, value.env, value.quoted)
|
||||
|
||||
label <- quoToLabel(eventQ, "eventReactive", label)
|
||||
if (is.null(label)) {
|
||||
label <- sprintf('eventReactive(%s)', paste(deparse(get_expr(eventExpr)), collapse='\n'))
|
||||
}
|
||||
|
||||
invisible(inject(bindEvent(
|
||||
ignoreNULL = ignoreNULL,
|
||||
ignoreInit = ignoreInit,
|
||||
label = label,
|
||||
!!eventQ,
|
||||
x = reactive(!!valueQ, domain = domain, label = label)
|
||||
!!eventExpr,
|
||||
x = reactive(!!valueExpr, domain = domain, label = label)
|
||||
)))
|
||||
}
|
||||
|
||||
@@ -2472,11 +2468,11 @@ debounce <- function(r, millis, priority = 100, domain = getDefaultReactiveDomai
|
||||
|
||||
# Ensure r() is called only after setting firstRun to FALSE since r()
|
||||
# may throw an error
|
||||
try(r(), silent = TRUE)
|
||||
r()
|
||||
return()
|
||||
}
|
||||
# This ensures r() is still tracked after firstRun
|
||||
try(r(), silent = TRUE)
|
||||
r()
|
||||
|
||||
# The value (or possibly millis) changed. Start or reset the timer.
|
||||
v$when <- getDomainTimeMs(domain) + millis()
|
||||
@@ -2509,7 +2505,7 @@ debounce <- function(r, millis, priority = 100, domain = getDefaultReactiveDomai
|
||||
# commenting it out and studying the unit test failure that results.
|
||||
primer <- observe({
|
||||
primer$destroy()
|
||||
try(er(), silent = TRUE)
|
||||
er()
|
||||
}, label = "debounce primer", domain = domain, priority = priority)
|
||||
|
||||
er
|
||||
@@ -2551,7 +2547,7 @@ throttle <- function(r, millis, priority = 100, domain = getDefaultReactiveDomai
|
||||
}
|
||||
|
||||
# Responsible for tracking when f() changes.
|
||||
observeEvent(try(r(), silent = TRUE), {
|
||||
observeEvent(r(), {
|
||||
if (v$pending) {
|
||||
# In a blackout period and someone already scheduled; do nothing
|
||||
} else if (blackoutMillisLeft() > 0) {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
####
|
||||
# Generated by `./tools/documentation/updateReexports.R`: do not edit by hand
|
||||
# Please call `source('tools/documentation/updateReexports.R')` from the root folder to update`
|
||||
# Generated by `./tools/updateReexports.R`: do not edit by hand
|
||||
# Please call `source('tools/updateReexports.R') from the root folder to update`
|
||||
####
|
||||
|
||||
|
||||
@@ -90,20 +90,17 @@ 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
|
||||
@@ -116,9 +113,6 @@ 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
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
#' When rendering an inline plot, you must provide numeric values (in pixels)
|
||||
#' to both \code{width} and \code{height}.
|
||||
#' @param res Resolution of resulting plot, in pixels per inch. This value is
|
||||
#' passed to [plotPNG()]. Note that this affects the resolution of PNG
|
||||
#' passed to [grDevices::png()]. Note that this affects the resolution of PNG
|
||||
#' rendering in R; it won't change the actual ppi of the browser.
|
||||
#' @param alt Alternate text for the HTML `<img>` tag if it cannot be displayed
|
||||
#' or viewed (i.e., the user uses a screen reader). In addition to a character
|
||||
@@ -44,9 +44,11 @@
|
||||
#' ggplot objects; for other plots, `NA` results in alt text of "Plot object".
|
||||
#' `NULL` or `""` is not recommended because those should be limited to
|
||||
#' decorative images.
|
||||
#' @param ... Arguments to be passed through to [plotPNG()].
|
||||
#' @param ... Arguments to be passed through to [grDevices::png()].
|
||||
#' These can be used to set the width, height, background color, etc.
|
||||
#' @inheritParams renderUI
|
||||
#' @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.
|
||||
#' @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`.
|
||||
@@ -63,13 +65,10 @@ renderPlot <- function(expr, width = 'auto', height = 'auto', res = 72, ...,
|
||||
execOnResize = FALSE, outputArgs = list()
|
||||
) {
|
||||
|
||||
func <- installExprFunction(
|
||||
expr, "func", env, quoted,
|
||||
label = "renderPlot",
|
||||
# This ..stacktraceon is matched by a ..stacktraceoff.. when plotFunc
|
||||
# is called
|
||||
..stacktraceon = TRUE
|
||||
)
|
||||
expr <- get_quosure(expr, env, quoted)
|
||||
# This ..stacktraceon is matched by a ..stacktraceoff.. when plotFunc
|
||||
# is called
|
||||
func <- quoToFunction(expr, "renderPlot", ..stacktraceon = TRUE)
|
||||
|
||||
args <- list(...)
|
||||
|
||||
@@ -187,7 +186,7 @@ renderPlot <- function(expr, width = 'auto', height = 'auto', res = 72, ...,
|
||||
outputFunc,
|
||||
renderFunc,
|
||||
outputArgs,
|
||||
cacheHint = list(userExpr = installedFuncExpr(func), res = res)
|
||||
cacheHint = list(userExpr = get_expr(expr), res = res)
|
||||
)
|
||||
class(markedFunc) <- c("shiny.renderPlot", class(markedFunc))
|
||||
markedFunc
|
||||
@@ -345,7 +344,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")) {
|
||||
@@ -612,7 +611,7 @@ getGgplotCoordmap <- function(p, width, height, res) {
|
||||
find_panel_info <- function(b) {
|
||||
# Structure of ggplot objects changed after 2.1.0. After 2.2.1, there was a
|
||||
# an API for extracting the necessary information.
|
||||
ggplot_ver <- get_package_version("ggplot2")
|
||||
ggplot_ver <- utils::packageVersion("ggplot2")
|
||||
|
||||
if (ggplot_ver > "2.2.1") {
|
||||
find_panel_info_api(b)
|
||||
|
||||
@@ -42,7 +42,9 @@
|
||||
#' (i.e. they either evaluate to `NA` or `NaN`).
|
||||
#' @param ... Arguments to be passed through to [xtable::xtable()]
|
||||
#' and [xtable::print.xtable()].
|
||||
#' @inheritParams renderUI
|
||||
#' @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.
|
||||
#' @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.
|
||||
@@ -72,7 +74,8 @@ renderTable <- function(expr, striped = FALSE, hover = FALSE,
|
||||
env = parent.frame(), quoted = FALSE,
|
||||
outputArgs=list())
|
||||
{
|
||||
func <- installExprFunction(expr, "func", env, quoted, label = "renderTable")
|
||||
expr <- get_quosure(expr, env, quoted)
|
||||
func <- quoToFunction(expr, "renderTable")
|
||||
|
||||
if (!is.function(spacing)) spacing <- match.arg(spacing)
|
||||
|
||||
|
||||
@@ -23,10 +23,10 @@
|
||||
#' @examples
|
||||
#' ## Only run this example in interactive R sessions
|
||||
#' if (interactive()) {
|
||||
#' runUrl('https://github.com/rstudio/shiny_example/archive/main.tar.gz')
|
||||
#' runUrl('https://github.com/rstudio/shiny_example/archive/master.tar.gz')
|
||||
#'
|
||||
#' # Can run an app from a subdirectory in the archive
|
||||
#' runUrl("https://github.com/rstudio/shiny_example/archive/main.zip",
|
||||
#' runUrl("https://github.com/rstudio/shiny_example/archive/master.zip",
|
||||
#' subdir = "inst/shinyapp/")
|
||||
#' }
|
||||
runUrl <- function(url, filetype = NULL, subdir = NULL, destdir = NULL, ...) {
|
||||
@@ -121,8 +121,7 @@ runGist <- function(gist, destdir = NULL, ...) {
|
||||
#' @param username GitHub username. If `repo` is of the form
|
||||
#' `"username/repo"`, `username` will be taken from `repo`.
|
||||
#' @param ref Desired git reference. Could be a commit, tag, or branch name.
|
||||
#' Defaults to `"HEAD"`, which means the default branch on GitHub, typically
|
||||
#' `"main"` or `"master"`.
|
||||
#' Defaults to `"master"`.
|
||||
#' @export
|
||||
#' @examples
|
||||
#' ## Only run this example in interactive R sessions
|
||||
@@ -134,7 +133,7 @@ runGist <- function(gist, destdir = NULL, ...) {
|
||||
#' runGitHub("shiny_example", "rstudio", subdir = "inst/shinyapp/")
|
||||
#' }
|
||||
runGitHub <- function(repo, username = getOption("github.user"),
|
||||
ref = "HEAD", subdir = NULL, destdir = NULL, ...) {
|
||||
ref = "master", subdir = NULL, destdir = NULL, ...) {
|
||||
|
||||
if (grepl('/', repo)) {
|
||||
res <- strsplit(repo, '/')[[1]]
|
||||
|
||||
12
R/runapp.R
12
R/runapp.R
@@ -22,13 +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 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.
|
||||
#' use a random port.
|
||||
#' @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. The value of this parameter can also be a
|
||||
#' interactive sessions only. This value of this parameter can also be a
|
||||
#' function to call with the application's URL.
|
||||
#' @param host The IPv4 address that the application should listen on. Defaults
|
||||
#' to the `shiny.host` option, if set, or `"127.0.0.1"` if not. See
|
||||
@@ -304,8 +301,7 @@ 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
|
||||
# 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)) {
|
||||
if (!port %in% c(3659, 4045, 6000, 6665:6669, 6697)) {
|
||||
break
|
||||
}
|
||||
}
|
||||
@@ -467,7 +463,7 @@ runExample <- function(example=NA,
|
||||
launch.browser = getOption('shiny.launch.browser', interactive()),
|
||||
host=getOption('shiny.host', '127.0.0.1'),
|
||||
display.mode=c("auto", "normal", "showcase")) {
|
||||
examplesDir <- system_file('examples', package='shiny')
|
||||
examplesDir <- system.file('examples', package='shiny')
|
||||
dir <- resolve(examplesDir, example)
|
||||
if (is.null(dir)) {
|
||||
if (is.na(example)) {
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#' 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()] [applyInputHandlers()]
|
||||
#' @seealso [removeInputHandler()]
|
||||
#' @export
|
||||
registerInputHandler <- function(type, fun, force=FALSE){
|
||||
if (inputHandlers$containsKey(type) && !force){
|
||||
@@ -181,7 +181,7 @@ registerInputHandler("shiny.datetime", function(val, ...){
|
||||
|
||||
registerInputHandler("shiny.action", function(val, shinysession, name) {
|
||||
# mark up the action button value with a special class so we can recognize it later
|
||||
class(val) <- c("shinyActionButtonValue", class(val))
|
||||
class(val) <- c(class(val), "shinyActionButtonValue")
|
||||
val
|
||||
})
|
||||
|
||||
|
||||
10
R/server.R
10
R/server.R
@@ -29,7 +29,7 @@ registerClient <- function(client) {
|
||||
|
||||
#' Define Server Functionality
|
||||
#'
|
||||
#' @description `r lifecycle::badge("superseded")`
|
||||
#' @description \lifecycle{superseded}
|
||||
#'
|
||||
#' @description Defines the server-side logic of the Shiny application. This generally
|
||||
#' involves creating functions that map user inputs to various kinds of output.
|
||||
@@ -49,7 +49,7 @@ registerClient <- function(client) {
|
||||
#' optional `session` parameter, which is used when greater control is
|
||||
#' needed.
|
||||
#'
|
||||
#' See the [tutorial](https://shiny.rstudio.com/tutorial/) for more
|
||||
#' See the [tutorial](https://rstudio.github.io/shiny/tutorial/) for more
|
||||
#' on how to write a server function.
|
||||
#'
|
||||
#' @param func The server function for this application. See the details section
|
||||
@@ -128,7 +128,7 @@ createAppHandlers <- function(httpHandlers, serverFuncSource) {
|
||||
appvars <- new.env()
|
||||
appvars$server <- NULL
|
||||
|
||||
sys.www.root <- system_file('www', package='shiny')
|
||||
sys.www.root <- system.file('www', package='shiny')
|
||||
|
||||
# This value, if non-NULL, must be present on all HTTP and WebSocket
|
||||
# requests as the Shiny-Shared-Secret header or else access will be
|
||||
@@ -331,7 +331,7 @@ argsForServerFunc <- function(serverFunc, session) {
|
||||
getEffectiveBody <- function(func) {
|
||||
if (is.null(func))
|
||||
NULL
|
||||
else if (isS4(func) && inherits(func, "functionWithTrace"))
|
||||
else if (isS4(func) && class(func) == "functionWithTrace")
|
||||
body(func@original)
|
||||
else
|
||||
body(func)
|
||||
@@ -385,7 +385,7 @@ startApp <- function(appObj, port, host, quiet) {
|
||||
list(
|
||||
# Always handle /session URLs dynamically, even if / is a static path.
|
||||
"session" = excludeStaticPath(),
|
||||
"shared" = system_file(package = "shiny", "www", "shared")
|
||||
"shared" = system.file(package = "shiny", "www", "shared")
|
||||
),
|
||||
.globals$resourcePaths
|
||||
)
|
||||
|
||||
@@ -94,10 +94,6 @@ getShinyOption <- function(name, default = NULL) {
|
||||
#' numbers to JSON format to send to the client web browser.}
|
||||
#' \item{shiny.launch.browser (defaults to `interactive()`)}{A boolean which controls the default behavior
|
||||
#' when an app is run. See [runApp()] for more information.}
|
||||
#' \item{shiny.mathjax.url (defaults to `"https://mathjax.rstudio.com/latest/MathJax.js"`)}{
|
||||
#' The URL that should be used to load MathJax, via [withMathJax()].}
|
||||
#' \item{shiny.mathjax.config (defaults to `"config=TeX-AMS-MML_HTMLorMML"`)}{The querystring
|
||||
#' used to load MathJax, via [withMathJax()].}
|
||||
#' \item{shiny.maxRequestSize (defaults to 5MB)}{This is a number which specifies the maximum
|
||||
#' web request size, which serves as a size limit for file uploads.}
|
||||
#' \item{shiny.minified (defaults to `TRUE`)}{By default
|
||||
@@ -129,9 +125,6 @@ getShinyOption <- function(name, default = NULL) {
|
||||
#' console.}
|
||||
#' \item{shiny.testmode (defaults to `FALSE`)}{If `TRUE`, then various features for testing Shiny
|
||||
#' applications are enabled.}
|
||||
#' \item{shiny.snapshotsortc (defaults to `FALSE`)}{If `TRUE`, test snapshot keys
|
||||
#' for \pkg{shinytest} will be sorted consistently using the C locale. Snapshots
|
||||
#' retrieved by \pkg{shinytest2} will always sort using the C locale.}
|
||||
#' \item{shiny.trace (defaults to `FALSE`)}{Print messages sent between the R server and the web
|
||||
#' browser client to the R console. This is useful for debugging. Possible
|
||||
#' values are `"send"` (only print messages sent to the client),
|
||||
@@ -140,10 +133,9 @@ getShinyOption <- function(name, default = NULL) {
|
||||
#' messages).}
|
||||
#' \item{shiny.autoload.r (defaults to `TRUE`)}{If `TRUE`, then the R/
|
||||
#' of a shiny app will automatically be sourced.}
|
||||
#' \item{shiny.useragg (defaults to `TRUE`)}{Set to `FALSE` to prevent PNG rendering via the
|
||||
#' ragg package. See [plotPNG()] for more information.}
|
||||
#' \item{shiny.usecairo (defaults to `TRUE`)}{Set to `FALSE` to prevent PNG rendering via the
|
||||
#' Cairo package. See [plotPNG()] for more information.}
|
||||
#' \item{shiny.usecairo (defaults to `TRUE`)}{This is used to disable graphical rendering by the
|
||||
#' Cairo package, if it is installed. See [plotPNG()] for more
|
||||
#' information.}
|
||||
#' \item{shiny.devmode (defaults to `NULL`)}{Option to enable Shiny Developer Mode. When set,
|
||||
#' different default `getOption(key)` values will be returned. See [devmode()] for more details.}
|
||||
### Not documenting as 'shiny.devmode.verbose' is for niche use only
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
## usethis namespace: start
|
||||
## usethis namespace: end
|
||||
#' @importFrom lifecycle deprecated is_present
|
||||
#' @importFrom lifecycle deprecated
|
||||
#' @importFrom grDevices dev.set dev.cur
|
||||
#' @importFrom fastmap fastmap
|
||||
#' @importFrom promises %...!%
|
||||
@@ -11,13 +11,11 @@
|
||||
#' promise promise_resolve promise_reject is.promising
|
||||
#' as.promise
|
||||
#' @importFrom rlang
|
||||
#' quo enquo enquo0 as_function get_expr get_env new_function enquos
|
||||
#' quo enquo 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
|
||||
|
||||
53
R/shiny.R
53
R/shiny.R
@@ -403,7 +403,7 @@ ShinySession <- R6Class(
|
||||
sendMessage = function(...) {
|
||||
# This function is a wrapper for $write
|
||||
msg <- list(...)
|
||||
if (any_unnamed(msg)) {
|
||||
if (anyUnnamed(msg)) {
|
||||
stop("All arguments to sendMessage must be named.")
|
||||
}
|
||||
private$write(toJSON(msg))
|
||||
@@ -478,35 +478,6 @@ ShinySession <- R6Class(
|
||||
# "json" unless requested otherwise. The only other valid value is
|
||||
# "rds".
|
||||
format <- params$format %||% "json"
|
||||
# Machines can test their snapshot under different locales.
|
||||
# R CMD check runs under the `C` locale.
|
||||
# However, before this parameter, existing snapshots were most likely not
|
||||
# under the `C` locale is would cause failures. This parameter allows
|
||||
# users to opt-in to the `C` locale.
|
||||
# From ?sort:
|
||||
# However, there are some caveats with the radix sort:
|
||||
# If ‘x’ is a ‘character’ vector, all elements must share the
|
||||
# same encoding. Only UTF-8 (including ASCII) and Latin-1
|
||||
# encodings are supported. Collation always follows the "C"
|
||||
# locale.
|
||||
# {shinytest2} will always set `sortC=1`
|
||||
# {shinytest} does not have `sortC` functionality.
|
||||
# Users should set `options(shiny.snapshotsortc = TRUE)` within their app.
|
||||
# The sortingMethod should always be `radix` going forward.
|
||||
sortMethod <-
|
||||
if (!is.null(params$sortC)) {
|
||||
if (params$sortC != "1") {
|
||||
stop("The `sortC` parameter can only be `1` or not supplied")
|
||||
}
|
||||
"radix"
|
||||
} else {
|
||||
# Allow users to set an option for {shinytest2}.
|
||||
if (isTRUE(getShinyOption("snapshotsortc", default = FALSE))) {
|
||||
"radix"
|
||||
} else {
|
||||
"auto"
|
||||
}
|
||||
}
|
||||
|
||||
values <- list()
|
||||
|
||||
@@ -549,7 +520,7 @@ ShinySession <- R6Class(
|
||||
}
|
||||
)
|
||||
|
||||
values$input <- sortByName(values$input, method = sortMethod)
|
||||
values$input <- sortByName(values$input)
|
||||
}
|
||||
|
||||
if (!is.null(params$output)) {
|
||||
@@ -577,7 +548,7 @@ ShinySession <- R6Class(
|
||||
}
|
||||
)
|
||||
|
||||
values$output <- sortByName(values$output, method = sortMethod)
|
||||
values$output <- sortByName(values$output)
|
||||
}
|
||||
|
||||
if (!is.null(params$export)) {
|
||||
@@ -598,7 +569,7 @@ ShinySession <- R6Class(
|
||||
)
|
||||
}
|
||||
|
||||
values$export <- sortByName(values$export, method = sortMethod)
|
||||
values$export <- sortByName(values$export)
|
||||
}
|
||||
|
||||
# Make sure input, output, and export are all named lists (at this
|
||||
@@ -854,7 +825,7 @@ ShinySession <- R6Class(
|
||||
dots <- eval(substitute(alist(...)))
|
||||
}
|
||||
|
||||
if (any_unnamed(dots))
|
||||
if (anyUnnamed(dots))
|
||||
stop("exportTestValues: all arguments must be named.")
|
||||
|
||||
names(dots) <- ns(names(dots))
|
||||
@@ -942,7 +913,7 @@ ShinySession <- R6Class(
|
||||
|
||||
# Copy `values` from scopeState to state, adding namespace
|
||||
if (length(scopeState$values) != 0) {
|
||||
if (any_unnamed(scopeState$values)) {
|
||||
if (anyUnnamed(scopeState$values)) {
|
||||
stop("All scope values in must be named.")
|
||||
}
|
||||
|
||||
@@ -1143,12 +1114,7 @@ ShinySession <- R6Class(
|
||||
structure(list(), class = "try-error", condition = cond)
|
||||
} else if (inherits(cond, "shiny.output.cancel")) {
|
||||
structure(list(), class = "cancel-output")
|
||||
} else if (cnd_inherits(cond, "shiny.silent.error")) {
|
||||
# The error condition might have been chained by
|
||||
# foreign code, e.g. dplyr. Find the original error.
|
||||
while (!inherits(cond, "shiny.silent.error")) {
|
||||
cond <- cond$parent
|
||||
}
|
||||
} else if (inherits(cond, "shiny.silent.error")) {
|
||||
# Don't let shiny.silent.error go through the normal stop
|
||||
# path of try, because we don't want it to print. But we
|
||||
# do want to try to return the same looking result so that
|
||||
@@ -1735,7 +1701,7 @@ ShinySession <- R6Class(
|
||||
dots <- eval(substitute(alist(...)))
|
||||
}
|
||||
|
||||
if (any_unnamed(dots))
|
||||
if (anyUnnamed(dots))
|
||||
stop("exportTestValues: all arguments must be named.")
|
||||
|
||||
# Create a named list where each item is a list with an expression and
|
||||
@@ -1748,7 +1714,7 @@ ShinySession <- R6Class(
|
||||
},
|
||||
|
||||
getTestSnapshotUrl = function(input = TRUE, output = TRUE, export = TRUE,
|
||||
format = "json", sortC = FALSE) {
|
||||
format = "json") {
|
||||
reqString <- function(group, value) {
|
||||
if (isTRUE(value))
|
||||
paste0(group, "=1")
|
||||
@@ -1762,7 +1728,6 @@ ShinySession <- R6Class(
|
||||
reqString("input", input),
|
||||
reqString("output", output),
|
||||
reqString("export", export),
|
||||
reqString("sortC", sortC),
|
||||
paste0("format=", format),
|
||||
sep = "&"
|
||||
)
|
||||
|
||||
@@ -193,7 +193,7 @@ shinyAppDir_serverR <- function(appDir, options=list()) {
|
||||
staticPaths <- list()
|
||||
}
|
||||
|
||||
fallbackWWWDir <- system_file("www-dir", package = "shiny")
|
||||
fallbackWWWDir <- system.file("www-dir", package = "shiny")
|
||||
|
||||
serverSource <- cachedFuncWithFile(appDir, "server.R", case.sensitive = FALSE,
|
||||
function(serverR) {
|
||||
@@ -286,7 +286,7 @@ shinyAppDir_serverR <- function(appDir, options=list()) {
|
||||
#
|
||||
# The return value is a function that halts monitoring when called.
|
||||
initAutoReloadMonitor <- function(dir) {
|
||||
if (!get_devmode_option("shiny.autoreload", FALSE)) {
|
||||
if (!getOption("shiny.autoreload", FALSE)) {
|
||||
return(function(){})
|
||||
}
|
||||
|
||||
@@ -339,7 +339,7 @@ initAutoReloadMonitor <- function(dir) {
|
||||
#' @param appDir The application directory. If `appDir` is `NULL` or
|
||||
#' not supplied, the nearest enclosing directory that is a Shiny app, starting
|
||||
#' with the current directory, is used.
|
||||
#' @param renv The environment in which the files in the `R/` directory should
|
||||
#' @param renv The environmeny in which the files in the `R/` directory should
|
||||
#' be evaluated.
|
||||
#' @param globalrenv The environment in which `global.R` should be evaluated. If
|
||||
#' `NULL`, `global.R` will not be evaluated at all.
|
||||
@@ -455,7 +455,7 @@ shinyAppDir_appR <- function(fileName, appDir, options=list())
|
||||
staticPaths <- list()
|
||||
}
|
||||
|
||||
fallbackWWWDir <- system_file("www-dir", package = "shiny")
|
||||
fallbackWWWDir <- system.file("www-dir", package = "shiny")
|
||||
|
||||
oldwd <- NULL
|
||||
monitorHandle <- NULL
|
||||
|
||||
54
R/shinyui.R
54
R/shinyui.R
@@ -14,11 +14,7 @@ NULL
|
||||
#' # now we can just write "static" content without withMathJax()
|
||||
#' div("more math here $$\\sqrt{2}$$")
|
||||
withMathJax <- function(...) {
|
||||
path <- paste0(
|
||||
getOption("shiny.mathjax.url", "https://mathjax.rstudio.com/latest/MathJax.js"),
|
||||
"?",
|
||||
getOption("shiny.mathjax.config", "config=TeX-AMS-MML_HTMLorMML")
|
||||
)
|
||||
path <- 'https://mathjax.rstudio.com/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML'
|
||||
tagList(
|
||||
tags$head(
|
||||
singleton(tags$script(src = path, type = 'text/javascript'))
|
||||
@@ -43,7 +39,7 @@ renderPage <- function(ui, showcase=0, testMode=FALSE) {
|
||||
|
||||
# Put the body into the default template
|
||||
ui <- htmlTemplate(
|
||||
system_file("template", "default.html", package = "shiny"),
|
||||
system.file("template", "default.html", package = "shiny"),
|
||||
lang = lang,
|
||||
body = ui,
|
||||
# this template is a complete HTML document
|
||||
@@ -59,14 +55,8 @@ renderPage <- function(ui, showcase=0, testMode=FALSE) {
|
||||
if (testMode) {
|
||||
# Add code injection listener if in test mode
|
||||
shiny_deps[[length(shiny_deps) + 1]] <-
|
||||
htmlDependency(
|
||||
"shiny-testmode",
|
||||
get_package_version("shiny"),
|
||||
src = "www/shared",
|
||||
package = "shiny",
|
||||
script = "shiny-testmode.js",
|
||||
all_files = FALSE
|
||||
)
|
||||
htmlDependency("shiny-testmode", shinyPackageVersion(),
|
||||
c(href="shared"), script = "shiny-testmode.js")
|
||||
}
|
||||
|
||||
html <- renderDocument(ui, shiny_deps, processDep = createWebDependency)
|
||||
@@ -78,19 +68,23 @@ jqueryDependency <- function() {
|
||||
if (version == 3) {
|
||||
return(htmlDependency(
|
||||
"jquery", version_jquery,
|
||||
src = "www/shared",
|
||||
src = c(
|
||||
href = "shared",
|
||||
file = "www/shared"
|
||||
),
|
||||
package = "shiny",
|
||||
script = "jquery.min.js",
|
||||
all_files = FALSE
|
||||
script = "jquery.min.js"
|
||||
))
|
||||
}
|
||||
if (version == 1) {
|
||||
return(htmlDependency(
|
||||
"jquery", "1.12.4",
|
||||
src = "www/shared/legacy",
|
||||
src = c(
|
||||
href = "shared/legacy",
|
||||
file = "www/shared/legacy"
|
||||
),
|
||||
package = "shiny",
|
||||
script = "jquery.min.js",
|
||||
all_files = FALSE
|
||||
script = "jquery.min.js"
|
||||
))
|
||||
}
|
||||
stop("Unsupported version of jQuery: ", version)
|
||||
@@ -101,9 +95,8 @@ shinyDependencies <- function() {
|
||||
bslib::bs_dependency_defer(shinyDependencyCSS),
|
||||
htmlDependency(
|
||||
name = "shiny-javascript",
|
||||
version = get_package_version("shiny"),
|
||||
src = "www/shared",
|
||||
package = "shiny",
|
||||
version = shinyPackageVersion(),
|
||||
src = c(href = "shared"),
|
||||
script =
|
||||
if (isTRUE(
|
||||
get_devmode_option(
|
||||
@@ -113,27 +106,24 @@ shinyDependencies <- function() {
|
||||
))
|
||||
"shiny.min.js"
|
||||
else
|
||||
"shiny.js",
|
||||
all_files = FALSE
|
||||
"shiny.js"
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
shinyDependencyCSS <- function(theme) {
|
||||
version <- get_package_version("shiny")
|
||||
version <- shinyPackageVersion()
|
||||
|
||||
if (!is_bs_theme(theme)) {
|
||||
return(htmlDependency(
|
||||
name = "shiny-css",
|
||||
version = version,
|
||||
src = "www/shared",
|
||||
package = "shiny",
|
||||
stylesheet = "shiny.min.css",
|
||||
all_files = FALSE
|
||||
src = c(href = "shared"),
|
||||
stylesheet = "shiny.min.css"
|
||||
))
|
||||
}
|
||||
|
||||
scss_home <- system_file("www/shared/shiny_scss", package = "shiny")
|
||||
scss_home <- system.file("www/shared/shiny_scss", package = "shiny")
|
||||
scss_files <- file.path(scss_home, c("bootstrap.scss", "shiny.scss"))
|
||||
scss_files <- lapply(scss_files, sass::sass_file)
|
||||
|
||||
@@ -148,7 +138,7 @@ shinyDependencyCSS <- function(theme) {
|
||||
|
||||
#' Create a Shiny UI handler
|
||||
#'
|
||||
#' @description `r lifecycle::badge("superseded")`
|
||||
#' @description \lifecycle{superseded}
|
||||
#'
|
||||
#' @description Historically this function was used in ui.R files to register a user
|
||||
#' interface with Shiny. It is no longer required as of Shiny 0.10; simply
|
||||
|
||||
@@ -2,23 +2,12 @@ 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
|
||||
@@ -48,7 +37,7 @@ utils::globalVariables('func', add = TRUE)
|
||||
#' is able to serve JS and CSS resources.
|
||||
#' @return The `renderFunc` function, with annotations.
|
||||
#'
|
||||
#' @seealso [createRenderFunction()]
|
||||
#' @seealso [createRenderFunction()], [quoToFunction()]
|
||||
#' @export
|
||||
markRenderFunction <- function(
|
||||
uiFunc,
|
||||
@@ -58,12 +47,6 @@ 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
|
||||
@@ -111,7 +94,6 @@ 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
|
||||
})
|
||||
@@ -151,27 +133,10 @@ print.shiny.render.function <- function(x, ...) {
|
||||
cat_line("<shiny.render.function>")
|
||||
}
|
||||
|
||||
#' Implement custom render functions
|
||||
#' Implement render functions
|
||||
#'
|
||||
#' 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).
|
||||
#' This function is a wrapper for [markRenderFunction()] which provides support
|
||||
#' for async computation via promises.
|
||||
#'
|
||||
#' @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
|
||||
@@ -188,24 +153,16 @@ print.shiny.render.function <- function(x, ...) {
|
||||
#' @return An annotated render function, ready to be assigned to an
|
||||
#' `output` slot.
|
||||
#'
|
||||
#' @seealso [quoToFunction()], [markRenderFunction()].
|
||||
#'
|
||||
#' @examples
|
||||
#' # 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)
|
||||
#' # 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")
|
||||
#'
|
||||
#' createRenderFunction(
|
||||
#' func,
|
||||
@@ -217,38 +174,10 @@ print.shiny.render.function <- function(x, ...) {
|
||||
#' }
|
||||
#'
|
||||
#' # Test render function from the console
|
||||
#' reactiveConsole(TRUE)
|
||||
#'
|
||||
#' v <- reactiveVal("basic")
|
||||
#' r <- renderTriple({ v() })
|
||||
#' a <- 1
|
||||
#' r <- renderTriple({ a + 1 })
|
||||
#' a <- 2
|
||||
#' 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,
|
||||
@@ -387,7 +316,9 @@ markOutputAttrs <- function(renderFunc, snapshotExclude = NULL,
|
||||
#' the output, see [plotPNG()].
|
||||
#'
|
||||
#' @param expr An expression that returns a list.
|
||||
#' @inheritParams renderUI
|
||||
#' @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.
|
||||
#' @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`;
|
||||
@@ -466,10 +397,11 @@ 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())
|
||||
{
|
||||
func <- installExprFunction(expr, "func", env, quoted, label = "renderImage")
|
||||
expr <- get_quosure(expr, env, quoted)
|
||||
func <- quoToFunction(expr, "renderImage")
|
||||
|
||||
# missing() must be used directly within the function with the given arg
|
||||
if (missing(deleteFile)) {
|
||||
@@ -591,7 +523,9 @@ isTemp <- function(path, tempDir = tempdir(), mustExist) {
|
||||
#' function return [invisible()].
|
||||
#'
|
||||
#' @param expr An expression to evaluate.
|
||||
#' @inheritParams renderUI
|
||||
#' @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.
|
||||
#' @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
|
||||
@@ -602,7 +536,8 @@ isTemp <- function(path, tempDir = tempdir(), mustExist) {
|
||||
renderPrint <- function(expr, env = parent.frame(), quoted = FALSE,
|
||||
width = getOption('width'), outputArgs=list())
|
||||
{
|
||||
func <- installExprFunction(expr, "func", env, quoted, label = "renderPrint")
|
||||
expr <- get_quosure(expr, env, quoted)
|
||||
func <- quoToFunction(expr, "renderPrint")
|
||||
|
||||
# Set a promise domain that sets the console width
|
||||
# and captures output
|
||||
@@ -634,7 +569,7 @@ renderPrint <- function(expr, env = parent.frame(), quoted = FALSE,
|
||||
outputArgs,
|
||||
cacheHint = list(
|
||||
label = "renderPrint",
|
||||
origUserExpr = installedFuncExpr(func)
|
||||
origUserExpr = get_expr(expr)
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -684,10 +619,11 @@ 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=" ") {
|
||||
|
||||
func <- installExprFunction(expr, "func", env, quoted, label = "renderText")
|
||||
expr <- get_quosure(expr, env, quoted)
|
||||
func <- quoToFunction(expr, "renderText")
|
||||
|
||||
createRenderFunction(
|
||||
func,
|
||||
@@ -708,13 +644,9 @@ 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.
|
||||
#' @template param-env
|
||||
#' @templateVar x expr
|
||||
#' @templateVar env env
|
||||
#' @templateVar quoted quoted
|
||||
#' @template param-quoted
|
||||
#' @templateVar x expr
|
||||
#' @templateVar quoted quoted
|
||||
#' @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.
|
||||
#' @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.
|
||||
@@ -743,7 +675,8 @@ renderText <- function(expr, env = parent.frame(), quoted = FALSE,
|
||||
renderUI <- function(expr, env = parent.frame(), quoted = FALSE,
|
||||
outputArgs = list())
|
||||
{
|
||||
func <- installExprFunction(expr, "func", env, quoted, label = "renderUI")
|
||||
expr <- get_quosure(expr, env, quoted)
|
||||
func <- quoToFunction(expr, "renderUI")
|
||||
|
||||
createRenderFunction(
|
||||
func,
|
||||
@@ -778,9 +711,9 @@ renderUI <- function(expr, env = parent.frame(), quoted = FALSE,
|
||||
#' function.)
|
||||
#' @param contentType A string of the download's
|
||||
#' [content type](https://en.wikipedia.org/wiki/Internet_media_type), for
|
||||
#' example `"text/csv"` or `"image/png"`. If `NULL`, the content type
|
||||
#' will be guessed based on the filename extension, or
|
||||
#' `application/octet-stream` if the extension is unknown.
|
||||
#' example `"text/csv"` or `"image/png"`. If `NULL` or
|
||||
#' `NA`, the content type will be guessed based on the filename
|
||||
#' extension, or `application/octet-stream` if the extension is unknown.
|
||||
#' @param outputArgs A list of arguments to be passed through to the implicit
|
||||
#' call to [downloadButton()] when `downloadHandler` is used
|
||||
#' in an interactive R Markdown document.
|
||||
@@ -810,7 +743,7 @@ renderUI <- function(expr, env = parent.frame(), quoted = FALSE,
|
||||
#' shinyApp(ui, server)
|
||||
#' }
|
||||
#' @export
|
||||
downloadHandler <- function(filename, content, contentType=NULL, outputArgs=list()) {
|
||||
downloadHandler <- function(filename, content, contentType=NA, outputArgs=list()) {
|
||||
renderFunc <- function(shinysession, name, ...) {
|
||||
shinysession$registerDownload(name, filename, contentType, content)
|
||||
}
|
||||
@@ -822,10 +755,6 @@ downloadHandler <- function(filename, content, contentType=NULL, 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
|
||||
@@ -900,7 +829,8 @@ renderDataTable <- function(expr, options = NULL, searchDelay = 500,
|
||||
)
|
||||
}
|
||||
|
||||
func <- installExprFunction(expr, "func", env, quoted, label = "renderDataTable")
|
||||
expr <- get_quosure(expr, env, quoted)
|
||||
func <- quoToFunction(expr, "renderDataTable")
|
||||
|
||||
renderFunc <- function(shinysession, name, ...) {
|
||||
if (is.function(options)) options <- options()
|
||||
@@ -953,7 +883,7 @@ renderDataTable <- function(expr, options = NULL, searchDelay = 500,
|
||||
DT10Names <- function() {
|
||||
rbind(
|
||||
utils::read.table(
|
||||
system_file('www/shared/datatables/upgrade1.10.txt', package = 'shiny'),
|
||||
system.file('www/shared/datatables/upgrade1.10.txt', package = 'shiny'),
|
||||
stringsAsFactors = FALSE
|
||||
),
|
||||
c('aoColumns', 'Removed') # looks like an omission on the upgrade guide
|
||||
|
||||
42
R/showcase.R
42
R/showcase.R
@@ -32,40 +32,26 @@ licenseLink <- function(licenseName) {
|
||||
showcaseHead <- function() {
|
||||
|
||||
deps <- list(
|
||||
jqueryuiDependency(),
|
||||
htmlDependency(
|
||||
"showdown",
|
||||
"0.3.1",
|
||||
src = "www/shared/showdown/compressed",
|
||||
package="shiny",
|
||||
script = "showdown.js"
|
||||
),
|
||||
htmlDependency(
|
||||
"highlight.js",
|
||||
"6.2",
|
||||
src = "www/shared/highlight",
|
||||
package="shiny",
|
||||
script = "highlight.pack.js",
|
||||
stylesheet = "rstudio.css"
|
||||
),
|
||||
htmlDependency(
|
||||
"showcase",
|
||||
"0.1.0",
|
||||
src = "www/shared",
|
||||
package = "shiny",
|
||||
script = "shiny-showcase.js",
|
||||
stylesheet = "shiny-showcase.css",
|
||||
all_files = FALSE
|
||||
)
|
||||
htmlDependency("jqueryui", "1.12.1", c(href="shared/jqueryui"),
|
||||
script = "jquery-ui.min.js"),
|
||||
htmlDependency("showdown", "0.3.1", c(href="shared/showdown/compressed"),
|
||||
script = "showdown.js"),
|
||||
htmlDependency("highlight.js", "6.2", c(href="shared/highlight"),
|
||||
script = "highlight.pack.js")
|
||||
)
|
||||
|
||||
mdfile <- file.path.ci(getwd(), 'Readme.md')
|
||||
html <- tagList(
|
||||
html <- with(tags, tagList(
|
||||
script(src="shared/shiny-showcase.js"),
|
||||
link(rel="stylesheet", type="text/css",
|
||||
href="shared/highlight/rstudio.css"),
|
||||
link(rel="stylesheet", type="text/css",
|
||||
href="shared/shiny-showcase.css"),
|
||||
if (file.exists(mdfile))
|
||||
tags$script(type="text/markdown", id="showcase-markdown-content",
|
||||
script(type="text/markdown", id="showcase-markdown-content",
|
||||
paste(readUTF8(mdfile), collapse="\n"))
|
||||
else ""
|
||||
)
|
||||
))
|
||||
|
||||
return(attachDependencies(html, deps))
|
||||
}
|
||||
|
||||
@@ -1,216 +0,0 @@
|
||||
# Generated by staticimports; do not edit by hand.
|
||||
# ======================================================================
|
||||
# Imported from pkg:staticimports
|
||||
# ======================================================================
|
||||
|
||||
# Given a vector, return TRUE if any elements are named, FALSE otherwise.
|
||||
# For zero-length vectors, always return FALSE.
|
||||
any_named <- function(x) {
|
||||
if (length(x) == 0) return(FALSE)
|
||||
nms <- names(x)
|
||||
!is.null(nms) && any(nzchar(nms))
|
||||
}
|
||||
|
||||
# Given a vector, return TRUE if any elements are unnamed, FALSE otherwise.
|
||||
# For zero-length vectors, always return FALSE.
|
||||
any_unnamed <- function(x) {
|
||||
if (length(x) == 0) return(FALSE)
|
||||
nms <- names(x)
|
||||
is.null(nms) || !all(nzchar(nms))
|
||||
}
|
||||
|
||||
# Borrowed from pkgload:::dev_meta, with some modifications.
|
||||
# Returns TRUE if `pkg` was loaded with `devtools::load_all()`.
|
||||
devtools_loaded <- function(pkg) {
|
||||
ns <- .getNamespace(pkg)
|
||||
if (is.null(ns) || is.null(ns$.__DEVTOOLS__)) {
|
||||
return(FALSE)
|
||||
}
|
||||
TRUE
|
||||
}
|
||||
|
||||
get_package_version <- function(pkg) {
|
||||
# `utils::packageVersion()` can be slow, so first try the fast path of
|
||||
# checking if the package is already loaded.
|
||||
ns <- .getNamespace(pkg)
|
||||
if (is.null(ns)) {
|
||||
utils::packageVersion(pkg)
|
||||
} else {
|
||||
as.package_version(ns$.__NAMESPACE__.$spec[["version"]])
|
||||
}
|
||||
}
|
||||
|
||||
is_installed <- function(pkg, version = NULL) {
|
||||
installed <- isNamespaceLoaded(pkg) || nzchar(system_file_cached(package = pkg))
|
||||
if (is.null(version)) {
|
||||
return(installed)
|
||||
}
|
||||
installed && isTRUE(get_package_version(pkg) >= version)
|
||||
}
|
||||
|
||||
register_upgrade_message <- function(pkg, version, error = FALSE) {
|
||||
|
||||
msg <- sprintf(
|
||||
"This version of '%s' is designed to work with '%s' >= %s.
|
||||
Please upgrade via install.packages('%s').",
|
||||
environmentName(environment(register_upgrade_message)),
|
||||
pkg, version, pkg
|
||||
)
|
||||
|
||||
cond <- if (error) stop else packageStartupMessage
|
||||
|
||||
if (pkg %in% loadedNamespaces() && !is_installed(pkg, version)) {
|
||||
cond(msg)
|
||||
}
|
||||
|
||||
# Always register hook in case pkg is loaded at some
|
||||
# point the future (or, potentially, but less commonly,
|
||||
# unloaded & reloaded)
|
||||
setHook(
|
||||
packageEvent(pkg, "onLoad"),
|
||||
function(...) {
|
||||
if (!is_installed(pkg, version)) cond(msg)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
# Simplified version rlang:::s3_register() that just uses
|
||||
# warning() instead of rlang::warn() when registration fails
|
||||
# https://github.com/r-lib/rlang/blob/main/R/compat-s3-register.R
|
||||
s3_register <- function(generic, class, method = NULL) {
|
||||
stopifnot(is.character(generic), length(generic) == 1)
|
||||
stopifnot(is.character(class), length(class) == 1)
|
||||
|
||||
pieces <- strsplit(generic, "::")[[1]]
|
||||
stopifnot(length(pieces) == 2)
|
||||
package <- pieces[[1]]
|
||||
generic <- pieces[[2]]
|
||||
|
||||
caller <- parent.frame()
|
||||
|
||||
get_method_env <- function() {
|
||||
top <- topenv(caller)
|
||||
if (isNamespace(top)) {
|
||||
asNamespace(environmentName(top))
|
||||
} else {
|
||||
caller
|
||||
}
|
||||
}
|
||||
get_method <- function(method, env) {
|
||||
if (is.null(method)) {
|
||||
get(paste0(generic, ".", class), envir = get_method_env())
|
||||
} else {
|
||||
method
|
||||
}
|
||||
}
|
||||
|
||||
register <- function(...) {
|
||||
envir <- asNamespace(package)
|
||||
|
||||
# Refresh the method each time, it might have been updated by
|
||||
# `devtools::load_all()`
|
||||
method_fn <- get_method(method)
|
||||
stopifnot(is.function(method_fn))
|
||||
|
||||
# Only register if generic can be accessed
|
||||
if (exists(generic, envir)) {
|
||||
registerS3method(generic, class, method_fn, envir = envir)
|
||||
} else {
|
||||
warning(
|
||||
"Can't find generic `", generic, "` in package ", package,
|
||||
" register S3 method. Do you need to update ", package,
|
||||
" to the latest version?", call. = FALSE
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
# Always register hook in case package is later unloaded & reloaded
|
||||
setHook(packageEvent(package, "onLoad"), function(...) {
|
||||
register()
|
||||
})
|
||||
|
||||
# Avoid registration failures during loading (pkgload or regular).
|
||||
# Check that environment is locked because the registering package
|
||||
# might be a dependency of the package that exports the generic. In
|
||||
# that case, the exports (and the generic) might not be populated
|
||||
# yet (#1225).
|
||||
if (isNamespaceLoaded(package) && environmentIsLocked(asNamespace(package))) {
|
||||
register()
|
||||
}
|
||||
|
||||
invisible()
|
||||
}
|
||||
|
||||
# Borrowed from pkgload::shim_system.file, with some modifications. This behaves
|
||||
# like `system.file()`, except that (1) for packages loaded with
|
||||
# `devtools::load_all()`, it will return the path to files in the package's
|
||||
# inst/ directory, and (2) for other packages, the directory lookup is cached.
|
||||
# Also, to keep the implementation simple, it doesn't support specification of
|
||||
# lib.loc or mustWork.
|
||||
system_file <- function(..., package = "base") {
|
||||
if (!devtools_loaded(package)) {
|
||||
return(system_file_cached(..., package = package))
|
||||
}
|
||||
|
||||
if (!is.null(names(list(...)))) {
|
||||
stop("All arguments other than `package` must be unnamed.")
|
||||
}
|
||||
|
||||
# If package was loaded with devtools (the package loaded with load_all),
|
||||
# also search for files under inst/, and don't cache the results (it seems
|
||||
# more likely that the package path will change during the development
|
||||
# process)
|
||||
pkg_path <- find.package(package)
|
||||
|
||||
# First look in inst/
|
||||
files_inst <- file.path(pkg_path, "inst", ...)
|
||||
present_inst <- file.exists(files_inst)
|
||||
|
||||
# For any files that weren't present in inst/, look in the base path
|
||||
files_top <- file.path(pkg_path, ...)
|
||||
present_top <- file.exists(files_top)
|
||||
|
||||
# Merge them together. Here are the different possible conditions, and the
|
||||
# desired result. NULL means to drop that element from the result.
|
||||
#
|
||||
# files_inst: /inst/A /inst/B /inst/C /inst/D
|
||||
# present_inst: T T F F
|
||||
# files_top: /A /B /C /D
|
||||
# present_top: T F T F
|
||||
# result: /inst/A /inst/B /C NULL
|
||||
#
|
||||
files <- files_top
|
||||
files[present_inst] <- files_inst[present_inst]
|
||||
# Drop cases where not present in either location
|
||||
files <- files[present_inst | present_top]
|
||||
if (length(files) == 0) {
|
||||
return("")
|
||||
}
|
||||
# Make sure backslashes are replaced with slashes on Windows
|
||||
normalizePath(files, winslash = "/")
|
||||
}
|
||||
|
||||
# A wrapper for `system.file()`, which caches the results, because
|
||||
# `system.file()` can be slow. Note that because of caching, if
|
||||
# `system_file_cached()` is called on a package that isn't installed, then the
|
||||
# package is installed, and then `system_file_cached()` is called again, it will
|
||||
# still return "".
|
||||
system_file_cached <- local({
|
||||
pkg_dir_cache <- character()
|
||||
|
||||
function(..., package = "base") {
|
||||
if (!is.null(names(list(...)))) {
|
||||
stop("All arguments other than `package` must be unnamed.")
|
||||
}
|
||||
|
||||
not_cached <- is.na(match(package, names(pkg_dir_cache)))
|
||||
if (not_cached) {
|
||||
pkg_dir <- system.file(package = package)
|
||||
pkg_dir_cache[[package]] <<- pkg_dir
|
||||
} else {
|
||||
pkg_dir <- pkg_dir_cache[[package]]
|
||||
}
|
||||
|
||||
file.path(pkg_dir, ...)
|
||||
}
|
||||
})
|
||||
239
R/utils-lang.R
239
R/utils-lang.R
@@ -51,199 +51,60 @@ formalsAndBody <- function(x) {
|
||||
}
|
||||
|
||||
|
||||
#' @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)
|
||||
}
|
||||
|
||||
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)
|
||||
# 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())
|
||||
}
|
||||
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
|
||||
}
|
||||
x <- new_quosure(x, env)
|
||||
|
||||
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)
|
||||
} else {
|
||||
x <- eval(substitute(substitute(x)), parent.frame())
|
||||
|
||||
# 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({})
|
||||
}
|
||||
|
||||
# `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")
|
||||
# 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))
|
||||
}
|
||||
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)
|
||||
)
|
||||
x
|
||||
}
|
||||
|
||||
274
R/utils.R
274
R/utils.R
@@ -2,11 +2,6 @@
|
||||
#' @include map.R
|
||||
NULL
|
||||
|
||||
# @staticimports pkg:staticimports
|
||||
# is_installed get_package_version system_file
|
||||
# s3_register register_upgrade_message
|
||||
# any_named any_unnamed
|
||||
|
||||
#' Make a random number generator repeatable
|
||||
#'
|
||||
#' Given a function that generates random data, returns a wrapped version of
|
||||
@@ -131,6 +126,34 @@ dropNullsOrEmpty <- function(x) {
|
||||
x[!vapply(x, nullOrEmpty, FUN.VALUE=logical(1))]
|
||||
}
|
||||
|
||||
# Given a vector/list, return TRUE if any elements are named, FALSE otherwise.
|
||||
anyNamed <- function(x) {
|
||||
# Zero-length vector
|
||||
if (length(x) == 0) return(FALSE)
|
||||
|
||||
nms <- names(x)
|
||||
|
||||
# List with no name attribute
|
||||
if (is.null(nms)) return(FALSE)
|
||||
|
||||
# List with name attribute; check for any ""
|
||||
any(nzchar(nms))
|
||||
}
|
||||
|
||||
# Given a vector/list, return TRUE if any elements are unnamed, FALSE otherwise.
|
||||
anyUnnamed <- function(x) {
|
||||
# Zero-length vector
|
||||
if (length(x) == 0) return(FALSE)
|
||||
|
||||
nms <- names(x)
|
||||
|
||||
# List with no name attribute
|
||||
if (is.null(nms)) return(TRUE)
|
||||
|
||||
# List with name attribute; check for any ""
|
||||
any(!nzchar(nms))
|
||||
}
|
||||
|
||||
|
||||
# Given a vector/list, returns a named vector/list (the labels will be blank).
|
||||
asNamed <- function(x) {
|
||||
@@ -150,7 +173,7 @@ empty_named_list <- function() {
|
||||
# name as elements in a, the element in a is dropped. Also, if there are any
|
||||
# duplicated names in a or b, only the last one with that name is kept.
|
||||
mergeVectors <- function(a, b) {
|
||||
if (any_unnamed(a) || any_unnamed(b)) {
|
||||
if (anyUnnamed(a) || anyUnnamed(b)) {
|
||||
stop("Vectors must be either NULL or have names for all elements")
|
||||
}
|
||||
|
||||
@@ -162,27 +185,15 @@ mergeVectors <- function(a, b) {
|
||||
# Sort a vector by the names of items. If there are multiple items with the
|
||||
# same name, preserve the original order of those items. For empty
|
||||
# vectors/lists/NULL, return the original value.
|
||||
sortByName <- function(x, method = "auto") {
|
||||
if (any_unnamed(x))
|
||||
sortByName <- function(x) {
|
||||
if (anyUnnamed(x))
|
||||
stop("All items must be named")
|
||||
|
||||
# Special case for empty vectors/lists, and NULL
|
||||
if (length(x) == 0)
|
||||
return(x)
|
||||
|
||||
# Must provide consistent sort order
|
||||
# https://github.com/rstudio/shinytest/issues/409
|
||||
# Using a flag in the snapshot url to determine the method
|
||||
# `method="radix"` uses `C` locale, which is consistent across platforms
|
||||
# Even if two platforms share `en_us.UTF-8`, they may not sort consistently
|
||||
# https://blog.zhimingwang.org/macos-lc_collate-hunt
|
||||
# (macOS) $ LC_ALL=en_US.UTF-8 sort <<<$'python-dev\npython3-dev'
|
||||
# python-dev
|
||||
# python3-dev
|
||||
# (Linux) $ LC_ALL=en_US.UTF-8 sort <<<$'python-dev\npython3-dev'
|
||||
# python3-dev
|
||||
# python-dev
|
||||
x[order(names(x), method = method)]
|
||||
x[order(names(x))]
|
||||
}
|
||||
|
||||
# Sort a vector. If a character vector, sort using C locale, which is consistent
|
||||
@@ -393,6 +404,165 @@ 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.
|
||||
@@ -484,7 +654,7 @@ shinyCallingHandlers <- function(expr) {
|
||||
withCallingHandlers(captureStackTraces(expr),
|
||||
error = function(e) {
|
||||
# Don't intercept shiny.silent.error (i.e. validation errors)
|
||||
if (cnd_inherits(e, "shiny.silent.error"))
|
||||
if (inherits(e, "shiny.silent.error"))
|
||||
return()
|
||||
|
||||
handle <- getOption('shiny.error')
|
||||
@@ -989,7 +1159,7 @@ reactiveStop <- function(message = "", class = NULL) {
|
||||
#'
|
||||
#' ui <- fluidPage(
|
||||
#' checkboxGroupInput('in1', 'Check some letters', choices = head(LETTERS)),
|
||||
#' selectizeInput('in2', 'Select a state', choices = c("", state.name)),
|
||||
#' selectizeInput('in2', 'Select a state', choices = state.name),
|
||||
#' plotOutput('plot')
|
||||
#' )
|
||||
#'
|
||||
@@ -1427,31 +1597,21 @@ 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, dots = TRUE) {
|
||||
wrapFunctionLabel <- function(func, name, ..stacktraceon = FALSE) {
|
||||
if (name == "name" || name == "func" || name == "relabelWrapper") {
|
||||
stop("Invalid name for wrapFunctionLabel: ", name)
|
||||
}
|
||||
assign(name, func, environment())
|
||||
registerDebugHook(name, environment(), name)
|
||||
|
||||
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())
|
||||
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 {
|
||||
# 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())
|
||||
body <- expr({ (!!name)(!!quote(...)) })
|
||||
}
|
||||
relabelWrapper <- new_function(pairlist2(... =), body, environment())
|
||||
|
||||
# Preserve the original function that was passed in; is used for caching.
|
||||
attr(relabelWrapper, "wrappedFunc") <- func
|
||||
@@ -1705,20 +1865,24 @@ findEnclosingApp <- function(path = ".") {
|
||||
}
|
||||
}
|
||||
|
||||
# Until `rlang::cnd_inherits()` is on CRAN
|
||||
cnd_inherits <- function(cnd, class) {
|
||||
cnd_some(cnd, ~ inherits(.x, class))
|
||||
}
|
||||
cnd_some <- function(.cnd, .p, ...) {
|
||||
.p <- rlang::as_function(.p)
|
||||
|
||||
while (rlang::is_condition(.cnd)) {
|
||||
if (.p(.cnd, ...)) {
|
||||
return(TRUE)
|
||||
}
|
||||
|
||||
.cnd <- .cnd$parent
|
||||
# Check if a package is installed, and if version is specified,
|
||||
# that we have at least that version
|
||||
is_available <- function(package, version = NULL) {
|
||||
installed <- nzchar(system.file(package = package))
|
||||
if (is.null(version)) {
|
||||
return(installed)
|
||||
}
|
||||
|
||||
FALSE
|
||||
installed && isTRUE(utils::packageVersion(package) >= version)
|
||||
}
|
||||
|
||||
|
||||
# cached version of utils::packageVersion("shiny")
|
||||
shinyPackageVersion <- local({
|
||||
version <- NULL
|
||||
function() {
|
||||
if (is.null(version)) {
|
||||
version <<- utils::packageVersion("shiny")
|
||||
}
|
||||
version
|
||||
}
|
||||
})
|
||||
|
||||
14
README.md
14
README.md
@@ -2,7 +2,7 @@
|
||||
|
||||
<!-- badges: start -->
|
||||
[](https://CRAN.R-project.org/package=shiny)
|
||||
[](https://github.com/rstudio/shiny/actions)
|
||||
[](https://github.com/rstudio/shiny/actions)
|
||||
[](https://community.rstudio.com/new-topic?category=shiny&tags=shiny)
|
||||
|
||||
<!-- badges: end -->
|
||||
@@ -16,7 +16,7 @@ Easily build rich and productive interactive web apps in R — no HTML/CSS/J
|
||||
* A prebuilt set of highly sophisticated, customizable, and easy-to-use widgets (e.g., plots, tables, sliders, dropdowns, date pickers, and more).
|
||||
* An attractive default look based on [Bootstrap](https://getbootstrap.com/) which can also be easily customized with the [bslib](https://github.com/rstudio/bslib) package or avoided entirely with more direct R bindings to HTML/CSS/JavaScript.
|
||||
* Seamless integration with [R Markdown](https://shiny.rstudio.com/articles/interactive-docs.html), making it easy to embed numerous applications natively within a larger dynamic document.
|
||||
* Tools for improving and monitoring performance, including native support for [async programming](https://www.rstudio.com/blog/shiny-1-1-0/), [caching](https://talks.cpsievert.me/20201117), [load testing](https://rstudio.github.io/shinyloadtest/), and more.
|
||||
* Tools for improving and monitoring performance, including native support for [async programming](https://blog.rstudio.com/2018/06/26/shiny-1-1-0/), [caching](https://talks.cpsievert.me/20201117), [load testing](https://rstudio.github.io/shinyloadtest/), and [more](https://support.rstudio.com/hc/en-us/articles/231874748-Scaling-and-Performance-Tuning-in-RStudio-Connect).
|
||||
* [Modules](https://shiny.rstudio.com/articles/modules.html): a framework for reducing code duplication and complexity.
|
||||
* An ability to [bookmark application state](https://shiny.rstudio.com/articles/bookmarking-state.html) and/or [generate code to reproduce output(s)](https://github.com/rstudio/shinymeta).
|
||||
* A rich ecosystem of extension packages for more [custom widgets](http://www.htmlwidgets.org/), [input validation](https://github.com/rstudio/shinyvalidate), [unit testing](https://github.com/rstudio/shinytest), and more.
|
||||
@@ -47,18 +47,14 @@ For help with learning fundamental Shiny programming concepts, check out the [Ma
|
||||
|
||||
## Getting Help
|
||||
|
||||
To ask a question about Shiny, please use the [RStudio Community website](https://community.rstudio.com/new-topic?category=shiny&tags=shiny).
|
||||
To ask a question about Shiny, please use the [RStudio Community website](https://community.rstudio.com/new-topic?category=shiny&tags=shiny).
|
||||
|
||||
For bug reports, please use the [issue tracker](https://github.com/rstudio/shiny/issues) and also keep in mind that by [writing a good bug report](https://github.com/rstudio/shiny/wiki/Writing-Good-Bug-Reports), you're more likely to get help with your problem.
|
||||
For bug reports, please use the [issue tracker](https://github.com/rstudio/shiny/issues) and also keep in mind that by [writing a good bug report](https://github.com/rstudio/shiny/wiki/Writing-Good-Bug-Reports), you're more likely to get help with your problem.
|
||||
|
||||
## Contributing
|
||||
|
||||
We welcome contributions to the **shiny** package. Please see our [CONTRIBUTING.md](https://github.com/rstudio/shiny/blob/main/.github/CONTRIBUTING.md) file for detailed guidelines of how to contribute.
|
||||
We welcome contributions to the **shiny** package. Please see our [CONTRIBUTING.md](https://github.com/rstudio/shiny/blob/master/.github/CONTRIBUTING.md) file for detailed guidelines of how to contribute.
|
||||
|
||||
## 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.
|
||||
|
||||
2
inst/app_template/tests/shinytest.R
Normal file
2
inst/app_template/tests/shinytest.R
Normal file
@@ -0,0 +1,2 @@
|
||||
library(shinytest)
|
||||
expect_pass(testApp("../", suffix = osName()))
|
||||
12
inst/app_template/tests/shinytest/mytest.R
Normal file
12
inst/app_template/tests/shinytest/mytest.R
Normal file
@@ -0,0 +1,12 @@
|
||||
app <- ShinyDriver$new("../../")
|
||||
app$snapshotInit("mytest")
|
||||
|
||||
app$snapshot()
|
||||
{{
|
||||
if (isTRUE(module)) {
|
||||
'
|
||||
app$setInputs(`examplemodule1-button` = "click")
|
||||
app$setInputs(`examplemodule1-button` = "click")
|
||||
app$snapshot()'
|
||||
}
|
||||
}}
|
||||
@@ -1 +1,9 @@
|
||||
shinytest2::test_app()
|
||||
library(testthat)
|
||||
|
||||
test_dir(
|
||||
"./testthat",
|
||||
# Run in the app's environment containing all support methods.
|
||||
env = shiny::loadSupport(),
|
||||
# Display the regular progress output and throw an error if any test error is found
|
||||
reporter = c("progress", "fail")
|
||||
)
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
# Load application support files into testing environment
|
||||
shinytest2::load_app_env()
|
||||
@@ -14,4 +14,5 @@ if (isTRUE(rdir)) {
|
||||
expect_equal(output$sequence, "1 2 3 4 5 6 7 8 9 10 11 12")
|
||||
'
|
||||
}
|
||||
}}})
|
||||
}}
|
||||
})
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
library(shinytest2)
|
||||
|
||||
test_that("Initial snapshot values are consistent", {
|
||||
app <- AppDriver$new(name = "init")
|
||||
app$expect_values()
|
||||
}){{
|
||||
if (isTRUE(module)) {
|
||||
HTML('
|
||||
|
||||
|
||||
test_that("Module values are consistent", {
|
||||
app <- AppDriver$new(name = "mod")
|
||||
app$click("examplemodule1-button")
|
||||
app$click("examplemodule1-button")
|
||||
app$expect_values()
|
||||
})')
|
||||
}
|
||||
}}
|
||||
File diff suppressed because one or more lines are too long
@@ -13,13 +13,6 @@
|
||||
-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 {
|
||||
@@ -179,9 +172,9 @@
|
||||
.irs--shiny .irs-bar {
|
||||
top: 25px;
|
||||
height: 8px;
|
||||
border-top: 1px solid #428bca;
|
||||
border-bottom: 1px solid #428bca;
|
||||
background: #428bca;
|
||||
border-top: 1px solid #337ab7;
|
||||
border-bottom: 1px solid #337ab7;
|
||||
background: #337ab7;
|
||||
}
|
||||
|
||||
.irs--shiny .irs-bar--single {
|
||||
@@ -235,7 +228,7 @@
|
||||
color: #fff;
|
||||
text-shadow: none;
|
||||
padding: 1px 3px;
|
||||
background-color: #428bca;
|
||||
background-color: #337ab7;
|
||||
border-radius: 3px;
|
||||
font-size: 11px;
|
||||
line-height: 1.333;
|
||||
|
||||
@@ -4,12 +4,6 @@
|
||||
@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: #428bca !default;
|
||||
$accent: $component-active-bg !default;
|
||||
|
||||
// "Low-level" coloring, borders, and fonts
|
||||
$line_bg: linear-gradient(to bottom, mix($bg, $fg, 87%) -50%, $bg 150%) !default;
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
6755
inst/www/shared/shiny-es5.js
Normal file
6755
inst/www/shared/shiny-es5.js
Normal file
File diff suppressed because it is too large
Load Diff
1
inst/www/shared/shiny-es5.js.map
Normal file
1
inst/www/shared/shiny-es5.js.map
Normal file
File diff suppressed because one or more lines are too long
4
inst/www/shared/shiny-es5.min.js
vendored
Normal file
4
inst/www/shared/shiny-es5.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
inst/www/shared/shiny-es5.min.js.map
Normal file
1
inst/www/shared/shiny-es5.min.js.map
Normal file
File diff suppressed because one or more lines are too long
@@ -1,2 +1,87 @@
|
||||
/*! shiny 1.7.3 | (c) 2012-2022 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}
|
||||
#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;
|
||||
}
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1,3 +1,8 @@
|
||||
/*! shiny 1.7.3 | (c) 2012-2022 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=shiny-testmode.js.map
|
||||
// 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);
|
||||
});
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
{
|
||||
"version": 3,
|
||||
"sources": ["../../../srcts/src/utils/eval.ts", "../../../srcts/extras/shiny-testmode.ts"],
|
||||
"sourcesContent": ["//esbuild.github.io/content-types/#direct-eval\n//tl/dr;\n// * Direct usage of `eval(\"x\")` is bad with bundled code.\n// * Instead, use indirect calls to `eval` such as `indirectEval(\"x\")`\n// * Even just renaming the function works well enough.\n// > This is known as \"indirect eval\" because eval is not being called directly, and so does not trigger the grammatical special case for direct eval in the JavaScript VM. You can call indirect eval using any syntax at all except for an expression of the exact form eval('x'). For example, var eval2 = eval; eval2('x') and [eval][0]('x') and window.eval('x') are all indirect eval calls.\n// > When you use indirect eval, the code is evaluated in the global scope instead of in the inline scope of the caller.\nvar indirectEval = eval;\nexport { indirectEval };", "/* eslint-disable unicorn/filename-case */\nimport { indirectEval } from \"../src/utils/eval\"; // Listen for messages from parent frame. This file is only added when the\n// shiny.testmode option is TRUE.\n\nwindow.addEventListener(\"message\", function (e) {\n var message = e.data;\n if (message.code) indirectEval(message.code);\n});"],
|
||||
"mappings": ";YAOA,GAAI,GAAe,KCHnB,OAAO,iBAAiB,UAAW,SAAU,EAAG,CAC9C,GAAI,GAAU,EAAE,KAChB,AAAI,EAAQ,MAAM,EAAa,EAAQ",
|
||||
"names": []
|
||||
}
|
||||
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,15 +295,6 @@ 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;
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
# 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.
|
||||
@@ -1,6 +0,0 @@
|
||||
# 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`.
|
||||
@@ -85,40 +85,40 @@ user. Always \code{NULL} for a \code{MockShinySesion}.}
|
||||
\section{Methods}{
|
||||
\subsection{Public methods}{
|
||||
\itemize{
|
||||
\item \href{#method-MockShinySession-new}{\code{MockShinySession$new()}}
|
||||
\item \href{#method-MockShinySession-onFlush}{\code{MockShinySession$onFlush()}}
|
||||
\item \href{#method-MockShinySession-onFlushed}{\code{MockShinySession$onFlushed()}}
|
||||
\item \href{#method-MockShinySession-onEnded}{\code{MockShinySession$onEnded()}}
|
||||
\item \href{#method-MockShinySession-isEnded}{\code{MockShinySession$isEnded()}}
|
||||
\item \href{#method-MockShinySession-isClosed}{\code{MockShinySession$isClosed()}}
|
||||
\item \href{#method-MockShinySession-close}{\code{MockShinySession$close()}}
|
||||
\item \href{#method-MockShinySession-cycleStartAction}{\code{MockShinySession$cycleStartAction()}}
|
||||
\item \href{#method-MockShinySession-fileUrl}{\code{MockShinySession$fileUrl()}}
|
||||
\item \href{#method-MockShinySession-setInputs}{\code{MockShinySession$setInputs()}}
|
||||
\item \href{#method-MockShinySession-.scheduleTask}{\code{MockShinySession$.scheduleTask()}}
|
||||
\item \href{#method-MockShinySession-elapse}{\code{MockShinySession$elapse()}}
|
||||
\item \href{#method-MockShinySession-.now}{\code{MockShinySession$.now()}}
|
||||
\item \href{#method-MockShinySession-defineOutput}{\code{MockShinySession$defineOutput()}}
|
||||
\item \href{#method-MockShinySession-getOutput}{\code{MockShinySession$getOutput()}}
|
||||
\item \href{#method-MockShinySession-ns}{\code{MockShinySession$ns()}}
|
||||
\item \href{#method-MockShinySession-flushReact}{\code{MockShinySession$flushReact()}}
|
||||
\item \href{#method-MockShinySession-makeScope}{\code{MockShinySession$makeScope()}}
|
||||
\item \href{#method-MockShinySession-setEnv}{\code{MockShinySession$setEnv()}}
|
||||
\item \href{#method-MockShinySession-setReturned}{\code{MockShinySession$setReturned()}}
|
||||
\item \href{#method-MockShinySession-getReturned}{\code{MockShinySession$getReturned()}}
|
||||
\item \href{#method-MockShinySession-genId}{\code{MockShinySession$genId()}}
|
||||
\item \href{#method-MockShinySession-rootScope}{\code{MockShinySession$rootScope()}}
|
||||
\item \href{#method-MockShinySession-unhandledError}{\code{MockShinySession$unhandledError()}}
|
||||
\item \href{#method-MockShinySession-freezeValue}{\code{MockShinySession$freezeValue()}}
|
||||
\item \href{#method-MockShinySession-onSessionEnded}{\code{MockShinySession$onSessionEnded()}}
|
||||
\item \href{#method-MockShinySession-registerDownload}{\code{MockShinySession$registerDownload()}}
|
||||
\item \href{#method-MockShinySession-getCurrentOutputInfo}{\code{MockShinySession$getCurrentOutputInfo()}}
|
||||
\item \href{#method-MockShinySession-clone}{\code{MockShinySession$clone()}}
|
||||
\item \href{#method-new}{\code{MockShinySession$new()}}
|
||||
\item \href{#method-onFlush}{\code{MockShinySession$onFlush()}}
|
||||
\item \href{#method-onFlushed}{\code{MockShinySession$onFlushed()}}
|
||||
\item \href{#method-onEnded}{\code{MockShinySession$onEnded()}}
|
||||
\item \href{#method-isEnded}{\code{MockShinySession$isEnded()}}
|
||||
\item \href{#method-isClosed}{\code{MockShinySession$isClosed()}}
|
||||
\item \href{#method-close}{\code{MockShinySession$close()}}
|
||||
\item \href{#method-cycleStartAction}{\code{MockShinySession$cycleStartAction()}}
|
||||
\item \href{#method-fileUrl}{\code{MockShinySession$fileUrl()}}
|
||||
\item \href{#method-setInputs}{\code{MockShinySession$setInputs()}}
|
||||
\item \href{#method-.scheduleTask}{\code{MockShinySession$.scheduleTask()}}
|
||||
\item \href{#method-elapse}{\code{MockShinySession$elapse()}}
|
||||
\item \href{#method-.now}{\code{MockShinySession$.now()}}
|
||||
\item \href{#method-defineOutput}{\code{MockShinySession$defineOutput()}}
|
||||
\item \href{#method-getOutput}{\code{MockShinySession$getOutput()}}
|
||||
\item \href{#method-ns}{\code{MockShinySession$ns()}}
|
||||
\item \href{#method-flushReact}{\code{MockShinySession$flushReact()}}
|
||||
\item \href{#method-makeScope}{\code{MockShinySession$makeScope()}}
|
||||
\item \href{#method-setEnv}{\code{MockShinySession$setEnv()}}
|
||||
\item \href{#method-setReturned}{\code{MockShinySession$setReturned()}}
|
||||
\item \href{#method-getReturned}{\code{MockShinySession$getReturned()}}
|
||||
\item \href{#method-genId}{\code{MockShinySession$genId()}}
|
||||
\item \href{#method-rootScope}{\code{MockShinySession$rootScope()}}
|
||||
\item \href{#method-unhandledError}{\code{MockShinySession$unhandledError()}}
|
||||
\item \href{#method-freezeValue}{\code{MockShinySession$freezeValue()}}
|
||||
\item \href{#method-onSessionEnded}{\code{MockShinySession$onSessionEnded()}}
|
||||
\item \href{#method-registerDownload}{\code{MockShinySession$registerDownload()}}
|
||||
\item \href{#method-getCurrentOutputInfo}{\code{MockShinySession$getCurrentOutputInfo()}}
|
||||
\item \href{#method-clone}{\code{MockShinySession$clone()}}
|
||||
}
|
||||
}
|
||||
\if{html}{\out{<hr>}}
|
||||
\if{html}{\out{<a id="method-MockShinySession-new"></a>}}
|
||||
\if{latex}{\out{\hypertarget{method-MockShinySession-new}{}}}
|
||||
\if{html}{\out{<a id="method-new"></a>}}
|
||||
\if{latex}{\out{\hypertarget{method-new}{}}}
|
||||
\subsection{Method \code{new()}}{
|
||||
Create a new MockShinySession.
|
||||
\subsection{Usage}{
|
||||
@@ -127,8 +127,8 @@ Create a new MockShinySession.
|
||||
|
||||
}
|
||||
\if{html}{\out{<hr>}}
|
||||
\if{html}{\out{<a id="method-MockShinySession-onFlush"></a>}}
|
||||
\if{latex}{\out{\hypertarget{method-MockShinySession-onFlush}{}}}
|
||||
\if{html}{\out{<a id="method-onFlush"></a>}}
|
||||
\if{latex}{\out{\hypertarget{method-onFlush}{}}}
|
||||
\subsection{Method \code{onFlush()}}{
|
||||
Define a callback to be invoked before a reactive flush
|
||||
\subsection{Usage}{
|
||||
@@ -146,8 +146,8 @@ Define a callback to be invoked before a reactive flush
|
||||
}
|
||||
}
|
||||
\if{html}{\out{<hr>}}
|
||||
\if{html}{\out{<a id="method-MockShinySession-onFlushed"></a>}}
|
||||
\if{latex}{\out{\hypertarget{method-MockShinySession-onFlushed}{}}}
|
||||
\if{html}{\out{<a id="method-onFlushed"></a>}}
|
||||
\if{latex}{\out{\hypertarget{method-onFlushed}{}}}
|
||||
\subsection{Method \code{onFlushed()}}{
|
||||
Define a callback to be invoked after a reactive flush
|
||||
\subsection{Usage}{
|
||||
@@ -165,8 +165,8 @@ Define a callback to be invoked after a reactive flush
|
||||
}
|
||||
}
|
||||
\if{html}{\out{<hr>}}
|
||||
\if{html}{\out{<a id="method-MockShinySession-onEnded"></a>}}
|
||||
\if{latex}{\out{\hypertarget{method-MockShinySession-onEnded}{}}}
|
||||
\if{html}{\out{<a id="method-onEnded"></a>}}
|
||||
\if{latex}{\out{\hypertarget{method-onEnded}{}}}
|
||||
\subsection{Method \code{onEnded()}}{
|
||||
Define a callback to be invoked when the session ends
|
||||
\subsection{Usage}{
|
||||
@@ -182,8 +182,8 @@ Define a callback to be invoked when the session ends
|
||||
}
|
||||
}
|
||||
\if{html}{\out{<hr>}}
|
||||
\if{html}{\out{<a id="method-MockShinySession-isEnded"></a>}}
|
||||
\if{latex}{\out{\hypertarget{method-MockShinySession-isEnded}{}}}
|
||||
\if{html}{\out{<a id="method-isEnded"></a>}}
|
||||
\if{latex}{\out{\hypertarget{method-isEnded}{}}}
|
||||
\subsection{Method \code{isEnded()}}{
|
||||
Returns \code{FALSE} if the session has not yet been closed
|
||||
\subsection{Usage}{
|
||||
@@ -192,8 +192,8 @@ Returns \code{FALSE} if the session has not yet been closed
|
||||
|
||||
}
|
||||
\if{html}{\out{<hr>}}
|
||||
\if{html}{\out{<a id="method-MockShinySession-isClosed"></a>}}
|
||||
\if{latex}{\out{\hypertarget{method-MockShinySession-isClosed}{}}}
|
||||
\if{html}{\out{<a id="method-isClosed"></a>}}
|
||||
\if{latex}{\out{\hypertarget{method-isClosed}{}}}
|
||||
\subsection{Method \code{isClosed()}}{
|
||||
Returns \code{FALSE} if the session has not yet been closed
|
||||
\subsection{Usage}{
|
||||
@@ -202,8 +202,8 @@ Returns \code{FALSE} if the session has not yet been closed
|
||||
|
||||
}
|
||||
\if{html}{\out{<hr>}}
|
||||
\if{html}{\out{<a id="method-MockShinySession-close"></a>}}
|
||||
\if{latex}{\out{\hypertarget{method-MockShinySession-close}{}}}
|
||||
\if{html}{\out{<a id="method-close"></a>}}
|
||||
\if{latex}{\out{\hypertarget{method-close}{}}}
|
||||
\subsection{Method \code{close()}}{
|
||||
Closes the session
|
||||
\subsection{Usage}{
|
||||
@@ -212,8 +212,8 @@ Closes the session
|
||||
|
||||
}
|
||||
\if{html}{\out{<hr>}}
|
||||
\if{html}{\out{<a id="method-MockShinySession-cycleStartAction"></a>}}
|
||||
\if{latex}{\out{\hypertarget{method-MockShinySession-cycleStartAction}{}}}
|
||||
\if{html}{\out{<a id="method-cycleStartAction"></a>}}
|
||||
\if{latex}{\out{\hypertarget{method-cycleStartAction}{}}}
|
||||
\subsection{Method \code{cycleStartAction()}}{
|
||||
Unsophisticated mock implementation that merely invokes
|
||||
\subsection{Usage}{
|
||||
@@ -229,8 +229,8 @@ Unsophisticated mock implementation that merely invokes
|
||||
}
|
||||
}
|
||||
\if{html}{\out{<hr>}}
|
||||
\if{html}{\out{<a id="method-MockShinySession-fileUrl"></a>}}
|
||||
\if{latex}{\out{\hypertarget{method-MockShinySession-fileUrl}{}}}
|
||||
\if{html}{\out{<a id="method-fileUrl"></a>}}
|
||||
\if{latex}{\out{\hypertarget{method-fileUrl}{}}}
|
||||
\subsection{Method \code{fileUrl()}}{
|
||||
Base64-encode the given file. Needed for image rendering.
|
||||
\subsection{Usage}{
|
||||
@@ -250,8 +250,8 @@ Base64-encode the given file. Needed for image rendering.
|
||||
}
|
||||
}
|
||||
\if{html}{\out{<hr>}}
|
||||
\if{html}{\out{<a id="method-MockShinySession-setInputs"></a>}}
|
||||
\if{latex}{\out{\hypertarget{method-MockShinySession-setInputs}{}}}
|
||||
\if{html}{\out{<a id="method-setInputs"></a>}}
|
||||
\if{latex}{\out{\hypertarget{method-setInputs}{}}}
|
||||
\subsection{Method \code{setInputs()}}{
|
||||
Sets reactive values associated with the \code{session$inputs}
|
||||
object and flushes the reactives.
|
||||
@@ -280,8 +280,8 @@ session$setInputs(x=1, y=2)
|
||||
|
||||
}
|
||||
\if{html}{\out{<hr>}}
|
||||
\if{html}{\out{<a id="method-MockShinySession-.scheduleTask"></a>}}
|
||||
\if{latex}{\out{\hypertarget{method-MockShinySession-.scheduleTask}{}}}
|
||||
\if{html}{\out{<a id="method-.scheduleTask"></a>}}
|
||||
\if{latex}{\out{\hypertarget{method-.scheduleTask}{}}}
|
||||
\subsection{Method \code{.scheduleTask()}}{
|
||||
An internal method which shouldn't be used by others.
|
||||
Schedules \code{callback} for execution after some number of \code{millis}
|
||||
@@ -301,8 +301,8 @@ milliseconds.
|
||||
}
|
||||
}
|
||||
\if{html}{\out{<hr>}}
|
||||
\if{html}{\out{<a id="method-MockShinySession-elapse"></a>}}
|
||||
\if{latex}{\out{\hypertarget{method-MockShinySession-elapse}{}}}
|
||||
\if{html}{\out{<a id="method-elapse"></a>}}
|
||||
\if{latex}{\out{\hypertarget{method-elapse}{}}}
|
||||
\subsection{Method \code{elapse()}}{
|
||||
Simulate the passing of time by the given number of milliseconds.
|
||||
\subsection{Usage}{
|
||||
@@ -318,8 +318,8 @@ Simulate the passing of time by the given number of milliseconds.
|
||||
}
|
||||
}
|
||||
\if{html}{\out{<hr>}}
|
||||
\if{html}{\out{<a id="method-MockShinySession-.now"></a>}}
|
||||
\if{latex}{\out{\hypertarget{method-MockShinySession-.now}{}}}
|
||||
\if{html}{\out{<a id="method-.now"></a>}}
|
||||
\if{latex}{\out{\hypertarget{method-.now}{}}}
|
||||
\subsection{Method \code{.now()}}{
|
||||
An internal method which shouldn't be used by others.
|
||||
\subsection{Usage}{
|
||||
@@ -331,8 +331,8 @@ Elapsed time in milliseconds.
|
||||
}
|
||||
}
|
||||
\if{html}{\out{<hr>}}
|
||||
\if{html}{\out{<a id="method-MockShinySession-defineOutput"></a>}}
|
||||
\if{latex}{\out{\hypertarget{method-MockShinySession-defineOutput}{}}}
|
||||
\if{html}{\out{<a id="method-defineOutput"></a>}}
|
||||
\if{latex}{\out{\hypertarget{method-defineOutput}{}}}
|
||||
\subsection{Method \code{defineOutput()}}{
|
||||
An internal method which shouldn't be used by others.
|
||||
Defines an output in a way that sets private$currentOutputName
|
||||
@@ -354,8 +354,8 @@ appropriately.
|
||||
}
|
||||
}
|
||||
\if{html}{\out{<hr>}}
|
||||
\if{html}{\out{<a id="method-MockShinySession-getOutput"></a>}}
|
||||
\if{latex}{\out{\hypertarget{method-MockShinySession-getOutput}{}}}
|
||||
\if{html}{\out{<a id="method-getOutput"></a>}}
|
||||
\if{latex}{\out{\hypertarget{method-getOutput}{}}}
|
||||
\subsection{Method \code{getOutput()}}{
|
||||
An internal method which shouldn't be used by others. Forces
|
||||
evaluation of any reactive dependencies of the output function.
|
||||
@@ -376,8 +376,8 @@ output.
|
||||
}
|
||||
}
|
||||
\if{html}{\out{<hr>}}
|
||||
\if{html}{\out{<a id="method-MockShinySession-ns"></a>}}
|
||||
\if{latex}{\out{\hypertarget{method-MockShinySession-ns}{}}}
|
||||
\if{html}{\out{<a id="method-ns"></a>}}
|
||||
\if{latex}{\out{\hypertarget{method-ns}{}}}
|
||||
\subsection{Method \code{ns()}}{
|
||||
Returns the given id prefixed by this namespace's id.
|
||||
\subsection{Usage}{
|
||||
@@ -396,8 +396,8 @@ The id with a namespace prefix.
|
||||
}
|
||||
}
|
||||
\if{html}{\out{<hr>}}
|
||||
\if{html}{\out{<a id="method-MockShinySession-flushReact"></a>}}
|
||||
\if{latex}{\out{\hypertarget{method-MockShinySession-flushReact}{}}}
|
||||
\if{html}{\out{<a id="method-flushReact"></a>}}
|
||||
\if{latex}{\out{\hypertarget{method-flushReact}{}}}
|
||||
\subsection{Method \code{flushReact()}}{
|
||||
Trigger a reactive flush right now.
|
||||
\subsection{Usage}{
|
||||
@@ -406,8 +406,8 @@ Trigger a reactive flush right now.
|
||||
|
||||
}
|
||||
\if{html}{\out{<hr>}}
|
||||
\if{html}{\out{<a id="method-MockShinySession-makeScope"></a>}}
|
||||
\if{latex}{\out{\hypertarget{method-MockShinySession-makeScope}{}}}
|
||||
\if{html}{\out{<a id="method-makeScope"></a>}}
|
||||
\if{latex}{\out{\hypertarget{method-makeScope}{}}}
|
||||
\subsection{Method \code{makeScope()}}{
|
||||
Create and return a namespace-specific session proxy.
|
||||
\subsection{Usage}{
|
||||
@@ -426,8 +426,8 @@ A new session proxy.
|
||||
}
|
||||
}
|
||||
\if{html}{\out{<hr>}}
|
||||
\if{html}{\out{<a id="method-MockShinySession-setEnv"></a>}}
|
||||
\if{latex}{\out{\hypertarget{method-MockShinySession-setEnv}{}}}
|
||||
\if{html}{\out{<a id="method-setEnv"></a>}}
|
||||
\if{latex}{\out{\hypertarget{method-setEnv}{}}}
|
||||
\subsection{Method \code{setEnv()}}{
|
||||
Set the environment associated with a testServer() call, but
|
||||
only if it has not previously been set. This ensures that only the
|
||||
@@ -449,8 +449,8 @@ The provided \code{env}.
|
||||
}
|
||||
}
|
||||
\if{html}{\out{<hr>}}
|
||||
\if{html}{\out{<a id="method-MockShinySession-setReturned"></a>}}
|
||||
\if{latex}{\out{\hypertarget{method-MockShinySession-setReturned}{}}}
|
||||
\if{html}{\out{<a id="method-setReturned"></a>}}
|
||||
\if{latex}{\out{\hypertarget{method-setReturned}{}}}
|
||||
\subsection{Method \code{setReturned()}}{
|
||||
Set the value returned by the module call and proactively
|
||||
flush. Note that this method may be called multiple times if modules
|
||||
@@ -472,8 +472,8 @@ The provided \code{value}.
|
||||
}
|
||||
}
|
||||
\if{html}{\out{<hr>}}
|
||||
\if{html}{\out{<a id="method-MockShinySession-getReturned"></a>}}
|
||||
\if{latex}{\out{\hypertarget{method-MockShinySession-getReturned}{}}}
|
||||
\if{html}{\out{<a id="method-getReturned"></a>}}
|
||||
\if{latex}{\out{\hypertarget{method-getReturned}{}}}
|
||||
\subsection{Method \code{getReturned()}}{
|
||||
Get the value returned by the module call.
|
||||
\subsection{Usage}{
|
||||
@@ -485,8 +485,8 @@ The value returned by the module call
|
||||
}
|
||||
}
|
||||
\if{html}{\out{<hr>}}
|
||||
\if{html}{\out{<a id="method-MockShinySession-genId"></a>}}
|
||||
\if{latex}{\out{\hypertarget{method-MockShinySession-genId}{}}}
|
||||
\if{html}{\out{<a id="method-genId"></a>}}
|
||||
\if{latex}{\out{\hypertarget{method-genId}{}}}
|
||||
\subsection{Method \code{genId()}}{
|
||||
Generate a distinct character identifier for use as a proxy
|
||||
namespace.
|
||||
@@ -499,8 +499,8 @@ A character identifier unique to the current session.
|
||||
}
|
||||
}
|
||||
\if{html}{\out{<hr>}}
|
||||
\if{html}{\out{<a id="method-MockShinySession-rootScope"></a>}}
|
||||
\if{latex}{\out{\hypertarget{method-MockShinySession-rootScope}{}}}
|
||||
\if{html}{\out{<a id="method-rootScope"></a>}}
|
||||
\if{latex}{\out{\hypertarget{method-rootScope}{}}}
|
||||
\subsection{Method \code{rootScope()}}{
|
||||
Provides a way to access the root \code{MockShinySession} from
|
||||
any descendant proxy.
|
||||
@@ -513,8 +513,8 @@ The root \code{MockShinySession}.
|
||||
}
|
||||
}
|
||||
\if{html}{\out{<hr>}}
|
||||
\if{html}{\out{<a id="method-MockShinySession-unhandledError"></a>}}
|
||||
\if{latex}{\out{\hypertarget{method-MockShinySession-unhandledError}{}}}
|
||||
\if{html}{\out{<a id="method-unhandledError"></a>}}
|
||||
\if{latex}{\out{\hypertarget{method-unhandledError}{}}}
|
||||
\subsection{Method \code{unhandledError()}}{
|
||||
Called by observers when a reactive expression errors.
|
||||
\subsection{Usage}{
|
||||
@@ -530,8 +530,8 @@ Called by observers when a reactive expression errors.
|
||||
}
|
||||
}
|
||||
\if{html}{\out{<hr>}}
|
||||
\if{html}{\out{<a id="method-MockShinySession-freezeValue"></a>}}
|
||||
\if{latex}{\out{\hypertarget{method-MockShinySession-freezeValue}{}}}
|
||||
\if{html}{\out{<a id="method-freezeValue"></a>}}
|
||||
\if{latex}{\out{\hypertarget{method-freezeValue}{}}}
|
||||
\subsection{Method \code{freezeValue()}}{
|
||||
Freeze a value until the flush cycle completes.
|
||||
\subsection{Usage}{
|
||||
@@ -549,8 +549,8 @@ Freeze a value until the flush cycle completes.
|
||||
}
|
||||
}
|
||||
\if{html}{\out{<hr>}}
|
||||
\if{html}{\out{<a id="method-MockShinySession-onSessionEnded"></a>}}
|
||||
\if{latex}{\out{\hypertarget{method-MockShinySession-onSessionEnded}{}}}
|
||||
\if{html}{\out{<a id="method-onSessionEnded"></a>}}
|
||||
\if{latex}{\out{\hypertarget{method-onSessionEnded}{}}}
|
||||
\subsection{Method \code{onSessionEnded()}}{
|
||||
Registers the given callback to be invoked when the session
|
||||
is closed (i.e. the connection to the client has been severed). The
|
||||
@@ -570,8 +570,8 @@ guaranteed.
|
||||
}
|
||||
}
|
||||
\if{html}{\out{<hr>}}
|
||||
\if{html}{\out{<a id="method-MockShinySession-registerDownload"></a>}}
|
||||
\if{latex}{\out{\hypertarget{method-MockShinySession-registerDownload}{}}}
|
||||
\if{html}{\out{<a id="method-registerDownload"></a>}}
|
||||
\if{latex}{\out{\hypertarget{method-registerDownload}{}}}
|
||||
\subsection{Method \code{registerDownload()}}{
|
||||
Associated a downloadable file with the session.
|
||||
\subsection{Usage}{
|
||||
@@ -598,8 +598,8 @@ function.)}
|
||||
}
|
||||
}
|
||||
\if{html}{\out{<hr>}}
|
||||
\if{html}{\out{<a id="method-MockShinySession-getCurrentOutputInfo"></a>}}
|
||||
\if{latex}{\out{\hypertarget{method-MockShinySession-getCurrentOutputInfo}{}}}
|
||||
\if{html}{\out{<a id="method-getCurrentOutputInfo"></a>}}
|
||||
\if{latex}{\out{\hypertarget{method-getCurrentOutputInfo}{}}}
|
||||
\subsection{Method \code{getCurrentOutputInfo()}}{
|
||||
Get information about the output that is currently being
|
||||
executed.
|
||||
@@ -614,8 +614,8 @@ output, or \code{NULL} if no output is currently executing.
|
||||
}
|
||||
}
|
||||
\if{html}{\out{<hr>}}
|
||||
\if{html}{\out{<a id="method-MockShinySession-clone"></a>}}
|
||||
\if{latex}{\out{\hypertarget{method-MockShinySession-clone}{}}}
|
||||
\if{html}{\out{<a id="method-clone"></a>}}
|
||||
\if{latex}{\out{\hypertarget{method-clone}{}}}
|
||||
\subsection{Method \code{clone()}}{
|
||||
The objects of this class are cloneable with this method.
|
||||
\subsection{Usage}{
|
||||
|
||||
@@ -62,19 +62,19 @@ shinyApp(ui, server)
|
||||
\section{Methods}{
|
||||
\subsection{Public methods}{
|
||||
\itemize{
|
||||
\item \href{#method-Progress-new}{\code{Progress$new()}}
|
||||
\item \href{#method-Progress-set}{\code{Progress$set()}}
|
||||
\item \href{#method-Progress-inc}{\code{Progress$inc()}}
|
||||
\item \href{#method-Progress-getMin}{\code{Progress$getMin()}}
|
||||
\item \href{#method-Progress-getMax}{\code{Progress$getMax()}}
|
||||
\item \href{#method-Progress-getValue}{\code{Progress$getValue()}}
|
||||
\item \href{#method-Progress-close}{\code{Progress$close()}}
|
||||
\item \href{#method-Progress-clone}{\code{Progress$clone()}}
|
||||
\item \href{#method-new}{\code{Progress$new()}}
|
||||
\item \href{#method-set}{\code{Progress$set()}}
|
||||
\item \href{#method-inc}{\code{Progress$inc()}}
|
||||
\item \href{#method-getMin}{\code{Progress$getMin()}}
|
||||
\item \href{#method-getMax}{\code{Progress$getMax()}}
|
||||
\item \href{#method-getValue}{\code{Progress$getValue()}}
|
||||
\item \href{#method-close}{\code{Progress$close()}}
|
||||
\item \href{#method-clone}{\code{Progress$clone()}}
|
||||
}
|
||||
}
|
||||
\if{html}{\out{<hr>}}
|
||||
\if{html}{\out{<a id="method-Progress-new"></a>}}
|
||||
\if{latex}{\out{\hypertarget{method-Progress-new}{}}}
|
||||
\if{html}{\out{<a id="method-new"></a>}}
|
||||
\if{latex}{\out{\hypertarget{method-new}{}}}
|
||||
\subsection{Method \code{new()}}{
|
||||
Creates a new progress panel (but does not display it).
|
||||
\subsection{Usage}{
|
||||
@@ -107,8 +107,8 @@ is for backward-compatibility).}
|
||||
}
|
||||
}
|
||||
\if{html}{\out{<hr>}}
|
||||
\if{html}{\out{<a id="method-Progress-set"></a>}}
|
||||
\if{latex}{\out{\hypertarget{method-Progress-set}{}}}
|
||||
\if{html}{\out{<a id="method-set"></a>}}
|
||||
\if{latex}{\out{\hypertarget{method-set}{}}}
|
||||
\subsection{Method \code{set()}}{
|
||||
Updates the progress panel. When called the first time, the
|
||||
progress panel is displayed.
|
||||
@@ -135,8 +135,8 @@ relative to \code{message}.}
|
||||
}
|
||||
}
|
||||
\if{html}{\out{<hr>}}
|
||||
\if{html}{\out{<a id="method-Progress-inc"></a>}}
|
||||
\if{latex}{\out{\hypertarget{method-Progress-inc}{}}}
|
||||
\if{html}{\out{<a id="method-inc"></a>}}
|
||||
\if{latex}{\out{\hypertarget{method-inc}{}}}
|
||||
\subsection{Method \code{inc()}}{
|
||||
Like \code{set}, this updates the progress panel. The difference
|
||||
is that \code{inc} increases the progress bar by \code{amount}, instead of
|
||||
@@ -163,8 +163,8 @@ relative to \code{message}.}
|
||||
}
|
||||
}
|
||||
\if{html}{\out{<hr>}}
|
||||
\if{html}{\out{<a id="method-Progress-getMin"></a>}}
|
||||
\if{latex}{\out{\hypertarget{method-Progress-getMin}{}}}
|
||||
\if{html}{\out{<a id="method-getMin"></a>}}
|
||||
\if{latex}{\out{\hypertarget{method-getMin}{}}}
|
||||
\subsection{Method \code{getMin()}}{
|
||||
Returns the minimum value.
|
||||
\subsection{Usage}{
|
||||
@@ -173,8 +173,8 @@ Returns the minimum value.
|
||||
|
||||
}
|
||||
\if{html}{\out{<hr>}}
|
||||
\if{html}{\out{<a id="method-Progress-getMax"></a>}}
|
||||
\if{latex}{\out{\hypertarget{method-Progress-getMax}{}}}
|
||||
\if{html}{\out{<a id="method-getMax"></a>}}
|
||||
\if{latex}{\out{\hypertarget{method-getMax}{}}}
|
||||
\subsection{Method \code{getMax()}}{
|
||||
Returns the maximum value.
|
||||
\subsection{Usage}{
|
||||
@@ -183,8 +183,8 @@ Returns the maximum value.
|
||||
|
||||
}
|
||||
\if{html}{\out{<hr>}}
|
||||
\if{html}{\out{<a id="method-Progress-getValue"></a>}}
|
||||
\if{latex}{\out{\hypertarget{method-Progress-getValue}{}}}
|
||||
\if{html}{\out{<a id="method-getValue"></a>}}
|
||||
\if{latex}{\out{\hypertarget{method-getValue}{}}}
|
||||
\subsection{Method \code{getValue()}}{
|
||||
Returns the current value.
|
||||
\subsection{Usage}{
|
||||
@@ -193,8 +193,8 @@ Returns the current value.
|
||||
|
||||
}
|
||||
\if{html}{\out{<hr>}}
|
||||
\if{html}{\out{<a id="method-Progress-close"></a>}}
|
||||
\if{latex}{\out{\hypertarget{method-Progress-close}{}}}
|
||||
\if{html}{\out{<a id="method-close"></a>}}
|
||||
\if{latex}{\out{\hypertarget{method-close}{}}}
|
||||
\subsection{Method \code{close()}}{
|
||||
Removes the progress panel. Future calls to \code{set} and
|
||||
\code{close} will be ignored.
|
||||
@@ -204,8 +204,8 @@ Removes the progress panel. Future calls to \code{set} and
|
||||
|
||||
}
|
||||
\if{html}{\out{<hr>}}
|
||||
\if{html}{\out{<a id="method-Progress-clone"></a>}}
|
||||
\if{latex}{\out{\hypertarget{method-Progress-clone}{}}}
|
||||
\if{html}{\out{<a id="method-clone"></a>}}
|
||||
\if{latex}{\out{\hypertarget{method-clone}{}}}
|
||||
\subsection{Method \code{clone()}}{
|
||||
The objects of this class are cloneable with this method.
|
||||
\subsection{Usage}{
|
||||
|
||||
@@ -40,9 +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="sourceCode">}}\preformatted{r <- reactive(\{ input$x * input$y \}) \%>\%
|
||||
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 \}) \%>\%
|
||||
bindCache(input$x, input$y)
|
||||
}\if{html}{\out{</div>}}
|
||||
|
||||
@@ -65,9 +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="sourceCode">}}\preformatted{r <- reactive(\{ compute(bigdata()) \} \%>\%
|
||||
data object, the cached reactive only needs to hash the timestamp.\if{html}{\out{<div class="NA">}}\preformatted{r <- reactive(\{ compute(bigdata()) \} \%>\%
|
||||
bindCache(\{ extract_most_recent_time(bigdata()) \})
|
||||
}\if{html}{\out{</div>}}
|
||||
|
||||
@@ -109,9 +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="sourceCode">}}\preformatted{r <- reactive(\{ input$x * input$y \}) \%>\%
|
||||
example, suppose we have this cached reactive:\if{html}{\out{<div class="NA">}}\preformatted{r <- reactive(\{ input$x * input$y \}) \%>\%
|
||||
bindCache(input$x, input$y)
|
||||
}\if{html}{\out{</div>}}
|
||||
|
||||
@@ -160,30 +154,24 @@ persists beyond the current R session.
|
||||
To use different settings for an application-scoped cache, you can call
|
||||
\code{\link[=shinyOptions]{shinyOptions()}} at the top of your app.R, server.R, or
|
||||
global.R. For example, this will create a cache with 500 MB of space
|
||||
instead of the default 200 MB:
|
||||
|
||||
\if{html}{\out{<div class="sourceCode">}}\preformatted{shinyOptions(cache = cachem::cache_mem(max_size = 500e6))
|
||||
}\if{html}{\out{</div>}}
|
||||
instead of the default 200 MB:\preformatted{shinyOptions(cache = cachem::cache_mem(max_size = 500e6))
|
||||
}
|
||||
|
||||
To use different settings for a session-scoped cache, you can set
|
||||
\code{self$cache} at the top of your server function. By default, it will create
|
||||
a 200 MB memory cache for each session, but you can replace it with
|
||||
something different. To use the session-scoped cache, you must also call
|
||||
\code{bindCache()} with \code{cache="session"}. This will create a 100 MB cache for
|
||||
the session:
|
||||
|
||||
\if{html}{\out{<div class="sourceCode">}}\preformatted{function(input, output, session) \{
|
||||
the session:\preformatted{function(input, output, session) \{
|
||||
session$cache <- cachem::cache_mem(max_size = 100e6)
|
||||
...
|
||||
\}
|
||||
}\if{html}{\out{</div>}}
|
||||
}
|
||||
|
||||
If you want to use a cache that is shared across multiple R processes, you
|
||||
can use a \code{\link[cachem:cache_disk]{cachem::cache_disk()}}. You can create a application-level shared
|
||||
cache by putting this at the top of your app.R, server.R, or global.R:
|
||||
|
||||
\if{html}{\out{<div class="sourceCode">}}\preformatted{shinyOptions(cache = cachem::cache_disk(file.path(dirname(tempdir()), "myapp-cache"))
|
||||
}\if{html}{\out{</div>}}
|
||||
cache by putting this at the top of your app.R, server.R, or global.R:\preformatted{shinyOptions(cache = cachem::cache_disk(file.path(dirname(tempdir()), "myapp-cache"))
|
||||
}
|
||||
|
||||
This will create a subdirectory in your system temp directory named
|
||||
\code{myapp-cache} (replace \code{myapp-cache} with a unique name of
|
||||
@@ -193,10 +181,8 @@ stops of the R process, as long as you do not reboot.
|
||||
|
||||
To have the cache persist even across multiple reboots, you can create the
|
||||
cache in a location outside of the temp directory. For example, it could
|
||||
be a subdirectory of the application:
|
||||
|
||||
\if{html}{\out{<div class="sourceCode">}}\preformatted{shinyOptions(cache = cachem::cache_disk("./myapp-cache"))
|
||||
}\if{html}{\out{</div>}}
|
||||
be a subdirectory of the application:\preformatted{shinyOptions(cache = cachem::cache_disk("./myapp-cache"))
|
||||
}
|
||||
|
||||
In this case, resetting the cache will have to be done manually, by deleting
|
||||
the directory.
|
||||
@@ -258,14 +244,12 @@ 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{htmlwidgets::shinyRenderWidget()}, if you've authored an htmlwidget) in
|
||||
\code{\link[htmlwidgets:htmlwidgets-shiny]{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:
|
||||
|
||||
\if{html}{\out{<div class="sourceCode">}}\preformatted{output$x1 <- renderText(\{ input$x \}) \%>\% bindCache(input$x)
|
||||
The potential problem is a cache collision. Consider the following:\preformatted{output$x1 <- renderText(\{ input$x \}) \%>\% bindCache(input$x)
|
||||
output$x2 <- renderText(\{ input$x * 2 \}) \%>\% bindCache(input$x)
|
||||
}\if{html}{\out{</div>}}
|
||||
}
|
||||
|
||||
Both \code{output$x1} and \code{output$x2} use \code{input$x} as part of their cache key,
|
||||
but if it were the only thing used in the cache key, then the two outputs
|
||||
@@ -274,9 +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="sourceCode">}}\preformatted{r <- renderText(\{ input$x \})
|
||||
\code{extractCacheHint()}:\if{html}{\out{<div class="NA">}}\preformatted{r <- renderText(\{ input$x \})
|
||||
shiny:::extractCacheHint(r)
|
||||
}\if{html}{\out{</div>}}
|
||||
|
||||
@@ -293,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[=createRenderFunction]{createRenderFunction()}}
|
||||
expression in another function, before passing it to \code{\link[=markRenderFunction]{markRenderFunction()}}
|
||||
(instead of \code{\link[=createRenderFunction]{createRenderFunction()}}). Because the user code is wrapped in
|
||||
another function, \code{createRenderFunction()} is not able to automatically
|
||||
another function, \code{markRenderFunction()} is not able to automatically
|
||||
extract the user-provided code and use it in the cache key. Instead,
|
||||
\code{renderPrint} calls \code{createRenderFunction()}, it explicitly passes along a
|
||||
\code{renderPrint} calls \code{markRenderFunction()}, 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
|
||||
@@ -307,22 +289,20 @@ that may influence the final value.
|
||||
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:
|
||||
cache hint, pass it to \code{shinyRenderWidget}. For example:\preformatted{renderMyWidget <- function(expr) \{
|
||||
expr <- substitute(expr)
|
||||
|
||||
\if{html}{\out{<div class="sourceCode">}}\preformatted{renderMyWidget <- function(expr) \{
|
||||
q <- rlang::enquo0(expr)
|
||||
|
||||
htmlwidgets::shinyRenderWidget(
|
||||
q,
|
||||
htmlwidgets::shinyRenderWidget(expr,
|
||||
myWidgetOutput,
|
||||
quoted = TRUE,
|
||||
cacheHint = list(label = "myWidget", userQuo = q)
|
||||
env = parent.frame(),
|
||||
cacheHint = list(label = "myWidget", userExpr = expr)
|
||||
)
|
||||
\}
|
||||
}\if{html}{\out{</div>}}
|
||||
}
|
||||
|
||||
If your \code{render} function sets any internal state, you may find it useful
|
||||
in your call to \code{\link[=createRenderFunction]{createRenderFunction()}} to use
|
||||
in your call to \code{\link[=createRenderFunction]{createRenderFunction()}} or \code{\link[=markRenderFunction]{markRenderFunction()}} 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
|
||||
@@ -341,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()}}{
|
||||
@@ -350,10 +330,8 @@ For developers of such code, they should call \code{\link[=createRenderFunction]
|
||||
|
||||
When \code{bindCache()} is used with \code{renderPlot()}, the \code{height} and \code{width}
|
||||
passed to the original \code{renderPlot()} are ignored. They are superseded by
|
||||
\code{sizePolicy} argument passed to `bindCache. The default is:
|
||||
|
||||
\if{html}{\out{<div class="sourceCode">}}\preformatted{sizePolicy = sizeGrowthRatio(width = 400, height = 400, growthRate = 1.2)
|
||||
}\if{html}{\out{</div>}}
|
||||
\code{sizePolicy} argument passed to `bindCache. The default is:\preformatted{sizePolicy = sizeGrowthRatio(width = 400, height = 400, growthRate = 1.2)
|
||||
}
|
||||
|
||||
\code{sizePolicy} must be a function that takes a two-element numeric vector as
|
||||
input, representing the width and height of the \verb{<img>} element in the
|
||||
|
||||
@@ -171,9 +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="sourceCode">}}\preformatted{r <- reactive(\{
|
||||
\code{bindCache()}, then \code{bindEvent()}. For example:\if{html}{\out{<div class="NA">}}\preformatted{r <- reactive(\{
|
||||
Sys.sleep(2) # Pretend this is an expensive computation
|
||||
input$x * input$y
|
||||
\}) \%>\%
|
||||
|
||||
@@ -28,7 +28,7 @@ This will be used as the lang in the \code{<html>} tag, as in \code{<html lang="
|
||||
The default (NULL) results in an empty string.}
|
||||
}
|
||||
\value{
|
||||
A UI definition that can be passed to the \link{shinyUI} function.
|
||||
A UI defintion that can be passed to the \link{shinyUI} function.
|
||||
}
|
||||
\description{
|
||||
Create a Shiny UI page that loads the CSS and JavaScript for
|
||||
|
||||
@@ -49,3 +49,4 @@ These functions give control over the \code{click}, \code{dblClick} and
|
||||
\seealso{
|
||||
\code{\link[=brushOpts]{brushOpts()}} for brushing events.
|
||||
}
|
||||
\keyword{internal}
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
% Generated by roxygen2: do not edit by hand
|
||||
% Please edit documentation in R/shinywrappers.R, R/utils-lang.R
|
||||
% Please edit documentation in R/shinywrappers.R
|
||||
\name{createRenderFunction}
|
||||
\alias{createRenderFunction}
|
||||
\alias{quoToFunction}
|
||||
\alias{installExprFunction}
|
||||
\title{Implement custom render functions}
|
||||
\title{Implement render functions}
|
||||
\usage{
|
||||
createRenderFunction(
|
||||
func,
|
||||
@@ -15,19 +13,6 @@ 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
|
||||
@@ -70,85 +55,23 @@ 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{
|
||||
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).
|
||||
This function is a wrapper for \code{\link[=markRenderFunction]{markRenderFunction()}} which provides support
|
||||
for async computation via promises.
|
||||
}
|
||||
\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 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)
|
||||
# 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")
|
||||
|
||||
createRenderFunction(
|
||||
func,
|
||||
@@ -160,36 +83,11 @@ renderTripleLegacy <- function(expr, env = parent.frame(), quoted = FALSE) {
|
||||
}
|
||||
|
||||
# Test render function from the console
|
||||
reactiveConsole(TRUE)
|
||||
|
||||
v <- reactiveVal("basic")
|
||||
r <- renderTriple({ v() })
|
||||
a <- 1
|
||||
r <- renderTriple({ a + 1 })
|
||||
a <- 2
|
||||
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)
|
||||
|
||||
}
|
||||
\seealso{
|
||||
\code{\link[=quoToFunction]{quoToFunction()}}, \code{\link[=markRenderFunction]{markRenderFunction()}}.
|
||||
}
|
||||
|
||||
@@ -71,7 +71,7 @@ registered \code{devmode_default} value will be used.}
|
||||
\code{TRUE} and the specified option is not set in \code{\link[=options]{options()}}.}
|
||||
}
|
||||
\description{
|
||||
\ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#experimental}{\figure{lifecycle-experimental.svg}{options: alt='[Experimental]'}}}{\strong{[Experimental]}}
|
||||
\lifecycle{experimental}
|
||||
|
||||
Developer Mode enables a number of \code{\link[=options]{options()}} to make a developer's life
|
||||
easier, like enabling non-minified JS and printing messages about
|
||||
@@ -84,26 +84,24 @@ Please see the function descriptions for more details.
|
||||
}
|
||||
\section{Functions}{
|
||||
\itemize{
|
||||
\item \code{devmode()}: Function to set two options to enable/disable Shiny
|
||||
\item \code{devmode}: Function to set two options to enable/disable Shiny
|
||||
Developer Mode and Developer messages
|
||||
|
||||
\item \code{in_devmode()}: Determines if Shiny is in Developer Mode. If the
|
||||
\item \code{in_devmode}: Determines if Shiny is in Developer Mode. If the
|
||||
\code{getOption("shiny.devmode")} is set to \code{TRUE} and not in testing inside
|
||||
\code{testthat}, then Shiny Developer Mode is enabled.
|
||||
|
||||
\item \code{with_devmode()}: Temporarily set Shiny Developer Mode and Developer
|
||||
\item \code{with_devmode}: Temporarily set Shiny Developer Mode and Developer
|
||||
message verbosity
|
||||
|
||||
\item \code{devmode_inform()}: If Shiny Developer Mode and verbosity are enabled,
|
||||
\item \code{devmode_inform}: If Shiny Developer Mode and verbosity are enabled,
|
||||
displays a message once every 8 hrs (by default)
|
||||
|
||||
\item \code{register_devmode_option()}: Registers a Shiny Developer Mode option with an updated
|
||||
\item \code{register_devmode_option}: Registers a Shiny Developer Mode option with an updated
|
||||
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="sourceCode 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="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)`",
|
||||
@@ -128,9 +126,7 @@ register_devmode_option(
|
||||
Other known, non-Shiny Developer Mode options:
|
||||
\itemize{
|
||||
\item Sass:
|
||||
}
|
||||
|
||||
\if{html}{\out{<div class="sourceCode r">}}\preformatted{# Display the full stack trace when errors occur during Shiny app execution
|
||||
}\if{html}{\out{<div class="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)`",
|
||||
@@ -138,7 +134,7 @@ register_devmode_option(
|
||||
)
|
||||
}\if{html}{\out{</div>}}
|
||||
|
||||
\item \code{get_devmode_option()}: Provides a consistent way to change the expected
|
||||
\item \code{get_devmode_option}: Provides a consistent way to change the expected
|
||||
\code{\link[=getOption]{getOption()}} behavior when Developer Mode is enabled. This method is very
|
||||
similar to \code{\link[=getOption]{getOption()}} where the globally set option takes precedence.
|
||||
See section "Avoiding direct dependency on shiny" for
|
||||
@@ -148,8 +144,8 @@ See section "Avoiding direct dependency on shiny" for
|
||||
\code{register_devmode_option()} to avoid supplying the same \code{devmode_default}
|
||||
and \code{devmode_message} values throughout your package. (This requires a
|
||||
\pkg{shiny} dependency.)
|
||||
|
||||
}}
|
||||
|
||||
\section{Avoiding direct dependency on shiny}{
|
||||
|
||||
|
||||
@@ -169,9 +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="sourceCode r">}}\preformatted{in_devmode <- function() \{
|
||||
\code{testthat} is not testing.\if{html}{\out{<div class="r">}}\preformatted{in_devmode <- function() \{
|
||||
isTRUE(getOption("shiny.devmode", FALSE)) &&
|
||||
!identical(Sys.getenv("TESTTHAT"), "true")
|
||||
\}
|
||||
@@ -207,9 +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="sourceCode 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="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))
|
||||
|
||||
@@ -40,7 +40,7 @@ deleted from disk. If \code{FALSE} (the default), it will do nothing when
|
||||
finalized.}
|
||||
|
||||
\item{missing}{A value to return when \code{get(key)} is called but the key is not
|
||||
present in the cache. The default is a \code{\link[cachem:key_missing]{key_missing()}} object. It is
|
||||
present in the cache. The default is a \code{\link[cachem:reexports]{key_missing()}} object. It is
|
||||
actually an expression that is evaluated each time there is a cache miss.
|
||||
See section Missing keys for more information.}
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
\alias{downloadHandler}
|
||||
\title{File Downloads}
|
||||
\usage{
|
||||
downloadHandler(filename, content, contentType = NULL, outputArgs = list())
|
||||
downloadHandler(filename, content, contentType = NA, outputArgs = list())
|
||||
}
|
||||
\arguments{
|
||||
\item{filename}{A string of the filename, including extension, that the
|
||||
@@ -19,9 +19,9 @@ function.)}
|
||||
|
||||
\item{contentType}{A string of the download's
|
||||
\href{https://en.wikipedia.org/wiki/Internet_media_type}{content type}, for
|
||||
example \code{"text/csv"} or \code{"image/png"}. If \code{NULL}, the content type
|
||||
will be guessed based on the filename extension, or
|
||||
\code{application/octet-stream} if the extension is unknown.}
|
||||
example \code{"text/csv"} or \code{"image/png"}. If \code{NULL} or
|
||||
\code{NA}, the content type will be guessed based on the filename
|
||||
extension, or \code{application/octet-stream} if the extension is unknown.}
|
||||
|
||||
\item{outputArgs}{A list of arguments to be passed through to the implicit
|
||||
call to \code{\link[=downloadButton]{downloadButton()}} when \code{downloadHandler} is used
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
% Generated by roxygen2: do not edit by hand
|
||||
% Please edit documentation in R/utils-lang.R
|
||||
% Please edit documentation in R/utils.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 quosure.}
|
||||
\item{expr}{A quoted or unquoted expression, or a function.}
|
||||
|
||||
\item{env}{The desired environment for the function. Defaults to the
|
||||
calling environment two steps back.}
|
||||
@@ -15,14 +15,47 @@ calling environment two steps back.}
|
||||
\item{quoted}{Is the expression quoted?}
|
||||
}
|
||||
\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[=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).
|
||||
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.
|
||||
}
|
||||
\details{
|
||||
Similar to \code{\link[=installExprFunction]{installExprFunction()}} but doesn't register debug hooks.
|
||||
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.
|
||||
}
|
||||
\seealso{
|
||||
\code{\link[=installExprFunction]{installExprFunction()}} for the modern approach to converting an expression to a function
|
||||
\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"
|
||||
}
|
||||
\keyword{internal}
|
||||
|
||||
@@ -11,8 +11,7 @@ fileInput(
|
||||
accept = NULL,
|
||||
width = NULL,
|
||||
buttonLabel = "Browse...",
|
||||
placeholder = "No file selected",
|
||||
capture = NULL
|
||||
placeholder = "No file selected"
|
||||
)
|
||||
}
|
||||
\arguments{
|
||||
@@ -43,19 +42,6 @@ see \code{\link[=validateCssUnit]{validateCssUnit()}}.}
|
||||
object.}
|
||||
|
||||
\item{placeholder}{The text to show before a file has been uploaded.}
|
||||
|
||||
\item{capture}{What source to use for capturing image, audio or video data.
|
||||
This attribute facilitates user access to a device's media capture
|
||||
mechanism, such as a camera, or microphone, from within a file upload
|
||||
control.
|
||||
|
||||
A value of \code{user} indicates that the user-facing camera and/or microphone
|
||||
should be used. A value of \code{environment} specifies that the outward-facing
|
||||
camera and/or microphone should be used.
|
||||
|
||||
By default on most phones, this will accept still photos or video. For
|
||||
still photos only, also use \code{accept="image/*"}. For video only, use
|
||||
\code{accept="video/*"}.}
|
||||
}
|
||||
\description{
|
||||
Create a file upload control that can be used to upload one or more files.
|
||||
|
||||
@@ -28,7 +28,7 @@ This will be used as the lang in the \code{<html>} tag, as in \code{<html lang="
|
||||
The default (NULL) results in an empty string.}
|
||||
}
|
||||
\value{
|
||||
A UI definition that can be passed to the \link{shinyUI} function.
|
||||
A UI defintion that can be passed to the \link{shinyUI} function.
|
||||
}
|
||||
\description{
|
||||
Functions for creating fixed page layouts. A fixed page layout consists of
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user